diff --git a/src/k_kart.c b/src/k_kart.c index 4602f1b21..ad8790573 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -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<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<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; } } diff --git a/src/k_objects.h b/src/k_objects.h index 6ed87cbfe..d39da4247 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -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); diff --git a/src/objects/spb.c b/src/objects/spb.c index 31963b037..49db1841c 100644 --- a/src/objects/spb.c +++ b/src/objects/spb.c @@ -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); + } +} diff --git a/src/p_inter.c b/src/p_inter.c index 38667b97b..5513064f0 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -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; diff --git a/src/p_mobj.c b/src/p_mobj.c index b9dd4a4cc..ab37b2e7c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -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: {