roundconditions_t - State tracking for events which occour mid-match and don't stay that way

- Exists on every player struct to simplify writes, but A) not netsynced and B) only checked for local players
- Updated in the relevant locations - no centralised ticking at the moment
- Has a number of new associated conditions that require playing (UCRP's).
    - The following require [True/False] as supplementary information.
        - FallOff
        - TouchOffroad
        - TouchSneakerPanel
        - RingDebt
    - The following have no supplementary information because they're universally a specific achievement.
        - TripwireHyuu
        - SPBNeuter
        - LandmineDunk
        - HitMidair
    - The following has specific requirements that can be set.
        - WetPlayer [name of fluid]
             - Append "Strict" to forbid even skimming the surface of the map's fluid.
This commit is contained in:
toaster 2023-03-06 22:31:35 +00:00
parent d540921f78
commit baeb48ca1f
11 changed files with 184 additions and 1 deletions

View file

@ -333,6 +333,21 @@ struct botvars_t
tic_t spindashconfirm; // When high enough, they will try spindashing
};
// player_t struct for round-specific condition tracking
struct roundconditions_t
{
// Trivial Yes/no events across multiple UCRP's
boolean fell_off;
boolean touched_offroad;
boolean touched_sneakerpanel;
boolean debt_rings;
boolean tripwire_hyuu;
boolean spb_neuter;
boolean landmine_dunk;
boolean hit_midair;
};
// player_t struct for all skybox variables
struct skybox_t {
mobj_t * viewpoint;
@ -682,6 +697,7 @@ struct player_t
#endif
sonicloopvars_t loop;
roundconditions_t roundconditions;
};
#ifdef __cplusplus

View file

@ -2617,6 +2617,45 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
return;
}
}
else if ((offset=0) || fastcmp(params[0], "FALLOFF")
|| (++offset && fastcmp(params[0], "TOUCHOFFROAD"))
|| (++offset && fastcmp(params[0], "TOUCHSNEAKERPANEL"))
|| (++offset && fastcmp(params[0], "RINGDEBT")))
{
PARAMCHECK(1);
ty = UCRP_FALLOFF + offset;
re = 1;
if (params[1][0] == 'F' || params[1][0] == 'N' || params[1][0] == '0')
re = 0;
}
else if ((offset=0) || fastcmp(params[0], "TRIPWIREHYUU")
|| (++offset && fastcmp(params[0], "SPBNEUTER"))
|| (++offset && fastcmp(params[0], "LANDMINEDUNK"))
|| (++offset && fastcmp(params[0], "HITMIDAIR")))
{
//PARAMCHECK(1);
ty = UCRP_TRIPWIREHYUU + offset;
}
else if (fastcmp(params[0], "WETPLAYER"))
{
PARAMCHECK(1);
ty = UCRP_WETPLAYER;
re = MFE_UNDERWATER;
x1 = 1;
stringvar = Z_StrDup(params[1]);
if (params[2])
{
if (fastcmp(params[2], "STRICT"))
re |= MFE_TOUCHWATER;
else
{
deh_warning("liquid strictness requirement \"%s\" invalid for condition ID %d", params[2], id+1);
return;
}
}
}
else
{
deh_warning("Invalid condition name %s for condition ID %d", params[0], id+1);

View file

@ -2371,6 +2371,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
INT32 khudfault;
INT32 kickstartaccel;
roundconditions_t roundconditions;
boolean saveroundconditions;
score = players[player].score;
lives = players[player].lives;
ctfteam = players[player].ctfteam;
@ -2461,6 +2464,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
khudfinish = 0;
khudcardanimation = 0;
starpostnum = 0;
saveroundconditions = false;
}
else
{
@ -2509,6 +2514,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
starpostnum = players[player].starpostnum;
pflags |= (players[player].pflags & (PF_STASIS|PF_ELIMINATED|PF_NOCONTEST|PF_FAULT|PF_LOSTLIFE));
memcpy(&roundconditions, &players[player].roundconditions, sizeof (roundconditions));
saveroundconditions = true;
}
if (!betweenmaps)
@ -2592,6 +2600,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
memcpy(&p->itemRoulette, &itemRoulette, sizeof (p->itemRoulette));
memcpy(&p->respawn, &respawn, sizeof (p->respawn));
if (saveroundconditions)
memcpy(&p->roundconditions, &roundconditions, sizeof (p->roundconditions));
if (follower)
P_RemoveMobj(follower);

