/ GUNRPG.Core / Combat / AwarenessModel.cs
AwarenessModel.cs
  1  using GUNRPG.Core.Operators;
  2  
  3  namespace GUNRPG.Core.Combat;
  4  
  5  /// <summary>
  6  /// Models awareness and visibility mechanics for operators.
  7  /// Awareness affects what information is available to opponents and AI decision-making.
  8  /// Full cover blocks information, not just damage.
  9  /// </summary>
 10  public static class AwarenessModel
 11  {
 12      /// <summary>
 13      /// Base recognition delay in milliseconds when an operator exits full cover.
 14      /// This is scaled by the observer's accuracy proficiency.
 15      /// </summary>
 16      public const float BaseRecognitionDelayMs = 200f;
 17  
 18      /// <summary>
 19      /// Minimum recognition delay even for highly proficient observers (ms).
 20      /// Represents human reaction time floor.
 21      /// </summary>
 22      public const float MinRecognitionDelayMs = 50f;
 23  
 24      /// <summary>
 25      /// Maximum recognition delay for low proficiency observers (ms).
 26      /// </summary>
 27      public const float MaxRecognitionDelayMs = 350f;
 28  
 29      /// <summary>
 30      /// Suppression modifier that increases recognition delay.
 31      /// At full suppression, recognition delay is multiplied by this factor.
 32      /// </summary>
 33      public const float MaxSuppressionRecognitionMultiplier = 1.5f;
 34  
 35      /// <summary>
 36      /// Heavy accuracy penalty applied during recognition delay period.
 37      /// Shots during this period are essentially blind fire.
 38      /// </summary>
 39      public const float RecognitionPenaltyAccuracyMultiplier = 0.3f;
 40  
 41      /// <summary>
 42      /// Determines if an observer can see a target based on the target's cover state.
 43      /// </summary>
 44      /// <param name="targetCoverState">The cover state of the target being observed.</param>
 45      /// <returns>True if the target is visible, false if fully concealed.</returns>
 46      public static bool CanSeeTarget(CoverState targetCoverState)
 47      {
 48          return targetCoverState switch
 49          {
 50              CoverState.None => true,     // Fully visible
 51              CoverState.Partial => true,  // Partially visible (peeking)
 52              CoverState.Full => false,    // Not visible (concealed)
 53              _ => true
 54          };
 55      }
 56  
 57      /// <summary>
 58      /// Gets the visibility level of a target (for graded awareness).
 59      /// </summary>
 60      /// <param name="targetCoverState">The cover state of the target.</param>
 61      /// <returns>
 62      /// 1.0 = fully visible (None)
 63      /// 0.5 = partially visible (Partial)
 64      /// 0.0 = not visible (Full)
 65      /// </returns>
 66      public static float GetVisibilityLevel(CoverState targetCoverState)
 67      {
 68          return targetCoverState switch
 69          {
 70              CoverState.None => 1.0f,
 71              CoverState.Partial => 0.5f,
 72              CoverState.Full => 0.0f,
 73              _ => 1.0f
 74          };
 75      }
 76  
 77      /// <summary>
 78      /// Calculates the recognition delay when a target becomes visible (exits full cover).
 79      /// Higher accuracy proficiency = faster recognition.
 80      /// </summary>
 81      /// <param name="observerAccuracyProficiency">Observer's accuracy proficiency (0.0-1.0)</param>
 82      /// <param name="observerSuppressionLevel">Observer's current suppression level (0.0-1.0)</param>
 83      /// <returns>Recognition delay in milliseconds</returns>
 84      public static float CalculateRecognitionDelayMs(
 85          float observerAccuracyProficiency,
 86          float observerSuppressionLevel = 0f)
 87      {
 88          observerAccuracyProficiency = Math.Clamp(observerAccuracyProficiency, 0f, 1f);
 89          observerSuppressionLevel = Math.Clamp(observerSuppressionLevel, 0f, 1f);
 90  
 91          // Base delay scaled inversely by proficiency
 92          // High proficiency (1.0) = low delay, Low proficiency (0.0) = high delay
 93          float proficiencyFactor = 1.0f - observerAccuracyProficiency;
 94          float baseDelay = BaseRecognitionDelayMs * proficiencyFactor;
 95  
 96          // Apply suppression modifier (suppressed observers take longer to recognize)
 97          float suppressionMultiplier = 1.0f + (observerSuppressionLevel * (MaxSuppressionRecognitionMultiplier - 1.0f));
 98          float adjustedDelay = baseDelay * suppressionMultiplier;
 99  
100          // Clamp to bounds
101          return Math.Clamp(adjustedDelay, MinRecognitionDelayMs, MaxRecognitionDelayMs);
102      }
103  
104      /// <summary>
105      /// Gets the accuracy penalty multiplier during recognition delay.
106      /// During recognition, shots are essentially blind fire.
107      /// </summary>
108      /// <param name="recognitionProgress">Progress through recognition (0.0 = just started, 1.0 = complete)</param>
109      /// <returns>Accuracy multiplier (lower = worse accuracy)</returns>
110      public static float GetRecognitionAccuracyMultiplier(float recognitionProgress)
111      {
112          recognitionProgress = Math.Clamp(recognitionProgress, 0f, 1f);
113  
114          // Linear interpolation from heavy penalty to no penalty
115          return RecognitionPenaltyAccuracyMultiplier + 
116                 (1.0f - RecognitionPenaltyAccuracyMultiplier) * recognitionProgress;
117      }
118  }