Add Flipover damage, change Invincibility interactions

This commit is contained in:
Anonimus 2025-05-31 05:10:46 -04:00
parent 83bf2d59c5
commit 27ea5021b4
14 changed files with 167 additions and 29 deletions

View file

@ -110,6 +110,8 @@ typedef enum
PF_SHRINKME = 1<<25, // "Shrink me" cheat preference
PF_SHRINKACTIVE = 1<<26, // "Shrink me" cheat is in effect. (Can't be disabled mid-race)
PF_JUSTFLIPPED = 1<<27, // Just got flipped over, handle the bump interaction.
// up to 1<<29 is free
PF_ATTACKDOWN = 1<<30, // For lua compat, don't use!
PF_SLIDING = 1<<31, // For lua compat, don't use!
@ -619,6 +621,10 @@ struct player_t
UINT16 flashing;
UINT16 spinouttimer; // Spin-out from a banana peel or oil slick (was "pw_bananacam")
UINT8 spinouttype; // Determines the mode of spinout/wipeout, see kartspinoutflags_t
UINT16 flipovertimer; // Flipped over by a player using Invincibility.
angle_t flipoverangle; // Movement angle for a flipped-over player.
UINT8 instashield; // Instashield no-damage animation timer
UINT8 wipeoutslow; // Timer before you slowdown when getting wiped out
UINT8 justbumped; // Prevent players from endlessly bumping into each other

View file

@ -1039,6 +1039,7 @@ struct int_const_s const INT_CONST[] = {
{"DMG_SQUISH",DMG_SQUISH},
{"DMG_VOLTAGE",DMG_VOLTAGE},
{"DMG_KARMA",DMG_KARMA},
{"DMG_FLIPOVER",DMG_FLIPOVER},
//// Death types
{"DMG_INSTAKILL",DMG_INSTAKILL},
{"DMG_DEATHPIT",DMG_DEATHPIT},

View file

@ -786,6 +786,9 @@ _(kc6c)
_(kc6d)
_(kc6e)
// MKDS sounds
_(mdse8)
// SRB2kart
_(slip)
_(screec)

View file

@ -706,12 +706,12 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
}
else if (t1Condition == true && t2Condition == false)
{
P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT);
P_DamageMobj(t2, t1, t1, 1, DMG_FLIPOVER);
return true;
}
else if (t1Condition == false && t2Condition == true)
{
P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT);
P_DamageMobj(t1, t2, t2, 1, DMG_FLIPOVER);
return true;
}

View file

