UCRP_TRIGGER: Convert to roundconditions_t

- Previously was extern scope UINT32 for all players, but this permits one player in a netgame taking a secret path while others don't.
- Attempted to make user-specifiable string, but while I can undo the effects of strtok for the condition, I cannot undo the effects of strupr - so it's disabled for now until we come up with a more robust and hopefully direct system.
- Also removed some old SRB2-originated assumptions that you couldn't unlock anything in multiplayer from the unlocktrigger system.
This commit is contained in:
toaster 2023-03-07 00:03:41 +00:00
parent 42a985d061
commit 4802f96249
7 changed files with 63 additions and 36 deletions

View file

@ -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 *>(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);

View file

@ -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

View file

@ -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"))

View file

@ -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);

View file

@ -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:

View file

@ -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

View file

@ -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
}