/ POST_VICTORY_EXFIL_FIX.md
POST_VICTORY_EXFIL_FIX.md
1 # Post-Victory Exfil Fix & Combat-Free Exfil 2 3 ## Problem 4 5 After winning a combat during an active infil, players were unable to exfil. The issue occurred because: 6 7 1. **Victory clears the active combat session** - This is intentional design to allow consecutive battles during a single infil 8 2. **Exfil required an active combat session** - The `ProcessExfil()` method checked for `ActiveSessionId` and failed if none existed 9 3. **Players got stuck in Infil mode** - They couldn't exfil despite having successfully completed combat 10 11 Additionally, the original design incorrectly required players to complete combat before exfiling, but the intended design is to allow exfil at any time during an infil. 12 13 ### User-Reported Symptoms 14 15 - "After I won I got sent back to base, rather than infil" (confusing - may be a separate issue) 16 - "I tried exfilling after infilling again, and I was still in infil mode" 17 - Error message: "Cannot exfil without completing a combat. You must engage and complete a combat encounter to exfil." 18 19 ## Root Cause 20 21 The exfil flow was designed around a single-combat-per-infil model. When the multi-combat feature was added (allowing consecutive battles), the victory flow properly: 22 - Emits `CombatVictoryEvent` (clears `ActiveCombatSessionId`) 23 - Clears `ActiveCombatSessionId` (allows starting new combat) 24 - Keeps operator in `Infil` mode (allows consecutive battles) 25 26 However, the exfil logic still required an active combat session, creating a catch-22: players needed to complete combat to exfil, but completing combat cleared the session needed for exfil. 27 28 ## Solution 29 30 ### New Endpoint: `POST /operators/{id}/infil/complete` 31 32 Added a new flow for completing infil successfully at any time, with or without combat. 33 34 #### Server-Side Changes 35 36 **OperatorExfilService.CompleteInfilSuccessfullyAsync** 37 - Validates operator is in `Infil` mode 38 - **No combat requirement** - Players can exfil immediately after starting infil 39 - Emits `InfilEndedEvent` with `wasSuccessful: true` 40 - Transitions operator to `Base` mode 41 - Preserves loot and streak (if any combat was completed) 42 43 **OperatorService.CompleteInfilAsync** 44 - Thin wrapper for API layer 45 - Delegates to `OperatorExfilService.CompleteInfilSuccessfullyAsync` 46 47 **API Controller** 48 - `POST /operators/{id}/infil/complete` 49 - Returns 200 OK on success 50 - Returns 400 BadRequest if validation fails (not in Infil mode) 51 52 #### Client-Side Changes 53 54 **ProcessExfil() in ConsoleClient/Program.cs** 55 56 Modified to handle two distinct cases: 57 58 1. **No active session + Infil mode** (NEW - post-victory or no-combat path) 59 - Calls `POST /operators/{id}/infil/complete` 60 - Shows "Exfil successful!" message 61 - Returns to base 62 63 2. **Active session exists** (LEGACY - in-progress combat path) 64 - Validates session is completed 65 - Calls `POST /infil/outcome` with sessionId 66 - Processes combat outcome 67 68 This maintains backward compatibility while enabling the new exfil flow. 69 70 ### Validation Logic 71 72 The fix includes minimal validation: 73 74 ```csharp 75 // Must be in Infil mode to complete infil 76 if (aggregate.CurrentMode != OperatorMode.Infil) 77 return ServiceResult.InvalidState("Cannot complete infil when not in Infil mode"); 78 ``` 79 80 No combat requirement - players can exfil at any time during an active infil. 81 82 ## Testing 83 84 ### New Test: `OperatorCanExfilImmediatelyWithoutCombat` 85 86 Validates that players can exfil immediately after starting infil: 87 1. Create operator and start infil 88 2. Verify operator is in Infil mode with ExfilStreak = 0 (no combat) 89 3. Call `CompleteInfilSuccessfullyAsync` 90 4. Verify operator is in Base mode with ExfilStreak still 0 91 92 ### Existing Test: `AfterVictory_OperatorCanCompleteInfilSuccessfully` 93 94 Validates the complete post-victory exfil flow: 95 1. Create operator and start infil 96 2. Win combat (emits `CombatVictoryEvent`, clears `ActiveCombatSessionId`) 97 3. Verify operator is in Infil mode with no active session 98 4. Call `CompleteInfilSuccessfullyAsync` 99 5. Verify operator is in Base mode with preserved `ExfilStreak` and XP 100 101 ### Test Results 102 103 All relevant test suites pass: 104 - ✅ 4 InfilVictoryFlowTests (including the new test) 105 - ✅ 39 OperatorExfilServiceTests 106 - ✅ 19 OperatorAggregateTests 107 - ✅ 112 Mode-related tests 108 109 ## Flow Diagrams 110 111 ### Victory Flow (Corrected) 112 113 ``` 114 Player wins combat 115 ↓ 116 ProcessCombatOutcome() → POST /infil/outcome 117 ↓ 118 Server emits CombatVictoryEvent 119 ↓ 120 ActiveCombatSessionId = null (but stays in Infil mode) 121 ↓ 122 MissionComplete screen: "MISSION SUCCESS" 123 ↓ 124 Player clicks OK → BaseCamp 125 ↓ 126 BaseCamp shows: [ENGAGE COMBAT] [EXFIL] [VIEW STATS] 127 ``` 128 129 ### Exfil Flow (NEW - No Combat Required) 130 131 ``` 132 Player starts infil 133 ↓ 134 BaseCamp shows: [ENGAGE COMBAT] [EXFIL] [VIEW STATS] 135 ↓ 136 Player clicks EXFIL (without engaging in combat) 137 ↓ 138 ProcessExfil() checks for active session 139 ↓ 140 No session found, CurrentMode = "Infil" 141 ↓ 142 POST /operators/{id}/infil/complete 143 ↓ 144 Server validates CurrentMode = Infil 145 ↓ 146 Emit InfilEndedEvent (wasSuccessful: true) 147 ↓ 148 Operator → Base mode 149 ↓ 150 Client shows "Exfil successful!" → BaseCamp 151 ↓ 152 BaseCamp shows: [INFIL] [CHANGE LOADOUT] [TREAT WOUNDS] etc. 153 ``` 154 155 ## Design Notes 156 157 ### Why Allow Exfil Without Combat? 158 159 The design allows players full agency over their infil experience: 160 - **Risk assessment** - Players can scout and decide to exfil if conditions are unfavorable 161 - **Resource management** - Exit before taking damage or using consumables 162 - **Time management** - Quick in/out without forced engagement 163 - **Learning/Training** - New players can practice infil mechanics without combat pressure 164 165 This creates a more flexible and player-friendly system. 166 167 ### Why Not Modify ProcessCombatOutcomeAsync? 168 169 We could have modified the victory path to not clear `ActiveCombatSessionId`, but this would: 170 - Break the multi-combat feature (can't start new combat with active session) 171 - Complicate session management (sessions would persist after completion) 172 - Require changes to auto-resume logic 173 174 The separate endpoint approach is cleaner and maintains clear separation of concerns. 175 176 ### Backward Compatibility 177 178 The legacy path (with active session) is preserved for edge cases: 179 - Old game states loaded from database 180 - Potential race conditions during combat completion 181 - Future features that might use different session management 182 183 ## Future Considerations 184 185 ### Potential Enhancements 186 187 1. **Multiple combats before exfil** - Already supported! Players can win multiple combats and only exfil when ready 188 2. **Exfil bonus for longer streaks** - Could be added by checking `ExfilStreak` in `CompleteInfilSuccessfullyAsync` 189 3. **Time-based exfil restrictions** - Could validate minimum time in infil before allowing exfil 190 4. **Exfil cost/penalty for no combat** - Could apply a resource cost for exfiling without engaging 191 192 ### Migration Path 193 194 No migration needed - this is a pure addition. Existing game states will work correctly: 195 - Operators in Base mode: No change 196 - Operators in Infil with active session: Uses legacy path 197 - Operators in Infil after victory: Uses new path 198 - Operators in Infil without combat: Uses new path 199 200 ## Related Issues 201 202 This fix addresses the core exfil problem. If users still report being "sent back to base" after victory, that would be a separate issue to investigate (possibly related to auto-cleanup or session management).