From 2bee969c6a2803247956b852f424012514337a89 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 11 May 2020 00:16:13 +0200 Subject: [PATCH] Add follower bubbles with the BUBBLESCALE field --- src/dehacked.c | 13 ++++++++++ src/info.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/info.h | 7 ++++++ src/p_mobj.c | 20 +++++++++++++--- src/p_user.c | 40 +++++++++++++++++++++++++++++++ src/r_things.c | 13 ++++++++++ src/r_things.h | 1 + 7 files changed, 153 insertions(+), 5 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index eb3a293fc..708d41cb6 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -719,6 +719,7 @@ static void readfollower(MYFILE *f) // Ready the default variables for followers. We will overwrite them as we go! We won't set the name or states RIGHT HERE as this is handled down instead. followers[numfollowers].scale = FRACUNIT; + followers[numfollowers].bubblescale = 0; // No bubble by default followers[numfollowers].atangle = 230; followers[numfollowers].dist = 32; // changed from 16 to 32 to better account for ogl models followers[numfollowers].height = 16; @@ -774,6 +775,11 @@ static void readfollower(MYFILE *f) DEH_WriteUndoline(word, va("%d", followers[numfollowers].scale), UNDO_NONE); followers[numfollowers].scale = get_number(word2); } + else if (fastcmp(word, "BUBBLESCALE")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].bubblescale), UNDO_NONE); + followers[numfollowers].bubblescale = get_number(word2); + } else if (fastcmp(word, "ATANGLE")) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].atangle), UNDO_NONE); @@ -917,6 +923,8 @@ if (followers[numfollowers].field < threshold) \ FALLBACK(bobamp, "BOBAMP", 0, 0); FALLBACK(bobspeed, "BOBSPEED", 0, 0); FALLBACK(hitconfirmtime, "HITCONFIRMTIME", 1, 1); + FALLBACK(scale, "SCALE", 1, 1); // No null/negative scale + FALLBACK(bubblescale, "BUBBLESCALE", 0, 0); // No negative scale // Special case for color I suppose if (followers[numfollowers].defaultcolor < 0 || followers[numfollowers].defaultcolor > MAXSKINCOLORS-1) @@ -7546,6 +7554,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_GCHAOHAPPY3", "S_GCHAOHAPPY4", + "S_FOLLOWERBUBBLE_FRONT", + "S_FOLLOWERBUBBLE_BACK", + "S_CHEESEIDLE", "S_CHEESEFLY", "S_CHEESESAD1", @@ -8404,6 +8415,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_BATTLECAPSULE_PIECE", "MT_FOLLOWER", + "MT_FOLLOWERBUBBLE_FRONT", + "MT_FOLLOWERBUBBLE_BACK", #ifdef SEENAMES "MT_NAMECHECK", diff --git a/src/info.c b/src/info.c index 780ec765b..75fb042be 100644 --- a/src/info.c +++ b/src/info.c @@ -73,8 +73,8 @@ char sprnames[NUMSPRITES + 1][5] = "ICEB","CNDL","DOCH","DUCK","GTRE","CHES","CHIM","DRGN","LZMN","PGSS", "ZTCH","MKMA","MKMP","RTCH","BOWL","BOWH","BRRL","BRRR","HRSE","TOAH", "BFRT","OFRT","RFRT","PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN", - "FWRK","MXCL","RGSP","DRAF","GRES","OTFG","DBOS","XMS4","XMS5","GCHA", - "CHEZ","VIEW" + "FWRK","MXCL","RGSP","DRAF","GRES","OTFG","DBOS","XMS4","XMS5","FBUB", + "GCHA","CHEZ","VIEW" }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -3513,6 +3513,10 @@ state_t states[NUMSTATES] = // followers: + // bubble + {SPR_FBUB, 11|FF_ANIMATE|FF_TRANS70|FF_FULLBRIGHT, -1, {NULL}, 10, 3, S_FOLLOWERBUBBLE_FRONT}, // S_FOLLOWERBUBBLE_FRONT + {SPR_FBUB, FF_ANIMATE|0|FF_FULLBRIGHT, -1, {NULL}, 10, 3, S_FOLLOWERBUBBLE_BACK}, // S_FOLLOWERBUBBLE_BACK + // generic chao: {SPR_GCHA, FF_ANIMATE, -1, {NULL}, 1, 4, S_GCHAOIDLE}, //S_GCHAOIDLE {SPR_GCHA, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_GCHAOFLY}, //S_GCHAOFLY @@ -20854,6 +20858,62 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // raisestate }, + { // MT_FOLLOWERBUBBLE_FRONT + -1, // doomednum + S_FOLLOWERBUBBLE_FRONT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8<target || P_MobjWasRemoved(mobj->target) || !mobj->target->player || mobj->target->player->spectator || mobj->target->player->followerskin < 0) + { + // Remove possible hnext list (bubble) + mobj_t *bub = mobj->hnext; + mobj_t *tmp; + + while (bub && !P_MobjWasRemoved(bub)) + { + tmp = bub->hnext; + P_RemoveMobj(bub); + bub = tmp; + } + P_RemoveMobj(mobj); - + } + return; + case MT_HOOP: if (mobj->fuse > 1) P_MoveHoop(mobj); @@ -8798,7 +8812,7 @@ void P_MobjThinker(mobj_t *mobj) if (curstate >= S_FLAMESHIELD1 && curstate < S_FLAMESHIELDDASH1 && ((curstate-S_FLAMESHIELD1) & 1)) viewingangle += ANGLE_180; - + destx = mobj->target->x + P_ReturnThrustX(mobj->target, viewingangle, mobj->scale>>4); desty = mobj->target->y + P_ReturnThrustY(mobj->target, viewingangle, mobj->scale>>4); } @@ -11847,7 +11861,7 @@ void P_SpawnPlayer(INT32 playernum) //awayview stuff p->awayviewmobj = NULL; p->awayviewtics = 0; - + p->follower = NULL; // cleanse follower from existence // set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't. diff --git a/src/p_user.c b/src/p_user.c index 1dd56eeab..83ad76bc5 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8084,6 +8084,10 @@ static void P_HandleFollower(player_t *player) fixed_t sx, sy, sz; UINT8 color; + fixed_t bubble; // bubble scale (0 if no bubble) + mobj_t *bmobj; // temp bubble mobj + + if (!player->followerready) return; // we aren't ready to perform anything follower related yet. @@ -8105,6 +8109,7 @@ static void P_HandleFollower(player_t *player) an = player->mo->angle + (fl.atangle)*ANG1; // it's aproximative but it really doesn't matter in the grand scheme of things... zoffs = (fl.zoffs)*FRACUNIT; + bubble = fl.bubblescale; // 0 if no bubble to spawn. // do you like angle maths? I certainly don't... sx = player->mo->x + FixedMul((player->mo->scale*fl.dist), FINECOSINE((an)>>ANGLETOFINESHIFT)); @@ -8151,6 +8156,18 @@ static void P_HandleFollower(player_t *player) P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear player->follower->angle = player->mo->angle; + // This is safe to only spawn it here, the follower is removed then respawned when switched. + if (bubble) + { + bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_FRONT); + P_SetTarget(&player->follower->hnext, bmobj); + P_SetTarget(&bmobj->target, player->follower); // Used to know if we have to despawn at some point. + + bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_BACK); + P_SetTarget(&player->follower->hnext->hnext, bmobj); // this seems absolutely stupid, I know, but this will make updating the momentums/flags of these a bit easier. + P_SetTarget(&bmobj->target, player->follower); // Ditto + } + player->follower->extravalue1 = 0; // extravalue1 is used to know what "state set" to use. /* 0 = idle @@ -8205,6 +8222,29 @@ static void P_HandleFollower(player_t *player) // if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward. // Make sure the follower itself is also moving however, otherwise we'll be facing angle 0 + // Finally, if the follower has bubbles, move them, set their scale, etc.... + // This is what I meant earlier by it being easier, now we can just use this weird lil loop to get the job done! + + bmobj = player->follower->hnext; // will be NULL if there's no bubble + + while (bmobj && !P_MobjWasRemoved(bmobj)) + { + // match follower's momentums and (e)flags(2). + bmobj->momx = player->follower->momx; + bmobj->momy = player->follower->momy; + bmobj->momz = player->follower->momz; + + P_SetScale(bmobj, FixedMul(bubble, player->mo->scale)); + K_GenericExtraFlagsNoZAdjust(bmobj, player->follower); + bmobj->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW); + + if (player->follower->threshold) // threshold means the follower was "despawned" with S_NULL (is actually just set to S_INVISIBLE) + P_SetMobjState(bmobj, S_INVISIBLE); // sooooo... let's do the same! + + bmobj = bmobj->hnext; // switch to other bubble layer or exit + } + + if (player->follower->threshold) return; // Threshold means the follower was "despanwed" with S_NULL. diff --git a/src/r_things.c b/src/r_things.c index 1b8cd3fe6..2eff0bd2a 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3043,6 +3043,8 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) void SetFollower(INT32 playernum, INT32 skinnum) { player_t *player = &players[playernum]; + mobj_t *bub; + mobj_t *tmp; player->followerready = true; // we are ready to perform follower related actions in the player thinker, now. if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! @@ -3053,6 +3055,17 @@ void SetFollower(INT32 playernum, INT32 skinnum) */ if (player->follower && skinnum != player->followerskin) // this is also called when we change colour so don't respawn the follower unless we changed skins { + + // Remove follower's possible hnext list (bubble) + bub = player->follower->hnext; + + while (bub && !P_MobjWasRemoved(bub)) + { + tmp = bub->hnext; + P_RemoveMobj(bub); + bub = tmp; + } + P_RemoveMobj(player->follower); player->follower = NULL; } diff --git a/src/r_things.h b/src/r_things.h index 859d1be0f..fc2cae479 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -113,6 +113,7 @@ typedef struct follower_s UINT8 defaultcolor; // default color for menus. fixed_t scale; // Scale relative to the player's. + fixed_t bubblescale; // Bubble scale relative to the player scale. If not set, no bubble will spawn (default) // some position shenanigans: INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player.