Skip to content

Commit

Permalink
libcommon: Added optional definition ID for finales
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
skyjake committed Feb 19, 2012
1 parent 07f7e77 commit daa3d66
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 9 deletions.
15 changes: 15 additions & 0 deletions doomsday/plugins/common/include/fi_lib.h
Expand Up @@ -59,13 +59,28 @@ 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
* @param mode @see finaleMode
*/
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.
*/
Expand Down
49 changes: 46 additions & 3 deletions doomsday/plugins/common/src/fi_lib.c
Expand Up @@ -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 --------------------------------------------
Expand Down Expand Up @@ -152,19 +160,42 @@ 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);
s = &finaleStack[finaleStackSize-1];
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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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();

Expand Down Expand Up @@ -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))
Expand Down
12 changes: 6 additions & 6 deletions doomsday/plugins/common/src/g_game.c
Expand Up @@ -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;
Expand All @@ -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;
}

Expand All @@ -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");
}

/**
Expand All @@ -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");
Expand Down Expand Up @@ -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.
{
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit daa3d66

Please sign in to comment.