@ -1896,6 +1896,13 @@ fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against)
return weight;
}
// Checks if the bump interaction was a flip-over.
static boolean K_CheckMobjFlippedOver(mobj_t* mobj1, mobj_t* mobj2)
{
return ((mobj1->player && (mobj1->player->pflags & PF_JUSTFLIPPED)) ||
(mobj2->player && (mobj2->player->pflags & PF_JUSTFLIPPED)));
}
boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid)
{
mobj_t *fx;
@ -1954,6 +1961,19 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean sol
return false;
}
// Don't bump if you're flipping over
if (mobj1->player && mobj1->player->flipovertimer && (!(mobj1->player->pflags & PF_JUSTFLIPPED)))
{
mobj1->player->justbumped = bumptime;
return false;
}
if (mobj2->player && mobj2->player->flipovertimer && (!(mobj2->player->pflags & PF_JUSTFLIPPED)))
{
mobj2->player->justbumped = bumptime;
return false;
}
mass1 = K_GetMobjWeight(mobj1, mobj2);
if (solid == true && mass1 > 0)
@ -2040,7 +2060,7 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean sol
// Do the bump fx when we've CONFIRMED we can bump.
if ((mobj1->player && mobj1->player->itemtype == KITEM_BUBBLESHIELD) || (mobj2->player && mobj2->player->itemtype == KITEM_BUBBLESHIELD))
S_StartSound(mobj1, sfx_s3k44);
else
else if (!K_CheckMobjFlippedOver(mobj1, mobj2))
S_StartSound(mobj1, sfx_s3k49);
fx = P_SpawnMobj(mobj1->x/2 + mobj2->x/2, mobj1->y/2 + mobj2->y/2, mobj1->z/2 + mobj2->z/2, MT_BUMP);
@ -2065,6 +2085,15 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean sol
P_PlayerRingBurst(mobj1->player, 1);
}
if (mobj1->player->pflags & PF_JUSTFLIPPED)
{
S_StartSound(mobj2, sfx_mdse8);
mobj1->player->flipoverangle = R_PointToAngle2(0,0,mobj1->player->rmomx,mobj1->player->rmomy);
P_InstaThrust(mobj1, mobj1->player->flipoverangle, FixedMul(FLIPOVERSPEED, mobj1->scale));
mobj1->momz = FixedMul(FlipOverZMomentum(gravity), mobj1->scale);
mobj1->player->pflags &= ~PF_JUSTFLIPPED;
}
if (mobj1->player->spinouttimer)
{
mobj1->player->wipeoutslow = wipeoutslowtime+1;
@ -2091,6 +2120,15 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean sol
mobj2->player->spinouttimer = max(wipeoutslowtime+1, mobj2->player->spinouttimer);
//mobj2->player->spinouttype = KSPIN_WIPEOUT; // Enforce type
}
if (mobj2->player->pflags & PF_JUSTFLIPPED)
{
S_StartSound(mobj1, sfx_mdse8);
mobj2->player->flipoverangle = R_PointToAngle2(0,0,mobj2->player->rmomx,mobj2->player->rmomy);
P_InstaThrust(mobj2, mobj2->player->flipoverangle, FixedMul(FLIPOVERSPEED, mobj2->scale));
mobj2->momz = FixedMul(FlipOverZMomentum(gravity), mobj2->scale);
mobj2->player->pflags &= ~PF_JUSTFLIPPED;
}
}
return true;
@ -3878,7 +3916,10 @@ SINT8 K_GetForwardMove(player_t *player)
}
}
if ((player->exiting || mapreset) || player->pflags & PF_STASIS || player->spinouttimer) // pw_introcam?
if ((player->exiting || mapreset) ||
player->pflags & PF_STASIS ||
player->spinouttimer ||
player->flipovertimer) // pw_introcam?
{
forwardmove = 0;
if (player->sneakertimer)
@ -4088,6 +4129,19 @@ void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 typ
P_SetPlayerMobjState(player->mo, S_KART_SPINOUT);
}
void K_FlipPlayer(player_t *player, mobj_t *inflictor, mobj_t *source)
{
(void)inflictor;
(void)source;
K_DirectorFollowAttack(player, inflictor, source);
K_StatPlayerHit(player, source ? source->player : NULL);
player->flipovertimer = 1;
player->pflags |= PF_JUSTFLIPPED;
P_SetPlayerMobjState(player->mo, S_KART_SPINOUT);
}
static void K_RemoveGrowShrink(player_t *player)
{
if (player->mo && !P_MobjWasRemoved(player->mo))
@ -4194,6 +4248,11 @@ INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A
return ringburst;
}
boolean K_IsPlayerDamaged(player_t *player)
{
return ((player->spinouttimer > 0) || (player->flipovertimer > 0));
}
void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers)
{
if (!(gametyperules & GTR_BUMPERS))
@ -7398,7 +7457,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
K_UpdateEngineSounds(player); // Thanks, VAda!
// update boost angle if not spun out
if (!player->spinouttimer && !player->wipeoutslow)
if (!K_IsPlayerDamaged(player) && !player->wipeoutslow)
player->boostangle = player->mo->angle;
K_GetKartBoostPower(player);
@ -7473,7 +7532,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
// Make ABSOLUTELY SURE that your flashing tics don't get set WHILE you're still in hit animations.
if (player->spinouttimer != 0
|| player->wipeoutslow != 0
|| player->squishedtimer != 0)
|| player->squishedtimer != 0
|| player->flipovertimer != 0)
{
player->flashing = K_GetKartFlashing(player);
}
@ -7482,6 +7542,22 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->flashing--;
}
if (player->flipovertimer)
{
player->flipovertimer++;
// Kill off any extra damage values.
player->spinouttimer = 0;
player->wipeoutslow = 0;
if (P_IsObjectOnGround(player->mo) &&
((INT32)(player->flipovertimer - 1) > 2))
{
player->flipovertimer = 0;
player->mo->rollangle = 0;
}
}
if (player->spinouttimer)
{
if ((P_IsObjectOnGround(player->mo)
@ -10009,12 +10085,12 @@ static void K_AdjustPlayerFriction(player_t *player)
player->mo->movefactor = 32;
}
// Wipeout slowdown
if (player->speed > 0 && player->spinouttimer && player->wipeoutslow)
// Wipeout slowdown, or getting flipped over
if ((player->speed > 0 && player->spinouttimer && player->wipeoutslow) || (player->flipovertimer))
{
if (player->offroad)
player->mo->friction -= 4912;
if (player->wipeoutslow == 1)
if ((player->wipeoutslow == 1) || (player->flipovertimer))
player->mo->friction -= 9824;
}
}

View file

@ -37,6 +37,31 @@ Make sure this matches the actual number of states
#define FLAMESTOREMAX TICRATE*2
// Fixed distance of a flipover.
#define FLIPOVERDIST (112<<FRACBITS)
// Time (in tics) of a flipover.
// (TICRATE * (0.56666 * FRACUNIT))
#define FLIPOVERTICS 19
#define FLIPOVERHALFTICS (FLIPOVERTICS>>1)
#define FLIPOVER_90DEGTIME ((FLIPOVERTICS>>2)+1) // 0.1666 * FRACUNIT
// Angle movement for a flipover
// ANGLE_90 / FLIPOVER_90DEGTIME
// Precalculating it here for some slight optimization.
#define FLIPOVERANG 0x0CCCCCCC
#define FLIPOVERSPEED (FLIPOVERDIST / FLIPOVERTICS)
#define FLIPOVERHEIGHT (32 << FRACBITS)
// By the middle of a jump arc, you'll reach the apex.
#define FlipOverZMomentum(grav) \
((FLIPOVERHEIGHT + \
((grav >> 1) * (FLIPOVERHALFTICS * FLIPOVERHALFTICS))) / \
FLIPOVERHALFTICS)
// Precalculated constants for stacked boost diminishing
// *Somewhat* matches old calc but doesn't use arrays, which makes it faster and more memory efficent
#define DIMINISHPARAM K_RAGuard(cv_kartstacking_diminishparam)
@ -153,7 +178,9 @@ void K_AwardPlayerRings(player_t *player, UINT16 rings, boolean overload);
void K_DoInstashield(player_t *player);
void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved);
void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type);
void K_FlipPlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
boolean K_IsPlayerDamaged(player_t *player);
void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers);
void K_DestroyBumpers(player_t *player, UINT8 amount);
void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount);

