< Summary

Information
Class: Trailblazer.Navigation.Motor.WaterLocomotion
Assembly: Trailblazer
File(s): /home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Navigation/Motor/Locomotion/WaterLocomotion.cs
Line coverage
98%
Covered lines: 68
Uncovered lines: 1
Coverable lines: 69
Total lines: 255
Line coverage: 98.5%
Branch coverage
83%
Covered branches: 15
Total branches: 18
Branch coverage: 83.3%
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(...)100%22100%
get_MaxSwimAcceleration()100%11100%
get_IsDrowning()75%44100%
RequireContext()50%22100%
get_DeltaTime()100%11100%
UpdateDiveTime()83.33%6690%
BindContext(...)100%11100%
RecordData(...)100%44100%

File(s)

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

#LineLine coverage
 1using Chronicler;
 2using FixedMathSharp;
 3using System;
 4using Trailblazer.Support;
 5
 6namespace Trailblazer.Navigation.Motor;
 7
 8/// <summary>
 9/// Handles water traversal mechanics, including active swimming, buoyancy, water resistance, and breath control.
 10/// </summary>
 11/// <remarks>
 12/// This locomotion module owns liquid-medium runtime state. Active swim input is one capability inside
 13/// that model alongside passive floating, sinking, breach behavior, and dive-time tracking.
 14/// </remarks>
 15public class WaterLocomotion : ILocomotion
 16{
 17    private TrailblazerWorldContext? _context;
 18
 19    #region Constants
 20
 21    /// <summary>
 22    /// The default duration the scout can hold its breath underwater before drowning begins.
 23    /// </summary>
 124    public static readonly Fixed64 DefaultHoldBreathTime = (Fixed64)60;
 25
 26    /// <summary>
 27    /// The default amount of breath regenerated per tick when resurfacing.
 28    /// </summary>
 129    public static readonly Fixed64 DefaultBreathRegenerateIncrement = (Fixed64)10;
 30
 31    /// <summary>
 32    /// The default maximum swimming speed.
 33    /// </summary>
 134    public static readonly Fixed64 DefaultMaxSwimSpeed = (Fixed64)1.5d;
 35
 36    /// <summary>
 37    /// The default maximum sideways swimming speed.
 38    /// </summary>
 139    public static readonly Fixed64 DefaultMaxSwimSidewaysSpeed = (Fixed64)1d;
 40
 41    /// <summary>
 42    /// The default maximum acceleration while swimming.
 43    /// </summary>
 144    public static readonly Fixed64 DefaultMaxSwimAcceleration = (Fixed64)5;
 45
 46    /// <summary>
 47    /// The default swim acceleration multiplier.
 48    /// </summary>
 149    public static readonly Fixed64 DefaultSwimAccelerationModifier = (Fixed64)1;
 50
 51    /// <summary>
 52    /// The default buoyancy factor, controlling how strongly the scout floats in water.
 53    /// </summary>
 154    public static readonly Fixed64 DefaultBouyancyFactor = Fixed64.One;
 55
 56    /// <summary>
 57    /// Default multiplier applied to jump force when breaching from water into air.
 58    /// </summary>
 159    public static readonly Fixed64 DefaultBreachJumpMultiplier = (Fixed64)0.75d;
 60
 61    #endregion
 62
 63    #region Configuration State
 64
 65    /// <summary>
 66    /// Determines whether water traversal mechanics are enabled.
 67    /// </summary>
 78568    private bool _isEnabled = true;
 69
 70    /// <summary>
 71    /// Determines whether the scout can actively swim while in water.
 72    /// </summary>
 78573    public bool CanSwim = true;
 74
 75    /// <summary>
 76    /// Determines whether the scout can breach the water surface when jumping.
 77    /// </summary>
 78578    public bool CanBreachWater = true;
 79
 80    /// <summary>
 81    /// Determines whether the scout can drown if underwater for too long.
 82    /// </summary>
 78583    public bool CanDrown = true;
 84
 85    /// <summary>
 86    /// The maximum forward swimming speed when active swim control is engaged.
 87    /// </summary>
 78588    public Fixed64 MaxSwimSpeed = DefaultMaxSwimSpeed;
 89
 90    /// <summary>
 91    /// The maximum sideways swimming speed when active swim control is engaged.
 92    /// </summary>
 78593    public Fixed64 MaxSwimSidewaysSpeed = DefaultMaxSwimSidewaysSpeed;
 94
 95    /// <summary>
 96    /// The maximum acceleration while actively swimming.
 97    /// </summary>
 78598    public Fixed64 MaxWaterAcceleration = DefaultMaxSwimAcceleration;
 99
 100    /// <summary>
 101    /// The acceleration multiplier applied to active swimming movement.
 102    /// </summary>
 785103    public Fixed64 SwimAccelerationModifier = DefaultSwimAccelerationModifier;
 104
 105    /// <summary>
 106    /// The buoyancy factor determining how strongly the scout floats or sinks in water.
 107    /// </summary>
 785108    public Fixed64 BuoyancyFactor = DefaultBouyancyFactor;
 109
 110    /// <summary>
 111    /// Multiplier applied to the jump velocity when the scout breaches water.
 112    /// Controls how forcefully the scout exits the water.
 113    /// </summary>
 114    /// <remarks>
 115    /// A value less than 1 results in a lower jump arc compared to standard ground jumps.
 116    /// </remarks>
 785117    public Fixed64 BreachJumpMultiplier = DefaultBreachJumpMultiplier;
 118
 119    /// <summary>
 120    /// The maximum time the scout can hold its breath underwater before drowning.
 121    /// </summary>
 785122    public Fixed64 HoldBreathTime = DefaultHoldBreathTime;
 123
 124    /// <summary>
 125    /// The amount of breath the scout regenerates per tick when resurfacing.
 126    /// </summary>
 785127    public Fixed64 BreathRegenerateIncrement = DefaultBreathRegenerateIncrement;
 128
 129    #endregion
 130
 131    #region Transient State
 132
 133    /// <inheritdoc cref="ILocomotion.IsEnabled"/>
 134    public bool IsEnabled
 135    {
 6561136        get => _isEnabled;
 137        set
 138        {
 8139            _isEnabled = value;
 8140            if (!_isEnabled)
 2141                this.ClearTransientState();
 8142        }
 143    }
 144
 145    /// <summary>
 146    /// Indicates whether the scout is currently under active swimming control.
 147    /// </summary>
 148    [Transient]
 149    public bool IsSwimming { get; set; }
 150
 151    /// <summary>
 152    /// Indicates whether the scout is currently diving (fully submerged).
 153    /// </summary>
 154    [Transient]
 155    public bool IsDiving { get; set; }
 156
 157    /// <summary>
 158    /// The amount of time the scout has been underwater.
 159    /// </summary>
 160    [Transient]
 161    public Fixed64 UnderwaterTimer { get; set; }
 162
 163    /// <summary>
 164    /// Stores the authored swim intent for the active traversal so entry into liquid can resolve after state refresh.
 165    /// </summary>
 166    [Transient]
 167    public bool RequestedSwimThisTraversal { get; set; }
 168
 169    /// <summary>
 170    /// The effective maximum acceleration while swimming, factoring in the acceleration modifier.
 171    /// </summary>
 18172    public Fixed64 MaxSwimAcceleration => MaxWaterAcceleration * SwimAccelerationModifier;
 173
 174    /// <summary>
 175    /// Determines whether the scout is drowning due to prolonged underwater exposure.
 176    /// </summary>
 177    public bool IsDrowning
 178    {
 179        get
 180        {
 176181            if (!_isEnabled || !CanDrown)
 2182                return false;
 183
 174184            return UnderwaterTimer >= HoldBreathTime;
 185        }
 186    }
 187
 188    #endregion
 189
 190    private TrailblazerWorldContext RequireContext() =>
 170191        _context ?? throw new InvalidOperationException("WaterLocomotion requires an explicit TrailblazerWorldContext.")
 192
 170193    private Fixed64 DeltaTime => RequireContext().DeltaTime;
 194
 195    /// <summary>
 196    /// Updates the dive timer, tracking underwater duration and regenerating breath when resurfacing.
 197    /// </summary>
 198    public void UpdateDiveTime()
 199    {
 174200        if (IsDiving)
 201        {
 159202            UnderwaterTimer += DeltaTime;
 159203            return;
 204        }
 205
 15206        if (UnderwaterTimer == Fixed64.Zero)
 4207            return;
 208
 11209        Fixed64 time = DeltaTime * BreathRegenerateIncrement;
 11210        UnderwaterTimer -= time;
 11211        if (UnderwaterTimer < Fixed64.Zero)
 0212            UnderwaterTimer = Fixed64.Zero;
 11213    }
 214
 215    internal void BindContext(TrailblazerWorldContext context)
 216    {
 864217        Trailblazer.Pathing.PathRequestContextResolver.ThrowIfUnusable(context);
 864218        _context = context;
 864219    }
 220
 221    /// <inheritdoc />
 222    public void RecordData(IChronicler chronicler)
 223    {
 100224        RecordValues.Look(chronicler, ref _isEnabled, "IsEnabled", true);
 100225        RecordValues.Look(chronicler, ref CanSwim, "CanSwim", true);
 100226        RecordValues.Look(chronicler, ref CanBreachWater, "CanBreachWater", true);
 100227        RecordValues.Look(chronicler, ref CanDrown, "CanDrown", true);
 100228        RecordValues.Look(chronicler, ref MaxSwimSpeed, "MaxSwimSpeed", DefaultMaxSwimSpeed);
 100229        RecordValues.Look(chronicler, ref MaxSwimSidewaysSpeed, "MaxSwimSidewaysSpeed", DefaultMaxSwimSidewaysSpeed);
 100230        RecordValues.Look(chronicler, ref MaxWaterAcceleration, "MaxWaterAcceleration", DefaultMaxSwimAcceleration);
 100231        RecordValues.Look(chronicler, ref SwimAccelerationModifier, "SwimAccelerationModifier", DefaultSwimAccelerationM
 100232        RecordValues.Look(chronicler, ref BuoyancyFactor, "BuoyancyFactor", DefaultBouyancyFactor);
 100233        RecordValues.Look(chronicler, ref BreachJumpMultiplier, "BreachJumpMultiplier", DefaultBreachJumpMultiplier);
 100234        RecordValues.Look(chronicler, ref HoldBreathTime, "HoldBreathTime", DefaultHoldBreathTime);
 100235        RecordValues.Look(chronicler, ref BreathRegenerateIncrement, "BreathRegenerateIncrement", DefaultBreathRegenerat
 236
 100237        bool isSwimming = IsSwimming;
 100238        bool isDiving = IsDiving;
 100239        Fixed64 underwaterTimer = UnderwaterTimer;
 240
 100241        RecordValues.Look(chronicler, ref isSwimming, "IsSwimming", false);
 100242        RecordValues.Look(chronicler, ref isDiving, "IsDiving", false);
 100243        RecordValues.Look(chronicler, ref underwaterTimer, "UnderwaterTimer", Fixed64.Zero);
 244
 100245        if (chronicler.Mode == SerializationMode.Loading)
 246        {
 50247            IsSwimming = isSwimming;
 50248            IsDiving = isDiving;
 50249            UnderwaterTimer = underwaterTimer;
 250
 50251            if (!_isEnabled)
 2252                this.ClearTransientState();
 253        }
 100254    }
 255}