From f6914942e7bdfce6f99d4995937e8cb4a43d6f33 Mon Sep 17 00:00:00 2001 From: Oni Date: Sat, 29 Jul 2023 18:29:08 +0000 Subject: [PATCH] Merge branch 'dashrings' into 'master' Hardcode MT_DASHRING & MT_RAINBOWDASHRING See merge request KartKrew/Kart!1345 --- src/d_player.h | 4 + src/deh_tables.c | 2 - src/info/actions.h | 1 + src/info/mobjs.h | 8 ++ src/info/sprites.h | 8 ++ src/info/states.h | 20 +++ src/k_kart.c | 4 +- src/k_objects.h | 15 ++ src/objects/CMakeLists.txt | 2 + src/objects/dash-rings.c | 267 ++++++++++++++++++++++++++++++++++++ src/objects/sneaker-panel.c | 122 ++++++++++++++++ src/p_enemy.c | 32 +++++ src/p_inter.c | 5 + src/p_map.c | 11 ++ src/p_mobj.c | 40 +++++- src/p_saveg.c | 3 + src/p_tick.c | 4 + src/p_tick.h | 1 + 18 files changed, 541 insertions(+), 8 deletions(-) create mode 100644 src/objects/dash-rings.c create mode 100644 src/objects/sneaker-panel.c diff --git a/src/d_player.h b/src/d_player.h index bb22235b7..99f7f562b 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -127,6 +127,7 @@ typedef enum // Specific level gimmicks. CR_SLIDING, CR_ZOOMTUBE, + CR_DASHRING, } carrytype_t; // carry typedef enum @@ -684,6 +685,9 @@ struct player_t UINT8 confirmVictim; // Player ID that you dealt damage to UINT8 confirmVictimDelay; // Delay before playing the sound + UINT8 dashRingPullTics; // Timer during which the player is pulled towards a dash ring + UINT8 dashRingPushTics; // Timer during which the player displays effects and has no gravity after being thrust by a dash ring + INT32 interpoints; // BlanKart (port from SRB2Kart CEP): override for number of points earned in intermission UINT32 roundscore; // battle score this round UINT8 emeralds; diff --git a/src/deh_tables.c b/src/deh_tables.c index 633ce0c6f..9d019b0f5 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -163,9 +163,7 @@ actionpointer_t actionpointers[] = #define _(name, upper, ...) {name, "A_"#upper}, #include "info/actions.h" #undef _ - {NULL, "NONE"}, - // This NULL entry must be the last in the list {NULL, NULL}, }; diff --git a/src/info/actions.h b/src/info/actions.h index 845e8ffee..1c2f6fd0b 100644 --- a/src/info/actions.h +++ b/src/info/actions.h @@ -222,3 +222,4 @@ _(A_RoamingShadowThinker, ROAMINGSHADOWTHINKER) _(A_MayonakaArrow, MAYONAKAARROW) _(A_MementosTPParticles, MEMENTOSTPPARTICLES) _(A_ReaperThinker, REAPERTHINKER) +_(A_SpawnSneakerPanel, SPAWNSNEAKERPANEL) diff --git a/src/info/mobjs.h b/src/info/mobjs.h index 448199356..6e804aca1 100644 --- a/src/info/mobjs.h +++ b/src/info/mobjs.h @@ -860,3 +860,11 @@ _(LOOPENDPOINT) _(LOOPCENTERPOINT) _(SCRIPT_THING) + +// Dash Rings +_(DASHRING) +_(RAINBOWDASHRING) + +// Sneaker Panels +_(SNEAKERPANEL) +_(SNEAKERPANELSPAWNER) diff --git a/src/info/sprites.h b/src/info/sprites.h index 3f87570ef..dfbb2f47c 100644 --- a/src/info/sprites.h +++ b/src/info/sprites.h @@ -641,5 +641,13 @@ _(BKAD) // Airdrop Dust _(TRCK) +// Dash Rings +_(RAIR) +// Sneaker Panels +_(BSTP) +_(BSTS) +_(BSTT) + + // 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 21102a637..4870e0e8e 100644 --- a/src/info/states.h +++ b/src/info/states.h @@ -3593,3 +3593,23 @@ _(AIRDROPDUST2) _(SLIPSPARK1) _(SLIPSPARK2) _(SLIPSPARK3) + +// Dash Rings +_(DASHRING_HORIZONTAL) +_(DASHRING_30DEGREES) +_(DASHRING_60DEGREES) +_(DASHRING_VERTICAL) +_(DASHRING_HORIZONTAL_FLASH1) +_(DASHRING_HORIZONTAL_FLASH2) +_(DASHRING_30DEGREES_FLASH1) +_(DASHRING_30DEGREES_FLASH2) +_(DASHRING_60DEGREES_FLASH1) +_(DASHRING_60DEGREES_FLASH2) +_(DASHRING_VERTICAL_FLASH1) +_(DASHRING_VERTICAL_FLASH2) + +// Sneaker Panels +_(SNEAKERPANEL) +_(SNEAKERPANEL_SMALL) +_(SNEAKERPANEL_TINY) +_(SNEAKERPANELSPAWNER) diff --git a/src/k_kart.c b/src/k_kart.c index 56a745b9f..7d5553c21 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3385,7 +3385,6 @@ void K_SquishPlayer(player_t *player, mobj_t *inflictor, mobj_t *source) } player->mo->flags |= MF_NOCLIP; - player->instashield = 15; } @@ -3420,6 +3419,7 @@ INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!! if (player->mo->eflags & MFE_UNDERWATER) player->mo->momz = (117 * player->mo->momz) / 200; + player->mo->momx = player->mo->momy = 0; player->spinouttype = KSPIN_EXPLOSION; @@ -6802,6 +6802,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_UpdateEngineSounds(player); // Thanks, VAda! + Obj_DashRingPlayerThink(player); + // update boost angle if not spun out if (!K_IsPlayerDamaged(player) && !player->wipeoutslow) player->boostangle = player->mo->angle; diff --git a/src/k_objects.h b/src/k_objects.h index ac7bc3f57..1351e36ac 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -15,6 +15,21 @@ void Obj_InitLoopCenter(mobj_t *center); void Obj_LinkLoopAnchor(mobj_t *anchor, mobj_t *center, UINT8 type); void Obj_LoopEndpointCollide(mobj_t *special, mobj_t *toucher); +/* Dash Rings */ +void Obj_RegularDashRingSpawn(mobj_t *mobj); +void Obj_RainbowDashRingSpawn(mobj_t *mobj); +void Obj_DashRingSetup(mobj_t *mobj, mapthing_t *mthing); +void Obj_RainbowDashRingThink(mobj_t *mobj); +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 fd213c5a7..e95643f31 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -1,3 +1,5 @@ target_sources(SRB2SDL2 PRIVATE loops.cpp + dash-rings.c + sneaker-panel.c ) diff --git a/src/objects/dash-rings.c b/src/objects/dash-rings.c new file mode 100644 index 000000000..ac24af20e --- /dev/null +++ b/src/objects/dash-rings.c @@ -0,0 +1,267 @@ +#include "../p_local.h" +#include "../k_kart.h" +#include "../k_objects.h" +#include "../s_sound.h" + +// Dash Rings are scaled by this much relative to the map scale +#define DASHRING_SCALE (3*FRACUNIT/2) + +// Dash Ring angles are defined by their mapthing's args[0] (previously used mapthing->options flags, hence the selections) +#define DASHRING_TYPE_HORIZONTAL 0 +#define DASHRING_TYPE_30DEGREES 1 +#define DASHRING_TYPE_60DEGREES 4 +#define DASHRING_TYPE_VERTICAL 8 + +// Dash Rings must be this far apart for players to interact with them in succession +#define DASHRING_MIN_SPACING_HORIZONTAL (512*FRACUNIT) +#define DASHRING_MIN_SPACING_VERTICAL (384*FRACUNIT) + +// timer values +#define DASHRING_PULL_TICS 5 +#define DASHRING_PUSH_TICS (TICRATE/2) +#define DASHRING_ANTIGRAVITY_TICS 5 + +// base launch speed +#define DASHRING_BASE_LAUNCH_SPEED (48*FRACUNIT) + +// factor of distance traveled per tic while being pulled towards a Dash Ring +#define DASHRING_PULL_FACTOR (FRACUNIT/3) + +static const skincolornum_t ring_colors[] = { + SKINCOLOR_GREY, // 1x + SKINCOLOR_TAN, // 1.25x + SKINCOLOR_YELLOW, // 1.5x + SKINCOLOR_TANGERINE, // 1.75x + SKINCOLOR_KETCHUP, // 2x + SKINCOLOR_MOONSET, // 2.25x + SKINCOLOR_ULTRAMARINE, // 2.5x + +}; + +static const skincolornum_t rainbow_colors[] = { + SKINCOLOR_PINK, + SKINCOLOR_CREAMSICLE, + SKINCOLOR_TAN, + SKINCOLOR_TURTLE, + SKINCOLOR_TURQUOISE, + SKINCOLOR_THISTLE, +}; + +void Obj_RegularDashRingSpawn(mobj_t *mobj) +{ + P_SetScale(mobj, mobj->destscale = FixedMul(mobj->scale, DASHRING_SCALE)); + mobj->renderflags |= RF_SEMIBRIGHT; + + P_SetTarget(&mobj->tracer, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY)); + P_SetTarget(&mobj->tracer->target, mobj); + P_SetMobjState(mobj->tracer, S_DASHRING_HORIZONTAL_FLASH1); + mobj->tracer->color = SKINCOLOR_WHITE; + mobj->tracer->renderflags |= RF_SEMIBRIGHT; +} + +void Obj_RainbowDashRingSpawn(mobj_t *mobj) +{ + P_SetScale(mobj, mobj->destscale = FixedMul(mobj->scale, DASHRING_SCALE)); + mobj->renderflags |= RF_FULLBRIGHT; +} + +void Obj_DashRingSetup(mobj_t *mobj, mapthing_t *mthing) +{ + static const UINT8 numColors = sizeof(rainbow_colors) / sizeof(skincolornum_t); + const UINT8 additionalThrust = mthing->args[1]; + statenum_t ringState, overlayState; + + mobj->extravalue1 = mthing->args[0]; + mobj->cusval = 4 + additionalThrust; + + switch (mobj->extravalue1) + { + case DASHRING_TYPE_30DEGREES: + ringState = S_DASHRING_30DEGREES; + overlayState = S_DASHRING_30DEGREES_FLASH1; + break; + case DASHRING_TYPE_60DEGREES: + ringState = S_DASHRING_60DEGREES; + overlayState = S_DASHRING_60DEGREES_FLASH1; + break; + case DASHRING_TYPE_VERTICAL: + ringState = S_DASHRING_VERTICAL; + overlayState = S_DASHRING_VERTICAL_FLASH1; + break; + case DASHRING_TYPE_HORIZONTAL: + default: + ringState = S_DASHRING_HORIZONTAL; + overlayState = S_DASHRING_HORIZONTAL_FLASH1; + break; + } + + P_SetMobjState(mobj, ringState); + if (!P_MobjWasRemoved(mobj->tracer)) + P_SetMobjState(mobj->tracer, overlayState); + + mobj->spriteyoffset = mobj->info->height >> 1; // I think this is to center the sprite within its hitbox regardless of height + mobj->color = ring_colors[min(additionalThrust, numColors - 1)]; +} + +void Obj_RainbowDashRingThink(mobj_t *mobj) +{ + static const UINT8 numColors = sizeof(rainbow_colors) / sizeof(skincolornum_t); + mobj->color = rainbow_colors[(leveltime / 2) % numColors]; +} + +static boolean DashRingsAreTooClose(mobj_t *ring1, mobj_t *ring2) +{ + if (ring1 == ring2) + return true; + + if ((FixedHypot(ring2->x - ring1->x, ring2->y - ring1->y) < FixedMul(DASHRING_MIN_SPACING_HORIZONTAL, mapobjectscale)) + && (abs(ring1->z - ring2->z) < FixedMul(DASHRING_MIN_SPACING_VERTICAL, mapobjectscale))) + return true; + + return false; +} + +void Obj_DashRingTouch(mobj_t *ring, player_t *player) +{ + if (player->carry != CR_NONE) + { + if (player->carry != CR_DASHRING) // being carried by something else + return; + + if (player->dashRingPullTics > 0) // being pulled into a dash ring already + return; + + if (player->dashRingPushTics > 0 && !P_MobjWasRemoved(player->mo->tracer) && DashRingsAreTooClose(player->mo->tracer, ring)) // dash ring is too close to recently used dash ring + return; + } + + P_SetTarget(&player->mo->tracer, ring); + player->carry = CR_DASHRING; + player->dashRingPullTics = DASHRING_PULL_TICS; + player->dashRingPushTics = 0; +} + +static fixed_t GetPlayerDashRingZ(player_t *player, mobj_t *ring) +{ + return (ring->z + (ring->height >> 1) - (player->mo->height >> 1)); +} + +static void DashRingLaunch(player_t *player, mobj_t *ring) +{ + mobj_t *ghost = P_SpawnGhostMobj(ring); + const fixed_t launchSpeed = FixedMul(DASHRING_BASE_LAUNCH_SPEED * ring->cusval / 4, mapobjectscale); + angle_t pitch; + + ghost->destscale = ring->scale * 8; + ghost->scalespeed = ring->scale / 12; + ghost->old_z = ghost->z += P_MobjFlip(ring) * FixedMul(ghost->spriteyoffset, ghost->scale); // apply sprite offset to physical position instead, so ghost is centered + ghost->spriteyoffset = 0; + + P_MoveOrigin(player->mo, ring->x, ring->y, GetPlayerDashRingZ(player, ring)); + player->dashRingPullTics = 0; + player->dashRingPushTics = DASHRING_PUSH_TICS; + + player->mo->rollangle = 0; + player->flashing = 0; + player->fastfall = 0; + + switch (ring->extravalue1) + { + case DASHRING_TYPE_30DEGREES: + pitch = 30 * ANG1; + break; + case DASHRING_TYPE_60DEGREES: + pitch = 60 * ANG1; + break; + case DASHRING_TYPE_VERTICAL: + pitch = 90 * ANG1; + break; + case DASHRING_TYPE_HORIZONTAL: + default: + pitch = 0; + break; + } + + P_InstaThrust(player->mo, ring->angle, P_ReturnThrustX(NULL, pitch, launchSpeed)); + player->mo->momz = P_MobjFlip(ring) * P_ReturnThrustY(NULL, pitch, launchSpeed); + + S_StartSound(player->mo, ring->info->seesound); +} + +static void RegularDashRingLaunch(player_t *player, mobj_t *ring) +{ + player->springstars = TICRATE/2; + player->springcolor = ring->color; + DashRingLaunch(player, ring); +} + +static void RainbowDashRingLaunch(player_t *player, mobj_t *ring) +{ + player->mo->eflags &= ~MFE_SPRUNG; + player->trickpanel = 1; + player->pflags |= PF_TRICKDELAY; + K_DoPogoSpring(player->mo, 0, 0); + DashRingLaunch(player, ring); +} + +void Obj_DashRingPlayerThink(player_t *player) +{ + if (player->carry != CR_DASHRING) + return; + + if (player->dashRingPullTics > 0) + { + mobj_t *ring = player->mo->tracer; + + if (P_MobjWasRemoved(player->mo->tracer)) + { + player->carry = CR_NONE; + player->dashRingPullTics = 0; + } + else + { + player->mo->momx = FixedMul(DASHRING_PULL_FACTOR, ring->x - player->mo->x); + player->mo->momy = FixedMul(DASHRING_PULL_FACTOR, ring->y - player->mo->y); + player->mo->momz = FixedMul(DASHRING_PULL_FACTOR, GetPlayerDashRingZ(player, ring) - player->mo->z); + player->mo->rollangle = (angle_t)FixedMul(DASHRING_PULL_FACTOR, (fixed_t)player->mo->rollangle); + + if (--player->dashRingPullTics == 0) + { + if (ring->type == MT_DASHRING) + { + RegularDashRingLaunch(player, ring); + } + else + { + RainbowDashRingLaunch(player, ring); + } + } + } + } + + if (player->dashRingPushTics > 0) + { + if (leveltime & 1) + { + mobj_t *ghost = P_SpawnGhostMobj(player->mo); + ghost->colorized = true; + ghost->fuse = 3; + } + + if (--player->dashRingPushTics == 0) + { + player->carry = CR_NONE; + P_SetTarget(&player->mo->tracer, NULL); + } + } +} + +boolean Obj_DashRingPlayerHasNoGravity(player_t *player) +{ + if (player->dashRingPullTics > 0) + return true; + + if (player->dashRingPushTics >= DASHRING_PUSH_TICS - DASHRING_ANTIGRAVITY_TICS) + return true; + + return false; +} 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 fbea63c8e..bddd58cf1 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -11953,3 +11953,35 @@ void A_ReaperThinker(void *thing) } } } + +// 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_inter.c b/src/p_inter.c index e8159c9c2..a75db91d6 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -921,6 +921,11 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } return; + case MT_DASHRING: + case MT_RAINBOWDASHRING: + Obj_DashRingTouch(special, player); + return; + default: // SOC or script pickup P_SetTarget(&special->target, toucher); break; diff --git a/src/p_map.c b/src/p_map.c index bc8e6a543..1ec06175a 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -874,6 +874,17 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) return K_FallingRockCollide(thing, g_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 13a0f703f..58a633a0b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -133,11 +133,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) { @@ -1175,6 +1174,11 @@ fixed_t P_GetMobjGravity(mobj_t *mo) { gravityadd = (5*gravityadd)/2; } + + if (mo->player->carry == CR_DASHRING && Obj_DashRingPlayerHasNoGravity(mo->player)) + { + gravityadd = 0; + } } else { @@ -10160,6 +10164,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj) trail->color = mobj->color; } break; + case MT_RAINBOWDASHRING: + Obj_RainbowDashRingThink(mobj); + break; default: // check mobj against possible water content, before movement code P_MobjCheckWater(mobj); @@ -10986,6 +10993,9 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) case MT_EMBLEM: thing->shadowscale = FRACUNIT/3; break; + case MT_SNEAKERPANEL: + thing->shadowscale = 0; + break; default: if (thing->flags & (MF_ENEMY|MF_BOSS)) thing->shadowscale = FRACUNIT; @@ -11484,6 +11494,15 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) break; case MT_TUMBLEGEM: mobj->color = P_RandomKey(numskincolors - 1) + 1; + case MT_DASHRING: + Obj_RegularDashRingSpawn(mobj); + break; + case MT_RAINBOWDASHRING: + Obj_RainbowDashRingSpawn(mobj); + break; + case MT_SNEAKERPANEL: + Obj_SneakerPanelSpawn(mobj); + break; default: break; } @@ -13731,6 +13750,17 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean if (mapnamespace == MNS_SRB2KART) mobj->flags ^= MF_NOGRAVITY; } + case MT_DASHRING: + case MT_RAINBOWDASHRING: + { + Obj_DashRingSetup(mobj, mthing); + break; + } + case MT_SNEAKERPANEL: + { + Obj_SneakerPanelSetup(mobj, mthing); + break; + } default: break; } diff --git a/src/p_saveg.c b/src/p_saveg.c index d35d1b3ad..ceaed57a3 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -712,6 +712,9 @@ static void P_NetSyncPlayers(savebuffer_t *save) SYNC(players[i].karmapoints); SYNC(players[i].wanted); + SYNC(players[i].dashRingPullTics); + SYNC(players[i].dashRingPushTics); + SYNC(players[i].glanceDir); SYNC(players[i].breathTimer); diff --git a/src/p_tick.c b/src/p_tick.c index 503689286..e9252c5ec 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -50,6 +50,7 @@ #endif tic_t leveltime; +boolean thinkersCompleted; UINT32 thinker_era = 0; @@ -660,6 +661,8 @@ void P_Ticker(boolean run) { INT32 i; + thinkersCompleted = false; + // Increment jointime and quittime even if paused for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) @@ -782,6 +785,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 for (i = 0; i < MAXPLAYERS; i++) diff --git a/src/p_tick.h b/src/p_tick.h index 766b8e50d..6a187e7d5 100644 --- a/src/p_tick.h +++ b/src/p_tick.h @@ -21,6 +21,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);