Shooting#
How shots are produced: shot types, how Shot Power becomes puck speed, how Accuracy becomes aim error, close-range and long-range rules, one-timers, bats and deflections.
Stats are normally 0–100 but can exceed 100 with bonuses.
1. Shot types#
Skaters.ShotType:
| Value | Name | Produced by |
|---|---|---|
| 0 | Wristshot | Tap shoot (SkaterAbility::WristShot) |
| 1 | Slapshot | Hold and release shoot (SkaterAbility::SlapShot, the _Slapshot coroutine) |
| 2 | Redirect | Deflections / headshot redirects |
| 3 | Bat | Swinging at an airborne puck (SkaterAbility::Bat) |
| 4 | Throw | Thrown puck (ThrowPuck) |
| 5 | OneTimer | One-timer (tracking/events; one-timer slapshots roll crits as Slapshot) |
| 6 | Yoyo | One-timer of a Yo-Yo ability puck |
Glossary (in-game): Wrist shot: "A quick shot performed by tapping the shot button. Less powerful than a slapshot." Slapshot: "hold the shot button to wind up, release to fire." One-Timer: "Shooting a pass the moment it arrives… Not considered a slapshot."
Game restrictions (State.GameRestriction) can disable shots: NoWristShot, NoSlapshot, NoBatting. Trying a restricted wrist shot makes the skater stagger instead (WristShot → Forward::Staggered).
Source: Skaters.ShotType, SkaterAbility::WristShot, SkaterAbility::VerifyRestriction.
2. Shot Power → puck speed#
All formulas use the skater's total Shot Power (ForwardStats::GetTotalShotPowerStats, which includes bonuses and curses such as CursedNoodle). x = max(0, ShotPower) / 100.
| Shot | Formula (verified) | Stat 0 | 25 | 50 | 75 | 100 | 150 | 200 |
|---|---|---|---|---|---|---|---|---|
| Wrist shot | max(10, 15 + 35·x^0.55) |
15.0 | 31.3 | 38.9 | 44.9 | 50.0 | 58.7 | 66.2 |
| Backhand wrist | max(8, 12 + 27·x^0.55), stat capped to 0–300 |
12.0 | 24.6 | 30.4 | 35.0 | 39.0 | 45.7 | 51.5 |
| Slapshot | max(13, 20 + 50·x^0.55) |
20.0 | 43.3 | 54.2 | 62.7 | 70.0 | 82.5 | 93.2 |
| Bat | 12 + 27·x^0.55 |
12.0 | 24.6 | 30.4 | 35.0 | 39.0 | 45.7 | 51.5 |
(x^0.55 is a Mathf.Pow call with exponent 0.55. Values are puck speed in world units per second (inferred unit).) The max() floors never take effect for non-negative stats.
Diminishing returns: going from 0 to 50 Shot Power gains more speed than going from 50 to 100.
ShotPowerOverride (set by some power-ups) replaces the slapshot power when present.
2.1 Modifiers applied at release#
Order for a wrist shot (GetWristShotDirection):
1. Base power: forehand formula, or the backhand formula when SkatingAnimation::IsBackhand (the shot direction falls in the backhand arc).
2. If critical: +CritPower (+50 base, see critical-shots.md) and +10 accuracy.
3. Close-range reduction, if power ≥ 15: t = clamp01((7.25 − distToNet) / 5), then power = max(15, power − power·0.2·t). At up to 2.25 units from the net, wrist shots lose 20% (but not below 15).
Order for a slapshot (<_Slapshot>::MoveNext):
1. Base power: ShotPowerOverride or GetSlapShotPower().
2. If critical: +CritPower.
3. If one-timer: +Random.Range(0, 15).
4. Close-range reduction, if power ≥ 20: power = max(20, power − power·0.5·t) with the same t. Point-blank slapshots lose up to 50%.
Wind-up time does not scale base slapshot power in the code examined. holdTime is used by Charge Shot (forced crit at full charge) and passed to power-ups (GetPowerupsInShots(holdTime, …)).
After the direction is built, release-time power-up hooks can still change the shot: ApplyWetNoodleDebuff, ApplyYipsDebuff, ApplySandShotDebuff, ApplyFairwayShotBoost, ApplyNoSlapshotPenalty, AimAtPost, plus golf-ball multipliers (GolfBallShotSpeedMultiplier = 0.8).
Source: Skaters.StatsUtil::GetWristShotPower/GetSlapshotPower/GetBackhandPower/GetBattingPower, ForwardStats::GetWristShotPower/GetSlapShotPower/GetBattingPower, SkaterAbility::GetWristShotDirection, SkaterAbility/<_Slapshot>d__100::MoveNext.
3. Accuracy → aim error#
3.1 Effective accuracy per shot type#
| Shot | Accuracy fed to the aim randomizer |
|---|---|
| Wrist shot (< 24 units from net) | Accuracy (+10 if crit) |
Wrist shot (≥ 24 units, SkatingConfiguration.distanceToManualShot = 24) |
0 (long shot, see §4) |
| Slapshot (< 24 units) | round(85 · (Accuracy/100)^1.5) (+10 if crit) |
| Slapshot (≥ 24 units) | 0, plus a random yaw of ±10° (unless the shooter uses AimMode.Pattern) |
Slapshot accuracy curve (before the crit bonus):
| Accuracy stat | 25 | 50 | 75 | 100 |
|---|---|---|---|---|
| Slapshot effective accuracy | 11 | 30 | 55 | 85 |
At equal stats, slapshots are far less accurate than wrist shots. For one-timer slapshots, an unidentified team relic adds (Speed − ForwardData base speed) to Accuracy before this curve (inferred).
3.2 Aim randomization (Skaters.ShotAccuracy::RandomizeShot)#
- Tier lookup:
index = clamp(RoundToInt(acc/10) − 1, 0, 9)intoGameManager.shotAccuracyDatas(Data.ShotAccuracyData). All tiers share oneDistributionCurve.
| Tier asset | maxX (horizontal) | maxY (vertical) |
|---|---|---|
| shotAccuracy1 | 0.50 | 0.55 |
| shotAccuracy2 | 0.48 | 0.53 |
| shotAccuracy3 | 0.46 | 0.50 |
| shotAccuracy4 | 0.44 | 0.48 |
| shotAccuracy5 | 0.42 | 0.45 |
| shotAccuracy6 | 0.39 | 0.42 |
| shotAccuracy7 | 0.35 | 0.38 |
| shotAccuracy8 | 0.30 | 0.33 |
| shotAccuracy9 | 0.24 | 0.26 |
| shotAccuracy10 | 0.18 | 0.20 |
(The list is assumed to run tier 1→10 in GameManager order (inferred).)
- Distance factor:
d = clamp01((distToNet − 3) / 13).distMult = lerp(0.5, 1.4 − 0.35·a, d)witha = clamp01(acc/100). Close shots get half the spread. Far shots get up to 1.4× (low accuracy) or 1.05× (100 accuracy). - Angle factor:
angleMult = 1 + 2.5 · clamp01((angleDot − 0.9) / −0.75). Straight-on shots (angleDot ≥ 0.9) get 1×, sharp-angle shots up to 3.5×. - Error magnitudes: horizontal
≈ distMult · maxX · angleMult · curve.Random(). Vertical usesmaxY, with the angle factor applied at half strength ((angleMult−1)·0.5 + 1). - Toward-center bias:
towardCenter = 0.45 + 0.3 · clamp01(acc/90)^0.8, blended toward 0.5 at bad angles. This is the chance that a miss is pulled toward the middle of the net instead of wide. Aims within ±0.15 (horizontal) or ±0.3 (vertical) of center pick a random side. - Near-perfect shot chance: before the normal error, a roll
Random.value < Pgives an almost-perfect shot (only ±0.03 jitter):P = ((0.2 + 0.5·a) · lerp(1 − 0.6·d, 1, 0.7·a) + 0.6·clamp01((8 − dist)/5)) · angleTerm angleTerm = 0.9·clamp01((angleDot − 0.15)/0.75) + c (c ≈ 0.1, unresolved constant) (inferred) - Post handling:
AvoidPostTarget/AvoidSidePostPathkeep the result out of a band around the posts (MinPostAvoidanceBand 0.25–MaxPostAvoidanceBand 0.35,PostAvoidanceInsideChance 0.8). Near the net (NoMissAboveNetDistance = 3) shots aren't sent over the crossbar (CrossbarClampInset 0.1) (constants verified, exact branch logic inferred).
A "perfect" aim (isPerfectShot = true, accuracy 100) skips the randomizer. The game uses it to compute the ideal direction first.
Stray Shot talent: shots are sent to CalculateWildMissTarget when Random.value < missChance. The data has missChance = 0.7, while the tooltip says 90%. It also gives +200 Shot Power.
Source: ShotAccuracy::RandomizeShot, GetSpreadFactor (0.5 − 0.45a), GetTowardCenterChance, GetNearPerfectChance, AdjustForLeftRightShot/HighLowShot, Data.ShotAccuracyData, SkaterAbility::GetShotDirectionFromController/GetAimPosition.
3.3 Aim cones (StatsUtil)#
StatsUtil defines max aim angles that grow with a stat (statUtil asset):
| Formula | Stat 0 | 50 | 100 | |
|---|---|---|---|---|
| Wrist shot max angle | 10 + 0.75·stat |
10° | 47.5° | 85° |
| Slapshot max angle | 5 + 0.45·stat |
5° | 27.5° | 50° |
ForwardStats::GetMaxSlapshotAngle(accuracy) uses the slapshot formula. No direct callers were found for these getters, so how they are used in gameplay is unverified (inferred: limits how far off the skater's facing a shot can be aimed).
3.4 Forehand / backhand arcs#
SkatingAnimations.ShotQuadrantSettings splits the shot direction, relative to the skater, into Backhand / Alt Forehand / Forehand arcs. Default boundaries: altForehandStart −151.1°, forehandStart −24.1°, forehandEnd 46.6°, with per-direction overrides (e.g. East −174.9/−20/40.5, South −129.8/−24.1/71.0). Shots in the backhand arc use backhand power and cannot crit unless the skater has a crit-modifier source.
4. Long shots, dump-ins and close-range clamps#
| Rule | Detail |
|---|---|
| Dump-in | A wrist shot from a human-controlled skater counts as a dump-in when IsOutOfZoneShot, the shot is not a headshot, and the skater has had the puck ≥ 0.15 s (FirstTouchShotWindow; first-touch shots are exempt). It uses GetDumpInDirection (consts: speed ×1.2, min speed 28, peak height 2.25–3.5, max angle 65°) |
| Long wrist shot (≥ 24 units) | Accuracy 0. Unless AimMode.Pattern, horizontal speed is clamped to Random(25, 35), vertical set to Random(6, 12), yaw rotated Random(−2°, 2°) |
| Long slapshot (≥ 24 units) | Accuracy 0 and Random(−10°, 10°) yaw. Out-of-zone slapshots that aren't IsLongRangeShotExempt get a further 10°–18° yaw (OutOfZoneSlapshotMinYaw 10, OutOfZoneSlapshotYaw 18) (inferred direction) |
| Low-speed vertical clamp | If the resulting shot speed < 19, the vertical velocity is clamped to [−10, 7] (both wrist and slap) |
| Goal-line clamp | ShotGoalLineClampBuffer = 0.2: the aim point is kept 0.2 inside the goal line (GetShotDirectionFromController) |
| Close-shot vertical cap | MaxCloseShotVerticalSpeed = 14 when flight time < CloseShotFlightTimeThreshold 0.16 s (constants only) |
| Open net | OpenNetAim retargets when the opposing net is abandoned (AbandonedNetGoalieDistance = 4) |
5. One-timers#
- A one-timer is a slapshot taken on a pass as it arrives (
SlapShot(..., oneTimer: true)). - Power:
+Random.Range(0, 15)speed on top of the slapshot formula. - Crit roll as
Slapshot. TheOneTimershot type is not passed, so Sweet Spot does nothing here (see critical-shots.md). - The goalie has less time to set up.
DifficultyManager.GetCpuGoalieOneTimerSaveBonusexists for CPU goalies (not traced). - Yo-Yo one-timers use shot type
Yoyo. At Yo-Yo Lv2 they are forced crits, and withoneTimerSelfSonicSlapthey set off a Sonic Slap with radius 8.
6. Bat shots#
SkaterAbility::Bat (glossary: "knock a flying puck out of the air"):
- Power
12 + 27·x^0.55(batting power), accuracy = Accuracy stat, aimed withShotType.Bat. - Mis-hit roll (puck above a height threshold, aim not
Pattern): ifRandom.value > 0.25andRandom(0,100) > Accuracy, the bat pops up: horizontal speedRandom(2.5, 14), verticalRandom(5, 25). Mis-hit chance ≈0.75 × (1 − Accuracy/100). - Another branch uses
1.4 ×batting power with a perfect aim (trigger condition not traced). A third branch bats the puck as a pass, using pass power plus a randominsideUnitCircleoffset. - Bats never roll for a crit.
7. Deflections / redirects#
- Glossary: "Changes the direction of a shot already in progress. Automatically occurs when a teammate is between the puck and the net during a shot."
- Deflections are tracked as touches (
State.TouchType:TipDeflect,HeadshotDeflect,BodyDeflect) and count toward goal and assist credit (LastTouches::OnDeflect). Puck.RedirectShotGraceTime = 0.1s (constant only). Headshot redirects useSkaterAbility::_Headshot(direction, redirect)andShotType.Redirect.- Redirects do not roll for a crit.
8. Sticks#
- Glossary: a skater without a stick can't shoot or pass until they get one. Spare sticks (Extra Stick) are used first when disarmed.
SkaterAbility::BreakStick/LoseStick(direction, xzForce, yForce, …)handle broken and lost sticks. Crit knockouts callKnockout(..., willLoseStick: true).- Glass Cannon (ShatterStrike): stick breaks on non-crit shots.
9. Posts and the goalie#
- Glossary: "Shots that hit the post without going in the net stay in play."
GoalPostraises post events (PuckOnPostEventArgs), used by relics such asFalseGoalOnPostController. - Goalie save chance is computed in
ShotQualityCalculator::CalculateShotQuality(goalie skill, distance, angle, time remaining, difficulty). Shot modifiers seen there: crit −25,CurveBallBonus10, shootout ×0.55, hard blue-line shots (speed 50–65) up to −12 save chance and up to +0.1 rebound chance. Goaltending has its own page.
Source: Skaters.ShotQualityCalculator constants and CalculateShotQuality.
Comments