The little SPB tweaks

- Explosion strength scales with how long you've been outrunning it. 0 seconds is even more punishing than it was before, while surviving 60+ seconds converts the damage into minimal stumble.
- When it starts properly chasing a new target, it will be intangible for 1 second
- Hot potato takes 2 seconds instead of 7
- Too much hot potato will make it run ahead & explode (untested)
This commit is contained in:
Sally Coolatta 2022-09-21 11:51:16 -04:00
parent 0e7d9ba84e
commit a790ffee79
5 changed files with 213 additions and 80 deletions

View file

@ -3820,10 +3820,38 @@ angle_t K_StumbleSlope(angle_t angle, angle_t pitch, angle_t roll)
return slope;
}
static void K_StumblePlayer(player_t *player)
{
P_ResetPlayer(player);
#if 0
// Single, medium bounce
player->tumbleBounces = TUMBLEBOUNCES;
player->tumbleHeight = 30;
#else
// Two small bounces
player->tumbleBounces = TUMBLEBOUNCES-1;
player->tumbleHeight = 20;
#endif
player->pflags &= ~PF_TUMBLESOUND;
S_StartSound(player->mo, sfx_s3k9b);
// and then modulate momz like that...
player->mo->momz = K_TumbleZ(player->mo, player->tumbleHeight * FRACUNIT);
P_SetPlayerMobjState(player->mo, S_KART_SPINOUT);
if (P_IsDisplayPlayer(player))
P_StartQuake(64<<FRACBITS, 10);
// Reset slope.
player->mo->pitch = player->mo->roll = 0;
}
boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, boolean fromAir)
{
angle_t steepVal = ANGLE_MAX;
fixed_t gravityadjust;
angle_t oldSlope, newSlope;
angle_t slopeDelta;
@ -3873,36 +3901,7 @@ boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, bool
// Oh jeez, you landed on your side.
// You get to tumble.
P_ResetPlayer(player);
#if 0
// Single, medium bounce
player->tumbleBounces = TUMBLEBOUNCES;
player->tumbleHeight = 30;
#else
// Two small bounces
player->tumbleBounces = TUMBLEBOUNCES-1;
player->tumbleHeight = 20;
#endif
player->pflags &= ~PF_TUMBLESOUND;
S_StartSound(player->mo, sfx_s3k9b);
gravityadjust = P_GetMobjGravity(player->mo)/2; // so we'll halve it for our calculations.
if (player->mo->eflags & MFE_UNDERWATER)
gravityadjust /= 2; // halve "gravity" underwater
// and then modulate momz like that...
player->mo->momz = -gravityadjust * player->tumbleHeight;
P_SetPlayerMobjState(player->mo, S_KART_SPINOUT);
if (P_IsDisplayPlayer(player))
P_StartQuake(64<<FRACBITS, 10);
// Reset slope.
player->mo->pitch = player->mo->roll = 0;
K_StumblePlayer(player);
return true;
}
@ -4136,23 +4135,45 @@ void K_ApplyTripWire(player_t *player, tripwirestate_t state)
INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A bit of a hack, we just throw the player up higher here and extend their spinout timer
{
INT32 ringburst = 10;
fixed_t spbMultiplier = FRACUNIT;
(void)source;
K_DirectorFollowAttack(player, inflictor, source);
player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!!
if (inflictor != NULL && P_MobjWasRemoved(inflictor) == false)
{
if (inflictor->type == MT_SPBEXPLOSION && inflictor->movefactor)
{
spbMultiplier = inflictor->movefactor;
if (spbMultiplier <= 0)
{
// Convert into stumble.
K_StumblePlayer(player);
return 0;
}
else if (spbMultiplier < FRACUNIT)
{
spbMultiplier = FRACUNIT;
}
}
}
player->mo->momz = 18 * mapobjectscale * P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!!
player->mo->momx = player->mo->momy = 0;
player->spinouttype = KSPIN_EXPLOSION;
player->spinouttimer = (3*TICRATE/2)+2;
if (inflictor && !P_MobjWasRemoved(inflictor))
if (spbMultiplier != FRACUNIT)
{
if (inflictor->type == MT_SPBEXPLOSION && inflictor->extravalue1)
player->mo->momz = FixedMul(player->mo->momz, spbMultiplier);
player->spinouttimer = FixedMul(player->spinouttimer, spbMultiplier + ((spbMultiplier - FRACUNIT) / 2));
ringburst = FixedMul(ringburst * FRACUNIT, spbMultiplier) / FRACUNIT;
if (ringburst > 20)
{
player->spinouttimer = ((5*player->spinouttimer)/2)+1;
player->mo->momz *= 2;
ringburst = 20;
}
}

View file

@ -22,6 +22,8 @@ fixed_t Obj_ItemDebrisBounce(mobj_t *debris, fixed_t momz);
/* SPB */
void Obj_SPBThink(mobj_t *spb);
void Obj_SPBExplode(mobj_t *spb);
void Obj_SPBTouch(mobj_t *spb, mobj_t *toucher);
/* SPB Juicebox Rings */
void Obj_MantaRingThink(mobj_t *manta);

View file

@ -24,13 +24,20 @@
#include "../k_waypoint.h"
#include "../k_respawn.h"
//#define SPB_SEEKTEST
#define SPB_SEEKTEST
#define SPB_SLIPTIDEDELTA (ANG1 * 3)
#define SPB_STEERDELTA (ANGLE_90 - ANG10)
#define SPB_DEFAULTSPEED (FixedMul(mapobjectscale, K_GetKartSpeedFromStat(9) * 2))
#define SPB_ACTIVEDIST (1024 * FRACUNIT)
#define SPB_HOTPOTATO (2*TICRATE)
#define SPB_MAXSWAPS (2)
#define SPB_FLASHING (TICRATE)
#define SPB_CHASETIMESCALE (60*TICRATE)
#define SPB_CHASETIMEMUL (3*FRACUNIT)
#define SPB_MANTA_SPACING (2750 * FRACUNIT)
#define SPB_MANTA_VSTART (150)
@ -48,10 +55,15 @@ enum
#define spb_modetimer(o) ((o)->extravalue2)
#define spb_nothink(o) ((o)->threshold)
#define spb_intangible(o) ((o)->cvmem)
#define spb_lastplayer(o) ((o)->lastlook)
#define spb_speed(o) ((o)->movefactor)
#define spb_pitch(o) ((o)->movedir)
#define spb_chasetime(o) ((o)->watertop) // running out of variables here...
#define spb_swapcount(o) ((o)->health)
#define spb_curwaypoint(o) ((o)->cusval)
#define spb_manta_vscale(o) ((o)->movecount)
@ -318,14 +330,31 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
(void)dist;
(void)activeDist;
#else
if (dist <= activeDist)
if (spb_swapcount(spb) > SPB_MAXSWAPS + 1)
{
S_StopSound(spb);
S_StartSound(spb, spb->info->attacksound);
spb_mode(spb) = SPB_MODE_CHASE; // TARGET ACQUIRED
spb_modetimer(spb) = 7*TICRATE;
spb_speed(spb) = desiredSpeed;
return;
// Too much hot potato.
// Go past our target and explode instead.
if (spb->fuse == 0)
{
spb->fuse = 2*TICRATE;
}
}
else
{
if (dist <= activeDist)
{
S_StopSound(spb);
S_StartSound(spb, spb->info->attacksound);
spb_mode(spb) = SPB_MODE_CHASE; // TARGET ACQUIRED
spb_swapcount(spb)++;
spb_modetimer(spb) = SPB_HOTPOTATO;
spb_intangible(spb) = SPB_FLASHING;
spb_speed(spb) = desiredSpeed;
return;
}
}
#endif
@ -379,10 +408,12 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
{
curWaypoint = (waypoint_t *)pathtoplayer.array[1].nodedata;
}
else if (destWaypoint->numnextwaypoints > 0)
#if 0
else if (spb->fuse > 0 && destWaypoint->numnextwaypoints > 0)
{
curWaypoint = destWaypoint->nextwaypoints[0];
}
#endif
else
{
curWaypoint = destWaypoint;
@ -522,6 +553,9 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer)
return;
}
// Increment chase time
spb_chasetime(spb)++;
baseSpeed = SPB_DEFAULTSPEED;
range = (160 * chase->scale);
@ -570,7 +604,7 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer)
// Switch targets if you're no longer 1st for long enough
if (bestPlayer != NULL && chasePlayer->position <= bestPlayer->position)
{
spb_modetimer(spb) = 7*TICRATE;
spb_modetimer(spb) = SPB_HOTPOTATO;
}
else
{
@ -582,6 +616,7 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer)
if (spb_modetimer(spb) <= 0)
{
spb_mode(spb) = SPB_MODE_SEEK; // back to SEEKING
spb_intangible(spb) = SPB_FLASHING;
}
}
}
@ -677,7 +712,8 @@ static void SPBWait(mobj_t *spb)
{
P_SetTarget(&spb_chase(spb), oldPlayer->mo);
spb_mode(spb) = SPB_MODE_CHASE;
spb_modetimer(spb) = 7*TICRATE;
spb_modetimer(spb) = SPB_HOTPOTATO;
spb_intangible(spb) = SPB_FLASHING;
spb_speed(spb) = SPB_DEFAULTSPEED;
}
}
@ -685,6 +721,7 @@ static void SPBWait(mobj_t *spb)
{
spb_mode(spb) = SPB_MODE_SEEK;
spb_modetimer(spb) = 0;
spb_intangible(spb) = SPB_FLASHING;
spbplace = -1;
}
}
@ -718,6 +755,7 @@ void Obj_SPBThink(mobj_t *spb)
// Init values, don't think yet.
spb_lastplayer(spb) = -1;
spb_curwaypoint(spb) = -1;
spb_chasetime(spb) = 0;
spbplace = -1;
spb_manta_totaldist(spb) = 0; // 30000?
@ -792,6 +830,36 @@ void Obj_SPBThink(mobj_t *spb)
}
}
// Flash on/off when intangible.
if (spb_intangible(spb) > 0)
{
spb_intangible(spb)--;
if (spb_intangible(spb) & 1)
{
spb->renderflags |= RF_DONTDRAW;
}
else
{
spb->renderflags &= ~RF_DONTDRAW;
}
}
// Flash white when about to explode!
if (spb->fuse > 0)
{
if (spb->fuse & 1)
{
spb->color = SKINCOLOR_INVINCFLASH;
spb->colorized = true;
}
else
{
spb->color = SKINCOLOR_NONE;
spb->colorized = false;
}
}
// Clamp within level boundaries.
if (spb->z < spb->floorz)
{
@ -802,3 +870,71 @@ void Obj_SPBThink(mobj_t *spb)
spb->z = spb->ceilingz - spb->height;
}
}
void Obj_SPBExplode(mobj_t *spb)
{
mobj_t *spbExplode = NULL;
// Don't continue playing the gurgle or the siren
S_StopSound(spb);
spbExplode = P_SpawnMobjFromMobj(spb, 0, 0, 0, MT_SPBEXPLOSION);
if (spb_owner(spb) != NULL && P_MobjWasRemoved(spb_owner(spb)) == false)
{
P_SetTarget(&spbExplode->target, spb_owner(spb));
}
// Tell the explosion to use alternate knockback.
spbExplode->movefactor = ((SPB_CHASETIMESCALE - spb_chasetime(spb)) * SPB_CHASETIMEMUL) / SPB_CHASETIMESCALE;
P_RemoveMobj(spb);
}
void Obj_SPBTouch(mobj_t *spb, mobj_t *toucher)
{
player_t *player = toucher->player;
if (spb_intangible(spb) > 0)
{
return;
}
if ((spb_owner(spb) == toucher || spb_owner(spb) == toucher->target)
&& (spb_nothink(spb) > 0))
{
return;
}
if (spb->health <= 0 || toucher->health <= 0)
{
return;
}
if (player->spectator == true)
{
return;
}
if (player->bubbleblowup > 0)
{
// Stun the SPB, and remove the shield.
K_DropHnextList(player, false);
spb_mode(spb) = SPB_MODE_WAIT;
spb_modetimer(spb) = 55; // Slightly over the respawn timer length
return;
}
if (spb_chase(spb) != NULL && P_MobjWasRemoved(spb_chase(spb)) == false
&& toucher == spb_chase(spb))
{
// Cause the explosion.
Obj_SPBExplode(spb);
return;
}
else
{
// Regular spinout, please.
P_DamageMobj(toucher, spb, spb_owner(spb), 1, DMG_NORMAL);
}
}

