< 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
100%
Covered lines: 69
Uncovered lines: 0
Coverable lines: 69
Total lines: 262
Line coverage: 100%
Branch coverage
100%
Covered branches: 18
Total branches: 18
Branch coverage: 100%
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()100%44100%
RequireContext()100%22100%
get_DeltaTime()100%11100%
UpdateDiveTime()100%66100%
BindContext(...)100%11100%
RecordData(...)100%44100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// WaterLocomotion.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9using Chronicler;
 10using FixedMathSharp;
 11using Trailblazer.Support;
 12
 13namespace Trailblazer.Navigation.Motor;
 14
 15/// <summary>
 16/// Handles water traversal mechanics, including active swimming, buoyancy, water resistance, and breath control.
 17/// </summary>
 18/// <remarks>
 19/// This locomotion module owns liquid-medium runtime state. Active swim input is one capability inside
 20/// that model alongside passive floating, sinking, breach behavior, and dive-time tracking.
 21/// </remarks>
 22public class WaterLocomotion : ILocomotion
 23{
 24    private TrailblazerWorldContext? _context;
 25
 26    #region Constants
 27
 28    /// <summary>
 29    /// The default duration the scout can hold its breath underwater before drowning begins.
 30    /// </summary>
 131    public static readonly Fixed64 DefaultHoldBreathTime = (Fixed64)60;
 32
 33    /// <summary>
 34    /// The default amount of breath regenerated per tick when resurfacing.
 35    /// </summary>
 136    public static readonly Fixed64 DefaultBreathRegenerateIncrement = (Fixed64)10;
 37
 38    /// <summary>
 39    /// The default maximum swimming speed.
 40    /// </summary>
 141    public static readonly Fixed64 DefaultMaxSwimSpeed = Fixed64.FromFraction(3, 2);
 42
 43    /// <summary>
 44    /// The default maximum sideways swimming speed.
 45    /// </summary>
 146    public static readonly Fixed64 DefaultMaxSwimSidewaysSpeed = (Fixed64)1d;
 47
 48    /// <summary>
 49    /// The default maximum acceleration while swimming.
 50    /// </summary>
 151    public static readonly Fixed64 DefaultMaxSwimAcceleration = (Fixed64)5;
 52
 53    /// <summary>
 54    /// The default swim acceleration multiplier.
 55    /// </summary>
 156    public static readonly Fixed64 DefaultSwimAccelerationModifier = (Fixed64)1;
 57
 58    /// <summary>
 59    /// The default buoyancy factor, controlling how strongly the scout floats in water.
 60    /// </summary>
 161    public static readonly Fixed64 DefaultBuoyancyFactor = Fixed64.One;
 62
 63    /// <summary>
 64    /// Default multiplier applied to jump force when breaching from water into air.
 65    /// </summary>
 166    public static readonly Fixed64 DefaultBreachJumpMultiplier = Fixed64.FromFraction(3, 4);
 67
 68    #endregion
 69
 70    #region Configuration State
 71
 72    /// <summary>
 73    /// Determines whether water traversal mechanics are enabled.
 74    /// </summary>
 115475    private bool _isEnabled = true;
 76
 77    /// <summary>
 78    /// Determines whether the scout can actively swim while in water.
 79    /// </summary>
 115480    public bool CanSwim = true;
 81
 82    /// <summary>
 83    /// Determines whether the scout can breach the water surface when jumping.
 84    /// </summary>
 115485    public bool CanBreachWater = true;
 86
 87    /// <summary>
 88    /// Determines whether the scout can drown if underwater for too long.
 89    /// </summary>
 115490    public bool CanDrown = true;
 91
 92    /// <summary>
 93    /// The maximum forward swimming speed when active swim control is engaged.
 94    /// </summary>
 115495    public Fixed64 MaxSwimSpeed = DefaultMaxSwimSpeed;
 96
 97    /// <summary>
 98    /// The maximum sideways swimming speed when active swim control is engaged.
 99    /// </summary>
 1154100    public Fixed64 MaxSwimSidewaysSpeed = DefaultMaxSwimSidewaysSpeed;
 101
 102    /// <summary>
 103    /// The maximum acceleration while actively swimming.
 104    /// </summary>
 1154105    public Fixed64 MaxWaterAcceleration = DefaultMaxSwimAcceleration;
 106
 107    /// <summary>
 108    /// The acceleration multiplier applied to active swimming movement.
 109    /// </summary>
 1154110    public Fixed64 SwimAccelerationModifier = DefaultSwimAccelerationModifier;
 111
 112    /// <summary>
 113    /// The buoyancy factor determining how strongly the scout floats or sinks in water.
 114    /// </summary>
 1154115    public Fixed64 BuoyancyFactor = DefaultBuoyancyFactor;
 116
 117    /// <summary>
 118    /// Multiplier applied to the jump velocity when the scout breaches water.
 119    /// Controls how forcefully the scout exits the water.
 120    /// </summary>
 121    /// <remarks>
 122    /// A value less than 1 results in a lower jump arc compared to standard ground jumps.
 123    /// </remarks>
 1154124    public Fixed64 BreachJumpMultiplier = DefaultBreachJumpMultiplier;
 125
 126    /// <summary>
 127    /// The maximum time the scout can hold its breath underwater before drowning.
 128    /// </summary>
 1154129    public Fixed64 HoldBreathTime = DefaultHoldBreathTime;
 130
 131    /// <summary>
 132    /// The amount of breath the scout regenerates per tick when resurfacing.
 133    /// </summary>
 1154134    public Fixed64 BreathRegenerateIncrement = DefaultBreathRegenerateIncrement;
 135
 136    #endregion
 137
 138    #region Transient State
 139
 140    /// <inheritdoc cref="ILocomotion.IsEnabled"/>
 141    public bool IsEnabled
 142    {
 6865143        get => _isEnabled;
 144        set
 145        {
 16146            _isEnabled = value;
 16147            if (!_isEnabled)
 5148                this.ClearTransientState();
 16149        }
 150    }
 151
 152    /// <summary>
 153    /// Indicates whether the scout is currently under active swimming control.
 154    /// </summary>
 155    [Transient]
 156    public bool IsSwimming { get; set; }
 157
 158    /// <summary>
 159    /// Indicates whether the scout is currently diving (fully submerged).
 160    /// </summary>
 161    [Transient]
 162    public bool IsDiving { get; set; }
 163
 164    /// <summary>
 165    /// The amount of time the scout has been underwater.
 166    /// </summary>
 167    [Transient]
 168    public Fixed64 UnderwaterTimer { get; set; }
 169
 170    /// <summary>
 171    /// Stores the authored swim intent for the active traversal so entry into liquid can resolve after state refresh.
 172    /// </summary>
 173    [Transient]
 174    public bool RequestedSwimThisTraversal { get; set; }
 175
 176    /// <summary>
 177    /// The effective maximum acceleration while swimming, factoring in the acceleration modifier.
 178    /// </summary>
 16179    public Fixed64 MaxSwimAcceleration => MaxWaterAcceleration * SwimAccelerationModifier;
 180
 181    /// <summary>
 182    /// Determines whether the scout is drowning due to prolonged underwater exposure.
 183    /// </summary>
 184    public bool IsDrowning
 185    {
 186        get
 187        {
 185188            if (!_isEnabled || !CanDrown)
 3189                return false;
 190
 182191            return UnderwaterTimer >= HoldBreathTime;
 192        }
 193    }
 194
 195    #endregion
 196
 197    private TrailblazerWorldContext RequireContext() =>
 176198        _context ?? throw new InvalidOperationException("WaterLocomotion requires an explicit TrailblazerWorldContext.")
 199
 176200    private Fixed64 DeltaTime => RequireContext().DeltaTime;
 201
 202    /// <summary>
 203    /// Updates the dive timer, tracking underwater duration and regenerating breath when resurfacing.
 204    /// </summary>
 205    public void UpdateDiveTime()
 206    {
 182207        if (IsDiving)
 208        {
 164209            UnderwaterTimer += DeltaTime;
 163210            return;
 211        }
 212
 18213        if (UnderwaterTimer == Fixed64.Zero)
 6214            return;
 215
 12216        Fixed64 time = DeltaTime * BreathRegenerateIncrement;
 12217        UnderwaterTimer -= time;
 12218        if (UnderwaterTimer < Fixed64.Zero)
 1219            UnderwaterTimer = Fixed64.Zero;
 12220    }
 221
 222    internal void BindContext(TrailblazerWorldContext context)
 223    {
 1241224        TrailblazerWorldContext.ThrowIfUnusable(context);
 1241225        _context = context;
 1241226    }
 227
 228    /// <inheritdoc />
 229    public void RecordData(IChronicler chronicler)
 230    {
 158231        RecordValues.Look(chronicler, ref _isEnabled, "IsEnabled", true);
 158232        RecordValues.Look(chronicler, ref CanSwim, "CanSwim", true);
 158233        RecordValues.Look(chronicler, ref CanBreachWater, "CanBreachWater", true);
 158234        RecordValues.Look(chronicler, ref CanDrown, "CanDrown", true);
 158235        RecordValues.Look(chronicler, ref MaxSwimSpeed, "MaxSwimSpeed", DefaultMaxSwimSpeed);
 158236        RecordValues.Look(chronicler, ref MaxSwimSidewaysSpeed, "MaxSwimSidewaysSpeed", DefaultMaxSwimSidewaysSpeed);
 158237        RecordValues.Look(chronicler, ref MaxWaterAcceleration, "MaxWaterAcceleration", DefaultMaxSwimAcceleration);
 158238        RecordValues.Look(chronicler, ref SwimAccelerationModifier, "SwimAccelerationModifier", DefaultSwimAccelerationM
 158239        RecordValues.Look(chronicler, ref BuoyancyFactor, "BuoyancyFactor", DefaultBuoyancyFactor);
 158240        RecordValues.Look(chronicler, ref BreachJumpMultiplier, "BreachJumpMultiplier", DefaultBreachJumpMultiplier);
 158241        RecordValues.Look(chronicler, ref HoldBreathTime, "HoldBreathTime", DefaultHoldBreathTime);
 158242        RecordValues.Look(chronicler, ref BreathRegenerateIncrement, "BreathRegenerateIncrement", DefaultBreathRegenerat
 243
 158244        bool isSwimming = IsSwimming;
 158245        bool isDiving = IsDiving;
 158246        Fixed64 underwaterTimer = UnderwaterTimer;
 247
 158248        RecordValues.Look(chronicler, ref isSwimming, "IsSwimming", false);
 158249        RecordValues.Look(chronicler, ref isDiving, "IsDiving", false);
 158250        RecordValues.Look(chronicler, ref underwaterTimer, "UnderwaterTimer", Fixed64.Zero);
 251
 158252        if (chronicler.Mode == SerializationMode.Loading)
 253        {
 74254            IsSwimming = isSwimming;
 74255            IsDiving = isDiving;
 74256            UnderwaterTimer = underwaterTimer;
 257
 74258            if (!_isEnabled)
 2259                this.ClearTransientState();
 260        }
 158261    }
 262}