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 }