Tape to Tape Wiki All games ☕ Support me

Skater Attributes & Stats#

How a skater's four attributes are stored, modified, combined into OVR, and fed into gameplay.


1. The attributes#

Enum value (Data.EForwardStat) Data.BasicStats Stored field (ForwardData) In-game meaning
Overall = 0 – (computed) Average of the four below
Speed = 1 Speed = 3 speed Top speed and acceleration (see skating-and-movement.md)
Accuracy = 2 ShotAccuracy = 1 shotAccuracy Shot aim spread
ShotPower = 3 ShotPower = 0 shotPower Shot speed
Checking = 4 Checking = 2 checking Bodycheck strength and resistance. Also skater "weight"

There are two enums with different orderings. EForwardStat is used by StatBonus/UI and BasicStats is used by ForwardData/ForwardStats. Goalies use EGoalieStat, covered in goalies.md.

ForwardData also has a set of hidden stats (_hiddenSpeed, _hiddenShotPower, …). The value shown in the UI is Display<Stat> = stat − hiddenStat, and GetDisplayOverallStats() averages those display values. The real stat, hidden part included, is what gameplay uses.

Source: Data.EForwardStat, Data.BasicStats, Data.ForwardData (get_DisplaySpeed, GetDisplayOverallStats).


2. Ranges and caps#

Stored (permanent) values#

ForwardData.SetSpeed/SetShotPower/SetChecking and IncrementStats clamp the stored base value:

Attribute Stored range
Speed 0 – 250
Shot Power 0 – 200
Checking 0 – 500
Accuracy 0 – 100

In-match (final) values#

ForwardStats clamps again after all modifiers, injury and fatigue:

Attribute Final clamp Notes
Accuracy 0 – 100 GetAccuracy
Checking 0 – 500 GetChecking
Shot Power 0 – 300 GetShotPower (total shot-power stat)
Speed none in GetSpeed Movement uses the cached speed clamped to 0–300, then 0–200 inside the speed curve. Speed above 200 does nothing for movement.

Source: ForwardData::SetSpeed (cmp 0xFA), SetShotPower (0xC8), SetChecking (0x1F4), IncrementStats (Accuracy 0x64); ForwardStats::GetAccuracy/GetChecking/GetShotPower/GetMaxSpeed.


3. Overall (OVR)#

OVR = RoundToInt( (max(0,Speed) + max(0,Accuracy) + max(0,ShotPower) + max(0,Checking)) × 0.25 )

This is a plain average with negative values counted as 0. T2TUtils.GetOverall, ForwardData.GetOverallStats and GetRealAverageStats all use the same code. In-match, ForwardStats.GetOverall uses the same 0.25 average but on modified stats (see §5). The rounding helper is presumably Mathf.RoundToInt (inferred).

When the UI turns a single-attribute bonus into an OVR change, it uses bonus / 4 rounded (DetailedSkaterDataUtils.GetStatBonusImpactOnOverallStat, SkaterStatsPreview.GetOverallBonusFromSingleForwardStatBonus). So +8 Speed shows as +2 OVR.

Source: Tape2Tape.T2TUtils::GetOverall, Data.ForwardData::GetOverallStats, Skaters.ForwardStats::GetOverall.


4. How "+X OVR" works#

An OVR bonus is not a separate number. It is added in full to every one of the four attributes:

  • Permanent: ForwardData.IncrementOverallStats(delta) calls IncrementStats with the same delta for Checking, Speed, Accuracy and ShotPower, so the average rises by exactly delta.
  • Temporary (in match): OVR relics and talents register the same x => x + bonus function into all four modifier lists. For example, ParticipationTrophyController.AddStats pushes into overallCheckingEvent, overallShotAccuracyEvent, overallShotPowerEvent and overallSpeedEvent.

So "+10 OVR" means +10 Speed, +10 Accuracy, +10 Shot Power and +10 Checking. Each attribute's own cap still applies. For example, Accuracy is capped at 100, so a +10 OVR bonus on a 95-accuracy skater adds only +5 accuracy.

Balanced Talent#

If the skater has the Balanced Talent ("All attributes are balanced evenly"), two things change:

  1. Permanent gains are spread. IncrementStats(stat, delta) becomes IncrementOverallStats(RoundToInt(delta × 0.25)). A +8 Speed reward becomes +2 to every attribute.
  2. In-match values are averaged. Every attribute getter returns ForwardStats.GetOverall(stat) instead of its own stat. That function takes base OVR, runs it through each attribute's modifier list, and averages the four results. All four attributes therefore show the same value.

Source: ForwardData::IncrementOverallStats, ForwardData::IncrementStats (balance branch ×0.25), ForwardStats::.ctor (_isBalanced), ForwardStats::GetOverall, Rogue.ParticipationTrophyController::AddStats.


5. In-match stat pipeline#

