BAB tables
This commit is contained in:
@@ -279,7 +279,7 @@ func (ctx *wikiContext) wikiTableSourceRows(def wikiTableDefinition, page wikiTe
|
||||
if page.Category != "classes" {
|
||||
return nil, nil
|
||||
}
|
||||
return ctx.classProgressionRows(def.Rows.Levels, nil, page.Row)
|
||||
return ctx.classProgressionRows(def.Rows.Levels, nil, wikiDataProviderAttacksConfig{}, page.Key, page.Row)
|
||||
case "class_feats":
|
||||
if page.Category != "classes" {
|
||||
return nil, nil
|
||||
@@ -303,7 +303,7 @@ func (ctx *wikiContext) wikiTableSourceRows(def wikiTableDefinition, page wikiTe
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *wikiContext) classProgressionRows(levels string, projections map[string]wikiDataProviderProjectionField, classRow map[string]any) ([]map[string]any, error) {
|
||||
func (ctx *wikiContext) classProgressionRows(levels string, projections map[string]wikiDataProviderProjectionField, attacks wikiDataProviderAttacksConfig, classKey string, classRow map[string]any) ([]map[string]any, error) {
|
||||
start, end, err := parseLevelRange(levels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -363,7 +363,17 @@ func (ctx *wikiContext) classProgressionRows(levels string, projections map[stri
|
||||
for level := start; level <= end; level++ {
|
||||
row := cloneRowMap(classRow)
|
||||
row["Level"] = level
|
||||
row["BAB"] = formatSignedInt(classBABAtLevel(stringValue(classRow, "AttackBonusTable"), level))
|
||||
bab := classBABAtLevel(stringValue(classRow, "AttackBonusTable"), level)
|
||||
row["BABValue"] = bab
|
||||
row["BAB"] = formatSignedInt(bab)
|
||||
attackCount := classAttackCount(attacks, classKey, bab, level)
|
||||
if attackCount < 1 {
|
||||
attackCount = 1
|
||||
}
|
||||
countField := defaultStringValue(attacks.CountField, "AttackCount")
|
||||
sequenceField := defaultStringValue(attacks.Field, "AttackSequence")
|
||||
row[countField] = attackCount
|
||||
row[sequenceField] = formatAttackSequence(bab, attackCount, attackStep(attacks))
|
||||
if saveRow := saveRows[strconv.Itoa(level)]; saveRow != nil {
|
||||
for key, value := range saveRow {
|
||||
row[key] = value
|
||||
@@ -418,6 +428,96 @@ func classBABAtLevel(table string, level int) int {
|
||||
}
|
||||
}
|
||||
|
||||
func classAttackCount(cfg wikiDataProviderAttacksConfig, classKey string, bab, level int) int {
|
||||
count := baseClassAttackCount(cfg, bab)
|
||||
for _, override := range cfg.Overrides {
|
||||
if !wikiAttackOverrideMatches(override, classKey, level) {
|
||||
continue
|
||||
}
|
||||
if override.Attacks > 0 {
|
||||
count = override.Attacks
|
||||
continue
|
||||
}
|
||||
count += override.Add
|
||||
}
|
||||
if count < 1 {
|
||||
return 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func baseClassAttackCount(cfg wikiDataProviderAttacksConfig, bab int) int {
|
||||
switch strings.TrimSpace(cfg.Mode) {
|
||||
case "fixed":
|
||||
if cfg.Attacks > 0 {
|
||||
return cfg.Attacks
|
||||
}
|
||||
case "bab_thresholds":
|
||||
return attackCountFromThresholds(cfg.Thresholds, bab)
|
||||
case "":
|
||||
return attackCountFromThresholds(defaultWikiAttackThresholds(), bab)
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func attackCountFromThresholds(thresholds []wikiDataProviderAttackThreshold, bab int) int {
|
||||
count := 1
|
||||
for _, threshold := range thresholds {
|
||||
if threshold.Attacks > 0 && bab >= threshold.MinBAB {
|
||||
count = threshold.Attacks
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func defaultWikiAttackThresholds() []wikiDataProviderAttackThreshold {
|
||||
return []wikiDataProviderAttackThreshold{
|
||||
{MinBAB: 1, Attacks: 1},
|
||||
{MinBAB: 6, Attacks: 2},
|
||||
{MinBAB: 11, Attacks: 3},
|
||||
{MinBAB: 16, Attacks: 4},
|
||||
}
|
||||
}
|
||||
|
||||
func wikiAttackOverrideMatches(override wikiDataProviderAttackOverrideRule, classKey string, level int) bool {
|
||||
foundClass := false
|
||||
for _, candidate := range override.Classes {
|
||||
if strings.TrimSpace(candidate) == classKey {
|
||||
foundClass = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundClass {
|
||||
return false
|
||||
}
|
||||
levels, err := parseWikiAttackLevels(override.Levels)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, ok := levels[level]
|
||||
return ok
|
||||
}
|
||||
|
||||
func attackStep(cfg wikiDataProviderAttacksConfig) int {
|
||||
if cfg.Step == 0 {
|
||||
return -5
|
||||
}
|
||||
return cfg.Step
|
||||
}
|
||||
|
||||
func formatAttackSequence(bab, attacks, step int) string {
|
||||
if attacks < 1 {
|
||||
attacks = 1
|
||||
}
|
||||
values := make([]string, 0, attacks)
|
||||
current := bab
|
||||
for i := 0; i < attacks; i++ {
|
||||
values = append(values, formatSignedInt(current))
|
||||
current += step
|
||||
}
|
||||
return strings.Join(values, "/")
|
||||
}
|
||||
|
||||
func formatSignedInt(value int) string {
|
||||
if value > 0 {
|
||||
return "+" + strconv.Itoa(value)
|
||||
@@ -884,6 +984,11 @@ func (p *wikiExprParser) callHelper(name string, args []any) (any, error) {
|
||||
default:
|
||||
return stringifyWikiTableValue(args[0]), nil
|
||||
}
|
||||
case "count":
|
||||
if len(args) != 1 {
|
||||
return nil, fmt.Errorf("count expects 1 argument")
|
||||
}
|
||||
return countWikiTemplateValue(args[0]), nil
|
||||
case "nonzero":
|
||||
if len(args) != 1 {
|
||||
return nil, fmt.Errorf("nonzero expects 1 argument")
|
||||
@@ -974,6 +1079,30 @@ func (p *wikiExprParser) callHelper(name string, args []any) (any, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func countWikiTemplateValue(value any) int {
|
||||
switch typed := value.(type) {
|
||||
case nil:
|
||||
return 0
|
||||
case []string:
|
||||
return len(typed)
|
||||
case []any:
|
||||
count := 0
|
||||
for _, item := range typed {
|
||||
if presentWikiValue(item) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
case map[string]any:
|
||||
return len(typed)
|
||||
default:
|
||||
if presentWikiValue(value) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (p *wikiExprParser) hiddenWikiReference(value any, targetDataset string) (bool, error) {
|
||||
targetDataset = strings.TrimSpace(targetDataset)
|
||||
if !isWikiVisibilityDataset(targetDataset) {
|
||||
|
||||
Reference in New Issue
Block a user