View file

@ -31,7 +31,9 @@ void K_StatTicker(void)
if (p->position == spbplace)
kartstats.spbtargettime++;
if (max(p->spinouttimer, p->wipeoutslow) > 0)
if (p->flipovertimer > 0)
kartstats.spinouttime++;
else if (max(p->spinouttimer, p->wipeoutslow) > 0)
kartstats.spinouttime++;
}
}

View file

@ -11324,7 +11324,7 @@ void A_RandomShadowFrame(mobj_t *actor)
if (actor->target && !actor->target->player->flashing
&& !actor->target->player->invincibilitytimer
&& !actor->target->player->growshrinktimer
&& !actor->target->player->spinouttimer
&& !K_IsPlayerDamaged(actor->target->player)
&& P_IsObjectOnGround(actor->target)
&& actor->z == actor->target->z)
{
@ -11369,7 +11369,7 @@ void A_RoamingShadowThinker(mobj_t *actor)
if (actor->target && !actor->target->player->flashing
&& !actor->target->player->invincibilitytimer
&& !actor->target->player->growshrinktimer
&& !actor->target->player->spinouttimer)
&& !K_IsPlayerDamaged(actor->target->player))
{
// send them flying and spawn the WIND!
P_InstaThrust(actor->target, 0, 0);
@ -11611,7 +11611,7 @@ void A_ReaperThinker(mobj_t *actor)
if (!(actor->target == targetplayermo && actor->target && !actor->target->player->flashing
&& !actor->target->player->invincibilitytimer
&& !actor->target->player->growshrinktimer
&& !actor->target->player->spinouttimer))
&& !K_IsPlayerDamaged(actor->target->player)))
P_SetTarget(&actor->target, actor->hnext);
// if the above isn't correct, then we should go back to targetting waypoints or something.
}

