Skip to content

bugfix(challenge): Fix Generals Challenge defeat during next mission intro cinematic#225

Merged
fbraz3 merged 1 commit into
fbraz3:mainfrom
coolswood:fix/challenge-continue-defeat-during-cinematic
Jul 20, 2026
Merged

bugfix(challenge): Fix Generals Challenge defeat during next mission intro cinematic#225
fbraz3 merged 1 commit into
fbraz3:mainfrom
coolswood:fix/challenge-continue-defeat-during-cinematic

Conversation

@coolswood

Copy link
Copy Markdown

Change

Fixes a defeat screen appearing during the intro cinematic when continuing from one Generals Challenge mission to the next via the score screen Continue button.

Root cause

startNextCampaignGame() performed popImmediate()/hideShell() before queuing MSG_NEW_GAME(GAME_SINGLE_PLAYER). Hiding the shell triggers the next menu's init (e.g. SaveLoad), which calls Shell::showShellMap(TRUE). That appends its own MSG_NEW_GAME(GAME_SHELL) for the shell map and overwrites m_pendingFile with the shell map name.

That GAME_SHELL message then races ahead of the GAME_SINGLE_PLAYER message and is processed first by onNewGame():

  1. m_gameMode becomes GAME_SHELL
  2. the isChallengeCampaign check in tryStartNewGame() evaluates to false (it requires m_gameMode == GAME_SINGLE_PLAYER)
  3. TheGameInfo stays null
  4. placeNetworkBuildingsForPlayer() is skipped
  5. the local player never receives a starting base
  6. the map's PLAYER_HAS_N_OR_FEWER_BUILDINGS script fires doDefeat() a few frames after the map loads, visible as the defeat screen during the intro cinematic

The actual GAME_SINGLE_PLAYER message is subsequently rejected by the onNewGame() guard (isInGame || isLoadingMap), so the wrong map/mode combination loads.

The bug only reproduces on the Continue path. Loading the same save directly via Load Game works because that path uses MSG_NEW_GAME with GAME_SINGLE_PLAYER and does not trigger the shell map race.

Fix

Reorder startNextCampaignGame() so that:

  • m_pendingFile is set to the current campaign map before any shell teardown
  • MSG_NEW_GAME(GAME_SINGLE_PLAYER) is queued before popImmediate()/hideShell(), guaranteeing it sits ahead of any MSG_NEW_GAME(GAME_SHELL) appended by Shell::showShellMap(TRUE)
  • m_pendingFile is re-asserted after the shell teardown, since Shell::showShellMap(TRUE) may have overwritten it with the shell map name (and prepareNewGame() copies m_pendingFile into m_mapName, so the value present at queue-drain time is what actually loads)

The shell's duplicate MSG_NEW_GAME(GAME_SHELL) now lands behind ours and is rejected by the existing onNewGame() guard.

Verification

Tested on macOS (Apple Silicon), preset macos-vulkan:

  • Before the fix: Load gc_americaboss -> win -> Continue -> next mission (GC_TankGeneral) loads and shows the defeat screen during the intro cinematic; the map has 0 objects owned by the local player.
  • After the fix: same scenario -> next mission starts normally, the local player receives a starting base, no defeat during the cinematic.

Instrumented trace (added temporarily during investigation, not part of this PR) confirmed the message ordering: without the fix the first onNewGame() call received gameMode == GAME_SHELL; with the fix it receives gameMode == GAME_SINGLE_PLAYER and the shell's follow-up message is rejected.

Scope

Single file, single function (ScoreScreen.cpp::startNextCampaignGame()). No gameplay balance, AI or rendering changes. Applies to Zero Hour only; the Generals replica can follow in a follow-up PR per the contribution guidelines if desired.

AI generated code

The fix was authored with LLM assistance and polished/tested by the author (coolswood). The change is 21 inserted lines (one reorder plus explanatory comments) in a single function.

@fbraz3
fbraz3 merged commit 5592605 into fbraz3:main Jul 20, 2026
32 of 40 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.

2 participants