View file

@ -411,7 +411,12 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
// Banana snipe!
if (t1->health > 1)
{
if (t1->target && t1->target->player)
t1->target->player->roundconditions.landmine_dunk = true;
S_StartSound(t2, sfx_bsnipe);
}
if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD)
{

View file

@ -1118,6 +1118,9 @@ static void K_UpdateOffroad(player_t *player)
if (player->offroad > offroadstrength)
player->offroad = offroadstrength;
if (player->offroad > (2*offroadstrength) / TICRATE)
player->roundconditions.touched_offroad = true;
}
else
player->offroad = 0;
@ -4065,7 +4068,11 @@ void K_ApplyTripWire(player_t *player, tripwirestate_t state)
K_TumblePlayer(player, NULL, NULL);
if (state == TRIPSTATE_PASSED)
{
S_StartSound(player->mo, sfx_ssa015);
if (player->hyudorotimer > 0)
player->roundconditions.tripwire_hyuu = true;
}
else if (state == TRIPSTATE_BLOCKED)
{
S_StartSound(player->mo, sfx_kc40);
@ -5731,6 +5738,9 @@ void K_DoSneaker(player_t *player, INT32 type)
{
const fixed_t intendedboost = FRACUNIT/2;
if (player->floorboost != 0)
player->roundconditions.touched_sneakerpanel = true;
if (player->floorboost == 0 || player->floorboost == 3)
{
const sfxenum_t normalsfx = sfx_cdfm01;

View file

@ -593,6 +593,23 @@ void M_UpdateConditionSetsPending(void)
break;
}
case UCRP_WETPLAYER:
{
if (cn->extrainfo1)
{
char *l;
for (l = cn->stringvar; *l != '\0'; l++)
{
*l = tolower(*l);
}
cn->extrainfo1 = 0;
}
break;
}
default:
break;
}
@ -773,6 +790,28 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
&& !K_CanChangeRules(false) // too easy to change cv_timelimit
&& player->realtime < timelimitintics
&& (timelimitintics + extratimeintics + secretextratime - player->realtime) >= (unsigned)cn->requirement);
case UCRP_FALLOFF:
return (player->roundconditions.fell_off == (cn->requirement == 1));
case UCRP_TOUCHOFFROAD:
return (player->roundconditions.touched_offroad == (cn->requirement == 1));
case UCRP_TOUCHSNEAKERPANEL:
return (player->roundconditions.touched_sneakerpanel == (cn->requirement == 1));
case UCRP_RINGDEBT:
return (!(gametyperules & GTR_SPHERES) && (player->roundconditions.debt_rings == (cn->requirement == 1)));
case UCRP_TRIPWIREHYUU:
return (player->roundconditions.tripwire_hyuu);
case UCRP_SPBNEUTER:
return (player->roundconditions.spb_neuter);
case UCRP_LANDMINEDUNK:
return (player->roundconditions.landmine_dunk);
case UCRP_HITMIDAIR:
return (player->roundconditions.hit_midair);
case UCRP_WETPLAYER:
return (((player->roundconditions.wet_player & cn->requirement) == 0)
|| player->roundconditions.fell_off); // Levels with water tend to texture their pits as water too
}
return false;
}
@ -1137,6 +1176,29 @@ static const char *M_GetConditionString(condition_t *cn)
G_TicsToSeconds(cn->requirement),
G_TicsToCentiseconds(cn->requirement));
case UCRP_FALLOFF:
return (cn->requirement == 1) ? "fall off the course" : "without falling off";
case UCRP_TOUCHOFFROAD:
return (cn->requirement == 1) ? "touch offroad" : "without touching any offroad";
case UCRP_TOUCHSNEAKERPANEL:
return (cn->requirement == 1) ? "touch a Sneaker Panel" : "without touching any Sneaker Panels";
case UCRP_RINGDEBT:
return (cn->requirement == 1) ? "go into Ring debt" : "without going into Ring debt";
case UCRP_TRIPWIREHYUU:
return "go through Tripwire after getting snared by Hyudoro";
case UCRP_SPBNEUTER:
return "shock a Self Propelled Bomb into submission";
case UCRP_LANDMINEDUNK:
return "dunk a Landmine on another racer's head";
case UCRP_HITMIDAIR:
return "hit another racer with a projectile while you're both in the air";
case UCRP_WETPLAYER:
return va("without %s %s",
(cn->requirement & MFE_TOUCHWATER) ? "touching any" : "going into",
cn->stringvar);
default:
break;
}

