Class feat injections moved to yaml
This commit is contained in:
+85
-31
@@ -364,7 +364,7 @@ func buildNativeUnchecked(p *project.Project, opts NativeBuildOptions, progress
|
||||
stats.SourceFragments,
|
||||
))
|
||||
}
|
||||
compiled, err := resolveNativeDataset(dataset, globalKeyToID, globalRowByKey, tableRegistry, compiler)
|
||||
compiled, err := resolveNativeDataset(dataset, globalKeyToID, globalRowByKey, tableRegistry, compiler, p.EffectiveConfig().TopData.ClassFeatInjections)
|
||||
if err != nil {
|
||||
return BuildResult{}, err
|
||||
}
|
||||
@@ -3078,13 +3078,13 @@ func collectPlainDataset(dataset nativeDataset) (nativeCollectedDataset, error)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func resolveNativeDataset(dataset nativeCollectedDataset, keyToID map[string]int, globalRowByKey map[string]map[string]any, tableRegistry resolvedTableRegistry, compiler *tlkCompiler) (map[string]any, error) {
|
||||
func resolveNativeDataset(dataset nativeCollectedDataset, keyToID map[string]int, globalRowByKey map[string]map[string]any, tableRegistry resolvedTableRegistry, compiler *tlkCompiler, classFeatInjections project.TopDataClassFeatInjectionConfig) (map[string]any, error) {
|
||||
rows := dataset.Rows
|
||||
if strings.HasPrefix(filepath.ToSlash(dataset.Dataset.Name), "classes/feats/") {
|
||||
classKey := "classes:" + dataset.Dataset.Name[strings.LastIndex(dataset.Dataset.Name, "/")+1:]
|
||||
featSuccessors := buildFeatSuccessorsIndex(globalRowByKey, keyToID)
|
||||
classSkills := buildClassSkillsIndex(tableRegistry, classKey)
|
||||
expanded, err := expandClassesFeatRows(rows, keyToID, globalRowByKey, featSuccessors, classSkills, globalRowByKey, classKey)
|
||||
expanded, err := expandClassesFeatRows(rows, keyToID, globalRowByKey, featSuccessors, classSkills, globalRowByKey, classKey, classFeatInjections)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dataset %s: %w", dataset.Dataset.Name, err)
|
||||
}
|
||||
@@ -3116,26 +3116,27 @@ func resolveNativeDataset(dataset nativeCollectedDataset, keyToID map[string]int
|
||||
}
|
||||
|
||||
var (
|
||||
classFeatGlobalRows = []map[string]any{
|
||||
{"FeatIndex": map[string]any{"id": "feat:combatexpertise"}, "List": "0", "GrantedOnLevel": "-1", "OnMenu": "1"},
|
||||
{"FeatIndex": map[string]any{"id": "feat:powerattack"}, "List": "0", "GrantedOnLevel": "-1", "OnMenu": "1"},
|
||||
{"FeatIndex": map[string]any{"id": "feat:specialattacks"}, "List": "3", "GrantedOnLevel": "1", "OnMenu": "1"},
|
||||
{"FeatIndex": map[string]any{"id": "feat:throw"}, "List": "3", "GrantedOnLevel": "1", "OnMenu": "1"},
|
||||
{"FeatIndex": map[string]any{"id": "feat:grapple"}, "List": "3", "GrantedOnLevel": "1", "OnMenu": "1"},
|
||||
{"FeatIndex": map[string]any{"id": "feat:offensivefighting"}, "List": "3", "GrantedOnLevel": "1", "OnMenu": "1"},
|
||||
{"FeatIndex": map[string]any{"id": "feat:defensivefighting"}, "List": "3", "GrantedOnLevel": "1", "OnMenu": "1"},
|
||||
{"FeatIndex": map[string]any{"id": "feat:horsemenu"}, "List": "3", "GrantedOnLevel": "1", "OnMenu": "1"},
|
||||
defaultClassFeatGlobalRules = []project.TopDataClassFeatGlobalRule{
|
||||
{Feat: "feat:combatexpertise", List: "0", GrantedOnLevel: "-1", OnMenu: "1"},
|
||||
{Feat: "feat:powerattack", List: "0", GrantedOnLevel: "-1", OnMenu: "1"},
|
||||
{Feat: "feat:specialattacks", List: "3", GrantedOnLevel: "1", OnMenu: "1"},
|
||||
{Feat: "feat:throw", List: "3", GrantedOnLevel: "1", OnMenu: "1"},
|
||||
{Feat: "feat:grapple", List: "3", GrantedOnLevel: "1", OnMenu: "1"},
|
||||
{Feat: "feat:offensivefighting", List: "3", GrantedOnLevel: "1", OnMenu: "1"},
|
||||
{Feat: "feat:defensivefighting", List: "3", GrantedOnLevel: "1", OnMenu: "1"},
|
||||
{Feat: "feat:horsemenu", List: "3", GrantedOnLevel: "1", OnMenu: "1"},
|
||||
}
|
||||
classFeatClassSkillShorthandRows = []map[string]any{
|
||||
{"FeatIndex": map[string]any{"id": "masterfeats:skill_focus", "filter": "classskills"}, "List": "1", "GrantedOnLevel": "-1", "OnMenu": "0"},
|
||||
{"FeatIndex": map[string]any{"id": "masterfeats:greater_skill_focus", "filter": "classskills"}, "List": "1", "GrantedOnLevel": "12", "OnMenu": "0"},
|
||||
defaultClassFeatClassSkillMasterfeatRules = []project.TopDataClassFeatMasterfeatRule{
|
||||
{Masterfeat: "masterfeats:skill_focus", List: "1", GrantedOnLevel: "-1", OnMenu: "0"},
|
||||
{Masterfeat: "masterfeats:greater_skill_focus", List: "1", GrantedOnLevel: "12", OnMenu: "0"},
|
||||
}
|
||||
)
|
||||
|
||||
func expandClassesFeatRows(rows []map[string]any, keyToID map[string]int, rowByKey map[string]map[string]any, featSuccessors map[string]string, classSkills map[string]bool, allRowByKey map[string]map[string]any, classKey string) ([]map[string]any, error) {
|
||||
func expandClassesFeatRows(rows []map[string]any, keyToID map[string]int, rowByKey map[string]map[string]any, featSuccessors map[string]string, classSkills map[string]bool, allRowByKey map[string]map[string]any, classKey string, classFeatInjections project.TopDataClassFeatInjectionConfig) ([]map[string]any, error) {
|
||||
|
||||
injected := make([]map[string]any, 0, len(classFeatGlobalRows)+len(classFeatClassSkillShorthandRows))
|
||||
existingRefIDs := make(map[string]struct{}, len(rows))
|
||||
globalRules, classSkillRules := effectiveClassFeatInjectionRules(classFeatInjections)
|
||||
injected := make([]map[string]any, 0, len(globalRules)+len(classSkillRules))
|
||||
presentRefIDs := make(map[string]struct{}, len(rows))
|
||||
for _, row := range rows {
|
||||
featRef, ok := row["FeatIndex"].(map[string]any)
|
||||
if !ok {
|
||||
@@ -3145,38 +3146,50 @@ func expandClassesFeatRows(rows []map[string]any, keyToID map[string]int, rowByK
|
||||
if refID == "" {
|
||||
continue
|
||||
}
|
||||
existingRefIDs[refID] = struct{}{}
|
||||
presentRefIDs[refID] = struct{}{}
|
||||
if strings.HasPrefix(refID, "feat:") || strings.HasPrefix(refID, "masterfeats:") {
|
||||
presentRefIDs[resolveCanonicalDatasetKey(refID, keyToID)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for _, row := range classFeatGlobalRows {
|
||||
featKey := row["FeatIndex"].(map[string]any)["id"].(string)
|
||||
if _, exists := existingRefIDs[featKey]; exists {
|
||||
for _, rule := range globalRules {
|
||||
featKey := resolveCanonicalDatasetKey(strings.TrimSpace(rule.Feat), keyToID)
|
||||
if _, exists := presentRefIDs[featKey]; exists {
|
||||
continue
|
||||
}
|
||||
if _, exists := allRowByKey[featKey]; exists {
|
||||
cloned := deepCopyValue(row).(map[string]any)
|
||||
if label := lookupReferencedFeatLabel(featKey, allRowByKey); label != "" {
|
||||
cloned["FeatLabel"] = label
|
||||
}
|
||||
injected = append(injected, cloned)
|
||||
if !classFeatInjectionConditionsMatch(rule, presentRefIDs, keyToID) {
|
||||
continue
|
||||
}
|
||||
if _, exists := allRowByKey[featKey]; !exists {
|
||||
continue
|
||||
}
|
||||
cloned := classFeatGlobalRuleRow(rule, featKey)
|
||||
if label := lookupReferencedFeatLabel(featKey, allRowByKey); label != "" {
|
||||
cloned["FeatLabel"] = label
|
||||
}
|
||||
injected = append(injected, cloned)
|
||||
presentRefIDs[featKey] = struct{}{}
|
||||
}
|
||||
|
||||
hasClassSkills := classSkills != nil && len(classSkills) > 0
|
||||
for _, row := range classFeatClassSkillShorthandRows {
|
||||
for _, rule := range classSkillRules {
|
||||
if !hasClassSkills {
|
||||
continue
|
||||
}
|
||||
refID := row["FeatIndex"].(map[string]any)["id"].(string)
|
||||
if _, exists := existingRefIDs[refID]; exists {
|
||||
refID := strings.TrimSpace(rule.Masterfeat)
|
||||
canonicalRefID := resolveCanonicalDatasetKey(refID, keyToID)
|
||||
if _, exists := presentRefIDs[canonicalRefID]; exists {
|
||||
continue
|
||||
}
|
||||
row := classFeatMasterfeatRuleRow(rule)
|
||||
rowItems, err := expandClassesFeatRow(row, keyToID, rowByKey, featSuccessors, classSkills, classKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rowItems) > 0 {
|
||||
injected = append(injected, rowItems...)
|
||||
presentRefIDs[refID] = struct{}{}
|
||||
presentRefIDs[canonicalRefID] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3199,6 +3212,47 @@ func expandClassesFeatRows(rows []map[string]any, keyToID map[string]int, rowByK
|
||||
return combined, nil
|
||||
}
|
||||
|
||||
func effectiveClassFeatInjectionRules(config project.TopDataClassFeatInjectionConfig) ([]project.TopDataClassFeatGlobalRule, []project.TopDataClassFeatMasterfeatRule) {
|
||||
if len(config.GlobalFeats) == 0 && len(config.ClassSkillMasterfeats) == 0 {
|
||||
return defaultClassFeatGlobalRules, defaultClassFeatClassSkillMasterfeatRules
|
||||
}
|
||||
return config.GlobalFeats, config.ClassSkillMasterfeats
|
||||
}
|
||||
|
||||
func classFeatInjectionConditionsMatch(rule project.TopDataClassFeatGlobalRule, presentRefIDs map[string]struct{}, keyToID map[string]int) bool {
|
||||
for _, required := range rule.RequirePresent {
|
||||
refID := resolveCanonicalDatasetKey(strings.TrimSpace(required), keyToID)
|
||||
if _, exists := presentRefIDs[refID]; !exists {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, blocked := range rule.UnlessPresent {
|
||||
refID := resolveCanonicalDatasetKey(strings.TrimSpace(blocked), keyToID)
|
||||
if _, exists := presentRefIDs[refID]; exists {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func classFeatGlobalRuleRow(rule project.TopDataClassFeatGlobalRule, featKey string) map[string]any {
|
||||
return map[string]any{
|
||||
"FeatIndex": map[string]any{"id": featKey},
|
||||
"List": strings.TrimSpace(rule.List),
|
||||
"GrantedOnLevel": strings.TrimSpace(rule.GrantedOnLevel),
|
||||
"OnMenu": strings.TrimSpace(rule.OnMenu),
|
||||
}
|
||||
}
|
||||
|
||||
func classFeatMasterfeatRuleRow(rule project.TopDataClassFeatMasterfeatRule) map[string]any {
|
||||
return map[string]any{
|
||||
"FeatIndex": map[string]any{"id": strings.TrimSpace(rule.Masterfeat), "filter": "classskills"},
|
||||
"List": strings.TrimSpace(rule.List),
|
||||
"GrantedOnLevel": strings.TrimSpace(rule.GrantedOnLevel),
|
||||
"OnMenu": strings.TrimSpace(rule.OnMenu),
|
||||
}
|
||||
}
|
||||
|
||||
func expandClassesFeatRow(row map[string]any, keyToID map[string]int, rowByKey map[string]map[string]any, featSuccessors map[string]string, classSkills map[string]bool, classKey string) ([]map[string]any, error) {
|
||||
featRef, ok := row["FeatIndex"].(map[string]any)
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user