diff --git a/src/deh_tables.c b/src/deh_tables.c index 0f0fe7a0f..69fe78828 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -301,6 +301,7 @@ actionpointer_t actionpointers[] = {{A_InvincSparkleRotate}, "A_INVINCSPARKLEROTATE"}, {{A_SpawnItemDebrisCloud}, "A_SPAWNITEMDEBRISCLOUD"}, {{A_RingShooterFace}, "A_RINGSHOOTERFACE"}, + {{A_SpawnSneakerPanel}, "A_SPAWNSNEAKERPANEL"}, {{NULL}, "NONE"}, @@ -4250,6 +4251,12 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_DASHRING_VERTICAL_FLASH1", "S_DASHRING_VERTICAL_FLASH2", + // Sneaker Panels + "S_SNEAKERPANEL", + "S_SNEAKERPANEL_SMALL", + "S_SNEAKERPANEL_TINY", + "S_SNEAKERPANELSPAWNER", + // Various plants "S_SONICBUSH", @@ -5624,6 +5631,10 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_DASHRING", "MT_RAINBOWDASHRING", + // Sneaker Panels + "MT_SNEAKERPANEL", + "MT_SNEAKERPANELSPAWNER", + // Various plants "MT_SONICBUSH", diff --git a/src/info.c b/src/info.c index 558f111ad..23b98bc66 100644 --- a/src/info.c +++ b/src/info.c @@ -720,6 +720,11 @@ char sprnames[NUMSPRITES + 1][5] = // Dash Rings "RAIR", + // Sneaker Panels + "BSTP", + "BSTS", + "BSTT", + // Various plants "SBUS", @@ -4938,6 +4943,12 @@ state_t states[NUMSTATES] = {SPR_NULL, 0, TICRATE/3 - 2, {NULL}, 0, 0, S_DASHRING_VERTICAL_FLASH2}, // S_DASHRING_VERTICAL_FLASH1 {SPR_RAIR, FF_ADD|3, 2, {NULL}, 0, 0, S_DASHRING_VERTICAL_FLASH1}, // S_DASHRING_VERTICAL_FLASH2 + // Sneaker Panels + {SPR_BSTP, FF_ANIMATE|FF_GLOBALANIM|FF_FLOORSPRITE|FF_FULLBRIGHT, -1, {NULL}, 5, 2, S_SNEAKERPANEL}, // S_SNEAKERPANEL + {SPR_BSTS, FF_ANIMATE|FF_GLOBALANIM|FF_FLOORSPRITE|FF_FULLBRIGHT, -1, {NULL}, 5, 2, S_SNEAKERPANEL_SMALL}, // S_SNEAKERPANEL_SMALL + {SPR_BSTT, FF_ANIMATE|FF_GLOBALANIM|FF_FLOORSPRITE|FF_FULLBRIGHT, -1, {NULL}, 5, 2, S_SNEAKERPANEL_TINY}, // S_SNEAKERPANEL_TINY + {SPR_NULL, 0, 65, {A_SpawnSneakerPanel}, 0, 0, S_SNEAKERPANELSPAWNER}, // S_SNEAKERPANELSPAWNER + // Various plants {SPR_SBUS, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SONICBUSH @@ -26803,6 +26814,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SNEAKERPANEL + 510, // doomednum + S_SNEAKERPANEL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // 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 + 91*FRACUNIT, // radius + 16*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_ENEMY, // flags -- NOTE: IIRC MF_ENEMY was added by mappers to make conveyor belt setups more convenient + S_NULL // raisestate + }, + + { // MT_SNEAKERPANELSPAWNER + 511, // doomednum + S_SNEAKERPANELSPAWNER, // spawnstate + 0, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // 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 + 32*FRACUNIT, // radius + 60*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOBLOCKMAP|MF_SCENERY|MF_NOCLIPTHING, // flags + S_NULL // raisestate + }, + { // MT_SONICBUSH, 715, // doomednum S_SONICBUSH, // spawnstate diff --git a/src/info.h b/src/info.h index 749b43c17..a5e84bf56 100644 --- a/src/info.h +++ b/src/info.h @@ -294,6 +294,7 @@ enum actionnum A_INVINCSPARKLEROTATE, A_SPAWNITEMDEBRISCLOUD, A_RINGSHOOTERFACE, + A_SPAWNSNEAKERPANEL, NUMACTIONS }; @@ -568,6 +569,7 @@ void A_FlameShieldPaper(); void A_InvincSparkleRotate(); void A_SpawnItemDebrisCloud(); void A_RingShooterFace(); +void A_SpawnSneakerPanel(); extern boolean actionsoverridden[NUMACTIONS]; @@ -1271,6 +1273,11 @@ typedef enum sprite // Dash Rings SPR_RAIR, + // Sneaker Panels + SPR_BSTP, + SPR_BSTS, + SPR_BSTT, + // Various plants SPR_SBUS, @@ -5386,6 +5393,12 @@ typedef enum state S_DASHRING_VERTICAL_FLASH1, S_DASHRING_VERTICAL_FLASH2, + // Sneaker Panels + S_SNEAKERPANEL, + S_SNEAKERPANEL_SMALL, + S_SNEAKERPANEL_TINY, + S_SNEAKERPANELSPAWNER, + // Various plants S_SONICBUSH, @@ -6795,6 +6808,10 @@ typedef enum mobj_type MT_DASHRING, MT_RAINBOWDASHRING, + // Sneaker Panels + MT_SNEAKERPANEL, + MT_SNEAKERPANELSPAWNER, + // Various plants MT_SONICBUSH, diff --git a/src/k_objects.h b/src/k_objects.h index ab4d259e6..26f6d14ac 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -193,6 +193,12 @@ void Obj_DashRingTouch(mobj_t *mobj, player_t *player); void Obj_DashRingPlayerThink(player_t *player); boolean Obj_DashRingPlayerHasNoGravity(player_t *player); +/* Sneaker Panels */ +void Obj_SneakerPanelSpriteScale(mobj_t *mobj); +void Obj_SneakerPanelSpawn(mobj_t *mobj); +void Obj_SneakerPanelSetup(mobj_t *mobj, mapthing_t *mthing); +void Obj_SneakerPanelCollide(mobj_t *pad, mobj_t *mo); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 2e0f65913..4eaa1d2f5 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -26,4 +26,5 @@ target_sources(SRB2SDL2 PRIVATE powerup-aura.cpp symbol.c dash-rings.c + sneaker-panel.c ) diff --git a/src/objects/sneaker-panel.c b/src/objects/sneaker-panel.c new file mode 100644 index 000000000..dc5c96c50 --- /dev/null +++ b/src/objects/sneaker-panel.c @@ -0,0 +1,122 @@ +#include "../r_main.h" +#include "../p_slopes.h" +#include "../p_local.h" +#include "../k_kart.h" + +#define SNEAKERPANEL_RADIUS (64*FRACUNIT) + +void Obj_SneakerPanelSpriteScale(mobj_t *mobj) +{ + statenum_t newState; + fixed_t spriteScale; + + if (mobj->scale == mobj->movefactor) + return; + + mobj->movefactor = mobj->scale; + + if (mobj->scale > FRACUNIT >> 1) + { + newState = S_SNEAKERPANEL; + spriteScale = FRACUNIT; + } + else if (mobj->scale > FRACUNIT >> 2) + { + newState = S_SNEAKERPANEL_SMALL; + spriteScale = FRACUNIT << 1; + } + else + { + newState = S_SNEAKERPANEL_TINY; + spriteScale = FRACUNIT << 2; + } + + if (((statenum_t)(mobj->state - states)) != newState) + { + P_SetMobjState(mobj, newState); + mobj->spritexscale = mobj->spriteyscale = spriteScale; + } +} + +void Obj_SneakerPanelSpawn(mobj_t *mobj) +{ + mobj->renderflags |= RF_OBJECTSLOPESPLAT | RF_NOSPLATBILLBOARD; + Obj_SneakerPanelSpriteScale(mobj); +} + +void Obj_SneakerPanelSetup(mobj_t *mobj, mapthing_t *mthing) +{ + if (mthing->options & MTF_OBJECTFLIP) + { + mobj->eflags |= MFE_VERTICALFLIP; + mobj->flags2 |= MF2_OBJECTFLIP; + } + P_TryMove(mobj, mobj->x, mobj->y, true, NULL); // sets standingslope + Obj_SneakerPanelSpriteScale(mobj); +} + +void Obj_SneakerPanelCollide(mobj_t *panel, mobj_t *mo) +{ + pslope_t *slope = panel->standingslope; + player_t *player = mo->player; + fixed_t playerTop = mo->z + mo->height, playerBottom = mo->z; + fixed_t panelTop, panelBottom, dist, x, y, radius; + angle_t angle; + + // only players can boost! + if (player == NULL) + return; + + // these aren't aerial boosters, so you do need to be on the ground + if (!P_IsObjectOnGround(mo)) + return; + + // player needs to have the same gravflip status as the panel + if ((panel->eflags & MFE_VERTICALFLIP) != (mo->eflags & MFE_VERTICALFLIP)) + return; + + // find the x and y coordinates of the player relative to the booster's angle + dist = R_PointToDist2(panel->x, panel->y, mo->x, mo->y); + angle = R_PointToAngle2(panel->x, panel->y, mo->x, mo->y) - panel->angle; + x = P_ReturnThrustX(NULL, angle, dist); + y = P_ReturnThrustY(NULL, angle, dist); + + // check that these coordinates fall within the square panel + radius = FixedMul(SNEAKERPANEL_RADIUS, panel->scale); + + if (x < -radius || x > radius || y < -radius || y > radius) + return; // out of bounds + + // check that the player is within reasonable vertical bounds + if (slope == NULL) + { + panelTop = panel->z + panel->height; + panelBottom = panel->z; + } + else + { + x = P_ReturnThrustX(NULL, slope->xydirection, panel->radius); + y = P_ReturnThrustY(NULL, slope->xydirection, panel->radius); + panelTop = P_GetSlopeZAt(slope, panel->x + x, panel->y + y); + panelBottom = P_GetSlopeZAt(slope, panel->x - x, panel->y - y); + + if (panelTop < panelBottom) + { + // variable swap + panelTop = panelTop + panelBottom; + panelBottom = panelTop - panelBottom; + panelTop = panelTop - panelBottom; + } + } + + if ((playerBottom > panelTop) || (playerTop < panelBottom)) + return; + + // boost! + if (player->floorboost == 0) + player->floorboost = 3; + else + player->floorboost = 2; + + K_DoSneaker(player, 0); +} diff --git a/src/p_enemy.c b/src/p_enemy.c index 478a7441b..2013a19dd 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -330,6 +330,7 @@ void A_FlameShieldPaper(mobj_t *actor); void A_InvincSparkleRotate(mobj_t *actor); void A_SpawnItemDebrisCloud(mobj_t *actor); void A_RingShooterFace(mobj_t *actor); +void A_SpawnSneakerPanel(mobj_t *actor); //for p_enemy.c @@ -13661,6 +13662,11 @@ A_SpawnItemDebrisCloud (mobj_t *actor) fixed_t kartspeed; fixed_t fade; + if (LUA_CallAction(A_SPAWNITEMDEBRISCLOUD, (actor))) + { + return; + } + if (target == NULL || target->player == NULL) { return; @@ -13721,7 +13727,8 @@ A_SpawnItemDebrisCloud (mobj_t *actor) } } -// sets the actor's +// Assumes the actor is the screen of a Ring Shooter +// Changes the screen to display the WANTED icon of the Ring Shooter's owner, stretching it to match the screen's dimensions // vars do nothing void A_RingShooterFace(mobj_t *actor) { @@ -13732,3 +13739,35 @@ void A_RingShooterFace(mobj_t *actor) Obj_UpdateRingShooterFace(actor); } + +// Function: A_SpawnSneakerPanel +// +// Description: Spawns a sneaker panel object relative to the location of the actor +// +// var1: +// var1 >> 16 = x offset +// var1 & 65535 = y offset +// var2: +// var2 >> 16 = z +// var2 & 65535 = unused +// +void A_SpawnSneakerPanel(mobj_t *actor) +{ + INT16 x, y, z; + mobj_t *mo; + INT32 locvar1 = var1; + INT32 locvar2 = var2; + + if (LUA_CallAction(A_SPAWNSNEAKERPANEL, actor)) + { + return; + } + + x = (INT16)(locvar1 >> 16); + y = (INT16)(locvar1 & 65535); + z = (INT16)(locvar2 >> 16); + + mo = P_SpawnMobjFromMobj(actor, x << FRACBITS, y << FRACBITS, z << FRACBITS, MT_SNEAKERPANEL); + mo->angle = actor->angle; + Obj_SneakerPanelSpriteScale(mo); +} diff --git a/src/p_map.c b/src/p_map.c index 16820869b..0882f7187 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1151,6 +1151,17 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) return K_FallingRockCollide(thing, tm.thing) ? BMIT_CONTINUE : BMIT_ABORT; } + if (thing->type == MT_SNEAKERPANEL) + { + Obj_SneakerPanelCollide(thing, tm.thing); + return BMIT_CONTINUE; + } + else if (tm.thing->type == MT_SNEAKERPANEL) + { + Obj_SneakerPanelCollide(tm.thing, thing); + return BMIT_CONTINUE; + } + //} if ((thing->type == MT_SPRINGSHELL || thing->type == MT_YELLOWSHELL) && thing->health > 0 diff --git a/src/p_mobj.c b/src/p_mobj.c index 9da8f043b..642630cdd 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -128,11 +128,10 @@ static void P_SetupStateAnimation(mobj_t *mobj, state_t *st) if (st->frame & FF_GLOBALANIM) { - // Attempt to account for the pre-ticker for objects spawned on load - if (!leveltime) return; - - mobj->anim_duration -= (leveltime + 2) % st->var2; // Duration synced to timer - mobj->frame += ((leveltime + 2) / st->var2) % (animlength + 1); // Frame synced to timer (duration taken into account) + mobj->anim_duration -= (leveltime % st->var2); // Duration synced to timer + mobj->frame += (leveltime / st->var2) % (animlength + 1); // Frame synced to timer (duration taken into account) + if (!thinkersCompleted) // objects spawned BEFORE (or during) thinkers will think during this tic... + mobj->anim_duration++; // ...so increase the duration of their current frame by 1 to sync with objects spawned AFTER thinkers } else if (st->frame & FF_RANDOMANIM) { @@ -10446,6 +10445,9 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) case MT_DRIFTCLIP: thing->shadowscale = FRACUNIT/3; break; + case MT_SNEAKERPANEL: + thing->shadowscale = 0; + break; default: if (thing->flags & (MF_ENEMY|MF_BOSS)) thing->shadowscale = FRACUNIT; @@ -11002,6 +11004,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_RAINBOWDASHRING: Obj_RainbowDashRingSpawn(mobj); break; + case MT_SNEAKERPANEL: + Obj_SneakerPanelSpawn(mobj); + break; default: break; } @@ -13582,6 +13587,11 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj) Obj_DashRingSetup(mobj, mthing); break; } + case MT_SNEAKERPANEL: + { + Obj_SneakerPanelSetup(mobj, mthing); + break; + } default: break; } diff --git a/src/p_tick.c b/src/p_tick.c index 7b7a64056..981e40fa2 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -48,6 +48,7 @@ #endif tic_t leveltime; +boolean thinkersCompleted; INT32 P_AltFlip(INT32 n, tic_t tics) { @@ -685,6 +686,8 @@ void P_Ticker(boolean run) quake_t *quake = NULL; INT32 i; + thinkersCompleted = false; + // Increment jointime and quittime even if paused for (i = 0; i < MAXPLAYERS; i++) { @@ -794,6 +797,7 @@ void P_Ticker(boolean run) ps_thinkertime = I_GetPreciseTime(); P_RunThinkers(); ps_thinkertime = I_GetPreciseTime() - ps_thinkertime; + thinkersCompleted = true; // Run any "after all the other thinkers" stuff { diff --git a/src/p_tick.h b/src/p_tick.h index a17a5358c..d05813c1c 100644 --- a/src/p_tick.h +++ b/src/p_tick.h @@ -25,6 +25,7 @@ extern "C" { #endif extern tic_t leveltime; +extern boolean thinkersCompleted; // Called by G_Ticker. Carries out all thinking of enemies and players. void Command_Numthinkers_f(void);