View file

@ -75,6 +75,18 @@ typedef enum
UCRP_FINISHTIME, // Finish <= [time, tics]
UCRP_FINISHTIMEEXACT, // Finish == [time, tics]
UCRP_FINISHTIMELEFT, // Finish with at least [time, tics] to spare
UCRP_FALLOFF, // Fall off (or don't)
UCRP_TOUCHOFFROAD, // Touch offroad (or don't)
UCRP_TOUCHSNEAKERPANEL, // Either touch sneaker panel (or don't)
UCRP_RINGDEBT, // Go into debt (or don't)
UCRP_TRIPWIREHYUU, // Go through tripwire with Hyudoro
UCRP_SPBNEUTER, // Kill an SPB with Lightning
UCRP_LANDMINEDUNK, // huh? you died? that's weird. all i did was try to hug you...
UCRP_HITMIDAIR, // Hit another player mid-air with a kartfielditem
UCRP_WETPLAYER, // Touch [fluid]
} conditiontype_t;
// Condition Set information

View file

@ -1940,6 +1940,7 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
{
case DMG_DEATHPIT:
// Respawn kill types
player->roundconditions.fell_off = true;
K_DoIngameRespawn(player);
return false;
case DMG_SPECTATOR:
@ -2025,6 +2026,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
{
player_t *player;
boolean force = false;
boolean spbpop = false;
INT32 laglength = 6;
@ -2058,6 +2060,14 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
Obj_MonitorOnDamage(target, inflictor, damage);
break;
case MT_SPB:
spbpop = (damagetype & DMG_TYPEMASK) == DMG_VOLTAGE;
if (spbpop && source && source->player)
{
source->player->roundconditions.spb_neuter = true;
}
break;
default:
break;
}
@ -2076,7 +2086,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (!force)
{
if (!(target->type == MT_SPB && (damagetype & DMG_TYPEMASK) == DMG_VOLTAGE))
if (!spbpop)
{
if (!(target->flags & MF_SHOOTABLE))
return false; // shouldn't happen...
@ -2130,6 +2140,14 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
}
}
if (inflictor && source && source->player)
{
if (P_IsKartFieldItem(source->type)
&& target->player->airtime > TICRATE/2
&& source->player->airtime > TICRATE/2)
source->player->roundconditions.hit_midair = true;
}
// Instant-Death
if ((damagetype & DMG_DEATHMASK))
{

View file

@ -3421,6 +3421,10 @@ void P_MobjCheckWater(mobj_t *mobj)
{
return;
}
p->roundconditions.wet_player |= (mobj->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER|MFE_GOOWATER));
if (p->roundconditions.wet_player)
CONS_Printf("%u\n", p->roundconditions.wet_player);
}
if (mobj->flags & MF_APPLYTERRAIN)

View file

@ -518,6 +518,11 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings)
player->rings += num_rings;
if (player->rings < 0)
{
player->roundconditions.debt_rings = true;
}
return num_rings;
}

View file

@ -44,6 +44,7 @@ TYPEDEF (discordRequest_t);
// d_player.h
TYPEDEF (respawnvars_t);
TYPEDEF (botvars_t);
TYPEDEF (roundconditions_t);
TYPEDEF (skybox_t);
TYPEDEF (itemroulette_t);
TYPEDEF (player_t);