Skip to content

Fix PvP and PVE2 turn flow and saved skirmish resume - #181

Merged
xcomcoopdev merged 3 commits into
mainfrom
fix/skirmish-save-load
Sep 4, 2026
Merged

xcomcoopdev merged 3 commits into
mainfrom
fix/skirmish-save-load

Conversation

@xcomcoopdev

Copy link
Copy Markdown
Collaborator

Summary

This PR fixes multiplayer tactical turn-flow and resume regressions affecting
PvP, PvP2, and parallel PVE2 skirmishes.

The changes:

  • Correct the player-controlled PvP/PvP2 round sequence.
  • Preserve reaction-fire TU until the actual full-round boundary.
  • Validate the real saved skirmish resume flow.
  • Prevent premature client actions behind the host's PVE2 turn screen.
  • Prevent a saved PVE2 battle from replaying its new-battle AI hand-off.
  • Add deterministic harness coverage for all affected flows.

PvP and PvP2 turn flow

PvP battles have two player-controlled sides. The normal HOSTILE AI phase must
not run between the XCOM and alien-player turns, because that would allow the AI
to control units assigned to the alien player.

After the alien player completes their half of the round, the battle now uses
the intended transition:

NEUTRAL -> PLAYER

The XCOM-to-alien-player hand-off remains controlled by the existing network
turn message. HOSTILE is skipped only at the completed player-controlled round
boundary.

The PvP neutral Next Turn screen is now allowed to open even though the original
BattlescapeGame logic normally suppresses it for FACTION_NEUTRAL.

Host clicks on the relevant PvP turn screens are mirrored to the client. This
prevents the client from remaining behind an End Turn screen after the host
advances the round.

PvP reaction-fire and round-boundary TU handling

The alien player's TU and energy are no longer reset during the
XCOM-to-alien-player hand-off.

TU spent on reaction fire during the XCOM half-round must remain spent when the
alien player's active half-round begins.

The old BattleUnit PvP exception that disabled alien TU and energy recovery on
every updateUnitStats call has also been removed. After eliminating the
premature hand-off reset, that exception would prevent the legitimate
full-round refresh.

Both active player-controlled sides now receive their TU and energy refresh at
the actual NEUTRAL -> PLAYER round boundary.

This ensures that:

  • Reaction-fire TU remains spent during the following alien half-round.
  • Both sides receive their new-round TU after the alien player ends the round.
  • The gamemode 2 host retains the correct current alien TU values.
  • A later hand-off cannot overwrite refreshed TU with stale host values.
  • Reaction fire during the new XCOM half-round is still deducted normally.

Saved skirmish resume

The saved-skirmish harness now loads generated skirmish saves through the real
LoadGameState menu path.

The raw TestServer load_save helper always installs a GeoscapeState after
parsing and is not a valid loading path for an _autobattle_.asav skirmish
save. Using it could terminate the host process and close the harness control
connection.

The verified production resume flow is now:

  1. Loading restores the host's existing battlescape.
  2. Opening COOP from the restored pause menu opens HostMenu.
  3. The host and client wait together in LobbyMenu.
  4. Joining alone does not download or enter the saved battle.
  5. CONTINUE BATTLE starts the map transfer.
  6. The client enters the restored battle without an incorrect reconnect hold.
  7. The multiplayer mode and unit ownership survive the transfer.
  8. The resumed battle continues through a complete round.

The saved PvP2 harness also covers its two-screen round transition: the host
closes NEUTRAL and PLAYER while the client advances back to the player side
and regains XCOM control.

PVE2 turn-screen action gate

In PVE2, both players control the alien team. A client can close its local
PLAYER Next Turn screen before the host closes the authoritative copy.

Without an additional gate, the client could see the tactical map and send an
action intent while the host was still behind NextTurnState.

PVE2 action admission is now rejected with not_top_state while
BattlescapeState is not the host's top state. Once the host dismisses the
boundary screen, the same action can be admitted normally.

The modal-state check is limited to gamemode 4. PvE gamemode 1, PvP, PvP2,
classic co-op, and single-player behavior remain unchanged.

Saved PVE2 initialization

Session teardown clears the transient pve2_init flag.

