< Summary

Information
Class: Trailblazer.Navigation.Motor.JumpLocomotion
Assembly: Trailblazer
File(s): /home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Navigation/Motor/Locomotion/JumpLocomotion.cs
Line coverage
98%
Covered lines: 82
Uncovered lines: 1
Coverable lines: 83
Total lines: 286
Line coverage: 98.7%
Branch coverage
81%
Covered branches: 13
Total branches: 16
Branch coverage: 81.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor()100%11100%
get_IsEnabled()100%11100%
set_IsEnabled(...)50%22100%
get_CanJump()100%22100%
RequireContext()50%22100%
get_DeltaTime()100%11100%
get_TotalTime()100%11100%
RegisterJump()100%22100%
StartCooldown()100%11100%
UpdateCooldown()75%4485.71%
ResetJumpCounter()100%11100%
BindContext(...)100%11100%
RecordData(...)100%44100%

File(s)

/home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Navigation/Motor/Locomotion/JumpLocomotion.cs

#LineLine coverage
 1using Chronicler;
 2using FixedMathSharp;
 3using System;
 4using Trailblazer.Support;
 5
 6namespace Trailblazer.Navigation.Motor;
 7
 8/// <summary>
 9/// Handles the scout’s jumping mechanics, including jump height, cooldown, and movement control while airborne.
 10/// </summary>
 11/// <remarks>
 12/// This locomotion component determines how high the scout can jump, how much control they retain mid-air,
 13/// and enforces a cooldown period between consecutive jumps.
 14/// </remarks>
 15public class JumpLocomotion : ILocomotion
 16{
 17    private TrailblazerWorldContext? _context;
 18
 19    #region Constants
 20
 21    /// <summary>
 22    /// The default base jump height, representing how high the scout jumps when pressing and immediately releasing the 
 23    /// </summary>
 124    public static readonly Fixed64 DefaultBaseJumpHeight = Fixed64.One;
 25
 26    /// <summary>
 27    /// The additional height added when the jump button is held longer.
 28    /// </summary>
 129    public static readonly Fixed64 DefaultExtraJumpHeight = (Fixed64)2f;
 30
 31    /// <summary>
 32    /// Determines how much the scout's jump direction is influenced by the slope of the surface they are on.
 33    /// </summary>
 134    public static readonly Fixed64 DefaultPerpendicularJumpAmount = (Fixed64)0.5f;
 35
 36    /// <summary>
 37    /// Determines how much the scout's jump direction is influenced by steep slopes.
 38    /// </summary>
 139    public static readonly Fixed64 DefaultSteepPerpendicularJumpAmount = (Fixed64)0.5f;
 40
 41    /// <summary>
 42    /// Determines how much movement input affects the scout while jumping.
 43    /// </summary>
 144    public static readonly Fixed64 DefaultJumpControlMultiplier = (Fixed64)0.375f; // 75% control when jumping
 45
 46    /// <summary>
 47    /// The default cooldown time after a jump before the scout can jump again.
 48    /// </summary>
 149    public static readonly Fixed64 DefaultCooldownTime = (Fixed64)0.2f;
 50
 51    /// <summary>
 52    /// The default duration after jumping during which ground checks should be ignored.
 53    /// </summary>
 154    public static readonly Fixed64 DefaultAvoidGroundingTimer = (Fixed64)0.05f;
 55
 56    #endregion
 57
 58    #region Configuration State
 59
 60    /// <summary>
 61    /// Determines whether jumping is enabled.
 62    /// If disabled, the scout will be unable to jump.
 63    /// </summary>
 78764    private bool _isEnabled = true;
 65
 66    /// <summary>
 67    /// Maximum number of consecutive jumps allowed (e.g., 2 = double jump).
 68    /// </summary>
 78769    public int MaxJumpCount = 1;
 70
 71    /// <summary>
 72    /// The cooldown time before another jump can be performed.
 73    /// </summary>
 78774    public Fixed64 CooldownTime = DefaultCooldownTime;
 75
 76    /// <summary>
 77    /// The duration after jumping where ground detection is temporarily disabled.
 78    /// </summary>
 78779    public Fixed64 AvoidGroundingTimer = DefaultAvoidGroundingTimer;
 80
 81    /// <summary>
 82    /// The base height the scout can jump when the button is pressed briefly.
 83    /// </summary>
 78784    public Fixed64 BaseJumpHeight = DefaultBaseJumpHeight;
 85
 86    /// <summary>
 87    /// Additional height gained when holding the jump button.
 88    /// </summary>
 78789    public Fixed64 ExtraJumpHeight = DefaultExtraJumpHeight;
 90
 91    /// <summary>
 92    /// Controls how much movement input affects the scout while jumping.
 93    /// Lower values reduce movement responsiveness.
 94    /// </summary>
 78795    public Fixed64 JumpControlMultiplier = DefaultJumpControlMultiplier;
 96
 97    /// <summary>
 98    /// Controls how much the scout jumps out perpendicular to the surface on walkable terrain.
 99    /// A value of 0 means fully vertical, and 1 means fully perpendicular.
 100    /// </summary>
 787101    public Fixed64 PerpendicularJumpAmount = DefaultPerpendicularJumpAmount;
 102
 103    /// <summary>
 104    /// Controls how much the scout jumps out perpendicular to the surface on steep terrain.
 105    /// A value of 0 means fully vertical, and 1 means fully perpendicular.
 106    /// </summary>
 787107    public Fixed64 SteepPerpendicularJumpAmount = DefaultSteepPerpendicularJumpAmount;
 108
 109    #endregion
 110
 111    #region Transient State
 112
 113    /// <inheritdoc cref="ILocomotion.IsEnabled"/>
 114    public bool IsEnabled
 115    {
 2096116        get => _isEnabled;
 117        set
 118        {
 1119            _isEnabled = value;
 1120            if (!_isEnabled)
 1121                this.ClearTransientState();
 1122        }
 123    }
 124
 125    /// <summary>
 126    /// Indicates whether the scout is currently performing a jump.
 127    /// </summary>
 128    /// <remarks>
 129    /// This is true if the jump button was pressed and the scout is not grounded.
 130    /// </remarks>
 131    [Transient]
 132    public bool IsJumping { get; set; }
 133
 134    /// <summary>
 135    /// Indicates whether the scout is holding the jump button.
 136    /// </summary>
 137    [Transient]
 138    public bool IsHoldingJump { get; set; }
 139
 140    /// <summary>
 141    /// The simulation frame when the scout started jumping.
 142    /// </summary>
 143    [Transient]
 144    public Fixed64 JumpStartTime { get; set; }
 145
 146    /// <summary>
 147    /// The direction in which the scout jumped during the current frame.
 148    /// </summary>
 149    [Transient]
 150    public Vector3d FrameJumpDirection { get; set; }
 151
 152    /// <summary>
 153    /// The elapsed time in the cooldown state.
 154    /// </summary>
 155    [Transient]
 156    public Fixed64 CooldownTimer { get; private set; }
 157
 158    /// <summary>
 159    /// Indicates whether the scout is currently in a jump cooldown period.
 160    /// </summary>
 161    [Transient]
 162    public bool IsCoolingDown { get; private set; }
 163
 164    /// <summary>
 165    /// The current number of jumps performed since the last grounding.
 166    /// </summary>
 167    [Transient]
 168    public int JumpCount { get; private set; }
 169
 170    /// <summary>
 171    /// Returns true if more jumps are allowed.
 172    /// </summary>
 219173    public bool CanJump => JumpCount < MaxJumpCount && !IsCoolingDown;
 174
 175    private TrailblazerWorldContext RequireContext() =>
 154176        _context ?? throw new InvalidOperationException("JumpLocomotion requires an explicit TrailblazerWorldContext.");
 177
 103178    private Fixed64 DeltaTime => RequireContext().DeltaTime;
 179
 51180    private Fixed64 TotalTime => RequireContext().TotalTime;
 181
 182    #endregion
 183
 184    #region Methods
 185
 186    /// <summary>
 187    /// Increments the jump counter.
 188    /// </summary>
 189    public void RegisterJump()
 190    {
 51191        JumpCount++;
 51192        IsJumping = true;
 51193        IsHoldingJump = true;
 51194        JumpStartTime = TotalTime;
 195
 196        // Start cooldown only if this was the last allowed jump
 51197        if (JumpCount >= MaxJumpCount)
 41198            StartCooldown();
 51199    }
 200
 201    /// <summary>
 202    /// Starts the jump cooldown period, preventing another jump until the cooldown expires.
 203    /// </summary>
 204    public void StartCooldown()
 205    {
 52206        IsCoolingDown = true;
 52207        CooldownTimer = Fixed64.Zero;
 52208    }
 209
 210    /// <summary>
 211    /// Updates the jump cooldown timer, resetting the jump state when the cooldown expires.
 212    /// </summary>
 213    public void UpdateCooldown()
 214    {
 103215        if (!IsCoolingDown)
 0216            return;
 103217        CooldownTimer += DeltaTime;
 103218        if (CooldownTimer >= CooldownTime)
 219        {
 12220            CooldownTimer = Fixed64.Zero;
 12221            IsCoolingDown = false;
 222        }
 103223    }
 224
 225    /// <summary>
 226    /// Resets jump state upon grounding.
 227    /// </summary>
 228    public void ResetJumpCounter()
 229    {
 7230        JumpCount = 0;
 7231        IsJumping = false;
 7232        IsHoldingJump = false;
 7233    }
 234
 235    internal void BindContext(TrailblazerWorldContext context)
 236    {
 866237        Trailblazer.Pathing.PathRequestContextResolver.ThrowIfUnusable(context);
 866238        _context = context;
 866239    }
 240
 241    #endregion
 242
 243    /// <inheritdoc />
 244    public void RecordData(IChronicler chronicler)
 245    {
 100246        RecordValues.Look(chronicler, ref _isEnabled, "IsEnabled", true);
 100247        RecordValues.Look(chronicler, ref MaxJumpCount, "MaxJumpCount", 1);
 100248        RecordValues.Look(chronicler, ref CooldownTime, "CooldownTime", DefaultCooldownTime);
 100249        RecordValues.Look(chronicler, ref AvoidGroundingTimer, "AvoidGroundingTimer", DefaultAvoidGroundingTimer);
 100250        RecordValues.Look(chronicler, ref BaseJumpHeight, "BaseJumpHeight", DefaultBaseJumpHeight);
 100251        RecordValues.Look(chronicler, ref ExtraJumpHeight, "ExtraJumpHeight", DefaultExtraJumpHeight);
 100252        RecordValues.Look(chronicler, ref JumpControlMultiplier, "JumpControlMultiplier", DefaultJumpControlMultiplier);
 100253        RecordValues.Look(chronicler, ref PerpendicularJumpAmount, "PerpendicularJumpAmount", DefaultPerpendicularJumpAm
 100254        RecordValues.Look(chronicler, ref SteepPerpendicularJumpAmount, "SteepPerpendicularJumpAmount", DefaultSteepPerp
 255
 100256        bool isJumping = IsJumping;
 100257        bool isHoldingJump = IsHoldingJump;
 100258        Fixed64 jumpStartTime = JumpStartTime;
 100259        Vector3d frameJumpDirection = FrameJumpDirection;
 100260        Fixed64 cooldownTimer = CooldownTimer;
 100261        bool isCoolingDown = IsCoolingDown;
 100262        int jumpCount = JumpCount;
 263
 100264        RecordValues.Look(chronicler, ref isJumping, "IsJumping", false);
 100265        RecordValues.Look(chronicler, ref isHoldingJump, "IsHoldingJump", false);
 100266        RecordValues.Look(chronicler, ref jumpStartTime, "JumpStartTime", Fixed64.Zero);
 100267        RecordValues.Look(chronicler, ref frameJumpDirection, "FrameJumpDirection", Vector3d.Zero);
 100268        RecordValues.Look(chronicler, ref cooldownTimer, "CooldownTimer", Fixed64.Zero);
 100269        RecordValues.Look(chronicler, ref isCoolingDown, "IsCoolingDown", false);
 100270        RecordValues.Look(chronicler, ref jumpCount, "JumpCount", 0);
 271
 100272        if (chronicler.Mode == SerializationMode.Loading)
 273        {
 50274            IsJumping = isJumping;
 50275            IsHoldingJump = isHoldingJump;
 50276            JumpStartTime = jumpStartTime;
 50277            FrameJumpDirection = frameJumpDirection;
 50278            CooldownTimer = cooldownTimer;
 50279            IsCoolingDown = isCoolingDown;
 50280            JumpCount = jumpCount;
 281
 50282            if (!_isEnabled)
 2283                this.ClearTransientState();
 284        }
 100285    }
 286}