Stats System
In short
Every effect on the batter is a
StatModifier(stat, Add or Percent, value).PlayerStatskeeps a flat list and computesfinal = (base + sum of Adds) x max(0, 1 + sum of Percents). Batters, level perks, upgrades, synergies, ballparks and boss pitchers all feed the same list.
How it works
RunState.RecalculateStats()clearsStatsand adds, in order: the batter’smodifiers, the batter’s level perks (LevelBonus, fixed for the run), every owned upgrade’smodifiers, then every active synergy’sbonus(SynergyResolver.GetActive).- It runs at run start, on every
AddUpgradeand on the dev menu’sResetUpgrades. - When an inning starts,
RunManager.PlayCurrentRoundclones the run’s stats and adds the park’sbatterModifiersand the pitcher’sbatterModifiers. The clone goes to the at-bat inRoundContext.stats. Co-op does the same inCoopRun; the batting cage adds the park’s. - Code reads a stat with
stats.Get(StatType.X)orGetInt(rounded).
Order does not matter: all Adds are summed, then all Percents are summed and applied once.
The stats
StatType | Base | Shown as | Read where |
|---|---|---|---|
Power | 1 | x1.00 | SwingResolver.FromContact (multiplies the contact zone’s exit velocity) |
Contact | 1 | x1.00 | SwingResolver.HitRadius (adds 0.12 m of reach around the bat’s axis per point) and SwingResolver.Classify (scales the perfect and nice zone widths, at least x0.2) |
TimingWindow | 0.11 | 110 ms | SwingResolver.HitRadius (scales that reach by sqrt(TimingWindow / 0.11), so below the base it shrinks) |
LaunchBonus | 0 | +0 deg | SwingResolver (added to launch angle) |
HomeRunMultiplier | 1 | x1.00 | ScoreCalculator.Score |
CoinBonus | 0 | 0 | RunManager.InningCoins (coins per home run) |
ExtraOuts | 0 | 0 | RunManager.PlayCurrentRound, CoopRun (outs per inning) |
RerollDiscount | 0 | 0 | ShopService.RerollCost |
GreenBonus | 0.25 | +25% | ScoreCalculator.GreenMultiplier |
GreenStreakBonus | 0 | +0% per green | ScoreCalculator.GreenMultiplier |
Display names come from StatInfo.DisplayName (Power, Contact, Timing Window, Launch Angle, HR Multiplier, Coins per HR, Extra Outs, Reroll Discount, Green HR Score, Green Streak Score). StatModifier.Describe() writes lines like “+20% Power” or “+10 ms Timing Window”.
Who provides modifiers
| Source | Field | Example from DefaultContent |
|---|---|---|
| Batter | BatterDefinition.modifiers | Baked Bean: +20% Power, -15% Timing Window |
| Batter level perk | BatterDefinition.levelPerks | Peapod: +2% Timing Window per perk (XP and Levels) |
| Upgrade | UpgradeDefinition.modifiers | see Upgrades |
| Synergy | SynergyDefinition.bonus | see Synergies |
| Ballpark | BallparkDefinition.batterModifiers | Midnight Yard: -10% Timing Window |
| Boss pitcher | PitcherDefinition.batterModifiers | The Ace: -15% Timing Window; Knuckle Sandwich: -20% Contact |
Numbers
| What | Value | Where to tune |
|---|---|---|
| Formula | (BaseValue + sum Add) x max(0, 1 + sum Percent) | PlayerStats.Get |
| Percent value meaning | 0.1 = +10% | ModifierOp.Percent |
| Green multiplier | 1 + GreenBonus + GreenStreakBonus x (greenStreak - 1) | ScoreCalculator.GreenMultiplier |
In the code
| File | What it does |
|---|---|
Scripts/Stats/StatType.cs | StatType enum; StatInfo.BaseValue, DisplayName, Format, All. |
Scripts/Stats/StatModifier.cs | ModifierOp, StatModifier.Add / Percent, Describe. |
Scripts/Stats/PlayerStats.cs | Add, AddRange, Clear, Clone, Get, GetInt. |
Scripts/Run/RunState.cs | RecalculateStats. |
Scripts/Upgrades/SynergyResolver.cs | CountTag, GetActive. |
Gotchas
- Percents can push a stat to zero but never below (
max(0, ...)), whatever the stack. - Park and pitcher modifiers are added to a clone for one inning only. They are not in
RunState.Stats, so the dev menu’s ATTRIBUTES list andBuildPaneldo not show them. - The at-bat keeps the stats it started with. Upgrades given mid-inning (dev menu) apply from the next inning.
MainMenuViewshows POWER, CONTACT and TIMING bars from the batter’s stats, under a style line such as “HIGH TRAJECTORY - PULL HITTER”.- The
StatTypeenum comments are stale:Contactdoes not multiply a sweet-spot radius around the aim point (it adds reach and widens the contact zones along the bat), andTimingWindowis not seconds early/late (it only scales the reach). See Hit radius (reach) and Batting Explained. - Batting style (trajectory, pull or push side, curve) is not a stat: it is read straight from
BatterDefinitionbySwingResolver.FromContact, so upgrades can’t change it.
Related
- Add a Stat: adding a new
StatType - Stats: the content view of each stat
- Upgrades and Synergies: how upgrades and tags stack
- Swing and Timing: where Power, Contact and Timing Window act
- Batting Explained: the contact zones Contact scales
- Scoring: where HR Multiplier and the green stats act