Class skill injection now points to authored entries
This commit is contained in:
+46
-16
@@ -2466,6 +2466,38 @@ func normalizeKeyIdentity(text string) string {
|
|||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resolveCanonicalDatasetKey(key string, keyToID map[string]int) string {
|
||||||
|
key = strings.TrimSpace(key)
|
||||||
|
if key == "" {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
if _, ok := keyToID[key]; ok {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
prefix, suffix, ok := strings.Cut(key, ":")
|
||||||
|
if !ok || prefix == "" || suffix == "" {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
normalizedSuffix := normalizeKeyIdentity(suffix)
|
||||||
|
match := ""
|
||||||
|
for candidate := range keyToID {
|
||||||
|
candidatePrefix, candidateSuffix, ok := strings.Cut(candidate, ":")
|
||||||
|
if !ok || candidatePrefix != prefix {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if normalizeKeyIdentity(candidateSuffix) != normalizedSuffix {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if match == "" || candidate < match {
|
||||||
|
match = candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match != "" {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
func affinityTitleFromKey(featKey, skillName string) string {
|
func affinityTitleFromKey(featKey, skillName string) string {
|
||||||
if strings.HasPrefix(featKey, "feat:partialskillaffinity") {
|
if strings.HasPrefix(featKey, "feat:partialskillaffinity") {
|
||||||
return fmt.Sprintf("Partial Skill Affinity (%s)", skillName)
|
return fmt.Sprintf("Partial Skill Affinity (%s)", skillName)
|
||||||
@@ -2652,8 +2684,8 @@ var (
|
|||||||
{"FeatIndex": map[string]any{"id": "feat:horsemenu"}, "List": "3", "GrantedOnLevel": "1", "OnMenu": "1"},
|
{"FeatIndex": map[string]any{"id": "feat:horsemenu"}, "List": "3", "GrantedOnLevel": "1", "OnMenu": "1"},
|
||||||
}
|
}
|
||||||
classFeatClassSkillShorthandRows = []map[string]any{
|
classFeatClassSkillShorthandRows = []map[string]any{
|
||||||
{"FeatIndex": map[string]any{"id": "masterfeats:skillfocus", "filter": "classskills"}, "List": "1", "GrantedOnLevel": "-1", "OnMenu": "0"},
|
{"FeatIndex": map[string]any{"id": "masterfeats:skill_focus", "filter": "classskills"}, "List": "1", "GrantedOnLevel": "-1", "OnMenu": "0"},
|
||||||
{"FeatIndex": map[string]any{"id": "masterfeats:greaterskillfocus", "filter": "classskills"}, "List": "1", "GrantedOnLevel": "12", "OnMenu": "0"},
|
{"FeatIndex": map[string]any{"id": "masterfeats:greater_skill_focus", "filter": "classskills"}, "List": "1", "GrantedOnLevel": "12", "OnMenu": "0"},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2743,7 +2775,7 @@ func expandClassesFeatRow(row map[string]any, keyToID map[string]int, rowByKey m
|
|||||||
}
|
}
|
||||||
return []map[string]any{cloned}, nil
|
return []map[string]any{cloned}, nil
|
||||||
case strings.HasPrefix(refKey, "masterfeats:"):
|
case strings.HasPrefix(refKey, "masterfeats:"):
|
||||||
masterfeatKey := refKey
|
masterfeatKey := resolveCanonicalDatasetKey(refKey, keyToID)
|
||||||
children, err := expandMasterfeatChildren(masterfeatKey, row, keyToID, rowByKey, classKey)
|
children, err := expandMasterfeatChildren(masterfeatKey, row, keyToID, rowByKey, classKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -2786,6 +2818,7 @@ func expandClassesFeatRow(row map[string]any, keyToID map[string]int, rowByKey m
|
|||||||
}
|
}
|
||||||
|
|
||||||
func expandMasterfeatChildren(masterfeatKey string, row map[string]any, keyToID map[string]int, rowByKey map[string]map[string]any, classKey string) ([]map[string]any, error) {
|
func expandMasterfeatChildren(masterfeatKey string, row map[string]any, keyToID map[string]int, rowByKey map[string]map[string]any, classKey string) ([]map[string]any, error) {
|
||||||
|
masterfeatKey = resolveCanonicalDatasetKey(masterfeatKey, keyToID)
|
||||||
if _, ok := keyToID[masterfeatKey]; !ok {
|
if _, ok := keyToID[masterfeatKey]; !ok {
|
||||||
return nil, fmt.Errorf("unknown masterfeat reference %q", masterfeatKey)
|
return nil, fmt.Errorf("unknown masterfeat reference %q", masterfeatKey)
|
||||||
}
|
}
|
||||||
@@ -2844,10 +2877,11 @@ func classFeatExpansionCanUseFeat(featRow map[string]any, classKey string, keyTo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func rowRefersToKey(value any, targetKey string, keyToID map[string]int) bool {
|
func rowRefersToKey(value any, targetKey string, keyToID map[string]int) bool {
|
||||||
|
targetKey = resolveCanonicalDatasetKey(targetKey, keyToID)
|
||||||
targetID, hasTargetID := keyToID[targetKey]
|
targetID, hasTargetID := keyToID[targetKey]
|
||||||
switch typed := value.(type) {
|
switch typed := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
text := strings.TrimSpace(typed)
|
text := resolveCanonicalDatasetKey(strings.TrimSpace(typed), keyToID)
|
||||||
if text == "" || text == nullValue || text == "****" {
|
if text == "" || text == nullValue || text == "****" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -2878,6 +2912,7 @@ func rowRefersToKey(value any, targetKey string, keyToID map[string]int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func rowReferencesMasterfeat(row map[string]any, masterfeatKey string, keyToID map[string]int) bool {
|
func rowReferencesMasterfeat(row map[string]any, masterfeatKey string, keyToID map[string]int) bool {
|
||||||
|
masterfeatKey = resolveCanonicalDatasetKey(masterfeatKey, keyToID)
|
||||||
masterID, ok := keyToID[masterfeatKey]
|
masterID, ok := keyToID[masterfeatKey]
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
@@ -2895,6 +2930,7 @@ func rowReferencesMasterfeat(row map[string]any, masterfeatKey string, keyToID m
|
|||||||
return int(typed) == masterID
|
return int(typed) == masterID
|
||||||
case map[string]any:
|
case map[string]any:
|
||||||
if id, ok := typed["id"].(string); ok {
|
if id, ok := typed["id"].(string); ok {
|
||||||
|
id = resolveCanonicalDatasetKey(id, keyToID)
|
||||||
if id == masterfeatKey {
|
if id == masterfeatKey {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -3369,6 +3405,12 @@ func shouldPreserveLiteralFeatIDObject(row map[string]any, field, refID string)
|
|||||||
|
|
||||||
func shouldPreserveFeatMinLevelClass(row map[string]any) bool {
|
func shouldPreserveFeatMinLevelClass(row map[string]any) bool {
|
||||||
rowKey, _ := row["key"].(string)
|
rowKey, _ := row["key"].(string)
|
||||||
|
if prefix, suffix, ok := strings.Cut(strings.TrimSpace(rowKey), ":"); ok && prefix == "masterfeats" {
|
||||||
|
switch normalizeKeyIdentity(suffix) {
|
||||||
|
case "weaponfocus", "weaponspecialization", "greaterweaponfocus", "greaterweaponspecialization", "improvedcritical", "overwhelmingcritical":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(rowKey, "feat:weaponfocus_"):
|
case strings.HasPrefix(rowKey, "feat:weaponfocus_"):
|
||||||
return true
|
return true
|
||||||
@@ -3386,18 +3428,6 @@ func shouldPreserveFeatMinLevelClass(row map[string]any) bool {
|
|||||||
return true
|
return true
|
||||||
case strings.HasPrefix(rowKey, "feat:epicweaponspecialization_"):
|
case strings.HasPrefix(rowKey, "feat:epicweaponspecialization_"):
|
||||||
return true
|
return true
|
||||||
case rowKey == "masterfeats:weaponfocus":
|
|
||||||
return true
|
|
||||||
case rowKey == "masterfeats:weaponspecialization":
|
|
||||||
return true
|
|
||||||
case rowKey == "masterfeats:greaterweaponfocus":
|
|
||||||
return true
|
|
||||||
case rowKey == "masterfeats:greaterweaponspecialization":
|
|
||||||
return true
|
|
||||||
case rowKey == "masterfeats:improvedcritical":
|
|
||||||
return true
|
|
||||||
case rowKey == "masterfeats:overwhelmingcritical":
|
|
||||||
return true
|
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -818,6 +818,29 @@ func TestBuildNativeExpandsClassesFeatClassskillsFilter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolveCanonicalDatasetKeyMatchesMasterfeatAliases(t *testing.T) {
|
||||||
|
keyToID := map[string]int{
|
||||||
|
"masterfeats:skill_focus": 4,
|
||||||
|
"masterfeats:greater_skill_focus": 15,
|
||||||
|
"masterfeats:weapon_focus": 1,
|
||||||
|
"masterfeats:greater_weapon_specialization": 11,
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := map[string]string{
|
||||||
|
"masterfeats:skillfocus": "masterfeats:skill_focus",
|
||||||
|
"masterfeats:greaterskillfocus": "masterfeats:greater_skill_focus",
|
||||||
|
"masterfeats:weaponfocus": "masterfeats:weapon_focus",
|
||||||
|
"masterfeats:greaterweaponspecialization": "masterfeats:greater_weapon_specialization",
|
||||||
|
"masterfeats:skill_focus": "masterfeats:skill_focus",
|
||||||
|
}
|
||||||
|
|
||||||
|
for input, want := range cases {
|
||||||
|
if got := resolveCanonicalDatasetKey(input, keyToID); got != want {
|
||||||
|
t.Fatalf("resolveCanonicalDatasetKey(%q) = %q, want %q", input, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateProjectRejectsLegacyAuthoredTLKModules(t *testing.T) {
|
func TestValidateProjectRejectsLegacyAuthoredTLKModules(t *testing.T) {
|
||||||
root := testProjectRoot(t)
|
root := testProjectRoot(t)
|
||||||
mkdirAll(t, filepath.Join(root, "topdata", "data", "feat"))
|
mkdirAll(t, filepath.Join(root, "topdata", "data", "feat"))
|
||||||
|
|||||||
Reference in New Issue
Block a user