Passing, Puck Control and Faceoffs#
Passing (power, saucer/lob passes, interception), pokechecks, possession and loose pucks, rebounds, faceoffs and the assist rule.
1. Passing#
1.1 Pass types#
Events.PassType: Normal (0), Yoyo (1), Megalob (2), Uninterceptable (3).
In-game glossary: Pass: "Send the puck to a teammate. The last two passers before a goal are credited with assists." Saucer pass: "A pass lofted through the air, over sticks. Hold the pass button to perform one."
1.2 Pass power#
If no explicit power is given, Forward::Pass uses Skater::GetPassPower():
power = 29 (39 under an unidentified condition, possibly goalie/AI) (inferred)
+ ExpressDelivery relic (+5 at Lv1, +5+5 = +10 at Lv2) "Increase the speed of your passes"
power = power × (1 + T/100) T = int % from an unidentified talent (field 0xC0) (inferred)
SkaterAbility::Pass then applies:
| Condition | Effect |
|---|---|
| Lobbed / saucer pass (not Yoyo/Megalob) | power × 0.85 |
| Lobbed pass while the team owns a particular relic (not Yoyo) | power × 0.80 instead of 0.85 (relic not identified) |
Normal pass and the passer has the QuickPasses talent |
power × QuickPasses.multiplier, and the pass becomes Uninterceptable |
| Passer is in their defensive zone and the team owns the zone relic | pass becomes Uninterceptable (matches Invisible Thread: "Your passes are uninterceptable in your zone") (relic id inferred) |
Requirements to pass (Forward::Pass): the skater must have the puck, _canPass must be true, and the game state must not be state 3 (a stoppage state). The receiver and lead point come from CalculatePass / EstimatePassExecutionTarget, which lead the receiver along their skating path (PassTargetMinConservatism = 0.88, StandardFrictionDecel = 0.625). Passes are kept out of your own net (AvoidOwnNetOnPass) and clamped for offside (ClampPassTargetForOffside). For CPU receivers, SkatingConfiguration predicts a stop zone in front of the opposing goalie (passReceiverGoalieRadius 2.5, passReceiverGoalLineHalfWidth 2.5, passReceiverMaxBrakeDeceleration 8).
Source: Skaters.Forward::Pass, Skaters.Skater::GetPassPower, Skaters.SkaterAbility::Pass, ::CalculatePass, Movements.SkatingConfiguration.
1.3 Interceptions#
Glossary: "Receiving an opponent's pass."
- Each fixed update, a skater intercepts a moving puck when their stick-to-puck distance (
Forward.currentStickPositionDistance) ≤ InterceptionDistance (Forward::Interception,OnFixedUpdate). - Right after a pass, the puck can't be intercepted for InterceptionImmunityAfterPass seconds (
Puck::OnPass).Uninterceptablepasses skip interception entirely. - Goalies read passes separately (
goaliePassInterceptChance, reaction delays).
Values per difficulty (Data.DifficultyLevel). "Player" values apply to skaters on human-controlled teams and "CPU" values to CPU skaters facing a human team (selection rule inferred from DifficultyManager getters):
| Difficulty | Interception dist. Player / CPU | Intercept immunity after pass Player / CPU (s) | Goalie pass-intercept chance |
|---|---|---|---|
| training | 0.15 / 0.85 | 0.50 / 0.06 | 0.10 |
| pro | 0.50 / 0.75 | 0.35 / 0.08 | 0.45 |
| veteran | 0.27 / 0.50 | 0.16 / 0.16 | 0.25 |
| nightmare | 0.27 / 0.50 | 0.16 / 0.16 | 0.65 |
1.4 Drop puck / flip#
Glossary: "Leave the puck on the ice, losing possession. Can be used as a drop pass to a trailing teammate. Hold the drop puck button to flip it into the air instead." Implemented by SkaterAbility::DropPuck(direction, duration) (the _DropPuck coroutine) and ThrowPuck(direction, xzForce, yForce) for the flip. MegaLob is a separate power-up pass type.
2. Pokechecks#
Glossary: "A stick jab that knocks the puck away from an opponent. Used automatically when a stick touches the puck."
Forward::Pokecheckfires automatically when the puck is possessed by an opponent andCanPokecheckCarrierForPredictionpasses. The stick must reach within PokecheckDistance of the puck. Some difficulties also require the contact to hold for pokecheckConsecutiveFrames frames.- Possession immunity: a carrier can only be pokechecked when
Time.time − timeWithPuck ≥ PokecheckImmunity, i.e. a short grace period after getting the puck. The asset field is named "Distance" but the code compares it against time (Forward::CanBePokeChecked). Carriers with theImmuneToPokecheckstatus tag, or with_canBePokecheckedfalse, can't be pokechecked. Referees have 999999 immunity. - After a pokecheck, the puck can't be picked up again for
pokecheckReceptionCooldownOther 0.7s (other skaters) /pokecheckReceptionCooldownPuckHolder 0.8s (the skater who lost it) (SkatingConfiguration) (exact application inferred).
| Difficulty | Pokecheck dist. Player / CPU | Pokecheck immunity Player / CPU (s) | Consecutive frames |
|---|---|---|---|
| training | 0.15 / 1.00 | 1.50 / 0.00 | 8 |
| pro | 0.80 / 1.05 | 1.00 / 0.25 | 6 |
| veteran | 0.55 / 0.55 | 0.75 / 0.20 | 3 |
| nightmare | 0.45 / 0.35 | 0.75 / 0.20 | 1 |
Immunity selection (verified in DifficultyManager::GetPokecheckImmunity): if the carrier's team has human players → Player value. Otherwise, if the opponent has humans → CPU value. Otherwise → Player value.
Goalie pokechecks (Goaltender.GoaltenderStates.PokecheckState) use goaliePokecheckDistanceMin/Max 0.5–1.75 and goaliePokecheckForceMin/Max 0.4–0.7. The multiplier is 0.5 on training and 1.0 elsewhere.
Source: Skaters.Forward::Pokecheck, ::CanBePokeChecked, DifficultyManager::GetPokecheckImmunity/GetPokecheckDistance/GetInterceptionDistance, Data.DifficultyLevel assets.
3. Possession and loose pucks#
- Glossary: "A puck no one controls. The first skater to reach it takes possession."
- Pickup is handled in
Forward::PickupPuck. It checksPuck::IsPossessed, game state, current pass info, and line of sight (HasObstacleBetweenSkaterAndPuck). It tracks the closest stick-to-puck approach over frames. Pucks moving at ≥ 35 speed use a frame-sampled closest-approach test, and slower pucks are picked up directly (inferred). A miss can still nudge a loose puck (TryPushLoosePuckWithSwing). - Losing the puck:
Skater::LosePuckPossession(force, puckPosition, pickupLockDuration)stops the skater from re-picking it forpickupLockDuration.Puck::DisableCollision(skater, time = 0.4, …)disables puck collision for a skater (default 0.4 s) after shots and passes. Forward._timeWithPuckrecords when possession started. It drives pokecheck immunity and the first-touch shot window (0.15 s, see shooting.md).
4. Rebounds#
- Glossary: "A puck the goalie stopped but couldn't hold, left loose in front of the net." "Some saves keep the puck; others leave a rebound."
- Scripted rebound speed caps (
Tape2Tape.Puckconstants):
| Contact | Max XZ speed | Max Y speed |
|---|---|---|
| Blocker | 18 | 8.5 |
| Glove | 10 | 8 |
| Body | 7 | 7.5 |
| Skater (shot block) | 32 | 8 |
- Hard blue-line shots (shot speed 50–65 from the point,
ShotQualityCalculator) add up to +0.1 rebound chance and +2 rebound threshold. - Crease Grease (FastRebound) gives +20% crit chance to shots taken within 2 s of a teammate's shot (see critical-shots.md).
5. Faceoffs#
Glossary: "Press the pass button just as the referee drops the puck on the ice to win the faceoff."
5.1 Resolution (FaceoffWithReferee::StandardFaceoffUpdate)#
- Each frame, after the puck is live (
isPuckLive; a 15 s total-time fallback also enables the check), the game reads each center's swipe-animation time (FaceoffState::GetTimeInAnim). This is the time since that center pressed (StickOnIce), or 500 if they haven't pressed. - The faceoff resolves on the first frame where either center's swipe time is in [0, 0.06) s.
- Winner: the center with the smaller swipe time. If only one center pressed, that center wins, since the other reads 500. If both pressed inside the 0.06 s window, the later presser wins (literal reading of the compare; edge case). The winner passes via
FaceoffWithReferee::Pass(Puck::ReleaseFromFaceoff, thenForward::Pass). - Pressing before the puck is live does nothing, because
StickOnIceonly works once_canPassis set.
5.2 CPU timing#
| Context | CPU press delay |
|---|---|
| Normal games | Countdown timer. On expiry the CPU presses and resets to Random.Range(0.15, 1.0) s |
| Campaign, pre-drop delay (act 1 / 2 / 3) | Random(0.2, 0.65) / Random(0.35, 0.95) / Random(0.5, 1.2) s (branches: act == 1, act == 2, else) |
| Campaign, drop reaction (act 1 / 2 / 3) | Random(0.16, 0.30) / Random(0.09, 0.20) / Random(0.045, 0.13) s |
| Campaign, retry delay (act 1 / 2 / 3) | Random(0.12, 0.25) / Random(0.08, 0.18) / Random(0.05, 0.12) s |
CPU centers react faster after the drop in later campaign acts, but wait longer before attempting.
5.3 Faceoff spots (Arena.FaceoffPreset)#
Each preset stores the puck position, the positions of all 10 skaters (C/LW/RW/LD/RD for North and South), two referee spots and a FaceoffZone.
| Preset | Puck (x, z) | Zone |
|---|---|---|
| center_faceoff | (0, 0) | CENTER |
| north_left_faceoff | (−7.22, 20.93) | TOP_LEFT |
| north_right_faceoff | (7.01, 20.93) | TOP_RIGHT |
| south_left_faceoff | (−7.22, −21.64) | BOTTOM_LEFT |
| south_right_faceoff | (7.01, −21.64) | BOTTOM_RIGHT |
| shootout_faceoff | (0, 1.5) | CENTER |
There are also scenario/tutorial presets (scenario1–9, practice_spot, onboard_spot, last_pass_scenario, national_anthem). After a stoppage, a preset is chosen by a per-preset score (<OnAfterStoppage>b__35_0), most likely nearest to the stoppage point (inferred). Other faceoff types: RiggedFaceoff, corner faceoffs (StartCornerFaceoff) and power-ups such as BananaFaceoff and FrogOnFaceoff.
6. Assists#
- Glossary: "The last two skaters on your team to touch the puck before the goal scorer are awarded an assist."
- Code:
State.LastToucheskeeps an ordered list of touches (TouchSkater { skater, touchType }). Touches include pickups, tip/headshot/body deflections, goalie pickups and faceoff launches.GetAssists()returns list entries [1] and [2]: entry [0] is the scorer, and up to two previous touchers get assists. The list is filtered by team and de-duplicated on pickup (OnPickup,OnReset) (exact filter inferred). - Goalies are recorded as touchers (
OnPickupGoalie), so a goalie can earn an assist.
Source: State.LastTouches::GetAssists, ::OnPickup, State.TouchType, GameStats.lastAssists.
Comments