Each attribute getter (ForwardStats.GetSpeed, GetAccuracy, GetChecking, GetTotalShotPowerStats) follows the same order:

  1. Stat lock. If the skater has a StatLockTalent for this attribute (e.g. Bedrock sets Checking, Railgun sets Shot Power), the locked value is returned at once. Steps 2–7 are skipped.
  2. Base. Start from the stored ForwardData value.
  3. Modifiers. GetStats(base, list) folds the attribute's list of Func<int,int> modifiers (overallSpeedEvent, overallShotAccuracyEvent, overallShotPowerEvent, overallCheckingEvent) in order. In practice each modifier is x => x + bonus, so flat bonuses simply add up. Relic and talent controllers add and remove these functions during a match. With Balanced Talent, GetOverall is used here instead (§4). - Speed only: if the team owns a certain relic (generic type not resolved) and the skater is not Balanced, the modifier step is skipped and the raw base speed is used.
  4. "Original" relics (+50% bonuses). stat = stat + (stat − base) / 2 (integer division, truncates toward 0) This applies to Speed with Original Skate, Accuracy with Original Stick, Checking with Original Glove, and Shot Power with Original Puck. It is skipped for referees (SkaterType.Referee = 5). Because it scales stat − base, it also enlarges penalties.
  5. Injury (only when includeInjury). See §8.
  6. Fatigue (Double Decker squad only). See §9.
  7. Clamp (§2).

At match start and on refresh, UpdateCachedStats() caches max speed, supernova max speed, speed with and without injury, and accuracy.

Source: Skaters.ForwardStats::GetSpeed/GetAccuracy/GetChecking/GetTotalShotPowerStats/GetStats/AddOriginalRelicModifier/UpdateCachedStats; Rogue.Powerups.Chaos.StatLockTalent.


6. StatBonus and AddStatBonuses / AddConditionalStatBonuses#

struct StatBonus {
  ESkateType  SkaterType;          // forward or goalie
  EForwardStat StatForward;
  EGoalieStat  StatGoalie;
  int  Bonus;
  bool IsConditional;              // only active under a condition (e.g. "while tied")
  bool IsBlockableStatGain;
}

Every PowerUp/Relic has a virtual AddStatBonuses(IList<StatBonus>, SkaterData, TeamData). A second pass, AddConditionalStatBonuses(..., IReadOnlyList<StatBonus> collectedBonuses), lets an item react to bonuses already collected. These lists feed the stat previews and tooltips (SkaterStatsPreview, SkaterStatBox.CumulateCustomStatBonuses, ForwardStatsPreview). In a StatBonus, StatForward = Overall stands for "+X OVR", which the preview converts with the ÷4 rule above. (inferred) Actual in-match values come from the modifier lists in §5. The two pipelines are kept consistent by each item's controller rather than shared code.

Source: Data.StatBonus, Rogue.Relic/PowerUp::AddStatBonuses/AddConditionalStatBonuses, Tape2Tape.Customization.UI.SkaterStatsPreview.


7. Permanent stat sources#

Training (Conditioning NPC)#

Event RewardType Bonus Who
RegularPlayerTraining 0 +15 to one attribute one skater
StatsTraining 1 +15 to one attribute several skaters
TeamTraining 2 +10 (statsBonusForward) whole team

Gains go through ForwardData.IncrementStats. That means stat locks block them, the Balanced spread applies, and caps apply. TrainingEvent.GetModifiedStatBonus multiplies the bonus by (1 + trainingMultiplier [+ trainingMultiplierUpgrade]):

  • Can of Worms: 0.5, so training gives +50% (+100% when upgraded).
  • Training Whistle: +10 Speed after training (+5 more when upgraded).

Bench Bonus (hidden relic)#

At game end (BenchBonusController.OnGameEnd), skaters who sat on the bench all game get one permanent reward. The reward is an OVR bonus, a single-attribute bonus, or a talent (AddRandomTalent/UpgradeRandomTalent).

Variant overallBonus single-stat bonus
Bench Bonus / Bum Bench Bonus +4 OVR +8
Gauntlet Bench Bonus +3 OVR +6

The amount is RoundToInt(bonus × GetBenchBonusMultiplier()). Contributors seen in the multiplier code are ×2 (Double bench bonus relic), ×1.5 (Butt Bench), a ×3 helmet-stack factor (HelmetStackOvrTalent.GetHelmetPowerupMultiplier), and a +X%/100 term (Double-Decker talent). (The mapping of each factor to a specific item is inferred.)

Hot / Cold benchwarmers#

After a run, a benchwarmer can be Hot (HotTalent, +15 OVR) or Cold (ColdTalent, −15 OVR). Both are hidden talents. The Hot & Cold relic grants a random +OVR and −OVR pair at game start.

Source: Rogue.NPC.ConditioningNPC.*Training, TrainingEvent::GetModifiedStatBonus, Rogue.VanillaTraining::get_TrainingMultiplier, Rogue.Relics.TrainingWhistle, Rogue.BenchBonus + BenchBonusController, Rogue.HotTalent/ColdTalent data.


8. Injuries (stat effect only)#

Each attribute is tied to one injury type, and an injured skater loses most of that attribute:

Injury (InjuryType) Attribute Effect
Eye (0) Accuracy stat − min(injuredStatPenalty, stat) with injuredStatPenalty = 100, so Accuracy drops to 0
Shoulder (1) Checking stat × injuredStatMultiplier, so ×0.2
Leg (2) Speed ×0.2
Arm (3) Shot Power ×0.2

The multiplier result is rounded. A specific talent (generic type not resolved) skips the injury check entirely. Other InjuryConfig values, for reference: knockout time added 8 s, base new-injury chance on a check 9%, same-team 0.5%, player max 3 natural injuries per act.

Source: ForwardStats::GetStatAfterInjuryCheck (types passed: Speed = 2, Checking = 1, Accuracy = 0, ShotPower = 3), Skaters.Injuries.InjuryConfig data.


9. Fatigue (Double Decker squad only)#

Stamina exists only when playing the "lines" squad (Double Decker Squad). At stamina s (0–1), every attribute is multiplied by:

statMult(s) = 1 − (1 − floor) × (1 − s)²        floor = 0.5

The result is rounded. Movement speed has its own, stronger multiplier, described in skating-and-movement.md.

Stamina 100% 75% 50% 25% 0%
Attribute multiplier 1.00 0.969 0.875 0.719 0.50

Source: ForwardStats::GetStatAfterFatigueCheck, Skaters.StaminaConfig, TeamStaminaSystem::Initialize (IsPlayingSquad("lines")).


10. What the attributes drive#

Speed is covered in skating-and-movement.md. The other mappings live in StatsUtil/ForwardStats (p = total shot-power stat, 0–300; a = accuracy):

Value Formula
Wrist-shot power max(10, 15 + 35·(p/100)^0.55)
Slapshot power max(13, 20 + 50·(p/100)^0.55)
Backhand power max(8, 12 + 27·(p/100)^0.55)
Batting power 12 + 27·(p/100)^0.55
Max wrist-shot angle 10 + 0.75·a (wristShotAngleMin/Increment)
Max slapshot angle 5 + 0.45·a
Slapshot / one-timer accuracy term 85·(a/100)^1.5 (exponent read as 1.5; use inferred)
Weight (GetWeight) Checking + 100
Shot-power stat 0 50 100 150 200 300
Wrist 15.0 38.9 50.0 58.7 66.2 79.0
Slap 20.0 54.2 70.0 82.5 93.2 111.5

See critical-shots.md for crits.

Source: Skaters.StatsUtil (data statUtil), ForwardStats::GetWristShotPower/GetSlapShotPower/GetMaxSlapshotAngle/GetSlapShotAccuracy/GetWeight.


11. Skater Size#

Data.SkaterSize: ExtraSmall 0, Small 1, Medium 2, Big 3, ExtraBig 4, ExtraExtraBig 5. The UI labels them XS/S/M/L/XL. The glossary says bigger skaters hit harder, absorb more, and take more space, while smaller skaters accelerate, stop and turn quicker.

Size can change during a run. ForwardData.SetSizeEffect/RemoveSizeEffect stack size steps, and SkaterSizeUtil.GetModifiedSize/Resize apply them (e.g. Shrink Serum).

Verified gameplay effects#

Effect XS S M L (Big) XL XXL Source
Body scale (GetBodySize, × sizeOffsetPercentage 0.9–1.1) 0.100 0.115 0.1375 0.16 0.19 0.23 ForwardData::GetBodySize
Relative to M 0.73 0.84 1.00 1.16 1.38 1.67
Supernova (overspeed) top-speed multiplier ×1.05 ×1.05 ×1.00 ×0.95 ×0.95 ×0.90 ForwardStats::GetMaxSpeedAtSupernova
Extra "min turning" (larger = wider turns, inferred) 0 0 0 0 +0.02 +0.04 ForwardStats::getMinTurning

Bodycheck size modifiers#

SkaterCollision.GetSizeBonus(checker, target) adds to the knockout score:

Situation Bonus
Checker is XXL +60 (always)
Checker bigger than target +30
Same size 0
Checker smaller than target −20

GetStaggerForceSizeMultiplier scales the stagger force by ×1.25 if the checker is bigger, ×0.75 if smaller, and ×1.0 if equal.

UI-only scale#

SkaterSizeToScaleSettings changes only portrait and preview scale: XS 0.925, S 0.97, L 1.05, XL 1.10, XXL 1.12. SkaterSizeData maps sizes to icons.

Not found: no size term in ForwardStats.GetAcceleration or GetMaxSpeed, and the capsule collider radius is not scaled by size in Forward.SetSize. The glossary's "smaller skaters accelerate/stop quicker" may come from physics mass or animation and was not verified.

Source: Data.ForwardData::GetBodySize, Skaters.SkaterCollision::GetSizeBonus/GetStaggerForceSizeMultiplier (consts SIZE_BONUS_*, STAGGER_FORCE_*), Customization.UI.SkaterSizeToScaleSettings data.