From daa3d6647c9494ede2dfa42c15c2295ab569e884 Mon Sep 17 00:00:00 2001 From: skyjake Date: Sun, 19 Feb 2012 15:55:17 +0000 Subject: [PATCH] libcommon: Added optional definition ID for finales Fixes the problem where a new instance of the "help" script would get pushed onto the stack every time F1 was pressed. Now only one instance is started -- attempts to start the "help" script again while it's already running will be ignored (with a console message). --- doomsday/plugins/common/include/fi_lib.h | 15 ++++++++ doomsday/plugins/common/src/fi_lib.c | 49 ++++++++++++++++++++++-- doomsday/plugins/common/src/g_game.c | 12 +++--- 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/doomsday/plugins/common/include/fi_lib.h b/doomsday/plugins/common/include/fi_lib.h index 8a2dfea427..fa745ac933 100644 --- a/doomsday/plugins/common/include/fi_lib.h +++ b/doomsday/plugins/common/include/fi_lib.h @@ -59,6 +59,8 @@ void FI_StackShutdown(void); /** * Push a new set of Finale commands onto the LIFO stack, suspending any * existing Finale on the stack until command interpretation completes. + * The script will have no definition ID on the stack; you can start an + * unlimited number of instances of the script. * * @param commands One or more Finale (script) commands to be executed. * @param flags @see finaleFlags @@ -66,6 +68,19 @@ void FI_StackShutdown(void); */ void FI_StackExecute(const char* commands, int flags, finale_mode_t mode); +/** + * Push a new set of Finale commands onto the LIFO stack, suspending any + * existing Finale on the stack until command interpretation completes. + * If a script with the same definition ID is already on the stack, the + * script is not started. + * + * @param commands One or more Finale (script) commands to be executed. + * @param flags @see finaleFlags + * @param mode @see finaleMode + * @param defId Script's definition ID. + */ +void FI_StackExecuteWithId(const char* scriptSrc, int flags, finale_mode_t mode, const char* defId); + /** * Clear the LIFO Finale stack of any active scripts. */ diff --git a/doomsday/plugins/common/src/fi_lib.c b/doomsday/plugins/common/src/fi_lib.c index 6401859818..0ee6a2e45f 100644 --- a/doomsday/plugins/common/src/fi_lib.c +++ b/doomsday/plugins/common/src/fi_lib.c @@ -52,11 +52,19 @@ typedef struct { } fi_state_conditions_t; typedef struct { - finaleid_t finaleId; + finaleid_t finaleId; finale_mode_t mode; fi_state_conditions_t conditions; + /// Gamestate before the finale began. gamestate_t initialGamestate; + + /** + * Optionally the ID of the source script definition. A new script is + * not started if its definition ID matches one already on the stack. + * @note Maximum ID length defined in the DED Reader implementation. + */ + char defId[64]; } fi_state_t; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -152,12 +160,25 @@ static fi_state_t* stateForFinaleId(finaleid_t id) return 0; } +static boolean stackHasDefId(const char* defId) +{ + uint i; + for(i = 0; i < finaleStackSize; ++i) + { + fi_state_t* s = &finaleStack[i]; + if(!stricmp(s->defId, defId)) + return true; + } + return false; +} + static __inline fi_state_t* stackTop(void) { return (finaleStackSize == 0? 0 : &finaleStack[finaleStackSize-1]); } -static fi_state_t* stackPush(finaleid_t finaleId, finale_mode_t mode, gamestate_t prevGamestate) +static fi_state_t* stackPush(finaleid_t finaleId, finale_mode_t mode, gamestate_t prevGamestate, + const char* defId) { fi_state_t* s; finaleStack = Z_Realloc(finaleStack, sizeof(*finaleStack) * ++finaleStackSize, PU_GAMESTATIC); @@ -165,6 +186,16 @@ static fi_state_t* stackPush(finaleid_t finaleId, finale_mode_t mode, gamestate_ s->finaleId = finaleId; s->mode = mode; s->initialGamestate = prevGamestate; + if(defId) + { + (void) strncpy(s->defId, defId, sizeof(s->defId) - 1); + s->defId[sizeof(s->defId) - 1] = 0; // terminate + } + else + { + // Source ID not provided. + memset(s->defId, 0, sizeof(s->defId)); + } initStateConditions(s); return s; } @@ -242,6 +273,11 @@ void FI_StackShutdown(void) } void FI_StackExecute(const char* scriptSrc, int flags, finale_mode_t mode) +{ + FI_StackExecuteWithId(scriptSrc, flags, mode, NULL); +} + +void FI_StackExecuteWithId(const char* scriptSrc, int flags, finale_mode_t mode, const char* defId) { fi_state_t* s, *prevTopScript; gamestate_t prevGamestate; @@ -251,6 +287,13 @@ void FI_StackExecute(const char* scriptSrc, int flags, finale_mode_t mode) if(!finaleStackInited) Con_Error("FI_StackExecute: Not initialized yet!"); + // Should we ignore this? + if(defId && stackHasDefId(defId)) + { + Con_Message("There already is a finale running with ID \"%s\"; won't execute again.\n", defId); + return; + } + prevGamestate = G_GameState(); prevTopScript = stackTop(); @@ -315,7 +358,7 @@ void FI_StackExecute(const char* scriptSrc, int flags, finale_mode_t mode) FI_ScriptSuspend(prevTopScript->finaleId); } - s = stackPush(finaleId, mode, prevGamestate); + s = stackPush(finaleId, mode, prevGamestate, defId); // Do we need to transmit the state conditions to clients? if(IS_SERVER && !(flags & FF_LOCAL)) diff --git a/doomsday/plugins/common/src/g_game.c b/doomsday/plugins/common/src/g_game.c index af67ba7f94..b5d3350b06 100644 --- a/doomsday/plugins/common/src/g_game.c +++ b/doomsday/plugins/common/src/g_game.c @@ -1084,7 +1084,7 @@ void G_ChangeGameState(gamestate_t state) DD_Executef(true, "%sactivatebcontext game", gameActive? "" : "de"); } -boolean G_StartFinale(const char* script, int flags, finale_mode_t mode) +boolean G_StartFinale(const char* script, int flags, finale_mode_t mode, const char* defId) { assert(script && script[0]); { uint i; @@ -1101,7 +1101,7 @@ boolean G_StartFinale(const char* script, int flags, finale_mode_t mode) #endif }} G_SetGameAction(GA_NONE); - FI_StackExecute(script, flags, mode); + FI_StackExecuteWithId(script, flags, mode, defId); return true; } @@ -1119,7 +1119,7 @@ void G_StartTitle(void) if(!Def_Get(DD_DEF_FINALE, "title", &fin)) Con_Error("G_StartTitle: A title script must be defined."); - G_StartFinale(fin.script, FF_LOCAL, FIMODE_NORMAL); + G_StartFinale(fin.script, FF_LOCAL, FIMODE_NORMAL, "title"); } /** @@ -1133,7 +1133,7 @@ void G_StartHelp(void) if(Def_Get(DD_DEF_FINALE, "help", &fin)) { Hu_MenuCommand(MCMD_CLOSEFAST); - G_StartFinale(fin.script, FF_LOCAL, FIMODE_NORMAL); + G_StartFinale(fin.script, FF_LOCAL, FIMODE_NORMAL, "help"); return; } Con_Message("Warning: InFine script 'help' not defined, ignoring.\n"); @@ -1295,7 +1295,7 @@ void G_DoLoadMap(void) // Start a briefing, if there is one. if(hasBrief) { - G_StartFinale(fin.script, 0, FIMODE_BEFORE); + G_StartFinale(fin.script, 0, FIMODE_BEFORE, 0); } else // No briefing, start the map. { @@ -2302,7 +2302,7 @@ void G_WorldDone(void) FI_StackClear(); if(G_DebriefingEnabled(gameEpisode, gameMap, &fin) && - G_StartFinale(fin.script, 0, FIMODE_AFTER)) + G_StartFinale(fin.script, 0, FIMODE_AFTER, 0)) { return; }