Skip to content

Commit dffdab9

Browse files
authored
feat: Implement game exit with ALT+F4 and game window close button (TheSuperHackers#2336)
On Windows the user can press ALT+F4 or click the game window close button (X) to exit the game
1 parent a808475 commit dffdab9

24 files changed

Lines changed: 431 additions & 145 deletions

File tree

Core/GameEngine/Source/GameClient/GUI/LoadScreen.cpp

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "Common/GameEngine.h"
6161
#include "Common/GameLOD.h"
6262
#include "Common/GameState.h"
63+
#include "Common/MessageStream.h"
6364
#include "Common/MultiplayerSettings.h"
6465
#include "Common/Player.h"
6566
#include "Common/PlayerList.h"
@@ -68,6 +69,7 @@
6869
#include "GameClient/Display.h"
6970
#include "GameClient/GadgetProgressBar.h"
7071
#include "GameClient/GadgetStaticText.h"
72+
#include "GameClient/GameClient.h"
7173
#include "GameClient/GameText.h"
7274
#include "GameClient/GameWindowManager.h"
7375
#include "GameClient/GameWindowTransitions.h"
@@ -157,7 +159,7 @@ LoadScreen::~LoadScreen()
157159
void LoadScreen::update( Int percent )
158160
{
159161
TheGameEngine->serviceWindowsOS();
160-
if (TheGameEngine->getQuitting())
162+
if (TheGameEngine->getQuitting() || (TheGameLogic && TheGameLogic->isQuitToDesktopRequested()))
161163
return; //don't bother with any of this if the player is exiting game.
162164

163165
TheWindowManager->update();
@@ -539,20 +541,11 @@ void SinglePlayerLoadScreen::init( GameInfo *game )
539541
Int shiftedPercent = -FRAME_FUDGE_ADD + 1;
540542
while (m_videoStream->frameIndex() < m_videoStream->frameCount() - 1 )
541543
{
542-
// TheSuperHackers @feature User can now skip video by pressing ESC
543-
if (TheKeyboard)
544+
if (GameClient::isMovieAbortRequested())
544545
{
545-
TheKeyboard->UPDATE();
546-
KeyboardIO *io = TheKeyboard->findKey(KEY_ESC, KeyboardIO::STATUS_UNUSED);
547-
if (io && BitIsSet(io->state, KEY_STATE_DOWN))
548-
{
549-
io->setUsed();
550-
break;
551-
}
546+
break;
552547
}
553548

554-
TheGameEngine->serviceWindowsOS();
555-
556549
if(!m_videoStream->isFrameReady())
557550
{
558551
Sleep(1);
@@ -634,6 +627,11 @@ void SinglePlayerLoadScreen::init( GameInfo *game )
634627
fudgeFactor = 30 * ((currTime - begin)/ INT_TO_REAL(delay ));
635628
GadgetProgressBarSetProgress(m_progressBar, fudgeFactor);
636629

630+
if (GameClient::isMovieAbortRequested())
631+
{
632+
break;
633+
}
634+
637635
TheWindowManager->update();
638636
TheDisplay->draw();
639637
Sleep(100);
@@ -1054,20 +1052,11 @@ void ChallengeLoadScreen::init( GameInfo *game )
10541052
Int shiftedPercent = -FRAME_FUDGE_ADD + 1;
10551053
while (m_videoStream->frameIndex() < m_videoStream->frameCount() - 1 )
10561054
{
1057-
// TheSuperHackers @feature User can now skip video by pressing ESC
1058-
if (TheKeyboard)
1055+
if (GameClient::isMovieAbortRequested())
10591056
{
1060-
TheKeyboard->UPDATE();
1061-
KeyboardIO *io = TheKeyboard->findKey(KEY_ESC, KeyboardIO::STATUS_UNUSED);
1062-
if (io && BitIsSet(io->state, KEY_STATE_DOWN))
1063-
{
1064-
io->setUsed();
1065-
break;
1066-
}
1057+
break;
10671058
}
10681059

1069-
TheGameEngine->serviceWindowsOS();
1070-
10711060
if(!m_videoStream->isFrameReady())
10721061
{
10731062
Sleep(1);
@@ -1109,7 +1098,13 @@ void ChallengeLoadScreen::init( GameInfo *game )
11091098
// if we're min speced
11101099
m_videoStream->frameGoto(m_videoStream->frameCount()); // zero based
11111100
while(!m_videoStream->isFrameReady())
1101+
{
1102+
if (GameClient::isMovieAbortRequested())
1103+
{
1104+
return;
1105+
}
11121106
Sleep(1);
1107+
}
11131108
m_videoStream->frameDecompress();
11141109
m_videoStream->frameRender(m_videoBuffer);
11151110
if(m_videoBuffer)
@@ -1126,6 +1121,11 @@ void ChallengeLoadScreen::init( GameInfo *game )
11261121
fudgeFactor = 30 * ((currTime - begin)/ INT_TO_REAL(delay ));
11271122
GadgetProgressBarSetProgress(m_progressBar, fudgeFactor);
11281123

1124+
if (GameClient::isMovieAbortRequested())
1125+
{
1126+
break;
1127+
}
1128+
11291129
TheWindowManager->update();
11301130
TheDisplay->draw();
11311131
Sleep(100);

Generals/Code/GameEngine/Include/Common/MessageStream.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ class GameMessage : public MemoryPoolObject
277277
MSG_META_TOGGLE_PAUSE_ALT, ///< TheSuperHackers @feature Toggle game pause (alternative mapping)
278278
MSG_META_STEP_FRAME, ///< TheSuperHackers @feature Step one frame
279279
MSG_META_STEP_FRAME_ALT, ///< TheSuperHackers @feature Step one frame (alternative mapping)
280+
MSG_META_DEMO_INSTANT_QUIT, ///< bail out of game immediately
280281

281282

282283
// META items that are really for debug/demo/development use only...
@@ -289,7 +290,6 @@ class GameMessage : public MemoryPoolObject
289290
MSG_META_DEMO_LOD_INCREASE, ///< increase LOD by 1
290291
MSG_META_DEMO_TOGGLE_ZOOM_LOCK, ///< Toggle the camera zoom lock on/off
291292
MSG_META_DEMO_PLAY_CAMEO_MOVIE, ///< Play a movie in the cameo spot
292-
MSG_META_DEMO_INSTANT_QUIT, ///< bail out of game immediately
293293
MSG_META_DEMO_TOGGLE_SPECIAL_POWER_DELAYS, ///< Toggle special power delays on/off
294294
MSG_META_DEMO_BATTLE_CRY, ///< battle cry
295295
MSG_META_DEMO_SWITCH_TEAMS, ///< switch local control to another team
@@ -725,6 +725,7 @@ class MessageStream : public GameMessageList
725725

726726
virtual GameMessage *appendMessage( GameMessage::Type type ); ///< Append a message to the end of the stream
727727
virtual GameMessage *insertMessage( GameMessage::Type type, GameMessage *messageToInsertAfter ); // Insert message after messageToInsertAfter.
728+
virtual Bool isReadyForMessages() const; ///< Return true if a local player is present and ready to accept messages
728729

729730
// Methods NOT Inherited ------------------------------------------------------------------------
730731
void propagateMessages(); ///< Propagate messages through attached translators

Generals/Code/GameEngine/Include/GameClient/GUICallbacks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ extern WindowMsgHandledType EstablishConnectionsControlInput( GameWindow *window
365365

366366
// The in game quit menu --------------------------------------------------------------------------
367367
extern void destroyQuitMenu();
368+
extern Bool canOpenQuitMenu();
368369
extern void ToggleQuitMenu();
369370
extern void HideQuitMenu();
370371
extern WindowMsgHandledType QuitMenuSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );

Generals/Code/GameEngine/Include/GameClient/GameClient.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ class GameClient : public SubsystemInterface,
150150
UnsignedInt getRenderedObjectCount() const { return m_renderedObjectCount; }
151151
void incrementRenderedObjectCount() { m_renderedObjectCount++; }
152152

153+
static Bool isMovieAbortRequested();
154+
153155
protected:
154156

155157
// snapshot methods

Generals/Code/GameEngine/Include/GameLogic/GameLogic.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ class GameLogic : public SubsystemInterface, public Snapshot
208208
UnsignedInt getFrameObjectsChangedTriggerAreas() {return m_frameObjectsChangedTriggerAreas;}
209209

210210
void exitGame();
211+
void quit(Bool toDesktop);
211212
void clearGameData(Bool showScoreScreen = TRUE); ///< Clear the game data
212213
void closeWindows();
213214

@@ -255,6 +256,8 @@ class GameLogic : public SubsystemInterface, public Snapshot
255256
// this should be called only by UpdateModule, thanks.
256257
void friend_awakenUpdateModule(Object* obj, UpdateModulePtr update, UnsignedInt whenToWakeUp);
257258

259+
Bool isQuitToDesktopRequested() const { return m_quitToDesktopAfterMatch; }
260+
258261
protected:
259262

260263
// snapshot methods
@@ -264,6 +267,8 @@ class GameLogic : public SubsystemInterface, public Snapshot
264267

265268
private:
266269

270+
void tryStartNewGame( Bool loadSaveGame );
271+
267272
void updateDisplayBusyState();
268273

269274
void pauseGameLogic(Bool paused);
@@ -308,6 +313,7 @@ class GameLogic : public SubsystemInterface, public Snapshot
308313
Bool m_loadingMap;
309314
Bool m_loadingSave;
310315
Bool m_clearingGameData;
316+
Bool m_quitToDesktopAfterMatch;
311317

312318
Bool m_isInUpdate;
313319
Bool m_hasUpdated;

Generals/Code/GameEngine/Source/Common/MessageStream.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ const char *GameMessage::getCommandTypeAsString(GameMessage::Type t)
382382
CASE_LABEL(MSG_META_TOGGLE_PAUSE_ALT)
383383
CASE_LABEL(MSG_META_STEP_FRAME)
384384
CASE_LABEL(MSG_META_STEP_FRAME_ALT)
385+
CASE_LABEL(MSG_META_DEMO_INSTANT_QUIT)
385386

386387
#if defined(RTS_DEBUG)
387388
CASE_LABEL(MSG_META_DEMO_TOGGLE_BEHIND_BUILDINGS)
@@ -391,7 +392,6 @@ const char *GameMessage::getCommandTypeAsString(GameMessage::Type t)
391392
CASE_LABEL(MSG_META_DEMO_LOD_INCREASE)
392393
CASE_LABEL(MSG_META_DEMO_TOGGLE_ZOOM_LOCK)
393394
CASE_LABEL(MSG_META_DEMO_PLAY_CAMEO_MOVIE)
394-
CASE_LABEL(MSG_META_DEMO_INSTANT_QUIT)
395395
CASE_LABEL(MSG_META_DEMO_TOGGLE_SPECIAL_POWER_DELAYS)
396396
CASE_LABEL(MSG_META_DEMO_BATTLE_CRY)
397397
CASE_LABEL(MSG_META_DEMO_SWITCH_TEAMS)
@@ -845,6 +845,11 @@ void MessageStream::update()
845845

846846
}
847847

848+
Bool MessageStream::isReadyForMessages() const
849+
{
850+
return (ThePlayerList != nullptr);
851+
}
852+
848853
/**
849854
* Create a new message of the given message type and append it
850855
* to this message stream. Return the message such that any data

Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -137,23 +137,9 @@ void destroyQuitMenu()
137137
*/
138138
static void exitQuitMenu()
139139
{
140+
TheGameLogic->quit(FALSE);
140141
// destroy the quit menu
141142
destroyQuitMenu();
142-
143-
// clear out all the game data
144-
if ( TheGameLogic->isInMultiplayerGame() && !TheGameLogic->isInSkirmishGame() && !TheGameInfo->isSandbox() )
145-
{
146-
GameMessage *msg = TheMessageStream->appendMessage(GameMessage::MSG_SELF_DESTRUCT);
147-
msg->appendBooleanArgument(TRUE);
148-
}
149-
TheGameLogic->exitGame();
150-
// TheGameLogic->clearGameData();
151-
// display the menu on top of the shell stack
152-
// TheShell->showShell();
153-
154-
// this will trigger an exit
155-
// TheGameEngine->setQuitting( TRUE );
156-
TheInGameUI->setClientQuiet( TRUE );
157143
}
158144
static void noExitQuitMenu()
159145
{
@@ -162,20 +148,9 @@ static void noExitQuitMenu()
162148

163149
static void quitToDesktopQuitMenu()
164150
{
151+
TheGameLogic->quit(TRUE);
165152
// destroy the quit menu
166153
destroyQuitMenu();
167-
168-
if (TheGameLogic->isInGame())
169-
{
170-
if (TheRecorder->getMode() == RECORDERMODETYPE_RECORD)
171-
{
172-
TheRecorder->stopRecording();
173-
}
174-
TheGameLogic->clearGameData();
175-
}
176-
TheGameEngine->setQuitting(TRUE);
177-
TheInGameUI->setClientQuiet( TRUE );
178-
179154
}
180155

181156
static void surrenderQuitMenu()
@@ -276,12 +251,23 @@ void HideQuitMenu()
276251

277252
}
278253

254+
Bool canOpenQuitMenu()
255+
{
256+
return (TheGameEngine != nullptr && TheGameEngine->isActive()
257+
&& TheGameLogic != nullptr
258+
&& (!TheInGameUI || !TheInGameUI->isQuitMenuVisible())
259+
&& !TheGameLogic->isLoadingMap()
260+
&& !TheGameLogic->isLoadingSave()
261+
&& !TheGameLogic->isIntroMoviePlaying()
262+
&& (TheScriptEngine == nullptr || !TheScriptEngine->isGameEnding()));
263+
}
264+
279265
//-------------------------------------------------------------------------------------------------
280266
/** Toggle visibility of the quit menu */
281267
//-------------------------------------------------------------------------------------------------
282268
void ToggleQuitMenu()
283269
{
284-
if (TheGameLogic->isIntroMoviePlaying() || TheGameLogic->isLoadingMap() ||TheScriptEngine->isGameEnding())
270+
if (!isVisible && !canOpenQuitMenu())
285271
return;
286272

287273
// BGC- If we are currently in the disconnect screen, don't let the quit menu come up.

Generals/Code/GameEngine/Source/GameClient/GameClient.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,11 @@ void GameClient::update()
537537
Int beginTime = timeGetTime();
538538
while(beginTime + 4000 > timeGetTime() )
539539
{
540+
if (GameClient::isMovieAbortRequested())
541+
{
542+
break;
543+
}
544+
540545
TheWindowManager->update();
541546
// redraw all views, update the GUI
542547
TheDisplay->draw();
@@ -754,6 +759,43 @@ void GameClient::updateHeadless()
754759
TheParticleSystemManager->update();
755760
}
756761

762+
Bool GameClient::isMovieAbortRequested()
763+
{
764+
if (TheGameEngine)
765+
{
766+
TheGameEngine->serviceWindowsOS();
767+
}
768+
769+
// TheSuperHackers @feature User can skip video by pressing ESC
770+
if (TheKeyboard)
771+
{
772+
TheKeyboard->UPDATE();
773+
KeyboardIO *io = TheKeyboard->findKey(KEY_ESC, KeyboardIO::STATUS_UNUSED);
774+
if (io && BitIsSet(io->state, KEY_STATE_DOWN))
775+
{
776+
io->setUsed();
777+
return TRUE;
778+
}
779+
}
780+
781+
if (TheGameEngine && TheGameEngine->getQuitting())
782+
{
783+
return TRUE;
784+
}
785+
786+
if (TheMessageStream && TheMessageStream->containsMessageOfType(GameMessage::MSG_META_DEMO_INSTANT_QUIT))
787+
{
788+
return TRUE;
789+
}
790+
791+
if (TheGameLogic && TheGameLogic->isQuitToDesktopRequested())
792+
{
793+
return TRUE;
794+
}
795+
796+
return FALSE;
797+
}
798+
757799
/** -----------------------------------------------------------------------------------------------
758800
* Call the given callback function for each object contained within the given region.
759801
*/

Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3699,27 +3699,15 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage
36993699
break;
37003700

37013701
}
3702-
3703-
3704-
#if defined(RTS_DEBUG)
3705-
//------------------------------------------------------------------------- BEGIN DEMO MESSAGES
3706-
//------------------------------------------------------------------------- BEGIN DEMO MESSAGES
3707-
//------------------------------------------------------------------------- BEGIN DEMO MESSAGES
3708-
//------------------------------------------------------------------------------- DEMO MESSAGES
3709-
//-----------------------------------------------------------------------------------------
3702+
37103703
case GameMessage::MSG_META_DEMO_INSTANT_QUIT:
3711-
if (TheGameLogic->isInGame())
3712-
{
3713-
if (TheRecorder->getMode() == RECORDERMODETYPE_RECORD)
3714-
{
3715-
TheRecorder->stopRecording();
3716-
}
3717-
TheGameLogic->clearGameData();
3718-
}
3719-
TheGameEngine->setQuitting(TRUE);
3704+
{
3705+
TheGameLogic->quit(TRUE);
37203706
disp = DESTROY_MESSAGE;
37213707
break;
3708+
}
37223709

3710+
#if defined(RTS_DEBUG)
37233711
//------------------------------------------------------------------------------- DEMO MESSAGES
37243712
//-----------------------------------------------------------------------------------------
37253713
case GameMessage::MSG_META_DEMO_SWITCH_TEAMS:
@@ -5054,6 +5042,7 @@ static Bool isSystemMessage( const GameMessage *msg )
50545042
case GameMessage::MSG_LOGIC_CRC:
50555043
case GameMessage::MSG_SET_REPLAY_CAMERA:
50565044
case GameMessage::MSG_FRAME_TICK:
5045+
case GameMessage::MSG_META_DEMO_INSTANT_QUIT:
50575046
return TRUE;
50585047
}
50595048
return FALSE;

Generals/Code/GameEngine/Source/GameClient/MessageStream/MetaEvent.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,10 @@ static const LookupListRec GameMessageMetaTypeNames[] =
179179
{ "TOGGLE_PAUSE_ALT", GameMessage::MSG_META_TOGGLE_PAUSE_ALT },
180180
{ "STEP_FRAME", GameMessage::MSG_META_STEP_FRAME },
181181
{ "STEP_FRAME_ALT", GameMessage::MSG_META_STEP_FRAME_ALT },
182+
{ "DEMO_INSTANT_QUIT", GameMessage::MSG_META_DEMO_INSTANT_QUIT },
182183

183184
#if defined(RTS_DEBUG)
184185
{ "HELP", GameMessage::MSG_META_HELP },
185-
{ "DEMO_INSTANT_QUIT", GameMessage::MSG_META_DEMO_INSTANT_QUIT },
186-
187186
{ "DEMO_TOGGLE_BEHIND_BUILDINGS", GameMessage::MSG_META_DEMO_TOGGLE_BEHIND_BUILDINGS },
188187
{ "DEMO_LOD_DECREASE", GameMessage::MSG_META_DEMO_LOD_DECREASE },
189188
{ "DEMO_LOD_INCREASE", GameMessage::MSG_META_DEMO_LOD_INCREASE },

0 commit comments

Comments
 (0)