From d11fe78e90e6678b7c232bae2e47431c45949a05 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 17 Oct 2023 01:07:03 +0100 Subject: [PATCH] UCRP_UFOATTACKMETHOD `Condition1 = UfoAttackMethod [type]` - "smash a UFO Catcher using only [type]" - Combine with `Prefix_SealedStar` or `Prefix_IsMap [special stage stage]` - Shows up as "???" - Types supported: - `Boost` - "boost power" (sneakers) - `Whip` - "Insta-Whip" - `Banana` - "Bananas" - `Orbinaut`- "Orbinauts" - `Jawz` - "Jawz" - `SPB` - "Self Propelled Bombs" - Other types could be added on request, these were just the easy ones In addition, the prototype for P_MobjWasRemoved was moved to `p_mobj.h`. It's EXTREMELY important that we're able to safely check mobj pointers anywhere a mobj_t is possible to observe, without including the full `p_local.h`... --- src/d_player.h | 14 +++++++++++ src/deh_soc.c | 19 ++++++++++++++ src/m_cond.c | 47 +++++++++++++++++++++++++++++++++++ src/m_cond.h | 2 ++ src/objects/ufo.c | 63 ++++++++++++++++++++++++++++++++++++++++------- src/p_local.h | 1 - src/p_mobj.h | 3 +++ 7 files changed, 139 insertions(+), 10 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 548ed8f7c..a4b9b0426 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -387,6 +387,18 @@ struct botvars_t // player_t struct for round-specific condition tracking +typedef enum +{ + UFOD_GENERIC = 1, + UFOD_BOOST = 1<<1, + UFOD_WHIP = 1<<2, + UFOD_BANANA = 1<<3, + UFOD_ORBINAUT = 1<<4, + UFOD_JAWZ = 1<<5, + UFOD_SPB = 1<<6, + // free up to and including 1<<31 +} ufodamaging_t; + struct roundconditions_t { // Reduce the number of checks by only updating when this is true @@ -404,6 +416,8 @@ struct roundconditions_t UINT8 hittrackhazard[((MAX_LAPS+1)/8) + 1]; + ufodamaging_t ufodamaging; + mobjeflag_t wet_player; // 32 triggers, one bit each, for map execution diff --git a/src/deh_soc.c b/src/deh_soc.c index d6d92a031..ea0b66dfe 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3007,6 +3007,25 @@ static void readcondition(UINT16 set, UINT32 id, char *word2) } } } + else if (fastcmp(params[0], "UFOATTACKMETHOD")) + { + PARAMCHECK(1); + ty = UCRP_UFOATTACKMETHOD; + + // See ufodamaging_t + if ((offset=1) || fastcmp(params[1], "BOOST") + || (++offset && fastcmp(params[1], "WHIP")) + || (++offset && fastcmp(params[1], "BANANA")) + || (++offset && fastcmp(params[1], "ORBINAUT")) + || (++offset && fastcmp(params[1], "JAWZ")) + || (++offset && fastcmp(params[1], "SPB"))) + re = offset; + else + { + deh_warning("Unknown attack method %s for condition ID %d", params[1], id+1); + return; + } + } else { deh_warning("Invalid condition name %s for condition ID %d", params[0], id+1); diff --git a/src/m_cond.c b/src/m_cond.c index 6d867e254..9a5bd39ee 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -1683,6 +1683,16 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) return (!(player->roundconditions.hittrackhazard[requiredlap] & requiredbit) != (cn->requirement == 1)); } + case UCRP_UFOATTACKMETHOD: + return ( + specialstageinfo.valid == true + && ( + P_MobjWasRemoved(specialstageinfo.ufo) + || specialstageinfo.ufo->health <= 1 + ) + && player->roundconditions.ufodamaging == (ufodamaging_t)(1<requirement) + ); + 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 @@ -2377,6 +2387,43 @@ static const char *M_GetConditionString(condition_t *cn) return va("%s on lap %u", work, cn->extrainfo1); } + case UCRP_UFOATTACKMETHOD: + { + if (!gamedata->everseenspecial) + return NULL; + + work = NULL; + + switch (cn->requirement) + { + case 1: + work = "boost power"; + break; + case 2: + work = "Insta-Whip"; + break; + case 3: + work = "Bananas"; + break; + case 4: + work = "Orbinauts"; + break; + case 5: + work = "Jawz"; + break; + case 6: + work = "Self Propelled Bombs"; + break; + default: + break; + } + + if (work == NULL) + return va("INVALID ATTACK CONDITION \"%d:%d\"", cn->type, cn->requirement); + + return va("smash the UFO Catcher using only %s", work); + } + case UCRP_WETPLAYER: return va("without %s %s", (cn->requirement & MFE_TOUCHWATER) ? "touching any" : "going into", diff --git a/src/m_cond.h b/src/m_cond.h index 0a78c3ad2..a95d38cc2 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -124,6 +124,8 @@ typedef enum UCRP_TRACKHAZARD, // (Don't) get hit by a track hazard (maybe specific lap) + UCRP_UFOATTACKMETHOD, // Defeat a UFO Catcher using only one method + UCRP_WETPLAYER, // Don't touch [strictness] [fluid] } conditiontype_t; diff --git a/src/objects/ufo.c b/src/objects/ufo.c index cb9351aed..5e1a721e6 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -763,52 +763,80 @@ static void UFOKillPieces(mobj_t *ufo) static UINT8 GetUFODamage(mobj_t *inflictor, UINT8 damageType) { + UINT8 ret = 0; + ufodamaging_t ufodamaging = UFOD_GENERIC; + if (inflictor != NULL && P_MobjWasRemoved(inflictor) == false) { switch (inflictor->type) { + // Shields deal chip damage. case MT_JAWZ_SHIELD: + { + ufodamaging = UFOD_JAWZ; + ret = 10; + break; + } case MT_ORBINAUT_SHIELD: + { + ufodamaging = UFOD_ORBINAUT; + ret = 10; + break; + } case MT_INSTAWHIP: { - // Shields deal chip damage. - return 10; + ufodamaging = UFOD_WHIP; + ret = 10; + break; } case MT_JAWZ: { // Thrown Jawz deal a bit extra. - return 15; + ufodamaging = UFOD_JAWZ; + ret = 15; + break; } case MT_ORBINAUT: { // Thrown orbinauts deal double damage. - return 20; + ufodamaging = UFOD_ORBINAUT; + ret = 20; + break; } case MT_SPB: { // SPB deals triple damage. - return 30; + ufodamaging |= UFOD_SPB; + ret = 30; + break; } case MT_BANANA: { + ufodamaging = UFOD_BANANA; + // Banana snipes deal triple damage, // laid down bananas deal regular damage. if (inflictor->health > 1) { - return 30; + ret = 30; + break; } - return 10; + ret = 10; + break; } case MT_PLAYER: { // Players deal damage relative to how many sneakers they used. - return 15 * max(1, inflictor->player->numsneakers); + ufodamaging = UFOD_BOOST; + ret = 15 * max(1, inflictor->player->numsneakers); + break; } case MT_SPECIAL_UFO: { // UFODebugSetHealth - return 1; + ret = 1; + break; } default: { @@ -817,6 +845,23 @@ static UINT8 GetUFODamage(mobj_t *inflictor, UINT8 damageType) } } + { + // We have to iterate over all players, otherwise a player who gets exactly one hit in will trick the Challenges system. + UINT8 i; + for (i = 0; i <= splitscreen; i++) + { + if (!playeringame[g_localplayers[i]]) + continue; + if (players[g_localplayers[i]].spectator) + continue; + players[i].roundconditions.ufodamaging |= ufodamaging; + players[i].roundconditions.checkthisframe = true; + } + } + + if (ret) + return ret; + // Guess from damage type. switch (damageType & DMG_TYPEMASK) { diff --git a/src/p_local.h b/src/p_local.h index 3c809034f..72abb1dec 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -260,7 +260,6 @@ void P_RecalcPrecipInSector(sector_t *sector); void P_PrecipitationEffects(void); void P_RemoveMobj(mobj_t *th); -boolean P_MobjWasRemoved(const mobj_t *th); void P_RemoveSavegameMobj(mobj_t *th); boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state); boolean P_SetMobjState(mobj_t *mobj, statenum_t state); diff --git a/src/p_mobj.h b/src/p_mobj.h index 585dff7c7..55d09e765 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -509,6 +509,9 @@ struct precipmobj_t tic_t lastThink; }; +// It's extremely important that all mobj_t*-reading code have access to this. +boolean P_MobjWasRemoved(const mobj_t *th); + struct actioncache_t { actioncache_t *next;