/ GUNRPG.Core / Combat / MovementModel.cs
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  }