diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index b6e1ce3a8..6c17767a5 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -1162,6 +1162,7 @@ bool CallFunc_HaveUnlockableTrigger(ACSVM::Thread *thread, const ACSVM::Word *ar { UINT8 id = 0; bool unlocked = false; + auto info = &static_cast(thread)->info; (void)argC; @@ -1171,9 +1172,11 @@ bool CallFunc_HaveUnlockableTrigger(ACSVM::Thread *thread, const ACSVM::Word *ar { CONS_Printf("Bad unlockable trigger ID %d\n", id); } - else + else if ((info != NULL) + && (info->mo != NULL && P_MobjWasRemoved(info->mo) == false) + && (info->mo->player != NULL)) { - unlocked = (unlocktriggers & (1 << id)); + unlocked = (info->mo->player->roundconditions.unlocktriggers & (1 << id)); } thread->dataStk.push(unlocked); diff --git a/src/d_player.h b/src/d_player.h index 7b0911e45..12a0ca6eb 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -348,6 +348,9 @@ struct roundconditions_t boolean hit_midair; mobjeflag_t wet_player; + + // 32 triggers, one bit each, for map execution + UINT32 unlocktriggers; }; // player_t struct for all skybox variables diff --git a/src/deh_soc.c b/src/deh_soc.c index 34b9ebefc..5519e7d67 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2346,6 +2346,10 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) INT32 offset = 0; +#if 0 + char *endpos = word2 + strlen(word2); +#endif + spos = strtok(word2, " "); for (i = 0; i < 5; ++i) @@ -2473,19 +2477,6 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) return; } } - else if (fastcmp(params[0], "TRIGGER")) - { - PARAMCHECK(1); - ty = UC_TRIGGER; - re = atoi(params[1]); - - // constrained by 32 bits - if (re < 0 || re > 31) - { - deh_warning("Trigger ID %d out of range (0 - 31) for condition ID %d", re, id+1); - return; - } - } else if (fastcmp(params[0], "TOTALMEDALS")) { PARAMCHECK(1); @@ -2617,6 +2608,40 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) return; } } + else if (fastcmp(params[0], "TRIGGER")) + { + PARAMCHECK(2); // strictly speaking at LEAST two + ty = UCRP_TRIGGER; + re = atoi(params[1]); + + // constrained by 32 bits + if (re < 0 || re > 31) + { + deh_warning("Trigger ID %d out of range (0 - 31) for condition ID %d", re, id+1); + return; + } + + // The following undid the effects of strtok. + // Unfortunately, there is no way it can reasonably undo the effects of strupr. + // If we want custom descriptions for map execution triggers, we're gonna need a different method. +#if 0 + // undo affect of strtok + i = 5; + // so spos will still be the strtok from earlier + while (i >= 2) + { + if (!spos) + continue; + while (*spos != '\0') + spos++; + if (spos < endpos) + *spos = ' '; + spos = params[--i]; + } +#endif + + stringvar = Z_StrDup(params[2]); + } else if ((offset=0) || fastcmp(params[0], "FALLOFF") || (++offset && fastcmp(params[0], "TOUCHOFFROAD")) || (++offset && fastcmp(params[0], "TOUCHSNEAKERPANEL")) diff --git a/src/g_game.c b/src/g_game.c index 86efdd11c..59948388c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -5096,9 +5096,6 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr } } - // Reset unlockable triggers - unlocktriggers = 0; - // clear itemfinder, just in case if (!dedicated) // except in dedicated servers, where it is not registered and can actually I_Error debug builds CV_StealthSetValue(&cv_itemfinder, 0); diff --git a/src/m_cond.c b/src/m_cond.c index 0f42e0576..0f19f9d8f 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -35,10 +35,6 @@ gamedata_t *gamedata = NULL; boolean netUnlocked[MAXUNLOCKABLES]; -// Map triggers for linedef executors -// 32 triggers, one bit each -UINT32 unlocktriggers; - // The meat of this system lies in condition sets conditionset_t conditionSets[MAXCONDITIONSETS]; @@ -704,8 +700,6 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) } case UC_MAPTIME: // Requires time on map <= x return (G_GetBestTime(cn->extrainfo1) <= (unsigned)cn->requirement); - case UC_TRIGGER: // requires map trigger set - return !!(unlocktriggers & (1 << cn->requirement)); case UC_TOTALMEDALS: // Requires number of emblems >= x return (M_GotEnoughMedals(cn->requirement)); case UC_EMBLEM: // Requires emblem x to be obtained @@ -791,6 +785,9 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) && player->realtime < timelimitintics && (timelimitintics + extratimeintics + secretextratime - player->realtime) >= (unsigned)cn->requirement); + case UCRP_TRIGGER: // requires map trigger set + return !!(player->roundconditions.unlocktriggers & (1 << cn->requirement)); + case UCRP_FALLOFF: return (player->roundconditions.fell_off == (cn->requirement == 1)); case UCRP_TOUCHOFFROAD: @@ -1176,6 +1173,9 @@ static const char *M_GetConditionString(condition_t *cn) G_TicsToSeconds(cn->requirement), G_TicsToCentiseconds(cn->requirement)); + case UCRP_TRIGGER: + return cn->stringvar; + case UCRP_FALLOFF: return (cn->requirement == 1) ? "fall off the course" : "without falling off"; case UCRP_TOUCHOFFROAD: diff --git a/src/m_cond.h b/src/m_cond.h index e5e9e0412..6c1f131d4 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -39,7 +39,6 @@ typedef enum UC_MAPENCORE, // MAPENCORE [map] UC_MAPSPBATTACK, // MAPSPBATTACK [map] UC_MAPTIME, // MAPTIME [map] [time to beat, tics] - UC_TRIGGER, // TRIGGER [trigger number] UC_TOTALMEDALS, // TOTALMEDALS [number of emblems] UC_EMBLEM, // EMBLEM [emblem number] UC_UNLOCKABLE, // UNLOCKABLE [unlockable number] @@ -76,6 +75,8 @@ typedef enum UCRP_FINISHTIMEEXACT, // Finish == [time, tics] UCRP_FINISHTIMELEFT, // Finish with at least [time, tics] to spare + UCRP_TRIGGER, // Map execution trigger [id] + UCRP_FALLOFF, // Fall off (or don't) UCRP_TOUCHOFFROAD, // Touch offroad (or don't) UCRP_TOUCHSNEAKERPANEL, // Either touch sneaker panel (or don't) @@ -262,8 +263,6 @@ extern unlockable_t unlockables[MAXUNLOCKABLES]; extern INT32 numemblems; -extern UINT32 unlocktriggers; - void M_NewGameDataStruct(void); // Challenges menu stuff diff --git a/src/p_spec.c b/src/p_spec.c index 75ad1be38..c7452db2d 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1541,17 +1541,18 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller return false; break; case 317: + if (actor && actor->player) { // Unlockable triggers required INT32 trigid = triggerline->args[1]; - if ((modifiedgame && !savemoddata) || (netgame || multiplayer)) - return false; - else if (trigid < 0 || trigid > 31) // limited by 32 bit variable + if (trigid < 0 || trigid > 31) // limited by 32 bit variable { CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger (sidedef %hu): bad trigger ID %d\n", triggerline->sidenum[0], trigid); return false; } - else if (!(unlocktriggers & (1 << trigid))) + else if (!(actor && actor->player)) + return false; + else if (!(actor->player->roundconditions.unlocktriggers & (1 << trigid))) return false; } break; @@ -3296,26 +3297,25 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha break; case 441: // Trigger unlockable - if (!(demo.playback || netgame || multiplayer)) { INT32 trigid = args[0]; if (trigid < 0 || trigid > 31) // limited by 32 bit variable CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger: bad trigger ID %d\n", trigid); - else + else if (mo && mo->player) { UINT32 flag = 1 << trigid; - if (unlocktriggers & flag) + if (mo->player->roundconditions.unlocktriggers & flag) { // Execute one time only break; } - unlocktriggers |= flag; + mo->player->roundconditions.unlocktriggers |= flag; // Unlocked something? - if (M_UpdateUnlockablesAndExtraEmblems(true)) + if (!demo.playback && M_UpdateUnlockablesAndExtraEmblems(true)) { gamedata->deferredsave = true; // only save if unlocked something }