/ GUNRPG.Core / Combat / CoverTransitionModel.cs
CoverTransitionModel.cs
  1  using GUNRPG.Core.Operators;
  2  
  3  namespace GUNRPG.Core.Combat;
  4  
  5  /// <summary>
  6  /// Models cover transition delays and exposure windows.
  7  /// Cover transitions are not instantaneous - they create vulnerability windows.
  8  /// </summary>
  9  public static class CoverTransitionModel
 10  {
 11      // Transition delay constants (in milliseconds)
 12      // These represent realistic times for moving into/out of cover positions
 13  
 14      /// <summary>
 15      /// Delay for transitioning from no cover to partial cover (80-150ms).
 16      /// Moving to a peeking position from exposed.
 17      /// </summary>
 18      public const int NoneToPartialDelayMs = 100;
 19  
 20      /// <summary>
 21      /// Delay for transitioning from partial cover to full cover (80-150ms).
 22      /// Fully concealing oneself from a peeking position.
 23      /// </summary>
 24      public const int PartialToFullDelayMs = 100;
 25  
 26      /// <summary>
 27      /// Delay for transitioning from full cover to partial cover (100-200ms).
 28      /// Exposing to peek from complete concealment - takes slightly longer.
 29      /// </summary>
 30      public const int FullToPartialDelayMs = 150;
 31  
 32      /// <summary>
 33      /// Delay for transitioning from partial cover to no cover (80-150ms).
 34      /// Leaving cover entirely from a peeking position.
 35      /// </summary>
 36      public const int PartialToNoneDelayMs = 100;
 37  
 38      /// <summary>
 39      /// Direct transition from full cover to no cover goes through partial first.
 40      /// Total delay = FullToPartialDelayMs + PartialToNoneDelayMs
 41      /// </summary>
 42      public const int FullToNoneDelayMs = FullToPartialDelayMs + PartialToNoneDelayMs;
 43  
 44      /// <summary>
 45      /// Direct transition from no cover to full cover goes through partial first.
 46      /// Total delay = NoneToPartialDelayMs + PartialToFullDelayMs
 47      /// </summary>
 48      public const int NoneToFullDelayMs = NoneToPartialDelayMs + PartialToFullDelayMs;
 49  
 50      /// <summary>
 51      /// Gets the base transition delay between two cover states.
 52      /// </summary>
 53      /// <param name="fromCover">Starting cover state</param>
 54      /// <param name="toCover">Target cover state</param>
 55      /// <returns>Base transition delay in milliseconds, or 0 if no change</returns>
 56      public static int GetTransitionDelayMs(CoverState fromCover, CoverState toCover)
 57      {
 58          if (fromCover == toCover)
 59              return 0;
 60  
 61          return (fromCover, toCover) switch
 62          {
 63              // Direct adjacent transitions
 64              (CoverState.None, CoverState.Partial) => NoneToPartialDelayMs,
 65              (CoverState.Partial, CoverState.Full) => PartialToFullDelayMs,
 66              (CoverState.Full, CoverState.Partial) => FullToPartialDelayMs,
 67              (CoverState.Partial, CoverState.None) => PartialToNoneDelayMs,
 68  
 69              // Multi-step transitions (going through intermediate state)
 70              (CoverState.None, CoverState.Full) => NoneToFullDelayMs,
 71              (CoverState.Full, CoverState.None) => FullToNoneDelayMs,
 72  
 73              _ => 0
 74          };
 75      }
 76  
 77      /// <summary>
 78      /// Gets the effective transition delay between two cover states, scaled by response proficiency.
 79      /// Higher response proficiency results in faster transitions.
 80      /// </summary>
 81      /// <param name="fromCover">Starting cover state</param>
 82      /// <param name="toCover">Target cover state</param>
 83      /// <param name="responseProficiency">Operator's response proficiency (0.0-1.0)</param>
 84      /// <returns>Effective transition delay in milliseconds</returns>
 85      public static int GetEffectiveTransitionDelayMs(CoverState fromCover, CoverState toCover, float responseProficiency)
 86      {
 87          int baseDelay = GetTransitionDelayMs(fromCover, toCover);
 88          if (baseDelay == 0)
 89              return 0;
 90  
 91          float effectiveDelay = ResponseProficiencyModel.CalculateEffectiveDelay(baseDelay, responseProficiency);
 92          return (int)Math.Round(effectiveDelay);
 93      }
 94  
 95      /// <summary>
 96      /// Gets the effective transition delay with multiplier info for logging/timeline display.
 97      /// </summary>
 98      /// <param name="fromCover">Starting cover state</param>
 99      /// <param name="toCover">Target cover state</param>
100      /// <param name="responseProficiency">Operator's response proficiency (0.0-1.0)</param>
101      /// <returns>Tuple of (effectiveDelayMs, baseDelayMs, multiplier)</returns>
102      public static (int effectiveDelayMs, int baseDelayMs, float multiplier) GetEffectiveTransitionDelayWithInfo(
103          CoverState fromCover, 
104          CoverState toCover, 
105          float responseProficiency)
106      {
107          int baseDelay = GetTransitionDelayMs(fromCover, toCover);
108          if (baseDelay == 0)
109              return (0, 0, 1.0f);
110  
111          var (effectiveDelay, multiplier) = ResponseProficiencyModel.CalculateEffectiveDelayWithMultiplier(
112              baseDelay, responseProficiency);
113          return ((int)Math.Round(effectiveDelay), baseDelay, multiplier);
114      }
115  }