Closing gaps and cleaning house

This commit is contained in:
2026-05-14 21:08:38 +02:00
parent 4199d615a3
commit 59994c8b29
6 changed files with 483 additions and 19 deletions
+159 -14
View File
@@ -747,6 +747,14 @@ func collectBaseDataset(dataset nativeDataset) (nativeCollectedDataset, error) {
lockAdded := 0
lockPruned := 0
retiredKeys := map[string]struct{}{}
featFamilyAliases := generatedFamilyAliases{}
if dataset.Name == "feat" {
var err error
featFamilyAliases, err = collectGeneratedFamilyAliases(dataset.GeneratedDir)
if err != nil {
return nativeCollectedDataset{}, err
}
}
for key, rowID := range lockData {
if rowID <= baseBoundaryID {
@@ -834,7 +842,7 @@ func collectBaseDataset(dataset nativeDataset) (nativeCollectedDataset, error) {
if parsedID == lockedID {
return override, nil
}
if dataset.Name == "feat" && generatedCanonicalLockCanMove(rawKey, parsedID, lockData) {
if dataset.Name == "feat" && generatedCanonicalLockCanMove(rawKey, parsedID, lockData, featFamilyAliases) {
delete(lockData, rawKey)
lockModified = true
return override, nil
@@ -847,7 +855,7 @@ func collectBaseDataset(dataset nativeDataset) (nativeCollectedDataset, error) {
}
for lockedKey, lockedID := range lockData {
if lockedKey != key && lockedID == rowID {
if dataset.Name == "feat" && generatedAliasLockForKey(key, lockedKey) {
if dataset.Name == "feat" && generatedAliasLockForKey(key, lockedKey, featFamilyAliases) {
delete(lockData, lockedKey)
lockModified = true
continue
@@ -2396,8 +2404,48 @@ func generatedFamilyPrereqKey(ctx *featGeneratedContext, sourceRow map[string]an
return featKey, true
}
func generatedFamilyLegacyAliases(familyKey string) []string {
switch familyKey {
type generatedFamilyAliases map[string][]string
func collectGeneratedFamilyAliases(generatedDir string) (generatedFamilyAliases, error) {
aliases := generatedFamilyAliases{}
if generatedDir == "" {
return aliases, nil
}
paths, err := collectModulePaths(generatedDir)
if err != nil {
return nil, err
}
for _, path := range paths {
obj, err := loadJSONObject(path)
if err != nil {
return nil, err
}
if !isFamilyExpansionObject(obj) {
continue
}
spec, err := parseFamilyExpansionSpec(path, obj)
if err != nil {
return nil, err
}
if len(spec.LegacyFamilyKeys) > 0 {
aliases[spec.FamilyKey] = spec.LegacyFamilyKeys
}
}
return aliases, nil
}
func configuredGeneratedFamilyLegacyAliases(familyKey string, aliases generatedFamilyAliases) []string {
normalized := normalizeKeyIdentity(familyKey)
for configuredFamilyKey, configured := range aliases {
if normalizeKeyIdentity(configuredFamilyKey) == normalized && len(configured) > 0 {
return configured
}
}
return compatibilityGeneratedFamilyLegacyAliases(familyKey)
}
func compatibilityGeneratedFamilyLegacyAliases(familyKey string) []string {
switch normalizeKeyIdentity(familyKey) {
case "skillfocus":
return []string{"skillfocus"}
case "greaterskillfocus":
@@ -2413,18 +2461,94 @@ func generatedFamilyLegacyAliases(familyKey string) []string {
}
}
func generatedAliasLockForKey(canonicalKey, lockedKey string) bool {
func knownGeneratedFamilyKeys(aliases generatedFamilyAliases) []string {
seen := map[string]struct{}{}
keys := make([]string, 0, 32)
add := func(key string) {
key = strings.TrimSpace(key)
if key == "" {
return
}
if _, ok := seen[key]; ok {
return
}
seen[key] = struct{}{}
keys = append(keys, key)
}
for _, key := range []string{
"greater_weapon_specialization",
"epic_weapon_specialization",
"greaterweaponspecialization",
"epicweaponspecialization",
"overwhelming_critical",
"epic_overwhelming_critical",
"overwhelmingcritical",
"epicoverwhelmingcritical",
"greater_weapon_focus",
"epic_weapon_focus",
"greaterweaponfocus",
"epicweaponfocus",
"greater_skill_focus",
"epic_skill_focus",
"greaterskillfocus",
"epicskillfocus",
"weapon_specialization",
"weaponspecialization",
"improved_critical",
"improvedcritical",
"weapon_of_choice",
"weaponofchoice",
"weapon_focus",
"weaponfocus",
"skill_focus",
"skillfocus",
} {
add(key)
}
for familyKey, legacyKeys := range aliases {
add(familyKey)
for _, legacyKey := range legacyKeys {
add(legacyKey)
}
}
slices.SortFunc(keys, func(a, b string) int {
if len(a) == len(b) {
return strings.Compare(a, b)
}
return len(b) - len(a)
})
return keys
}
func splitKnownGeneratedFamilyIdentity(text string, aliases generatedFamilyAliases) familyIdentity {
text = strings.TrimPrefix(strings.TrimSpace(text), "feat:")
for _, parent := range knownGeneratedFamilyKeys(aliases) {
if text == parent {
return familyIdentity{Parent: parent}
}
prefix := parent + "_"
if strings.HasPrefix(text, prefix) {
return familyIdentity{
Parent: parent,
Child: strings.TrimPrefix(text, prefix),
}
}
}
return splitFamilyExpansionIdentity(text)
}
func generatedAliasLockForKey(canonicalKey, lockedKey string, aliases generatedFamilyAliases) bool {
canonicalKey = strings.TrimPrefix(strings.TrimSpace(canonicalKey), "feat:")
lockedKey = strings.TrimPrefix(strings.TrimSpace(lockedKey), "feat:")
if canonicalKey == "" || lockedKey == "" {
return false
}
identity := splitFamilyExpansionIdentity(canonicalKey)
identity := splitKnownGeneratedFamilyIdentity(canonicalKey, aliases)
if identity.Parent == "" || identity.Child == "" {
return false
}
legacyChildren := legacyFamilyChildAliases(identity.Parent, identity.Child)
for _, alias := range generatedFamilyLegacyAliases(identity.Parent) {
for _, alias := range configuredGeneratedFamilyLegacyAliases(identity.Parent, aliases) {
prefix := alias + "_"
if !strings.HasPrefix(lockedKey, prefix) {
continue
@@ -2439,18 +2563,38 @@ func generatedAliasLockForKey(canonicalKey, lockedKey string) bool {
return false
}
func generatedCanonicalLockCanMove(canonicalKey string, rowID int, lockData map[string]int) bool {
func generatedCanonicalLockCanMove(canonicalKey string, rowID int, lockData map[string]int, aliases generatedFamilyAliases) bool {
if rowID <= 0 {
return false
}
for lockedKey, lockedID := range lockData {
if lockedID == rowID && generatedAliasLockForKey(canonicalKey, lockedKey) {
if lockedID == rowID && generatedAliasLockForKey(canonicalKey, lockedKey, aliases) {
return true
}
}
return false
}
func (c *featGeneratedContext) generatedFamilyAliases() generatedFamilyAliases {
aliases := generatedFamilyAliases{}
for familyKey, spec := range c.familySpecs {
if len(spec.LegacyFamilyKeys) > 0 {
aliases[familyKey] = spec.LegacyFamilyKeys
}
}
return aliases
}
func (c *featGeneratedContext) generatedFamilyLegacyAliases(familyKey string) []string {
normalized := normalizeKeyIdentity(familyKey)
for specFamilyKey, spec := range c.familySpecs {
if normalizeKeyIdentity(specFamilyKey) == normalized && len(spec.LegacyFamilyKeys) > 0 {
return spec.LegacyFamilyKeys
}
}
return compatibilityGeneratedFamilyLegacyAliases(familyKey)
}
func (c *featGeneratedContext) generatedFeatKeyForID(familyKey string, rowID int) (string, bool) {
if rowID <= 0 {
return "", false
@@ -2467,7 +2611,7 @@ func (c *featGeneratedContext) generatedFeatKeyForID(familyKey string, rowID int
return best, true
}
}
for _, alias := range generatedFamilyLegacyAliases(familyKey) {
for _, alias := range c.generatedFamilyLegacyAliases(familyKey) {
for _, candidates := range []map[string]int{c.lockData, c.supplementalID} {
matches := make([]string, 0)
prefix := "feat:" + alias + "_"
@@ -2489,7 +2633,7 @@ func (c *featGeneratedContext) generatedFeatKeyForID(familyKey string, rowID int
func (c *featGeneratedContext) generatedFeatKeyFromLegacyAlias(familyKey, sourceSlug string) (string, int, bool) {
targets := legacyFamilyChildAliases(familyKey, sourceSlug)
canonicalKey := c.preferredGeneratedFeatKey(familyKey, sourceSlug)
for _, alias := range generatedFamilyLegacyAliases(familyKey) {
for _, alias := range c.generatedFamilyLegacyAliases(familyKey) {
for _, candidates := range []map[string]int{c.lockData, c.supplementalID} {
prefix := "feat:" + alias + "_"
for _, target := range targets {
@@ -2510,7 +2654,8 @@ func (c *featGeneratedContext) generatedFeatKeyFromLegacyAlias(familyKey, source
func legacyFamilyChildAliases(familyKey, sourceSlug string) []string {
aliases := []string{}
if familyKey != "greaterskillfocus" && familyKey != "skillfocus" {
normalizedFamilyKey := normalizeKeyIdentity(familyKey)
if normalizedFamilyKey != "greaterskillfocus" && normalizedFamilyKey != "skillfocus" {
return []string{sourceSlug}
}
switch normalizeKeyIdentity(sourceSlug) {
@@ -2539,12 +2684,12 @@ func legacyFamilyChildAliases(familyKey, sourceSlug string) []string {
func (c *featGeneratedContext) generatedFeatKeyFromCanonicalAlias(canonicalKey string) (string, int, bool) {
stripped := strings.TrimPrefix(strings.TrimSpace(canonicalKey), "feat:")
identity := splitFamilyExpansionIdentity(stripped)
identity := splitKnownGeneratedFamilyIdentity(stripped, c.generatedFamilyAliases())
if identity.Parent == "" || identity.Child == "" {
return "", 0, false
}
legacyChildren := legacyFamilyChildAliases(identity.Parent, identity.Child)
for _, alias := range generatedFamilyLegacyAliases(identity.Parent) {
for _, alias := range c.generatedFamilyLegacyAliases(identity.Parent) {
for _, legacyChild := range legacyChildren {
if _, rowID, ok := c.featKeyForFamilyChild(alias, legacyChild); ok {
return "feat:" + identity.Parent + "_" + identity.Child, rowID, true