View file

@ -352,41 +352,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
}
return;
case MT_SPB:
if ((special->target == toucher || special->target == toucher->target) && (special->threshold > 0))
return;
if (special->health <= 0 || toucher->health <= 0)
return;
if (player->spectator)
return;
if (special->tracer && !P_MobjWasRemoved(special->tracer) && toucher == special->tracer)
{
mobj_t *spbexplode;
if (player->bubbleblowup > 0)
{
K_DropHnextList(player, false);
special->extravalue1 = 2; // WAIT...
special->extravalue2 = 52; // Slightly over the respawn timer length
return;
}
S_StopSound(special); // Don't continue playing the gurgle or the siren
spbexplode = P_SpawnMobj(toucher->x, toucher->y, toucher->z, MT_SPBEXPLOSION);
spbexplode->extravalue1 = 1; // Tell K_ExplodePlayer to use extra knockback
if (special->target && !P_MobjWasRemoved(special->target))
P_SetTarget(&spbexplode->target, special->target);
P_RemoveMobj(special);
Obj_SPBTouch(special, toucher);
return;
}
else
{
P_DamageMobj(player->mo, special, special->target, 1, DMG_NORMAL);
}
return;
case MT_EMERALD:
if (!P_CanPickupItem(player, 0))
return;

View file

@ -6269,6 +6269,11 @@ static void P_MobjSceneryThink(mobj_t *mobj)
case MT_DRIFTELECTRICSPARK:
mobj->renderflags ^= RF_DONTDRAW;
break;
case MT_SPB:
{
Obj_SPBExplode(mobj);
return;
}
case MT_VWREF:
case MT_VWREB:
{