Player class
#include <Jazz2/Actors/Player.h>
Represents a controllable player.
The player-controlled rabbit character (Jazz, Spaz or Lori in JJ2) that runs, jumps, fires weapons, collects items and takes damage. Each character has its own special move (e.g., buttstomp, uppercut or sidekick), can enter a temporary Sugar Rush and may be morphed into other forms such as the Frog.
Base classes
- class ActorBase
- Base class of an object.
Derived classes
- class MpPlayer
- Player in online session.
Public types
- struct CharacterTraits
- Constant per-character properties.
- struct InventoryState
- Collected items and weapon loadout of a player.
-
enum class Modifier : std::
uint8_t { None, Airboard, Copter, LizardCopter } - Modifier.
-
enum class SpecialMoveType : std::
uint8_t { None, Buttstomp, Uppercut, Sidekick } - Special move type.
- enum class InvulnerableType { Transient, Blinking, Shielded }
- Type of invulnerability.
- enum class FlightType { Normal, Cheat }
- Type of copter flight state.
Public static variables
- static float LegacyEnemyStompBounce constexpr
- Non-Reforged bounce off an enemy hit by a buttstomp (original 13 px/tick).
- static float LegacyLandingTolerance constexpr
- How far below a ledge's surface the non-Reforged player may arrive and still be put on top of it.
- static float LegacyFallSpeedCap constexpr
- Non-Reforged terminal fall speed (original 12 px/tick).
- static float LegacyButtstompSpeed constexpr
- Non-Reforged buttstomp descent speed (original 10 px/tick, the same for all three characters).
- static float LegacyButtstompDriftSpeed constexpr
- Non-Reforged sideways drift speed during a buttstomp (original 12000/65536 px/tick, doubled with Run).
- static float LegacyPushSpeed constexpr
- Non-Reforged speed while pushing a solid object (original 0.375 px/tick).
- static float LegacyVineSpeed constexpr
- Non-Reforged vine climbing speed cap (original 2 px/tick, doubled while Run is held).
- static float LegacyCopterDescentSpeed constexpr
- Non-Reforged copter descent speed (original 129/128 px/tick).
- static float LegacyCopterWindUp constexpr
- Rate the non-Reforged copter winds its descent up at (original 1/128 px/tick²).
- static float LegacyDoubleJumpSpeed constexpr
- Non-Reforged launch speed of Spaz's double jump (original 8 px/tick).
- static float LegacyDoubleJumpWindowTime constexpr
- How long Spaz's non-Reforged double jump stays available (original 30 ticks).
- static float LegacyUppercutSpeed constexpr
- Launch speed of Jazz's non-Reforged uppercut (original 6.59375 px/tick of travel).
- static float LegacyUppercutGravity constexpr
- Gravity while a non-Reforged uppercut is driving (original 2048/65536 px/tick²).
- static float LegacyUppercutTicks constexpr
- Ticks a non-Reforged uppercut drives for before ordinary gravity resumes (original 30).
- static float LegacyRFBlastSpeed constexpr
- Speed a non-Reforged RF blast throws the player at (original 8.0 px/tick).
- static float LegacyRFBlastRiseSpeed constexpr
- Upward kick a non-Reforged RF blast gives a player who is off the ground (original 12 px/tick).
- static float LegacyRFBlastLiftBase constexpr
- Upward kick the same blast gives a player standing on the floor, at zero range.
- static float LegacyRFBlastLiftSlope constexpr
- How much LegacyRFBlastLiftBase grows per pixel of range (original 0.1732 per px).
- static float LegacyRFBlastHoldTicks constexpr
- Ticks a non-Reforged RF blast holds the thrown speed for before friction resumes (original 30).
- static float LegacyRFBlastReach constexpr
- How close to a non-Reforged RF blast the player has to be to be thrown by it (original ~40 px).
- static float LegacySpazSidekickSpeed constexpr
- Non-Reforged sidekick launch speed for Spaz (original 16 px/tick).
- static float LegacySpazSidekickDistance constexpr
- Distance the driven part of Spaz's non-Reforged sidekick covers (original 440 px).
- static float LegacySpringHoldTicks constexpr
- Ticks a horizontal spring's launch speed is held before it clamps (original 4).
- static float LegacySpringRebuildTicks constexpr
- Ticks a spring launch's speed takes to rebuild after a double jump discards it (original 32).
- static float LegacyWarpOutControlTime constexpr
- Ticks after a warp lands before control comes back outside Reforged (original 2 ticks).
- static float LegacyLoriSidekickDistance constexpr
- Distance one of Lori's non-Reforged kicks covers (original 204.75 px).
- static float LegacyLoriKickPushDrain constexpr
- Rate a blocked kick of Lori's spends LegacyLoriSidekickDistance at, per frame.
- static float LegacyLoriKickRamp constexpr
- Quadratic ramp of Lori's non-Reforged kick, per tick squared.
- static float LegacyLoriSidekickSpeed constexpr
- Non-Reforged sidekick launch speed for Lori (original 42.25 px/tick).
- static float LegacyPoleLaunchBonus constexpr
- Non-Reforged speed a vertical pole adds to the one the player arrived with (original 15.625 px/tick).
- static float LegacyHPoleLaunchBonus constexpr
- Non-Reforged speed a horizontal pole adds to the one the player arrived with (original 8 px/tick).
- static float LegacyHPoleMaxLaunch constexpr
- Ceiling on the non-Reforged horizontal pole launch (original 20 px/tick).
- static float LegacyFloatUpSpeed constexpr
- Speed a non-Reforged float-up area holds the player at (original 8 px/tick).
- static float LegacyFloatUpFallCap constexpr
- Fastest a non-Reforged player descends while inside a float-up area (original 4 px/tick).
- static float LegacyWindFactor constexpr
- Pixels a non-Reforged wind area moves the player per unit of its strength (original 0.5).
- static float LegacyBeltFactor constexpr
- Pixels a non-Reforged belt moves the player per unit of its strength (original 1.0).
- static float LegacyAccBeltSpeed constexpr
- Speed a non-Reforged accelerating belt drives the player to, per unit of strength (orig 1.5).
- static float LegacyAccBeltStep constexpr
- How much an accelerating belt adds per tick, per unit of strength (original 0.5 px/tick^2).
-
static std::
int32_t LegacyAccBeltRunBonus constexpr - Strength a non-Reforged accelerating belt gains while Run is held (original 8).
- static float LegacyCarryExitRunSpeed constexpr
- Speed a non-Reforged carry clamps to when it ends with Run held (original 16 px/tick).
Public static functions
-
static auto GetInvulnerableStrength(InvulnerableType type) -> std::
int32_t constexpr - How much of an effect a InvulnerableType shows, for comparing two of them.
- static auto GetCharacterTraits(PlayerType type) -> const CharacterTraits&
- Returns constant per-character properties for a given player type.
-
static void DrawShield(RenderQueue& renderQueue,
ShieldType shieldType,
float shieldTime,
Metadata* metadata,
float elapsedFrames,
Vector2f pos,
std::
uint16_t baseLayer, std:: unique_ptr<RenderCommand>(&shieldRenderCommands)[2]) - Draws the active shield decoration around a position.
-
static void SpawnSugarRushStar(ILevelHandler* levelHandler,
Metadata* metadata,
Vector2f pos,
std::
uint16_t baseLayer) - Spawns a single star of the sugar rush trail at a position.
Constructors, destructors, conversion operators
Public functions
-
auto GetPlayerIndex() const -> std::
uint8_t - Returns player index.
- auto GetPlayerType() const -> PlayerType
- Returns player type.
-
auto GetEffectiveFurColor() const -> std::
uint32_t - Returns the fur color to actually use for this player.
-
auto GetPaletteOffset() const -> std::
int32_t - Returns this player's flat offset into the shared palette texture (for the palette-aware shader).
- auto GetSpecialMove() const -> SpecialMoveType
- Returns current special move.
-
auto GetWeaponAmmo() const -> ArrayView<const std::
uint16_t> - Return weapon ammo.
-
auto GetWeaponUpgrades() const -> ArrayView<const std::
uint8_t> - Returns weapon upgrades.
- auto HasSugarRush() const -> bool
- Returns
trueif sugar rush is active. - auto CanJump() const -> bool
- Returns
trueif the player can jump. - auto CanBreakSolidObjects() const -> bool
- Returns
trueif the player can bread solid objects. - auto CanMoveVertically() const -> bool
- Returns
trueif the player can move vertically, i.e. not affected by gravity. - auto IsInWater() const -> bool
- Returns
trueif the player is currently in water. - auto IsContinuousJumpAllowed() const -> bool virtual
- Returns
trueif continuous jump is allowed. - auto IsLedgeClimbAllowed() const -> bool virtual
- Returns
trueif ledge climbing is allowed. - auto GetCameraLookAhead() const -> float
- Returns how far the non-Reforged camera should lead the player horizontally, in pixels.
- auto GetCameraVerticalOffset() const -> float
- Returns how far the non-Reforged camera should drop below the player, in pixels.
-
auto OnLevelChanging(Actors::
ActorBase* initiator, ExitType exitType) -> bool virtual - Called when the level is about to change.
- void ReceiveLevelCarryOver(ExitType exitType, const PlayerCarryOver& carryOver) virtual
- Called at the beginning of the next level to reveive carry over information.
- auto PrepareLevelCarryOver() -> PlayerCarryOver virtual
- Returns current carry over information.
- void ReceiveReturnCarryOver(ExitType exitType, const PlayerCarryOver& carryOver)
- Called when the player returns to a restored level to receive carry over information.
-
void InitializeFromStream(ILevelHandler* levelHandler,
Stream& src,
std::
uint16_t version) - Initializes player state from a stream.
- void SerializeResumableToStream(Stream& dest)
- Serializes player state to a stream.
- auto Respawn(Vector2f pos) -> bool virtual
- Respawns the player.
- void WarpToPosition(Vector2f pos, WarpFlags flags) virtual
- Warps to a given position.
- void WarpToCheckpoint()
- Warps to the last checkpoint.
- auto GetModifier() const -> Modifier
- Returns current modifier.
-
auto SetModifier(Modifier modifier,
const std::
shared_ptr<ActorBase>& decor = nullptr) -> bool virtual - Sets current modifier.
- auto IsFlyCheatActive() const -> bool
- Returns whether fly-cheat behavior is currently active.
-
void SetCopterFlight(float timeLeft,
FlightType type = FlightType::
Normal) - Sets copter flight duration and type (
timeLeft <= 0disables flight cheat state). - void EnableFlyCheat(bool active) virtual
- Enables or disables fly cheat behavior, enabling it applies to the next cheat flight activation.
-
auto TakeDamage(std::
int32_t amount, float pushForce = 0.0f, bool ignoreInvulnerable = false) -> bool virtual - Takes damage.
- auto Freeze(float timeLeft) -> bool virtual
- Freezes the player for specified time.
- void SetInvulnerability(float timeLeft, InvulnerableType type) virtual
- Sets invulnerability to exactly
timeLeftoftype, replacing whatever is running. - void GrantInvulnerability(float timeLeft, InvulnerableType type)
- Grants invulnerability without ever weakening what is already running.
- auto GetInvulnerableType() const -> InvulnerableType
- Returns the effect the currently running invulnerability is showing.
-
auto GetScore() const -> std::
int32_t - Returns score.
-
void AddScore(std::
int32_t amount) virtual - Adds score.
-
auto AddHealth(std::
int32_t amount) -> bool virtual - Adds health.
-
auto GetLives() const -> std::
int32_t - Returns lives.
-
auto AddLives(std::
int32_t count) -> bool virtual - Adds lives.
-
auto GetCoins() const -> std::
int32_t - Returns coins.
-
void AddCoins(std::
int32_t count) - Adds coins.
-
void AddCoinsInternal(std::
int32_t count) - Adds coins without notification (internal use only).
-
auto GetGems(std::
uint8_t gemType) const -> std:: int32_t - Returns gems.
-
void AddGems(std::
uint8_t gemType, std:: int32_t count) - Adds gems.
-
auto GetConsumedFood() const -> std::
int32_t - Returns food eaten.
- void ConsumeFood(bool isDrinkable)
- Consumes food.
- void ActivateSugarRush(float duration)
- Activates sugar rush.
- void DeactivateSugarRush()
- Deactivates sugar rush immediately and restores the default renderer.
-
auto AddAmmo(WeaponType weaponType,
std::
int16_t count) -> bool virtual - Adds weapon ammo.
-
void AddWeaponUpgrade(WeaponType weaponType,
std::
uint8_t upgrade) virtual - Adds weapon upgrade.
-
auto AddFastFire(std::
int32_t count) -> bool - Adds fast fire.
- auto MorphTo(PlayerType type) -> bool virtual
- Morphs to a given player type.
- auto MorphToInstantly(PlayerType type) -> bool virtual
- Changes the player type immediately, without any transition or effect.
- void MorphRevert()
- Reverts morpth to the original player type.
- auto SetDizzy(float timeLeft) -> bool virtual
- Sets duration of dizziness.
- auto ApplyBlastKnockback(bool pushLeft, float distanceSqr) -> bool
- Throws the player away from a shot's blast.
- auto GetActiveShield() const -> ShieldType
- Returns active shield.
- auto SetShield(ShieldType shieldType, float timeLeft) -> bool virtual
- Sets active shield.
- auto IncreaseShieldTime(float timeLeft) -> bool virtual
- Increases active shield time.
-
auto SpawnBird(std::
uint8_t type, Vector2f pos) -> bool - Spawns bird companion.
- auto DisableControllable(float timeout) -> bool
- Disables controls for specified time.
- void SetCheckpoint(Vector2f pos, float ambientLight)
- Sets checkpoint.
- auto GetCurrentAmbientLight() const -> float
- Returns the ambient light intensity currently applied to this player.
- void SetCurrentAmbientLight(float value)
- Records the ambient light intensity currently applied to this player, called by the level handler.
- void SetCheckpointAmbientLight(float ambientLight)
- Overrides the ambient light intensity to restore when respawning at the checkpoint.
- auto GetCarryingObject() const -> ActorBase*
- Returns carrying object.
- void CancelCarryingObject(ActorBase* expectedActor = nullptr)
- Cancels carrying object.
-
void UpdateCarryingObject(ActorBase* actor,
SuspendType suspendType = SuspendType::
None) - Updates carrying object.
-
void SwitchToWeaponByIndex(std::
uint32_t weaponIndex) - Switches current weapon to a given index.
- void GetFirePointAndAngle(Vector3i& initialPos, Vector2f& gunspotPos, float& angle)
- Returns weapon fire point and angle.
- void ResetRevUpState()
- Clears all run-in-place rev-up state, so it cannot survive a teleport or a probe reset.
Protected types
- enum class LevelExitingState { None, Waiting, WaitingForWarp, Transition, Ready }
- State of level exiting.
- enum class WeaponWheelState { Hidden, Opening, Visible, Closing }
- State of HUD weapon wheel.
- enum class SetCurrentWeaponReason { Unknown, User, Rollback, AddAmmo, AddUpgrade, Shield }
- Reason the current weapon was changed.
Protected functions
- auto OnActivatedAsync(const ActorActivationDetails& details) -> Task<bool> override
- Called when the object is created and activated.
- auto OnTileDeactivated() -> bool override
- Called when corresponding tile should be deactivated.
- auto OnPerish(ActorBase* collider) -> bool override
- Called when the object has no health left and should perish.
- void OnUpdate(float timeMult) override
- Called every frame to update the object state.
- void OnSerializeState(Stream& dest) override
- Serializes the live state of the object to a level state snapshot.
- void OnDeserializeState(Stream& src) override
- Restores the live state of the object from a level state snapshot.
- void OnUpdateHitbox() override
- Called when the hitbox needs to be updated.
- auto OnDraw(RenderQueue& renderQueue) -> bool override
- Called when the object needs to be drawn.
- void OnEmitLights(SmallVectorImpl<LightEmitter>& lights) override
- Called when emitting lights.
- void OnEmitRemotedLights(SmallVectorImpl<LightEmitter>& lights) override
- Called when emitting lights that have to be replicated to remote peers.
- auto OnHandleCollision(ActorBase* other) -> bool override
- Called when the object collides with another object.
- void OnHitFloor(float timeMult) override
- Called when the object hits a floor.
- void OnHitCeiling(float timeMult) override
- Called when the object hits a ceiling.
- void OnHitWall(float timeMult) override
- Called when the object hits a wall.
- auto GetGravityModifier(float baseGravity, bool isRising) const -> float override
- Returns the gravity applied this frame, allowing it to be direction-dependent (e.g., the player's asymmetric jump arc).
- auto ApplyPlayerBump(Player& other, bool stackingEnabled) -> bool
- Keeps this player and
otherfrom overlapping (so they can't pass through) and bumps them apart. - void UpdatePlayerStacking(float timeMult, bool snap)
- Resolves this player standing on top of another player as a one-way platform.
- void DecreaseShieldTime(float time) virtual
- Reduces remaining shield time when a hit is absorbed (only if more than
timeremains). - void OnHandleSpectate(float timeMult) virtual
- Called each frame while the player is in PlayerType::
Spectate mode. - void OnPushSolidObject(float timeMult, float pushSpeedX) virtual
- Called when a solid object is pushed.
- void OnHitSpring(Vector2f pos, Vector2f force, bool keepSpeedX, bool keepSpeedY, bool& removeSpecialMove) virtual
- Called when a spring is hit.
- void OnWaterSplash(Vector2f pos, bool inwards) virtual
- Called when water should splash.
-
auto PlayPlayerSfx(StringView identifier,
float gain = 1.0f,
float pitch = 1.0f) -> std::
shared_ptr<AudioBufferPlayer> - Plays a sound effect for the player.
- auto SetPlayerTransition(AnimState state, bool cancellable, bool removeControl, SpecialMoveType specialMove, Function<void()>&& callback = {}) -> bool
- Starts a player animation transition.
- auto CanFreefall() -> bool
- Returns
trueif the player should freefall. - void EndDamagingMove()
- Ends active damaging move.
- auto FireCurrentWeapon(WeaponType weaponType) -> bool virtual
- Fires currently equipped weapon.
- void EmitWeaponFlare() virtual
- Emits weapon flare after firing.
- void SetCurrentWeapon(WeaponType weaponType, SetCurrentWeaponReason reason) virtual
- Sets current weapon.
Protected static variables
- static const char* WeaponNames constexpr
- Display names of all weapons.
Constants
- static float MaxDashingSpeed protected constexpr
- Maximum horizontal speed while dashing.
- static float MaxRunningSpeed protected constexpr
- Maximum horizontal speed while running.
- static float MaxVineSpeed protected constexpr
- Maximum speed while climbing a vine.
- static float MaxDizzySpeed protected constexpr
- Maximum horizontal speed while dizzy.
- static float MaxShallowWaterSpeed protected constexpr
- Maximum horizontal speed in shallow water.
- static float Acceleration protected constexpr
- Horizontal acceleration.
- static float Deceleration protected constexpr
- Horizontal deceleration.
- static float MaxPushingSpeed protected constexpr
- Speed above which the player is no longer considered to be pushing.
Non-Reforged movement constants
Measured off the original game rather than guessed: the probe in _pt.j2as drives the player through a fixed matrix of scripted input and logs its position and speed every tick, in both games, so these can be compared run for run. The original ticks at a measured 70.02 Hz and works in 1/65536 px units, which is why its values come out as clean fractions of 65536.
Everything below is the original's own per-tick figure times a conversion to this engine's 60 Hz timeMult baseline: velocities by LegacyFrameRateScale, accelerations by its square, because position is the double integral of acceleration. Scaling both exactly preserves jump height and real-time duration at once, so no further tuning factor belongs here.
- static float LegacyTickRate constexpr
- Tick rate of the original game.
- static float LegacyFrameRateScale constexpr
- Velocity scale that maps original per-tick values (px/tick at 70 Hz) onto this engine's 60 Hz timeMult baseline.
- static float LegacyFrameRateScaleSqr constexpr
- Acceleration scale for the same mapping, squared because position is the double integral of acceleration.
- static float LegacyWalkSpeed constexpr
- Non-Reforged walk speed cap (original 4 px/tick).
- static float LegacyDashSpeed constexpr
- Non-Reforged dash speed cap while Run is held (original 16 px/tick).
- static float LegacyWalkAccel constexpr
- Non-Reforged horizontal acceleration with no Run (original 12000/65536 px/tick^2).
- static float LegacyDashAccel constexpr
- Non-Reforged horizontal acceleration while Run is held (original 24000/65536 px/tick^2).
- static float LegacyWalkDecel constexpr
- Non-Reforged deceleration with no direction held (original 8000/65536 px/tick^2).
- static float LegacyDashDecel constexpr
- Non-Reforged deceleration with no direction held while the dash is still active (original 28000/65536 px/tick^2).
- static float LegacySlideWalkDecel constexpr
- Non-Reforged deceleration on a slide tile of strength 0, no direction held (original 4000/65536).
- static float LegacySlideDashDecel constexpr
- Non-Reforged deceleration on a strength-0 slide while the dash is active (original 12000/65536).
- static float LegacySlideWalkDecelStep constexpr
- How much each step of a slide tile's Strength takes off LegacySlideWalkDecel (orig 800/65536).
- static float LegacySlideDashDecelStep constexpr
- The same step for LegacySlideDashDecel (original 2500/65536 px/tick^2 per strength).
- static float TubeControlTime constexpr
- How long a sucker tube keeps hold of the player (original 16 ticks).
- static float TubeSnapY constexpr
- Where in its tile a horizontal sucker tube holds the player (original: 15 px below the top).
- static float LegacyDashGraceTicks constexpr
- Ticks the non-Reforged dash outlives the Run key (original 16 ticks).
- static float LegacyCameraWalkLead constexpr
- How far the non-Reforged camera leads the player while walking (original 28 px).
- static float LegacyCameraDashLead constexpr
- How far the non-Reforged camera leads the player while dashing (original 120 px).
- static float LegacyCameraButtstompDrop constexpr
- How far the non-Reforged camera drops during a buttstomp, so more is visible below (original 60 px).
- static float LandAnimFrameTicks constexpr
- Ticks the non-Reforged landing pose spends on each of its frames.
- static float SidekickEndSpeedUp constexpr
- How much faster the sidekick's ending animation plays, in both modes.
- static float LegacyRevUpJumpBoost constexpr
- Extra launch speed a jump out of a run-in-place gets, worth about a tile of height.
- static float LegacyJumpSpeed constexpr
- Non-Reforged jump launch speed from a standstill (original 10 px/tick).
- static float LegacySpeedJumpScale constexpr
- Extra non-Reforged launch speed per unit of horizontal speed (original exactly 1/4).
- static float LegacyRiseGravity constexpr
- Non-Reforged rise gravity while the jump key is held (original 0.375 px/tick^2).
- static float LegacyRiseGravityReleased constexpr
- Non-Reforged rise gravity once the jump key is released (original 0.875 px/tick^2).
- static float LegacyRiseGravityReleasedNearApex constexpr
- Non-Reforged rise gravity once the jump key is released and the rise is nearly spent (original 0.625 px/tick^2).
- static float LegacyRiseBrakeEaseSpeed constexpr
- Rise speed below which the non-Reforged released rise eases off (original 1 px/tick).
- static float LegacyFallGravity constexpr
- Non-Reforged fall gravity (original 0.125 px/tick^2).
- static float LegacyWaterGravity constexpr
- Non-Reforged underwater gravity (original 1024/65536 px/tick^2 - a very slow sink).
- static float LegacyAppliedSpeedCap constexpr
- Non-Reforged cap on applied movement (the original clamps it to 8 px/tick on both axes).
- static float LegacyRiseSpeedCap constexpr
- Non-Reforged applied upward-speed cap - the same limit, on the vertical axis.
- static float LegacyVerticalSpeedLimit constexpr
- Non-Reforged ceiling on internal speed, on either axis (original 32 px/tick).
Flying carrot
Measured from a hand-played recording, because scripted input cannot reach it: with the carrot collected and jjPLAYER.fly reading FLYCARROT for the whole run, eighty ticks of scripted Jump move the original's player not at all — not even an ordinary jump. Left and Right do get through, so it is specifically the flight that reads the raw key, exactly as the rev-up does.
The recording is 3871 ticks of continuous flight and settles every figure here. Vertically it is two accelerations and nothing else: hold Up and the player accelerates upwards at a flat 0.25 per tick (934 ticks of the recording step by exactly that) to the ordinary LegacyVerticalSpeedLimit of 32; let go and they accelerate downwards at 0.0625 (1243 ticks) to a terminal of 12. Horizontally it is ordinary ground movement, 4 px/tick walking and 16 with Run held. Jumping inside the flight is an ordinary jump, launching at −10 and decaying at the usual held and released rates.
Neither probe can log the Up key — JJ2+ exposes no keyUp — so those 934 ticks appear in the trace with every logged key at zero. That is what a rise with no input means here.
Down is not measured: the recording never presses it, so it falls like no input rather than inventing a rate for the "descends slowly on Down" the mechanic is reported to have.
- static float LegacyFlyRiseAccel constexpr
- static float LegacyFlyRiseBrake constexpr
- What a climb decays at once Up is let go, until it reaches zero (original 0.75 px/tick^2).
- static float LegacyFlyRiseBrakeHeld constexpr
- What a climb decays at with Jump held (original 0.25 px/tick^2).
- static float LegacyFlyDescentSpeed constexpr
- The descent Down holds the player to (original 1.0625 px/tick).
- static float LegacyFlyFallAccel constexpr
- Downward acceleration once the flight is actually falling, to LegacyFlyTerminalSpeed.
- static float LegacyFlyTerminalSpeed constexpr
- Terminal speed of the flight's unpowered descent (original 12 px/tick).
- static float LegacyLizardCopterDuration constexpr
- How long a ride on the level's lizard copter lasts outside Reforged (original 279 ticks).
- static float LegacyFlyDuration constexpr
- How long the flight lasts, which is for as long as the player keeps it.
Run-in-place wind-up accumulator
The wind-up is entered on a charge rather than on a tap count, which is the only shape that fits all of what the recordings show: two taps never start it however they are spaced; three do if the last one is held longer; four do without holding; tapping faster reaches it sooner; gaps of about 20 ticks or more never reach it at all because the decay outruns the gain; and once it is running there is a grace in which another tap resumes it rather than starting over, which is simply the charge not having decayed to nothing yet.
The shape is measured, the four numbers are fitted to the boundary cases above rather than read off a trace - a human tapping cannot separate gain-per-tap from gain-per-held-tick closely enough for that. They are named for what they do, not Legacy*, for the reason the block above gives.
- static float RevUpChargeGainTap constexpr
- What one press is worth.
- static float RevUpChargeGainHeld constexpr
- static float RevUpChargeHeldCap constexpr
- The most one press can add by being held.
- static float RevUpChargeDecay constexpr
-
static std::
int32_t RevUpPressesRequired constexpr - Presses below which nothing happens at all, whatever the charge.
- static float RevUpChargeLightThreshold constexpr
- Enough to launch when the tapping stops, but not to show the wind-up.
- static float RevUpChargeThreshold constexpr
- Where the wind-up itself begins.
Sliding to a halt
The original runs a fixed chain of three animations, and only one boundary in it is a speed. walk_stop plays once for its own length whatever the player was doing, dash_stop then loops until the speed falls below this, and run_stop plays once and hands over to standing - which is why walk_stop is exported for all three characters and was read by nothing here. Measured on an_slide_stop (released at 16 px/tick), g_walk_rel20 and sd_walk (released at 4, one of them on a slide tile), which disagree on every speed and agree on the first stage lasting exactly 12 ticks.
Deliberately not named Legacy* even though it was measured like one, and deliberately not gated on ILevelHandler::Legacy* constant on this class is legacy-only, and that invariant is what makes the prefix worth reading. Reforged runs the same chain because the chain is an animation, not a movement model - it reads the speed once and changes none of it, so it costs Reforged nothing to share and leaves one description of stopping instead of two. Only this boundary is in speed units at all, and the rest of the chain is timed, so the two modes' different friction simply makes the middle stage longer or shorter.
- static float StopSettleSpeed constexpr
- static float LegacyDashAnimSpeed constexpr
- Speed above which the dash animation is shown, measured on
an_run_start. - static float LegacyRunAnimFrameTicks constexpr
- Ticks one frame of a ground-run animation lasts at a standstill, before the speed is taken off it.
- static float LegacyRunAnimMinFrameTicks constexpr
- The floor on LegacyRunAnimFrameTicks — one frame per tick is all the original can draw.
- static AnimState HorizontalAnimMask constexpr
- The two-bit horizontal speed field of a composite animation state.
- static AnimState VerticalAnimMask constexpr
- The vertical field beside it, AnimState::
Jump | AnimState:: Fall. -
static std::
int32_t StopPhaseNone constexpr - Not sliding to a halt - moving under the player's own steering, or not on the ground at all.
-
static std::
int32_t StopPhaseSkid constexpr walk_stop, played once for its own 12 ticks however fast the player was going-
static std::
int32_t StopPhaseSlide constexpr dash_stop, looping until the speed falls below StopSettleSpeed-
static std::
int32_t StopPhaseSettle constexpr run_stop, played once and followed by standing, even after the player has stopped-
static std::
int32_t StopPhaseHold constexpr - The skid held on its last frame, for a slide too slow to have reached StopPhaseSlide.
Enum documentation
enum class Jazz2:: Actors:: Player:: Modifier : std:: uint8_t
Modifier.
| Enumerators | |
|---|---|
| None |
No modifier |
| Airboard |
Riding an airboard |
| Copter |
Using a copter |
| LizardCopter |
Using a lizard copter |
enum class Jazz2:: Actors:: Player:: SpecialMoveType : std:: uint8_t
Special move type.
| Enumerators | |
|---|---|
| None |
No special move |
| Buttstomp |
Buttstomp |
| Uppercut |
Uppercut |
| Sidekick |
Sidekick |
enum class Jazz2:: Actors:: Player:: InvulnerableType
Type of invulnerability.
| Enumerators | |
|---|---|
| Transient |
Invulnerable without any visual effect |
| Blinking |
Invulnerable with blinking effect |
| Shielded |
Invulnerable due to an active shield |
enum class Jazz2:: Actors:: Player:: FlightType
Type of copter flight state.
| Enumerators | |
|---|---|
| Normal |
Timed/regular flight |
| Cheat |
Cheat-enabled flight |
enum class Jazz2:: Actors:: Player:: LevelExitingState protected
State of level exiting.
| Enumerators | |
|---|---|
| None |
Not exiting |
| Waiting |
Waiting before exiting |
| WaitingForWarp |
Waiting for a warp to complete |
| Transition |
Playing the exit transition |
| Ready |
Ready to exit |
enum class Jazz2:: Actors:: Player:: WeaponWheelState protected
State of HUD weapon wheel.
| Enumerators | |
|---|---|
| Hidden |
Hidden |
| Opening |
Opening |
| Visible |
Visible |
| Closing |
Closing |
enum class Jazz2:: Actors:: Player:: SetCurrentWeaponReason protected
Reason the current weapon was changed.
| Enumerators | |
|---|---|
| Unknown |
Unspecified |
| User |
Set by the user |
| Rollback |
Set due to rollback |
| AddAmmo |
Set because an ammo for a new weapon was collected |
| AddUpgrade |
Set because a new upgrade for a weapon was collected |
| Shield |
Set because a shield was activated |
Function documentation
static std:: int32_t Jazz2:: Actors:: Player:: GetInvulnerableStrength(InvulnerableType type) constexpr
How much of an effect a InvulnerableType shows, for comparing two of them.
GrantInvulnerability() will not replace a running effect with one that ranks lower. The order is the declaration order, but spelled out here because it is a rule rather than an accident of it.
static void Jazz2:: Actors:: Player:: DrawShield(RenderQueue& renderQueue,
ShieldType shieldType,
float shieldTime,
Metadata* metadata,
float elapsedFrames,
Vector2f pos,
std:: uint16_t baseLayer,
std:: unique_ptr<RenderCommand>(&shieldRenderCommands)[2])
Draws the active shield decoration around a position.
| Parameters | |
|---|---|
| renderQueue | Render queue the shield draw commands are added to |
| shieldType | Active shield type |
| shieldTime | Remaining shield time, in frames (drives the fade-in/out alpha and scale) |
| metadata | Metadata holding the shield animations (the player's metadata) |
| elapsedFrames | Elapsed level frames, used to animate the shield |
| pos | World position the shield is centered on |
| baseLayer | Base render layer; the shield is drawn just behind and in front of it |
| shieldRenderCommands | Render commands owned by the caller, reused across frames |
Shared by the locally-controlled Player and by remote players (rendered as Actors::
static void Jazz2:: Actors:: Player:: SpawnSugarRushStar(ILevelHandler* levelHandler,
Metadata* metadata,
Vector2f pos,
std:: uint16_t baseLayer)
Spawns a single star of the sugar rush trail at a position.
| Parameters | |
|---|---|
| levelHandler | Level handler whose tile map owns the debris |
| metadata | Metadata holding the star animation (the player's metadata) |
| pos | World position the star is spawned at |
| baseLayer | Base render layer; the star is drawn just behind it |
Shared by the locally-controlled Player and by remote players (rendered as Actors::
std:: uint32_t Jazz2:: Actors:: Player:: GetEffectiveFurColor() const
Returns the fur color to actually use for this player.
The configured color, or 0 = none when the "Apply Colors" preference disables recoloring in the current session (or for this player index).
std:: int32_t Jazz2:: Actors:: Player:: GetPaletteOffset() const
Returns this player's flat offset into the shared palette texture (for the palette-aware shader).
Returns -1 if the player is not being recolored.
float Jazz2:: Actors:: Player:: GetCameraLookAhead() const
Returns how far the non-Reforged camera should lead the player horizontally, in pixels.
Zero unless a direction is actually held: measured, the original aims the lead at the input rather than at the speed, which is why a sidekick carrying 16 px/tick with nothing pressed does not pan the view at all. See Rendering::
float Jazz2:: Actors:: Player:: GetCameraVerticalOffset() const
Returns how far the non-Reforged camera should drop below the player, in pixels.
Zero except during a buttstomp, which is the one place the original leads vertically at all — it pans down through the wind-up so more of what is below comes into view, holds while the stomp runs, and returns to centre when it ends. See LegacyCameraButtstompDrop.
void Jazz2:: Actors:: Player:: ReceiveReturnCarryOver(ExitType exitType,
const PlayerCarryOver& carryOver)
Called when the player returns to a restored level to receive carry over information.
The player was already restored from the level state snapshot, so only the progress made in the (special) level it's returning from is applied. The speed the player had when it left is dropped, and the level exit the player is standing on is ignored until the player steps off it.
void Jazz2:: Actors:: Player:: SetInvulnerability(float timeLeft,
InvulnerableType type) virtual
Sets invulnerability to exactly timeLeft of type, replacing whatever is running.
The unconditional setter, and the one a server's authoritative state is applied through - so it has to be able to LOWER the value and to swap a stronger effect for a weaker one, otherwise a correction can never arrive. timeLeft <= 0 clears it.
Gameplay wants GrantInvulnerability() instead: picking something up should not be able to cut short an invulnerability that is already running.
void Jazz2:: Actors:: Player:: GrantInvulnerability(float timeLeft,
InvulnerableType type)
Grants invulnerability without ever weakening what is already running.
Keeps whichever of the two is longer and whichever effect is stronger (InvulnerableType::
bool Jazz2:: Actors:: Player:: MorphToInstantly(PlayerType type) virtual
Changes the player type immediately, without any transition or effect.
Used where the player isn't supposed to be seen morphing, e.g., when the type is restored after respawning at a checkpoint. Returns false if the player already has the specified type.
bool Jazz2:: Actors:: Player:: ApplyBlastKnockback(bool pushLeft,
float distanceSqr)
Throws the player away from a shot's blast.
| Parameters | |
|---|---|
| pushLeft | Whether the blast is to the player's right, so they are thrown left |
| distanceSqr | Squared distance from the blast, tested against the measured reach |
| Returns | false if the blast was too far away to move the player |
Only the RF does this, and only in non-Reforged mode, where it is a movement technique rather than an accident - see LegacyRFBlastSpeed for the measurements. Reforged keeps the small external force it always had, at whatever range the caller's own search allowed.
void Jazz2:: Actors:: Player:: SetCheckpointAmbientLight(float ambientLight)
Overrides the ambient light intensity to restore when respawning at the checkpoint.
Used in online sessions, where checkpoints are activated by the server and the owning client would otherwise fall back to the level default after dying (see SetCheckpoint).
void Jazz2:: Actors:: Player:: OnSerializeState(Stream& dest) override protected
Serializes the live state of the object to a level state snapshot.
Called only if IsSerializable() returns true. Anything that is initialized from the event parameters in OnActivatedAsync() doesn't need to be stored, because the object is spawned again with the same parameters before OnDeserializeState() is called. Derived classes must call the base implementation first and read the data back in the same order.
void Jazz2:: Actors:: Player:: OnDeserializeState(Stream& src) override protected
Restores the live state of the object from a level state snapshot.
Called right after the object was spawned again (i.e., after OnActivatedAsync()) and before it's added to the level. Running transitions are not restored, because their callbacks cannot be serialized, so derived classes have to put the object into a state that doesn't wait for such a callback.
void Jazz2:: Actors:: Player:: OnEmitRemotedLights(SmallVectorImpl<LightEmitter>& lights) override protected
Called when emitting lights that have to be replicated to remote peers.
An object simulated on a multiplayer server is drawn on the clients by a stand-in that runs none of its logic, so the lights it emits have to be described to them explicitly. This defaults to OnEmitLights(), which is right for anything whose lighting belongs to the object itself.
Overriding it describes only the part that every observer must see. Lights that a client already produces on its own — decoration driven by a local preference, or an object whose stand-in replays the whole effect — are left out, so they aren't sent (and applied) twice.
bool Jazz2:: Actors:: Player:: ApplyPlayerBump(Player& other,
bool stackingEnabled) protected
Keeps this player and other from overlapping (so they can't pass through) and bumps them apart.
| Parameters | |
|---|---|
| other | The other player in contact |
| stackingEnabled | When true, vertical overlap is left to UpdatePlayerStacking instead of bumped |
| Returns | true if the players were separated (a bump was applied) |
Equal-mass elastic separation along the axis of least penetration. Shared by local splitscreen co-op and online sessions (where Multiplayer::
void Jazz2:: Actors:: Player:: UpdatePlayerStacking(float timeMult,
bool snap) protected
Resolves this player standing on top of another player as a one-way platform.
| Parameters | |
|---|---|
| timeMult | Frame time multiplier |
| snap | Whether to reposition our feet onto the player below (the side that simulates this player); false only grounds it (the server's shadow of a remote player, whose position comes from its client) |
Carrying makes CanJump() return true (so the player can jump off) and zeroes vertical speed so it rests instead of falling through. Call before the physics update so jump input sees it. Shared by local splitscreen co-op and online sessions.
void Jazz2:: Actors:: Player:: OnHandleSpectate(float timeMult) virtual protected
Called each frame while the player is in PlayerType::
The default implementation flies the (invisible) player around the level with the movement keys, which is what the camera follows. Multiplayer overrides it to let the spectator lock onto another player instead.
Variable documentation
static float Jazz2:: Actors:: Player:: LegacyEnemyStompBounce constexpr
Non-Reforged bounce off an enemy hit by a buttstomp (original 13 px/tick).
Measured against the test level's own tube turtle: the stomp arrives at its governed 10 px/tick and leaves at a flat 13, so the bounce is faster than the fall that caused it and no fraction of the impact could ever produce it. The ascent that follows decays at the released rise rate rather than the held one, like a pole launch and unlike a spring - see GetGravityModifier().
static float Jazz2:: Actors:: Player:: LegacyLandingTolerance constexpr
How far below a ledge's surface the non-Reforged player may arrive and still be put on top of it.
Measured by running at gaps of one to six tiles: the original clears a two-tile walk (arriving 6.25 px below the far edge) and a five-tile dash (18.1 px below), and fails a three-tile walk (20.25 px) and a six-tile dash (27.6 px) - so the allowance sits between 18.1 and 20.25 px, five eighths of a tile. Fed to ActorBase::_landingTolerance; without it the player lands a whole gap short of the original at both speeds.
static float Jazz2:: Actors:: Player:: LegacyButtstompDriftSpeed constexpr
Non-Reforged sideways drift speed during a buttstomp (original 12000/65536 px/tick, doubled with Run).
A flat rate rather than an acceleration, applied through both phases of the move - the pause at the top and the descent alike. In the original this bypasses the player's speed entirely (its xSpeed reads zero for the whole stomp while the position still creeps sideways), which is worth knowing if this is ever measured again: watch the position, not the speed.
static float Jazz2:: Actors:: Player:: LegacyPushSpeed constexpr
Non-Reforged speed while pushing a solid object (original 0.375 px/tick).
Player and object move together at this flat rate whatever the player was doing beforehand - walking into a box and dashing into a rock push at exactly the same speed. As with the buttstomp drift, the original's own xSpeed reads zero throughout while the positions creep along.
static float Jazz2:: Actors:: Player:: LegacyVineSpeed constexpr
Non-Reforged vine climbing speed cap (original 2 px/tick, doubled while Run is held).
Measured on the level's own vine: half the walk cap on its own, exactly the walk cap with Run. The engine's vine cap happened to be right but its Run bonus was 1.6x rather than 2x.
static float Jazz2:: Actors:: Player:: LegacyCopterDescentSpeed constexpr
Non-Reforged copter descent speed (original 129/128 px/tick).
Measured identical for Jazz and Lori. Their horizontal speed while coptering tops out at the walk cap (LegacyWalkSpeed), which the ordinary ground handling already produces.
Read as a round 1 for a long time, and it is not: the original holds 1.0078125, which is 129/128 and exactly one LegacyCopterWindUp above a pixel a tick. The two constants being one step apart is almost certainly not a coincidence.
static float Jazz2:: Actors:: Player:: LegacyCopterWindUp constexpr
Rate the non-Reforged copter winds its descent up at (original 1/128 px/tick²).
The copter does not simply assign LegacyCopterDescentSpeed. It is a ceiling the descent accelerates towards at this rate, and the acceleration is only visible when the copter is engaged while falling slower than the ceiling - which is rare, because an ordinary fall passes it within nine ticks of the apex. Where it shows: engaging at the peak of a jump, off a ceiling, or straight after a warp, all of which start from little or no vertical speed.
Measured on cp_tap65, which engages at 0.625 px/tick and then climbs 0.633, 0.641, 0.648, 0.656 for the rest of the scenario - 0.0078125 a tick, still only 0.977 forty-five ticks later. Its neighbour cp_tap70 engages at 1.250, above the ceiling, and snaps to 1.0078 in one tick. So the rule is a clamp on both sides: below the ceiling it ramps, at or above it drops straight to it. wp_walk shows the same ramp from a standing start after a warp - 0.008, 0.016, 0.023, 0.031.
static float Jazz2:: Actors:: Player:: LegacyDoubleJumpSpeed constexpr
Non-Reforged launch speed of Spaz's double jump (original 8 px/tick).
A straight speed assignment, like the ordinary jump and unlike the engine's old model of a small speed plus a sustained internal force - which is what made it feel far too weak. Height is then controlled by how long the key is held, through the same LegacyRiseGravityReleased switch the first jump uses. Measured: it adds about 80 px over a plain 132 px jump, and it only triggers within roughly 20 of the original's ticks after the apex - pressed 30 ticks late it does nothing at all.
static float Jazz2:: Actors:: Player:: LegacyDoubleJumpWindowTime constexpr
How long Spaz's non-Reforged double jump stays available (original 30 ticks).
The window is a timer started when the jump key is let go, not a band of fall speeds. Those two look identical if the first press is always released at the same tick, because the fall speed then grows with the delay - which is exactly what the sp_dj_d* family does, and why it was originally read as a speed window of (0, 3.75).
Releasing at a different tick separates them. Measured across four release ticks: accepted at delays of 5, 10, 20, 22, 25, 28 and 29 ticks, refused at 30, 35 and 40. sp_dj_r60_d28 is accepted at a fall speed of 3.00 while sp_dj_hold is refused at the same 3.00, differing only in a delay of 28 against 35 - so the speed cannot be what decides it. Nine scenarios used to double jump here where the original does nothing at all.
Thirty rather than the twenty-nine of the longest accepted delay, because it is counted the way the original counts it: set on the tick of the release, decremented at the top of every tick after it, and tested for "still above zero" - so a press 29 ticks later sees 1 and one 30 ticks later sees 0.
A duration, so it divides by LegacyFrameRateScale rather than multiplying.
static float Jazz2:: Actors:: Player:: LegacyUppercutSpeed constexpr
Launch speed of Jazz's non-Reforged uppercut (original 6.59375 px/tick of travel).
The move is a straight speed assignment like every other launch, and what makes it climb ~225 px on so modest a speed is that gravity is almost switched off while it runs - see LegacyUppercutGravity.
This is taken from the distance travelled, not from the speed column, because the two disagree: the original reports ys = −7.5 at the launch decaying to −6.59, while the position advances 6.59/tick decaying to 5.69 - a constant offset of about 0.84 that the applied rise cap cannot explain, since every value involved is already under it. The same reported-versus-travelled decoupling turns up in the dash and the buttstomp drift, and as there, the travel is what the player sees and nothing reads this move's vertical speed.
static float Jazz2:: Actors:: Player:: LegacyUppercutGravity constexpr
Gravity while a non-Reforged uppercut is driving (original 2048/65536 px/tick²).
A twelfth of the ordinary rise gravity, which is why the rise stays nearly linear for its whole length: measured ys = −7.5 at the launch decaying by exactly 0.03125 a tick to −6.59375 thirty ticks later, then ordinary gravity from the tick the move ends.
static float Jazz2:: Actors:: Player:: LegacyRFBlastSpeed constexpr
Speed a non-Reforged RF blast throws the player at (original 8.0 px/tick).
Firing an RF into a nearby wall throws the player away from it, which is how some jumps are made. Measured by parking the player a set distance from the wall on the test level's upper floor and firing from a standstill: the speed snaps to exactly this - the applied-movement cap, so every pixel of it is travelled - and is then held for LegacyRFBlastHoldTicks with friction and the direction keys both suppressed, even on the ground, where nothing else in the game slides at a constant speed. When the hold ends the speed snaps to the walk cap and decelerates at the walking rate, exactly as Spaz's sidekick does, for about 302 px of travel in all.
One case disagrees and is not reproduced: fired while jammed against the wall the original throws the player at 6.3125 rather than 8.0, for 226.6 px. Two clean measurements at 24 and 36 px both give a round 8.0, so that is what this follows.
static float Jazz2:: Actors:: Player:: LegacyRFBlastLiftBase constexpr
Upward kick the same blast gives a player standing on the floor, at zero range.
A separate figure from LegacyRFBlastRiseSpeed and a much smaller one, and unlike it this one is not constant — it grows with the range, which is the opposite of what a blast of fixed strength does and is why it went unfitted for so long. Measured at four ranges against the true gap:
| Gap | 0.6 px | 11.7 | 20.6 | 23.7 | 35.7 |
|---|---|---|---|---|---|
Assigned ys | −0.75 | −2.75 | −4.25 | −4.75 | none |
A straight line through those four is -(0.646 + 0.1732 * gap) and reproduces every one of them to within 0.08, with the last point covered by LegacyRFBlastReach cutting the blast off entirely. The horizontal component saturates at 8 over the same range, so the two clearly do not share a direction; the reading that would explain it is a shot that falls as it flies, putting the explosion further below the player the longer it travels and tilting the push upward. Nothing here measures the shot's own gravity, so this is a fit of the measurement rather than a model of it.
static float Jazz2:: Actors:: Player:: LegacyRFBlastReach constexpr
How close to a non-Reforged RF blast the player has to be to be thrown by it (original ~40 px).
Measured as a bracket rather than a value: parked 24 and 36 px from the wall the player is thrown, parked 48 and 64 px away nothing happens. Parked 12 px away nothing happens either, but that is the muzzle ending up inside the wall rather than a minimum range - the ammo is still spent - and firing from against the wall does throw the player, so no lower bound is applied.
The bracket was read off the scenario names and came out as (36, 48], which is why this was 40. Both halves of that were wrong. The names count from the wall tile and the player's own half-width eats the rest, so in true gaps - taken from wb_wallfind, the position a dash comes to rest at - those two scenarios are 23.7 and 35.7 px. And what reaches this is the distance from the shot to the player, not the gap: the shot stops half its own width short of the wall, so it is gap + playerHalf - shotHalf, about gap + 3. The bracket in the units actually compared here is therefore (26.7, 38.7], and 40 sits outside it - which is why wb_rf_r48 still threw the player 306 px after the flight itself had been fixed, where the original moves them 0.9.
static float Jazz2:: Actors:: Player:: LegacySpazSidekickDistance constexpr
Distance the driven part of Spaz's non-Reforged sidekick covers (original 440 px).
His dash is not a special case at all: it holds a speed of 16 - so the applied cap makes him travel 8 px/tick - for about 55 of the original's ticks, and then the speed simply snaps back to the walk cap and the ordinary walk deceleration coasts him to a stop. That coast adds a further ~65 px, for the ~505 px he is measured to cover in total, so only the driven 440 px belongs here. Expressed as a distance rather than a duration because that is what was measured reliably.
static float Jazz2:: Actors:: Player:: LegacySpringHoldTicks constexpr
Ticks a horizontal spring's launch speed is held before it clamps (original 4).
The launch itself is the spring's own figure — 16, 24 and 32 for red, green and blue — but the original only carries that for four ticks and then clamps to LegacyCarryExitRunSpeed, which is the same 16 every other carry ends at. Red is unaffected, being at the cap already; green and blue both drop, measured on ob_spring_green_h and ob_spring_blue_h at exactly tick 31 to 35.
Worth knowing what this does not change: the applied-movement cap means the player travels 8 px a tick whether the speed reads 32 or 16, so the positions are identical either way and only the speed column moves. It still matters, because anything that reads the speed sees it - a jump out of the launch takes its boost from there.
static float Jazz2:: Actors:: Player:: LegacySpringRebuildTicks constexpr
Ticks a spring launch's speed takes to rebuild after a double jump discards it (original 32).
The rebuild runs at the launch speed divided by this, so a faster spring comes back faster. Measured off all three horizontal springs, and the third one tested the rule rather than adding to it: red launches at 16 and rebuilds at 0.50 px/tick², green at 24 at 0.75, which made 1.00 the prediction for blue's 32 - and that is what blue does. The ceiling is a flat LegacyDashSpeed however fast the launch was, so blue reaches it well before the 32 ticks are up.
Only a spring arms this. A dash's speed, when a double jump takes it, comes back at the ordinary air acceleration - measured on sp_dj_dash - so a rule keyed on "was carrying speed" would be wrong.
static float Jazz2:: Actors:: Player:: LegacyWarpOutControlTime constexpr
Ticks after a warp lands before control comes back outside Reforged (original 2 ticks).
Measured on wp_walk: the original lands on tick 73, shows one frame of its warp-out pose on 74, and is walking again on 75. Ours waited for the whole AnimState::
static float Jazz2:: Actors:: Player:: LegacyLoriKickPushDrain constexpr
Rate a blocked kick of Lori's spends LegacyLoriSidekickDistance at, per frame.
Her ramp runs n = 2..13 and 0.25 * n^2 over those twelve original ticks sums to exactly LegacyLoriSidekickDistance - but summing the same quadratic at 60 FPS does not reach that total over the same elapsed time. It needs twelve frames, which is fourteen original ticks. The distance is what shows in an unobstructed kick and it is already right, so the ramp stands there. The duration only shows when something holds the kick up, where it becomes how long the player shoves a pushable, and there the budget is drained at the flat rate those two numbers imply instead. Twelve ticks either way, and 4.5 px of rock per kick rather than 6.
static float Jazz2:: Actors:: Player:: LegacyLoriKickRamp constexpr
Quadratic ramp of Lori's non-Reforged kick, per tick squared.
Her kick accelerates rather than starting at its peak: measured tick by tick, its speed is exactly (n / 2)^2 px per original tick - 6.25, 9, 12.25, 16, 20.25, 25, 30.25, 36, 42.25 - i.e. quadratic in the elapsed time, running for 13 ticks and summing to LegacyLoriSidekickDistance. Starting it at the peak instead (a sixth of the way in) is what made the kick look far too fast.
The 0.25 is the original's own coefficient; the cube of the frame-rate scale converts a speed that is itself quadratic in ticks - one factor for the speed and two for the time.
static float Jazz2:: Actors:: Player:: LegacyLoriSidekickSpeed constexpr
Non-Reforged sidekick launch speed for Lori (original 42.25 px/tick).
Far above the applied-movement cap, so most of it is momentum: she travels at the cap and the excess is what keeps her going, which is why her dash covers so much more ground than Spaz's. Her kick is also the one move exempt from LegacyAppliedSpeedCap - see OnUpdatePhysics() - since its measured 204.75 px in 13 ticks is more than the cap would allow.
static float Jazz2:: Actors:: Player:: LegacyPoleLaunchBonus constexpr
Non-Reforged speed a vertical pole adds to the one the player arrived with (original 15.625 px/tick).
The launch is 15.625 + |entry speed|, measured with a slope of exactly 1.0 over entry speeds from 6.6 to 24.5 - so a pole hands back everything it was given plus a fixed bonus. That is what makes poles compound: chain them, or enter one off a spring, and each adds its bonus to an already larger speed. A single pole entered from a jump reaches ~4 tiles more than the jump alone; entered off a blue spring the same pole throws the player 25 tiles.
static float Jazz2:: Actors:: Player:: LegacyHPoleLaunchBonus constexpr
Non-Reforged speed a horizontal pole adds to the one the player arrived with (original 8 px/tick).
The same shape as LegacyPoleLaunchBonus, with a smaller bonus and a ceiling: the launch is |entry speed| + 8, capped at LegacyHPoleMaxLaunch. Five entry speeds fit it exactly – 0 -> 8, 4 -> 12, 9.89 -> 17.888, 17.888 -> 20 (capped from 25.9) and 20 -> 20 (from 28).
This replaced a multiply by 3 clamped between a floor of 8 and the ceiling, which fitted the four points then available because three of them sat on those limits and only 4 -> 12 constrained the factor. The fifth point is the one that discriminates: entering at 9.89 the multiply predicts 29.7 and the player leaves at the capped 20, where the original leaves at 17.888 – 12% apart, and invisible in travel because every scenario that reached the pole at a run ended against the same wall. 2*v + 4 and 4*v - 4, the two alternatives the old fit could not separate, are ruled out by it as well. The floor is gone with the multiply: v + 8 never returns less than 8 on its own, so what looked like a separate rule at an entry speed of zero was only the bonus.
Reading the entry speed off the original's trace needs care, and this is where the 9.89 comes from: it logs the speed before the tick's acceleration and this engine after it, so the grab tick reads 9.5215 there and the pole actually saw one dash step more. Taking the logged figure at face value gives 17.5 against a measured 17.888 and makes an exact rule look approximate.
static float Jazz2:: Actors:: Player:: LegacyFloatUpSpeed constexpr
Speed a non-Reforged float-up area holds the player at (original 8 px/tick).
Not an acceleration and not a force: measured inside a solid ten-tile column of them, the original reads exactly -8.0 on every one of the 43 ticks the player is in the field, and only starts decaying - at the ordinary rise gravity - once they leave it. It needs no jump and lifts a grounded player straight off the floor: pressing nothing gives a trace identical to jumping.
The sawtooth a diagonal ladder of them produces is therefore geometry, not the rule. The player keeps leaving and re-entering discrete tiles, and the ascent decays in the gaps between - which averages half the assignment, ~3.9 px/tick, whatever the decay rate happens to be.
static float Jazz2:: Actors:: Player:: LegacyFloatUpFallCap constexpr
Fastest a non-Reforged player descends while inside a float-up area (original 4 px/tick).
The field cannot simply assign its rise to someone whose move drives its own downward speed, so what it does instead is hold their descent down to this. Measured on fu_col_butt, a buttstomp dropped into the column from clear air: outside it the stomp covers 10 px a tick, and from the tick its feet enter the field it covers exactly 4.00, for all 52 ticks it takes to cross - while ys keeps climbing underneath, 12 then 16. So it is a cap on the movement, not on the speed.
Three entry speeds land on the same figure - 6 on an ordinary fall, 12 and 16 on the stomp - which is what rules out the other reading, that the field's 8 px lift is simply subtracted: 6 − 8 would carry the player upwards, and 16 − 8 would be twice what is measured.
This engine skipped the whole field while a buttstomp was running and fell through at the full 10, which is the reported "buttstomping a float-up event should slow the fall noticeably".
static float Jazz2:: Actors:: Player:: LegacyWindFactor constexpr
Pixels a non-Reforged wind area moves the player per unit of its strength (original 0.5).
A position move, not a speed: the original's xs reads 0.0000 for the whole time a player is being blown along. Fitted on two strengths, 8 -> 4.0 px/tick and 4 -> 2.0, so it is linear.
static float Jazz2:: Actors:: Player:: LegacyBeltFactor constexpr
Pixels a non-Reforged belt moves the player per unit of its strength (original 1.0).
Also a position move, and twice LegacyWindFactor - the two mechanics share an event type in this engine but not a strength. Fitted on 8 -> 8.0 px/tick and the parameterless default -> 2.0, which is what pins the default strength at 2 rather than the 3 the converter used to substitute.
static float Jazz2:: Actors:: Player:: LegacyAccBeltSpeed constexpr
Speed a non-Reforged accelerating belt drives the player to, per unit of strength (orig 1.5).
The accelerating belt is the one of the three that works on the speed: it ramps xs up by about a third of the target each tick and then holds it there. Fitted on 8 -> 12.0 px/tick and the parameterless default -> 6.0, which pins that default at 4.
static float Jazz2:: Actors:: Player:: LegacyAccBeltStep constexpr
How much an accelerating belt adds per tick, per unit of strength (original 0.5 px/tick^2).
A third of LegacyAccBeltSpeed, which is what makes the ramp take three ticks. Applied after the brake, not before: the original's xs runs 2.0, 3.878, 5.756 and then holds exactly 6.000 on a default belt, and those middle values are this step minus one friction step of 0.122 each tick. Adding it before the brake - where the floor-event handler naturally runs - leaves the steady state one friction step short, at 5.862 instead of 6.000.
static std:: int32_t Jazz2:: Actors:: Player:: LegacyAccBeltRunBonus constexpr
Strength a non-Reforged accelerating belt gains while Run is held (original 8).
Holding Run on one raises the speed it drives to, and by a flat amount rather than a factor: measured 18 px/tick at strength 4 and 24 at strength 8, against 6 and 12 with nothing held, which is the same 1.5 per unit shifted by 12 - exactly what eight more strength is worth. Two strengths were needed to see that, because one point cannot tell 4 x 4.5 from 6 x 3. See bl_acc_right_run and bl_acc_right_p8_run.
Only the target moves. The ramp's step is unchanged; what differs about the ramp with Run held is that the brake it is applied after becomes the dash brake, which is visible as the step reading 1.573 instead of 1.878 - one LegacyDashDecel rather than one LegacyWalkDecel.
static float Jazz2:: Actors:: Player:: LegacyCarryExitRunSpeed constexpr
Speed a non-Reforged carry clamps to when it ends with Run held (original 16 px/tick).
Leaving a sucker tube or an accelerating belt clamps the horizontal speed — to LegacyWalkSpeed with nothing held, and to this with Run. It is one rule across every site that snaps, and it took the accelerating belt to see it: a tube carries 8 px/tick and Spaz's sidekick leaves at 15.88, so a clamp to 16 is inert at both and they looked like "no clamp at all while Run is held". The belt reaches 18 and 24, where it is not inert, and both strengths snap to exactly 16 before decaying at LegacyDashDecel.
Measured on bl_acc_right_run and bl_acc_right_p8_run against bl_acc_right / bl_acc_right_p8, which snap to 4 and decay at LegacyWalkDecel.
static float Jazz2:: Actors:: Player:: MaxPushingSpeed protected constexpr
Speed above which the player is no longer considered to be pushing.
While genuinely pushing, the player is held to a slow speed (a wall pins it to 0, a movable object to roughly PushSpeed times 1.2). Once they break free and accelerate past this, the push animation ends even if the push grace timer (_pushFramesLeft) hasn't expired, so it doesn't linger while walking in open space.
static float Jazz2:: Actors:: Player:: LegacyTickRate constexpr
Tick rate of the original game.
Measured at 70.021 Hz, which is what Tests/Tools/CompareTraces.ps1 converts the original's logged speeds with; a round 70.0 is used here, so every comparison against a reference trace carries a systematic 0.03% offset. That is an order of magnitude below the per-scenario accuracy the reference page reports, so it has been left alone deliberately rather than re-tuning every constant below — but the two figures are one measurement and should not drift further apart.
static float Jazz2:: Actors:: Player:: LegacyWalkAccel constexpr
Non-Reforged horizontal acceleration with no Run (original 12000/65536 px/tick^2).
The original applies this in the direction of the input, whichever way the player happens to be moving and whether it is on the ground or in the air - one table for everything. There is no skid factor, no separate airborne rate and no loss of momentum when the direction is flipped, which is what makes turning around symmetric and, in the air, slow enough to be a commitment.
static float Jazz2:: Actors:: Player:: LegacySlideWalkDecel constexpr
Non-Reforged deceleration on a slide tile of strength 0, no direction held (original 4000/65536).
A MODIFIER_SLIDE tile does not add anything to the movement; it swaps the brake for a weaker one, so the player coasts further. At strength 0 this is exactly half of LegacyWalkDecel, but the dash pair is not halved - 12000 against 28000, a ratio of 3:7 - so the two are measured separately rather than one factor being applied to both.
static float Jazz2:: Actors:: Player:: LegacySlideWalkDecelStep constexpr
How much each step of a slide tile's Strength takes off LegacySlideWalkDecel (orig 800/65536).
The tile carries a 2-bit Strength, and all four values are linear in it. Measured across the whole range: the walk brake runs 4000, 3200, 2400, 1600 and the dash brake 12000, 9500, 7000, 4500, so the steps are 800 and 2500. Worth measuring all four rather than two - the ratios between strength 0 and 3 are 2.5 and 2.67, which fits no single divisor and would have been fitted wrongly.
static float Jazz2:: Actors:: Player:: TubeControlTime constexpr
How long a sucker tube keeps hold of the player (original 16 ticks).
No Legacy prefix and no gate: measured from the original, but applied in both modes at the user's request. The tube is the original's mechanic either way - its speed comes out of the level in the original's units - and what Reforged had instead was a flat 10 ticks that never re-armed. See A sucker tube re-arms while you are inside it.
A duration, so it divides by LegacyFrameRateScale rather than multiplying. Re-armed every tick the player is inside a tube tile, which is what makes the total depend on the tube's own speed: a tube set to 8 px/tick keeps the player in its 32 px tile for four ticks and so holds for 20, while one set to 20 keeps them for two and holds for 18. That difference is exactly the two tiles' worth of re-arming, which is how the window was separated from the exit condition - a single tube cannot tell them apart, and a 30-tile row of them carries the player the whole way in both games.
The window suppresses horizontal friction but not gravity: a tube firing straight up holds its speed only while the player is still in the tile, and the ascent decays normally from there.
static float Jazz2:: Actors:: Player:: TubeSnapY constexpr
Where in its tile a horizontal sucker tube holds the player (original: 15 px below the top).
Reported as a centring problem and measured as one. Riding the row of tubes in tb_row, the original holds y at exactly 1327 for the whole ride, which is 15 px into the tile at row 41 — one pixel above its centre. This engine snapped to 8, a quarter of the way down, and rode 7 px high. The horizontal snap on the other branch has always used the tile centre; only the vertical one was off.
About 1.8 px of that 7 is not the tube's: the two games' player origins differ by that much for the same standing position everywhere in the harness (1330.5 against 1328.7 on a floor, and the same gap after leaving the tube) and nothing else corrects for it. Matching the measured number leaves that residual rather than inventing a correction for it.
No Legacy prefix and no gate, for the reason given on TubeControlTime: the whole tube is shared with Reforged, which rode 7 px high for the same reason this did.
static float Jazz2:: Actors:: Player:: LegacyDashGraceTicks constexpr
Ticks the non-Reforged dash outlives the Run key (original 16 ticks).
The dash is a timed state rather than just a higher cap. Letting go of Run does not bleed the speed off at all - the player keeps the full LegacyDashSpeed for this long and then the speed is clamped straight back to LegacyWalkSpeed in a single tick, in mid-air exactly as on the ground.
static float Jazz2:: Actors:: Player:: LegacyCameraWalkLead constexpr
How far the non-Reforged camera leads the player while walking (original 28 px).
Measured as the steady value of camera - player once the walk cap is reached, and it is a distance, so unlike a velocity it needs no frame-rate conversion. Reforged leads 80 px here, nearly three times as far, which is the whole of "the pan is more aggressive than the original".
static float Jazz2:: Actors:: Player:: LegacyCameraDashLead constexpr
How far the non-Reforged camera leads the player while dashing (original 120 px).
Keyed on IsDashActive() rather than on the speed, which is the same thing by construction: the grace that outlives the Run key is exactly the window in which the speed is still the dash cap. g_dash_relrun is what establishes that - releasing Run does not shorten the lead, and it only starts closing once UpdateDashState() clamps the speed back to LegacyWalkSpeed.
static float Jazz2:: Actors:: Player:: LegacyCameraButtstompDrop constexpr
How far the non-Reforged camera drops during a buttstomp, so more is visible below (original 60 px).
The one place the original's camera leads vertically, and the only thing that stopped this being found earlier is that it is the sole exception to "there is no vertical lead to reproduce". Measured on sp_butt_right: camy sits at −225.87 while standing, and from the tick the wind-up pose appears it runs to −165.07 and stays there for the rest of the stomp — 60.8 px, reached in about 30 ticks.
The approach is the horizontal one's, at twice the step: a flat 2.00 px/tick through the middle of it and an ease-out that halves exactly (1.00, 0.50, 0.25, 0.13, 0.06), which is Jazz2::Rendering::PlayerViewport::LegacyCameraApproach against a doubled Jazz2::Rendering::PlayerViewport::LegacyCameraMaxStep. The original's first five ticks briefly overshoot to 3.88 px/tick before settling back to 2, which this does not reproduce — it is a fifth of a second at the very start of a 30-tick pan.
static float Jazz2:: Actors:: Player:: LandAnimFrameTicks constexpr
Ticks the non-Reforged landing pose spends on each of its frames.
Deliberately slower than the original, chosen by eye after three passes. The original's rate is not in doubt: an_land shows its five frames at ticks 76, 78, 81, 84 and 87 and leaves at 91, and a scan of every landing in the captured traces finds 254 complete ones, all five frames, all 14 ticks end to end — so 2.75 a frame, a fifth of a second. This engine's own rate is 128/256 of a second across the same five frames, so 7.0 a frame.
Both were rejected on sight: 7.0 as "very slow and chopped", then 2.75 as "too fast", then 4.0 — already half again slower than the original — as "still quite fast". The original's landing is simply over before it registers at this engine's framerate, so matching it was abandoned as the goal. Anything here above 2.75 is a preference and nothing above it should be read as measured.
Per frame, not per landing, because the characters do not agree on how many there are: Jazz and Spaz land in five frames and Lori in seven. A whole-pose duration would run Lori's at five-sevenths the speed of theirs.
static float Jazz2:: Actors:: Player:: SidekickEndSpeedUp constexpr
How much faster the sidekick's ending animation plays, in both modes.
A deliberate departure from the measurement, asked for after seeing it. The original holds its end pose for 12 ticks on sp_spaz_side_rel where ours runs 10, so by the trace the length was already right and this takes it further from the original — it is here because the pose reads as slow in play and ending it sooner was preferred to matching the number.
Applied in code rather than in the shared SidekickC metadata entry (FrameRate: 30) so that the rate stays visible next to the reason for it, and so the measured value it departs from is recorded somewhere. What made the ending look wrong in the first place was separate and is measured: nothing could interrupt it, so it played over a jump. That is fixed in Player::BeginStandardJump() rather than by making the transition cancellable, which ends it after a single frame.
static float Jazz2:: Actors:: Player:: LegacyRevUpJumpBoost constexpr
Extra launch speed a jump out of a run-in-place gets, worth about a tile of height.
Reported from play: "a run-in-place running jump jumps you 1 tile higher than a regular running jump". This engine gave the same height, and near enough exactly so — the carry runs at 17.31 and the dash cap is 18.67, so the speed term of the launch differs by a tenth of a pixel a tick.
Fitted here, not measured against the original, for the same reason as LegacyRevUpButtstompCostFraction: the wind-up cannot be driven from a script. rt_jump rises 177.4 px without it, and the value is chosen to put that 32 px higher; the rise is checked against that target rather than against a trace.
static float Jazz2:: Actors:: Player:: LegacySpeedJumpScale constexpr
Extra non-Reforged launch speed per unit of horizontal speed (original exactly 1/4).
The original's launch is -(10 + |xSpeed| / 4) with no threshold and no dead zone, so a dashing jump leaves the ground at -14 against a standing jump's -10. The factor needs no frame-rate conversion of its own, both sides of it being velocities.
static float Jazz2:: Actors:: Player:: LegacyRiseGravityReleased constexpr
Non-Reforged rise gravity once the jump key is released (original 0.875 px/tick^2).
Releasing jump early shortens the jump by making the rest of the rise heavier, not by clamping the speed - the original switches to this from the tick the key comes up and keeps it for the rest of the ascent, which is what gives the tap hop its own arc rather than a truncated one.
It eases off for the last part of that ascent - see LegacyRiseGravityReleasedNearApex.
static float Jazz2:: Actors:: Player:: LegacyRiseGravityReleasedNearApex constexpr
Non-Reforged rise gravity once the jump key is released and the rise is nearly spent (original 0.625 px/tick^2).
The released rise is not one rate: it decelerates at LegacyRiseGravityReleased while the rise is still faster than LegacyRiseBrakeEaseSpeed and at this lighter rate below it. Measured on a sweep of thirty standing jumps released one tick apart (ap_r01..ap_r30): the step is 0.875 at ys = -1.125 and 0.625 at -1.000, on either side of a threshold of exactly one pixel per tick.
The held rise has no such band - it is a flat 0.375 from the launch to the apex.
static float Jazz2:: Actors:: Player:: LegacyAppliedSpeedCap constexpr
Non-Reforged cap on applied movement (the original clamps it to 8 px/tick on both axes).
Measured: a dash reports an xSpeed of 16 px/tick but only ever travels 8, so the speed above the cap is momentum rather than motion. It still counts wherever the speed itself is read - most visibly in the jump launch boost, which is what makes a dashing jump launch at -14 while moving no faster than a capped run.
static float Jazz2:: Actors:: Player:: LegacyVerticalSpeedLimit constexpr
Non-Reforged ceiling on internal speed, on either axis (original 32 px/tick).
Read off a pole launch in the original: it assigns 40.125 and the very next tick reports exactly 32.00. Applied movement is capped at LegacyRiseSpeedCap regardless, so this never changes how fast the player visibly moves - only how long a launch keeps riding that cap, and therefore how far it gets. It happens to equal a blue spring's own launch exactly, so a spring passes through untouched while a pole is clamped, which is what the original does to each.
The original applies the same figure horizontally, and so does OnUpdatePhysics(): a spring carries the identical strength on either axis, so anything lower here silently flattens the horizontal colours into one another. Only a sidekick is assigned above it.
static float Jazz2:: Actors:: Player:: LegacyFlyRiseBrake constexpr
What a climb decays at once Up is let go, until it reaches zero (original 0.75 px/tick^2).
The flight has two downward rates, not one, and missing this is what made the carrot fly far too high: releasing Up at the 32 cap brakes to a stop in 44 ticks, where treating it as the fall rate below would have taken 512 and carried the player most of a level upwards first. Measured across a clean release in the recording - ticks 1331 to 1375 step by exactly 0.75 and the last one clamps to zero rather than overshooting.
Deliberately its own constant and not LegacyRiseGravityReleased, which is 0.875: an ordinary released jump and a released climb decay at different rates, and the recording contains 297 steps of 0.75 and none of 0.875.
static float Jazz2:: Actors:: Player:: LegacyFlyRiseBrakeHeld constexpr
What a climb decays at with Jump held (original 0.25 px/tick^2).
The flight has the same held/released pair an ordinary jump does, with its own two numbers. Measured on the second recording, which jumps into the carrot from the ground: the launch decays at the ordinary 0.375 for the six ticks it is still a jump and then at 0.25 from the tick the flight animation takes over, for thirty ticks with Jump held throughout.
Numerically the same as LegacyFlyRiseAccel and not derived from it — one is what Up adds and the other is what Jump fails to stop.
static float Jazz2:: Actors:: Player:: LegacyFlyDescentSpeed constexpr
The descent Down holds the player to (original 1.0625 px/tick).
Assigned outright rather than accelerated towards: the recording snaps from a 3.3125 px/tick fall to 1.0625 on the tick Down goes down, and then holds it for 734 consecutive ticks without varying. Letting go resumes the ordinary LegacyFlyFallAccel from wherever the speed is.
Close to the copter's LegacyCopterDescentSpeed but not the same, which is what "descends slowly on Down at a rate that is not the copter's" turns out to mean: 1.0625 against 1.0078.
Only ever observed entered from a fall. Whether holding Down during a climb reverses it as abruptly is not measured; assigning is the simpler rule and the one every sample shows.
static float Jazz2:: Actors:: Player:: LegacyLizardCopterDuration constexpr
How long a ride on the level's lizard copter lasts outside Reforged (original 279 ticks).
Measured on the cp_mod_* set, which catches six or seven separate copters per scenario: every ride that begins on a freshly spawned one lasts 279 ticks, in all eight scenarios and whatever is held during it, so it is a plain timer and not something the flying affects. That is a hair under four seconds at the original's 70.021 Hz. Reforged keeps its three, which is where this engine had both.
The rest of the ride — the two accelerations, the descent under Down, the horizontal — needed no constants of its own: it is LegacyFlyRiseAccel and the rest of the flying carrot's model, which the lizard copter turned out to share exactly. See Player::HandleWaterAndModifierMovement().
static float Jazz2:: Actors:: Player:: LegacyFlyDuration constexpr
How long the flight lasts, which is for as long as the player keeps it.
The original flies until a Fly Off area or a death - the recording holds it for 3871 ticks without expiring. This engine gave it ten seconds. Expressed as a large duration rather than as a flag so every existing countdown, animation and decor path keeps working unchanged; the flight now ends where it should, at Jazz2::
static float Jazz2:: Actors:: Player:: RevUpChargeGainTap constexpr
What one press is worth.
Set so that **three presses can never reach RevUpChargeThreshold however fast they come** - at 4.0 three quick taps landed within a whisker of it and tipped over, which showed as the wind-up starting on three taps when they were rattled off quickly and not when they were spaced. Three still reach it if the last is held, because the hold adds on top of this.
static float Jazz2:: Actors:: Player:: RevUpChargeHeldCap constexpr
The most one press can add by being held.
Without it, simply holding Run while standing still would wind the player up, which it must not - holding Run on the spot is what an ordinary player does before setting off. The cap is what makes holding a press worth something without making one long press worth everything.
static std:: int32_t Jazz2:: Actors:: Player:: RevUpPressesRequired constexpr
Presses below which nothing happens at all, whatever the charge.
The charge on its own cannot promise this: a press held to the cap is worth 3.4 + 4, so two of them reach 14.7 and would not only arm but wind up. Since "two taps never do anything" is a rule rather than a consequence, it is enforced as one.
static float Jazz2:: Actors:: Player:: RevUpChargeLightThreshold constexpr
Enough to launch when the tapping stops, but not to show the wind-up.
Reached together with RevUpPressesRequired, both being needed. Three plain taps land between this and RevUpChargeThreshold, which is the case with its own behaviour: the player stands idle the whole time, and letting the key go plays the animations and sets them running.
static float Jazz2:: Actors:: Player:: RevUpChargeThreshold constexpr
Where the wind-up itself begins.
Sits above what three plain taps reach, which is what makes the third one needing to be held - or a fourth press - the difference between standing still and revving. At a ~10 tick cadence: two taps reach 8.9, three 12.6, three with the last held 16.6, four 16.4, one press held indefinitely only 8.
static float Jazz2:: Actors:: Player:: LegacyDashAnimSpeed constexpr
Speed above which the dash animation is shown, measured on an_run_start.
Speeding up is picked from the current speed in three bands, exactly as sliding to a halt is: run to the walk cap, dash_start (the spinning feet) from there to this, and dash above it. The original spends 11 ticks in the middle band and reaches the dash pose 23 ticks after the key; playing dash_start as a transition instead runs all eight of its frames and took 47.
static float Jazz2:: Actors:: Player:: LegacyRunAnimFrameTicks constexpr
Ticks one frame of a ground-run animation lasts at a standstill, before the speed is taken off it.
The original does not play the run animations at a rate of their own — it plays them at a rate set by how fast the player is moving, and a frame lasts max(1, 12 - |speed|) of its ticks. The assets say nothing about this: run.aura, dash_start.aura and dash.aura all carry a flat 0.5 s for Jazz and Spaz, so the rate is in the game's code and nowhere else.
Measured across every scenario in the committed original traces, which agree on both characters: 10 ticks per frame at speed 2, 9 at 3, 8 at 4, 3 at 9, 2 at 10 and 1 from 11 up. The two ends are the ones that can be checked against a whole loop rather than a single frame, and both land exactly — walking, 8 frames at 8 ticks is the 63-tick loop g_walk shows; dashing, 4 frames at 1 tick is the 4-tick loop g_dash shows. This engine used the animation's own duration with no speed term at all, which made the walk 1.5x too fast and the dash 4.3x too slow.
LegacyRunAnimMinFrameTicks is the floor, and it is a real one rather than a safety clamp: the original cannot show two frames in one tick, so every speed from 11 up gives the same 1.
static AnimState Jazz2:: Actors:: Player:: HorizontalAnimMask constexpr
The two-bit horizontal speed field of a composite animation state.
Used as a mask rather than as flags, because AnimState::& finds the walk too.