View file

@ -127,7 +127,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
{
// Invulnerable
if (player->flashing > 0
|| player->spinouttimer > 0
|| K_IsPlayerDamaged(player)
|| player->squishedtimer > 0
|| player->invincibilitytimer > 0
|| player->growshrinktimer > 0
@ -2055,7 +2055,7 @@ static UINT8 P_ShouldHookDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sourc
status = shouldDamage;
if (shouldSpin > 0 && ((type == DMG_NORMAL) || (type == DMG_WIPEOUT)))
if (shouldSpin > 0 && ((type == DMG_NORMAL) || (type == DMG_WIPEOUT) || (type == DMG_FLIPOVER)))
status = shouldSpin;
else if (shouldExplode > 0 && ((type == DMG_EXPLODE) || (type == DMG_KARMA)))
status = shouldExplode;
@ -2266,7 +2266,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
K_DestroyBumpers(player, 1);
}
if (!(type == DMG_NORMAL || type == DMG_WIPEOUT || type == DMG_VOLTAGE))
if (!(type == DMG_NORMAL || type == DMG_WIPEOUT || type == DMG_VOLTAGE || type == DMG_FLIPOVER))
{
player->sneakertimer = 0;
player->mo->flags2 &= ~MF2_WATERRUN;
@ -2345,6 +2345,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
K_SquishPlayer(player, inflictor, source);
LUA_HookPlayerSquish(target, inflictor, source, damage, damagetype);
break;
case DMG_FLIPOVER:
if (P_IsDisplayPlayer(player))
P_StartQuake(32<<FRACBITS, 5);
K_FlipPlayer(player, inflictor, source);
LUA_HookPlayerSpin(target, inflictor, source, damage, damagetype);
K_KartPainEnergyFling(player);
case DMG_WIPEOUT:
if (P_IsDisplayPlayer(player))
P_StartQuake(32<<FRACBITS, 5);

View file

@ -570,12 +570,13 @@ struct BasicFF_t
/* Damage/death types, for P_DamageMobj and related */
//// Damage types
#define DMG_NORMAL 0x00
#define DMG_WIPEOUT 0x01 // Normal, but with extra flashy effects
#define DMG_EXPLODE 0x02
#define DMG_SQUISH 0x03
#define DMG_VOLTAGE 0x04 // Normal but for killing the spb
#define DMG_KARMA 0x05 // Karma Bomb explosion -- works like DMG_EXPLODE, but steals half of their bumpers & deletes the rest
#define DMG_NORMAL 0x00
#define DMG_WIPEOUT 0x01 // Normal, but with extra flashy effects
#define DMG_EXPLODE 0x02
#define DMG_SQUISH 0x03
#define DMG_VOLTAGE 0x04 // Normal but for killing the spb
#define DMG_KARMA 0x05 // Karma Bomb explosion -- works like DMG_EXPLODE, but steals half of their bumpers & deletes the rest
#define DMG_FLIPOVER 0x06 // Makes a player flip around in the air at a fixed speed.
//// Death types - cannot be combined with damage types
#define DMG_INSTAKILL 0x80
#define DMG_DEATHPIT 0x81

View file

@ -495,6 +495,14 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING)))
return BMIT_CONTINUE;
// Get rid of the "just flipped" flag.
// We only use that to confirm flipover hits.
if (thing->player)
thing->player->pflags &= ~PF_JUSTFLIPPED;
if (g_tm.thing->player)
g_tm.thing->player->pflags &= ~PF_JUSTFLIPPED;
blockdist = thing->radius + g_tm.thing->radius;
if (abs(thing->x - g_tm.x) >= blockdist || abs(thing->y - g_tm.y) >= blockdist)
@ -1267,7 +1275,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
}
// no interaction
if (g_tm.thing->player->flashing > 0 || g_tm.thing->player->hyudorotimer > 0 || g_tm.thing->player->spinouttimer > 0)
if (g_tm.thing->player->flashing > 0 || g_tm.thing->player->hyudorotimer > 0 || K_IsPlayerDamaged(g_tm.thing->player))
return BMIT_CONTINUE;
// collide