When a saved PVE2 skirmish was loaded, the cleared flag caused the restored
battle to be treated as a newly created PVE2 battle. As soon as
BattlescapeState began processing, the host replayed PVE2's one-time initial
AI hand-off and called endTurnCoop() before either player pressed END TURN.

A battle saved on the PLAYER side was therefore advanced immediately to
HOSTILE. The host and client also entered different initialization states,
eventually leaving both players stuck at an End Turn screen.

The skirmish resume path now marks PVE2's one-time initial hand-off as already
consumed on both machines. This is done while the resume UI still covers
BattlescapeState, preventing the restored battle from advancing before the
state is ready.

The saved battle now:

  • Resumes on its original shared PLAYER side.
  • Initializes the host and client without an automatic AI hand-off.
  • Retains the normal two-seat END TURN readiness gate.
  • Enters the opponent AI turn only after both players are ready.
  • Completes the AI cycle.
  • Returns both players to the following shared PLAYER side.

Harness coverage

PvP and PvP2 round transitions

test_pvp_skirmish_end_turn.py covers both gamemode 2 and gamemode 3.

It verifies that:

  • The XCOM-to-alien hand-off preserves spent reaction-fire TU.
  • The completed player-controlled round produces NEUTRAL and then PLAYER.
  • The battle never enters the unwanted HOSTILE AI phase.
  • The battle does not incorrectly end after one complete round.
  • Both player-controlled sides receive their full-round TU refresh.
  • Subsequent hand-offs preserve the correct refreshed values.

Saved skirmish join

test_skirmish_saved_battle_join.py covers the real
LoadGameState-to-CONTINUE BATTLE resume flow and verifies that a saved PvP2
battle retains its mode, ownership, and turn behavior.

PVE2 boundary input gate

test_parallel_pve2_turn_screen_gate.py closes only the client's PLAYER screen
and attempts a real client move while the host screen remains open.

It verifies that:

  • The host rejects the early action with not_top_state.
  • Neither process moves the unit.
  • The same action is admitted after the host closes its screen.

Saved PVE2 end-turn regression

test_skirmish_saved_pve2_end_turn.py:

  1. Creates a real PVE2 skirmish.
  2. Saves it during the shared PLAYER side.
  3. Replaces both processes.
  4. Loads it through the production skirmish resume flow.
  5. Verifies that loading does not start the opponent AI automatically.
  6. Presses the real END TURN button on both seats.
  7. Drives host-owned boundary screens through their normal UI path.
  8. Verifies that the AI cycle completes and both players regain the shared
    PLAYER side.

The fixture uses a Battleship deployment and fixed RNG seeds so the battle
cannot randomly end during setup like the previous one-unit Small Scout
fixture.

The following harness suites pass:

ALL SAVED PVE2 END-TURN TESTS PASSED
ALL PARALLEL PVE2 TURN-SCREEN GATE TESTS PASSED
ALL SAVED CUSTOM-BATTLE JOIN TESTS PASSED

PvP and PvP2 turn flow

PvP battles are controlled by two players, so the vanilla HOSTILE phase
must not run between their turns. Entering that phase allows the normal alien
AI to take control of units that belong to the human alien player.

Change the completed PvP/PvP2 round transition to skip HOSTILE and use the
intended sequence:

    NEUTRAL -> PLAYER

The XCOM-to-alien-player handoff is still handled by the existing network turn
message. The HOSTILE phase is skipped only when the alien player has completed
the human half of the round and the battle advances to the neutral phase.

Allow the PvP neutral Next Turn screen to open even though the original
BattlescapeGame condition normally suppresses the screen for FACTION_NEUTRAL.
Mirror the host's PvP turn-screen clicks to the client so that the client does
not remain stuck behind an End Turn screen after the host advances the round.

PvP reaction-fire and round-boundary TU handling

Do not reset the alien player's TU and energy during the XCOM-to-alien handoff.
TU spent by alien reaction fire during the XCOM half-round must remain spent
when the human alien player's active half-round begins.

Remove the old BattleUnit PvP exception that disabled alien TU and energy
recovery on every updateUnitStats call. That exception was previously hidden by
the premature handoff reset, but after removing that reset, it also prevented
the legitimate full-round refresh.

Refresh TU and energy for both living human-controlled combat seats at the real
NEUTRAL-to-PLAYER round boundary. This keeps the host and client copies equal:

- reaction-fire TU remains spent during the following alien half-round;
- all combat units receive their new-round TU after the alien player ends;
- the gm2 host holds the correct current alien TU values;
- the next XCOM-to-alien handoff cannot overwrite the client's full TU with an
  old value from the host;
- any reaction fire during the new XCOM half-round is still deducted before the
  next alien handoff.

Saved Custom Battle / skirmish resume regression

Fix the saved-skirmish harness to load the generated custom-battle save through
the real LoadGameState menu path. The raw TestServer load_save helper always
installs a GeoscapeState after parsing and is not a valid loading path for an
_autobattle_.asav Custom Battle save; using it can terminate the host process
and close the harness control connection.

After the real load completes, open COOP through the restored battlescape pause
menu. Verify the campaign-style resume flow:

- loading restores the host's existing battlescape;
- COOP opens HostMenu rather than ServerList;
- the host and client wait together in LobbyMenu;
- joining does not download or enter the saved map early;
- CONTINUE BATTLE is the sole map-transfer trigger;
- the client enters the restored battle without Waiting for host to resume;
- PvP2 gamemode and XCOM/alien ownership survive the transfer;
- the battle continues after a complete XCOM and alien round.

Update the saved PvP2 round portion of the test for the two-screen flow. The
host closes NEUTRAL first and PLAYER second, while the client advances to the
player side and regains XCOM control.

PvP/PvP2 harness coverage

Expand test_pvp_skirmish_end_turn.py to cover both gamemode 2 and gamemode 3.
The test deterministically models reaction-fire expenditure by setting one
alien's TU below its maximum on both process copies, without depending on a
random reaction shot.

For both PvP variants, verify that:

- the XCOM-to-alien handoff preserves the spent TU value;
- completing the alien half-round produces NEUTRAL then PLAYER, never HOSTILE;
- the battle does not incorrectly enter Debriefing after one complete round;
- the alien TU returns to tuMax at the full-round boundary on both machines;
- the following handoff keeps the full value when no new reaction TU was spent;
- the gm2 client is not overwritten by a stale alien TU value from the host.

PVE2 parallel-turn boundary input gate

In PVE2, both players control aliens. A client can close its own PLAYER Next
Turn screen before the host closes the host copy. The client then sees the map
and can send an action intent even though the authoritative host is still behind
NextTurnState.

Reject PVE2 action admission while BattlescapeState is not the host's top state.
This prevents client movement and other actions until the host has dismissed
the turn-boundary screen. Once BattlescapeState is active again, the same action
intent is admitted normally.

Keep this new modal-state check strictly limited to gamemode 4. PvE gamemode 1,
PvP, PvP2, classic co-op, and single-player admission behavior are unchanged.

Add test_parallel_pve2_turn_screen_gate.py. The test creates a parallel PVE2
skirmish, closes only the client's PLAYER screen, and sends a real client alien
move intent while the host screen remains open. It verifies that the host
rejects the intent with not_top_state and that neither process moves the unit.
After the host closes its screen, the test sends the same intent again and
requires the host to execute it.
A resumed PVE2 custom battle was treated like a newly created battle because
the session reset cleared pve2_init. When the restored BattlescapeState started
thinking, the host replayed PVE2's one-time initial AI hand-off and advanced
the saved PLAYER side to HOSTILE before either player pressed END TURN.

Mark the initial PVE2 hand-off as already consumed on both the host and client
custom-battle resume paths. Do this while the resume UI still covers the
battlescape so the restored state cannot advance prematurely.

Add a deterministic saved-PVE2 harness regression using a Battleship fixture
and a fixed RNG seed. The test verifies that:

- the saved battle resumes on its original shared PLAYER side;
- both processes restore gamemode 4 with parallel turns active;
- loading does not start the opponent AI automatically;
- both players can ready the shared side normally;
- host turn-screen dismissal releases the client where applicable;
- the AI cycle completes and returns both players to the next PLAYER side.
@xcomcoopdev

Copy link
Copy Markdown
Collaborator Author

Hi, I'll merge this, and if any issues come up later, we can revisit these fixes.

@xcomcoopdev
xcomcoopdev merged commit 3f69989 into main Sep 4, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant