MovementModel.cs
1 using GUNRPG.Core.Operators; 2 3 namespace GUNRPG.Core.Combat; 4 5 /// <summary> 6 /// Models operator movement mechanics and their impact on combat effectiveness. 7 /// Movement affects accuracy, weapon sway, ADS time, and suppression without requiring spatial coordinates. 8 /// </summary> 9 public static class MovementModel 10 { 11 // Accuracy Multipliers (applied to base accuracy) 12 public const float StationaryAccuracyMultiplier = 1.0f; 13 public const float WalkingAccuracyMultiplier = 0.85f; 14 public const float SprintingAccuracyMultiplier = 0.45f; 15 public const float CrouchingAccuracyMultiplier = 1.1f; 16 17 // Weapon Sway (angular noise in degrees, layered on top of recoil) 18 public const float StationarySwayDegrees = 0.0f; 19 public const float WalkingSwayDegrees = 0.05f; 20 public const float SprintingSwayDegrees = 0.15f; 21 public const float CrouchingSwayDegrees = 0.02f; 22 23 // ADS Time Multipliers 24 public const float StationaryADSMultiplier = 1.0f; 25 public const float WalkingADSMultiplier = 1.2f; 26 public const float SprintingADSMultiplier = 1.6f; 27 public const float CrouchingADSMultiplier = 0.9f; 28 29 // Suppression Interaction 30 public const float WalkingSuppressionBuildupMultiplier = 1.15f; 31 public const float SprintingSuppressionBuildupMultiplier = 1.3f; 32 public const float CrouchingSuppressionReduction = 0.2f; // 20% reduction 33 public const float CrouchingSuppressionDecayMultiplier = 1.4f; // 40% faster decay 34 35 // Directional Movement Modifiers (applied based on movement direction) 36 public const float AdvancingSuppressionBuildupMultiplier = 1.2f; // Moving toward = more exposed 37 public const float RetreatingSuppressionBuildupMultiplier = 0.85f; // Moving away = less exposed 38 public const float AdvancingHitProbabilityMultiplier = 1.15f; // Moving toward = easier to hit 39 public const float RetreatingHitProbabilityMultiplier = 0.9f; // Moving away = harder to hit 40 41 // Cover Modifiers 42 public const float PartialCoverHitProbabilityMultiplier = 0.7f; // 30% reduction in hit chance 43 public const float FullCoverHitProbabilityMultiplier = 0.0f; // Blocks all hits when not peeking 44 45 /// <summary> 46 /// Gets the accuracy multiplier for the given movement state. 47 /// </summary> 48 public static float GetAccuracyMultiplier(MovementState movement) 49 { 50 return movement switch 51 { 52 MovementState.Stationary => StationaryAccuracyMultiplier, 53 MovementState.Idle => StationaryAccuracyMultiplier, 54 MovementState.Walking => WalkingAccuracyMultiplier, 55 MovementState.Sprinting => SprintingAccuracyMultiplier, 56 MovementState.Crouching => CrouchingAccuracyMultiplier, 57 MovementState.Sliding => SprintingAccuracyMultiplier, // Same penalty as sprinting 58 _ => StationaryAccuracyMultiplier 59 }; 60 } 61 62 /// <summary> 63 /// Gets the weapon sway in degrees for the given movement state. 64 /// Sway is additional angular noise layered on top of recoil. 65 /// </summary> 66 public static float GetWeaponSwayDegrees(MovementState movement) 67 { 68 return movement switch 69 { 70 MovementState.Stationary => StationarySwayDegrees, 71 MovementState.Idle => StationarySwayDegrees, 72 MovementState.Walking => WalkingSwayDegrees, 73 MovementState.Sprinting => SprintingSwayDegrees, 74 MovementState.Crouching => CrouchingSwayDegrees, 75 MovementState.Sliding => SprintingSwayDegrees, 76 _ => StationarySwayDegrees 77 }; 78 } 79 80 /// <summary> 81 /// Gets the ADS time multiplier for the given movement state. 82 /// </summary> 83 public static float GetADSTimeMultiplier(MovementState movement) 84 { 85 return movement switch 86 { 87 MovementState.Stationary => StationaryADSMultiplier, 88 MovementState.Idle => StationaryADSMultiplier, 89 MovementState.Walking => WalkingADSMultiplier, 90 MovementState.Sprinting => SprintingADSMultiplier, 91 MovementState.Crouching => CrouchingADSMultiplier, 92 MovementState.Sliding => SprintingADSMultiplier, 93 _ => StationaryADSMultiplier 94 }; 95 } 96 97 /// <summary> 98 /// Gets the suppression buildup multiplier for the given movement state. 99 /// Higher values mean the operator is more susceptible to suppression while moving. 100 /// </summary> 101 public static float GetSuppressionBuildupMultiplier(MovementState movement) 102 { 103 return movement switch 104 { 105 MovementState.Stationary => 1.0f, 106 MovementState.Idle => 1.0f, 107 MovementState.Walking => WalkingSuppressionBuildupMultiplier, 108 MovementState.Sprinting => SprintingSuppressionBuildupMultiplier, 109 MovementState.Crouching => 1.0f - CrouchingSuppressionReduction, 110 MovementState.Sliding => SprintingSuppressionBuildupMultiplier, 111 _ => 1.0f 112 }; 113 } 114 115 /// <summary> 116 /// Gets the suppression decay multiplier for the given movement state. 117 /// Higher values mean faster decay (recovery from suppression). 118 /// </summary> 119 public static float GetSuppressionDecayMultiplier(MovementState movement) 120 { 121 return movement switch 122 { 123 MovementState.Crouching => CrouchingSuppressionDecayMultiplier, 124 _ => 1.0f 125 }; 126 } 127 128 /// <summary> 129 /// Gets the hit probability multiplier for the given cover state. 130 /// This is applied to incoming shots to reduce hit chance. 131 /// </summary> 132 public static float GetCoverHitProbabilityMultiplier(CoverState cover, bool isPeeking = false) 133 { 134 if (cover == CoverState.Full && !isPeeking) 135 { 136 return FullCoverHitProbabilityMultiplier; // Full cover blocks hits 137 } 138 139 return cover switch 140 { 141 CoverState.None => 1.0f, 142 CoverState.Partial => PartialCoverHitProbabilityMultiplier, 143 CoverState.Full => 1.0f, // When peeking, full cover acts like no cover 144 _ => 1.0f 145 }; 146 } 147 148 /// <summary> 149 /// Checks if the operator can enter cover based on movement state. 150 /// Cover can only be entered when stationary or crouching. 151 /// </summary> 152 public static bool CanEnterCover(MovementState movement) 153 { 154 return movement == MovementState.Stationary 155 || movement == MovementState.Idle 156 || movement == MovementState.Crouching; 157 } 158 159 /// <summary> 160 /// Gets the suppression buildup multiplier for movement direction. 161 /// Advancing = more exposed to suppression, Retreating = less exposed. 162 /// </summary> 163 public static float GetDirectionalSuppressionMultiplier(MovementDirection direction) 164 { 165 return direction switch 166 { 167 MovementDirection.Advancing => AdvancingSuppressionBuildupMultiplier, 168 MovementDirection.Retreating => RetreatingSuppressionBuildupMultiplier, 169 MovementDirection.Holding => 1.0f, 170 _ => 1.0f 171 }; 172 } 173 174 /// <summary> 175 /// Gets the hit probability multiplier for movement direction. 176 /// Advancing = easier to hit, Retreating = harder to hit. 177 /// Applied by inverting the multiplier on aim error standard deviation in hit resolution. 178 /// </summary> 179 public static float GetDirectionalHitProbabilityMultiplier(MovementDirection direction) 180 { 181 return direction switch 182 { 183 MovementDirection.Advancing => AdvancingHitProbabilityMultiplier, 184 MovementDirection.Retreating => RetreatingHitProbabilityMultiplier, 185 MovementDirection.Holding => 1.0f, 186 _ => 1.0f 187 }; 188 } 189 }