View file

@ -2765,7 +2765,7 @@ void P_PlayerZMovement(mobj_t *mo)
K_UpdateMobjTerrain(mo, (mo->eflags & MFE_VERTICALFLIP ? g_tm.ceilingpic : g_tm.floorpic));
// Get up if you fell.
if (mo->player->panim == PA_HURT && mo->player->spinouttimer == 0 && mo->player->squishedtimer == 0)
if (mo->player->panim == PA_HURT && !K_IsPlayerDamaged(mo->player) && mo->player->squishedtimer == 0)
{
P_SetPlayerMobjState(mo, S_KART_STILL);
}

View file

@ -256,6 +256,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT16(save->p, players[i].flashing);
WRITEUINT16(save->p, players[i].spinouttimer);
WRITEUINT8(save->p, players[i].spinouttype);
WRITEUINT16(save->p, players[i].flipovertimer);
WRITEANGLE(save->p, players[i].flipoverangle);
WRITEINT16(save->p, players[i].squishedtimer);
WRITEUINT8(save->p, players[i].instashield);
WRITEUINT8(save->p, players[i].wipeoutslow);
@ -595,6 +597,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].flashing = READUINT16(save->p);
players[i].spinouttimer = READUINT16(save->p);
players[i].spinouttype = READUINT8(save->p);
players[i].flipoverangle = READUINT16(save->p);
players[i].flipovertimer = READANGLE(save->p);
players[i].squishedtimer = READINT16(save->p);
players[i].instashield = READUINT8(save->p);
players[i].wipeoutslow = READUINT8(save->p);

View file

@ -464,7 +464,7 @@ UINT8 P_FindHighestLap(void)
//
boolean P_PlayerInPain(player_t *player)
{
if (player->spinouttimer || player->squishedtimer)
if (K_IsPlayerDamaged(player) || player->squishedtimer)
return true;
return false;
@ -1374,7 +1374,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean fromAir)
// Cut momentum in half when you hit the ground and
// aren't pressing any controls.
if (!(player->cmd.forwardmove || player->cmd.sidemove) && !player->cmomx && !player->cmomy
&& !(player->spinouttimer))
&& !K_IsPlayerDamaged(player))
{
player->mo->momx = player->mo->momx/2;
player->mo->momy = player->mo->momy/2;
@ -1806,7 +1806,7 @@ static void P_3dMovement(player_t *player)
{
movepushangle = player->mo->angle - (ANGLE_45/5) * player->drift;
}
else if (player->spinouttimer || player->wipeoutslow) // if spun out, use the boost angle
else if (K_IsPlayerDamaged(player) || player->wipeoutslow) // if spun out, use the boost angle
{
movepushangle = (angle_t)player->boostangle;
}
@ -1896,13 +1896,13 @@ static void P_3dMovement(player_t *player)
totalthrust.x += P_ReturnThrustX(player->mo, movepushangle, movepushforward);
totalthrust.y += P_ReturnThrustY(player->mo, movepushangle, movepushforward);
}
else if (!(player->spinouttimer))
else if (!K_IsPlayerDamaged(player))
{
K_MomentumToFacing(player);
}
// Sideways movement
if (cmd->sidemove != 0 && !((player->exiting || mapreset) || player->spinouttimer))
if (cmd->sidemove != 0 && !((player->exiting || mapreset) || K_IsPlayerDamaged(player)))
{
if (cmd->sidemove > 0)
movepushside = (cmd->sidemove * FRACUNIT/128) + FixedDiv(player->speed, K_GetKartSpeed(player, true, true));
@ -2169,6 +2169,10 @@ void P_MovePlayer(player_t *player)
player->glanceDir = 0;
player->pflags &= ~PF_GAINAX;
}
else if (player->flipovertimer > 0)
{
player->mo->rollangle = (angle_t)(player->flipovertimer - 1) * FLIPOVERANG;
}
else if ((player->spinouttimer > 0))
{
UINT16 speed = player->spinouttimer/8;