From 0aa54559f9ce584e816ed3eba27f820c894db737 Mon Sep 17 00:00:00 2001 From: yamamama Date: Tue, 2 Dec 2025 19:56:23 -0500 Subject: [PATCH] Add (mostly) finalized visuals for the Arrow Bullet Adds an 'arrowbullet' parameter to player_t; most of the general precautions for that have been taken --- src/d_player.h | 1 + src/info/sprites.h | 3 +++ src/info/states.h | 3 +++ src/k_kart.c | 11 +++++++- src/lua_playerlib.c | 7 +++++ src/p_local.h | 1 + src/p_mobj.c | 6 ++++- src/p_saveg.c | 3 +++ src/p_user.c | 64 ++++++++++++++++++++++++++++++++++++++++++++- 9 files changed, 96 insertions(+), 3 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 464846045..649d8c082 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -669,6 +669,7 @@ struct player_t INT16 altshrinktimeshit; // (Alt. Shrink) How many times were you flipped over while shrunk? INT16 growcancel; // Duration of grow canceling INT16 squishedtimer; // Duration of being squished + mobj_t *arrowbullet; // (Alt. Shrink) Arrow Bullet aura visual UINT16 rocketsneakertimer; // Rocket Sneaker duration timer diff --git a/src/info/sprites.h b/src/info/sprites.h index 6fc86bddc..0f8c11cbb 100644 --- a/src/info/sprites.h +++ b/src/info/sprites.h @@ -650,5 +650,8 @@ _(SSWB) _(GSPR) _(SSWG) +// Arrow Bullet +_(AWBT) + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later _(VIEW) diff --git a/src/info/states.h b/src/info/states.h index c99f7b6d1..e824bfce6 100644 --- a/src/info/states.h +++ b/src/info/states.h @@ -3618,3 +3618,6 @@ _(GHORIZ2) _(GHORIZ3) _(GHORIZ4) +// Arrow Bullet +_(ALTSHRINK_ARROWBULLET) + diff --git a/src/k_kart.c b/src/k_kart.c index 1ea4610aa..3f52923dd 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1407,7 +1407,7 @@ void K_SpawnNormalSpeedLines(player_t *player) else if (player->tripwireLeniency) { // Make it pink+blue+big when you can go through tripwire - fast->color = (leveltime & 1) ? SKINCOLOR_LILAC : SKINCOLOR_JAWZ; + fast->color = (K_IsAltShrunk(player)) ? SKINCOLOR_PERIWINKLE : ((leveltime & 1) ? SKINCOLOR_LILAC : SKINCOLOR_JAWZ); fast->colorized = true; fast->renderflags |= RF_ADD; } @@ -10501,8 +10501,17 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } if (player->growshrinktimer == 0) + { player->altshrinktimeshit = 0; + if (player->arrowbullet && (!P_MobjWasRemoved(player->arrowbullet))) + { + P_SetTarget(&player->arrowbullet->target, NULL); + P_RemoveMobj(player->arrowbullet); + P_SetTarget(&player->arrowbullet, NULL); + } + } + if (player->growshrinktimer <= 0) player->growcancel = -1; diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index e08b6c6b8..8a3efe306 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -357,6 +357,7 @@ enum player_e player_growshrinktimer, player_growcancel, player_squishedtimer, + player_arrowbullet, player_rocketsneakertimer, player_invincibilitytimer, player_maxinvincibilitytime, @@ -575,6 +576,7 @@ static const char *const player_opt[] = { "growshrinktimer", "growcancel", "squishedtimer", + "arrowbullet", "rocketsneakertimer", "invincibilitytimer", "maxinvincibilitytime", @@ -1071,6 +1073,9 @@ static int player_get(lua_State *L) case player_squishedtimer: lua_pushinteger(L, plr->squishedtimer); break; + case player_arrowbullet: + LUA_PushUserdata(L, plr->arrowbullet, META_MOBJ); + break; case player_rocketsneakertimer: lua_pushinteger(L, plr->rocketsneakertimer); break; @@ -1836,6 +1841,8 @@ static int player_set(lua_State *L) plr->squishedtimer = squishtimer; break; } + case player_arrowbullet: + return NOSET; case player_rocketsneakertimer: plr->rocketsneakertimer = luaL_checkinteger(L, 3); break; diff --git a/src/p_local.h b/src/p_local.h index 576f60e88..847042a24 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -323,6 +323,7 @@ void P_RunOverlays(void); #define OV_DONTXYSCALE 1<<2 #define OV_DONTROLL 1<<3 #define OV_DONTBAKEOFFSET 1<<4 +#define OV_DONTCOPYANGLE 1<<5 void P_HandleMinecartSegments(mobj_t *mobj); void P_MobjThinker(mobj_t *mobj); diff --git a/src/p_mobj.c b/src/p_mobj.c index acc44cb2c..3d0eaf2d0 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6718,7 +6718,11 @@ void P_RunOverlays(void) mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP) | (mo->target->eflags & MFE_VERTICALFLIP); mo->scale = mo->destscale = FixedMul(mo->target->scale, mo->movefactor); - mo->angle = (mo->target->player ? mo->target->player->drawangle : mo->target->angle) + mo->movedir; + + if (!(mo->threshold & OV_DONTCOPYANGLE)) + { + mo->angle = (mo->target->player ? mo->target->player->drawangle : mo->target->angle) + mo->movedir; + } if (!(mo->threshold & OV_DONTSCREENOFFSET)) { diff --git a/src/p_saveg.c b/src/p_saveg.c index a9dade76e..54291a256 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -686,6 +686,8 @@ static void P_NetSyncPlayers(savebuffer_t *save) SYNC(players[i].growshrinktimer); SYNC(players[i].growcancel); + SYNC(players[i].altshrinktimeshit); + RSYNC(players[i].arrowbullet); SYNC(players[i].rocketsneakertimer); @@ -3776,6 +3778,7 @@ static void P_RelinkPointers(savebuffer_t *save) RELINK(&players[i].currentwaypoint); RELINK(&players[i].nextwaypoint); RELINK(&players[i].shieldtracer); + RELINK(&players[i].arrowbullet); } } diff --git a/src/p_user.c b/src/p_user.c index ac3fe96ca..822ae819e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -64,6 +64,8 @@ #include "k_items.h" #include "g_party.h" +#include "m_easing.h" // Arrow Bullet aura + #include "acs/interface.h" #ifdef HW3SOUND @@ -2315,10 +2317,70 @@ void P_MovePlayer(player_t *player) K_SpawnSparkleTrail(player->mo); } +#define MINAURAPCT (cv_kartaltshrink_arrowbulletthres.value - (FRACUNIT / 4)) +#define AURA_DIFF (max(1, (FRACUNIT << 1) - MINAURAPCT)) + // The Arrow Bullet aura should spawn before you actually enter the Arrow Bullet state. + if (cv_kartaltshrink_arrowbullet.value && K_IsAltShrunk(player) && (K_GetSpeedPercentage(player) >= MINAURAPCT)) + { + fixed_t aura_scalar = FixedDiv(min(max(0, K_GetSpeedPercentage(player) - MINAURAPCT), (FRACUNIT << 1)), AURA_DIFF); + + INT32 auravis = 10 - FixedMul(7, Easing_OutCubic(aura_scalar, 0, FRACUNIT)); + auravis = min(9, max(1, auravis)) << FF_TRANSSHIFT; + + fixed_t aura_scale = Easing_OutCubic(aura_scalar, FRACHALF, FRACUNIT + FRACHALF); + + if ((!player->arrowbullet) || (P_MobjWasRemoved(player->arrowbullet))) + { + mobj_t *newbullet = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_OVERLAY); + + P_SetMobjState(newbullet, S_ALTSHRINK_ARROWBULLET); + + newbullet->movefactor = aura_scale; + newbullet->threshold |= OV_DONTCOPYANGLE; + + P_SetTarget(&newbullet->target, player->mo); + + P_SetTarget(&player->arrowbullet, newbullet); + + player->arrowbullet->angle = K_MomentumAngle(player->mo); + player->arrowbullet->sloperoll = player->mo->sloperoll; + player->arrowbullet->slopepitch = player->mo->slopepitch; + + player->arrowbullet->frame &= ~FF_TRANSMASK; + player->arrowbullet->frame |= auravis; + + player->arrowbullet->dispoffset = min(2, player->mo->dispoffset + 1); + } + else + { + player->arrowbullet->angle = K_MomentumAngle(player->mo); + player->arrowbullet->sloperoll = player->mo->sloperoll; + player->arrowbullet->slopepitch = player->mo->slopepitch; + + player->arrowbullet->movefactor = aura_scale; + + player->arrowbullet->frame &= ~FF_TRANSMASK; + player->arrowbullet->frame |= auravis; + + player->arrowbullet->dispoffset = min(2, player->mo->dispoffset + 1); + } + } + else + { + if ((player->arrowbullet) && (!P_MobjWasRemoved(player->arrowbullet))) + { + P_SetTarget(&player->arrowbullet->target, NULL); + P_RemoveMobj(player->arrowbullet); + P_SetTarget(&player->arrowbullet, NULL); + } + } + if (K_AltShrinkArrowBulletCondition(player)) { - K_SpawnSparkleTrail(player->mo); + K_SpawnNormalSpeedLines(player); } +#undef MINAURAPCT +#undef AURA_DIFF if (player->wipeoutslow > 1 && (leveltime & 1)) K_SpawnWipeoutTrail(player->mo, false);