From 5767315787d8fc7a14c4dc87d3c36fcaf284828b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 18 Sep 2022 01:07:46 -0400 Subject: [PATCH] SPB in its own file + better waypoint movement --- src/deh_tables.c | 1 - src/info.c | 40 +-- src/info.h | 2 - src/k_objects.h | 3 + src/objects/Sourcefile | 1 + src/objects/spb.c | 777 +++++++++++++++++++++++++++++++++++++++++ src/p_enemy.c | 511 --------------------------- src/p_mobj.c | 6 +- 8 files changed, 805 insertions(+), 536 deletions(-) create mode 100644 src/objects/spb.c diff --git a/src/deh_tables.c b/src/deh_tables.c index 5fe950e9e..8ea363fd9 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -322,7 +322,6 @@ actionpointer_t actionpointers[] = {{A_ItemPop}, "A_ITEMPOP"}, {{A_JawzChase}, "A_JAWZCHASE"}, {{A_JawzExplode}, "A_JAWZEXPLODE"}, - {{A_SPBChase}, "A_SPBCHASE"}, {{A_SSMineSearch}, "A_SSMINESEARCH"}, {{A_SSMineExplode}, "A_SSMINEEXPLODE"}, {{A_LandMineExplode}, "A_LANDMINEEXPLODE"}, diff --git a/src/info.c b/src/info.c index 186f04022..9e8108f76 100644 --- a/src/info.c +++ b/src/info.c @@ -4191,26 +4191,26 @@ state_t states[NUMSTATES] = {SPR_BHBM, FF_FULLBRIGHT|14, 1, {NULL}, 0, 0, S_BALLHOGBOOM16}, // S_BALLHOGBOOM15 {SPR_BHBM, FF_FULLBRIGHT|15, 1, {NULL}, 0, 0, S_NULL}, // S_BALLHOGBOOM16 - {SPR_SPBM, 0, 1, {A_SPBChase}, 0, 0, S_SPB2}, // S_SPB1 - {SPR_SPBM, 1, 1, {A_SPBChase}, 0, 0, S_SPB3}, // S_SPB2 - {SPR_SPBM, 0, 1, {A_SPBChase}, 0, 0, S_SPB4}, // S_SPB3 - {SPR_SPBM, 2, 1, {A_SPBChase}, 0, 0, S_SPB5}, // S_SPB4 - {SPR_SPBM, 0, 1, {A_SPBChase}, 0, 0, S_SPB6}, // S_SPB5 - {SPR_SPBM, 3, 1, {A_SPBChase}, 0, 0, S_SPB7}, // S_SPB6 - {SPR_SPBM, 0, 1, {A_SPBChase}, 0, 0, S_SPB8}, // S_SPB7 - {SPR_SPBM, 4, 1, {A_SPBChase}, 0, 0, S_SPB9}, // S_SPB8 - {SPR_SPBM, 0, 1, {A_SPBChase}, 0, 0, S_SPB10}, // S_SPB9 - {SPR_SPBM, 5, 1, {A_SPBChase}, 0, 0, S_SPB11}, // S_SPB10 - {SPR_SPBM, 0, 1, {A_SPBChase}, 0, 0, S_SPB12}, // S_SPB11 - {SPR_SPBM, 6, 1, {A_SPBChase}, 0, 0, S_SPB13}, // S_SPB12 - {SPR_SPBM, 0, 1, {A_SPBChase}, 0, 0, S_SPB14}, // S_SPB13 - {SPR_SPBM, 7, 1, {A_SPBChase}, 0, 0, S_SPB15}, // S_SPB14 - {SPR_SPBM, 0, 1, {A_SPBChase}, 0, 0, S_SPB16}, // S_SPB15 - {SPR_SPBM, 8, 1, {A_SPBChase}, 0, 0, S_SPB17}, // S_SPB16 - {SPR_SPBM, 0, 1, {A_SPBChase}, 0, 0, S_SPB18}, // S_SPB17 - {SPR_SPBM, 8, 1, {A_SPBChase}, 0, 0, S_SPB19}, // S_SPB18 - {SPR_SPBM, 0, 1, {A_SPBChase}, 0, 0, S_SPB20}, // S_SPB19 - {SPR_SPBM, 8, 1, {A_SPBChase}, 0, 0, S_SPB1}, // S_SPB20 + {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_SPB2}, // S_SPB1 + {SPR_SPBM, 1, 1, {NULL}, 0, 0, S_SPB3}, // S_SPB2 + {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_SPB4}, // S_SPB3 + {SPR_SPBM, 2, 1, {NULL}, 0, 0, S_SPB5}, // S_SPB4 + {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_SPB6}, // S_SPB5 + {SPR_SPBM, 3, 1, {NULL}, 0, 0, S_SPB7}, // S_SPB6 + {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_SPB8}, // S_SPB7 + {SPR_SPBM, 4, 1, {NULL}, 0, 0, S_SPB9}, // S_SPB8 + {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_SPB10}, // S_SPB9 + {SPR_SPBM, 5, 1, {NULL}, 0, 0, S_SPB11}, // S_SPB10 + {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_SPB12}, // S_SPB11 + {SPR_SPBM, 6, 1, {NULL}, 0, 0, S_SPB13}, // S_SPB12 + {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_SPB14}, // S_SPB13 + {SPR_SPBM, 7, 1, {NULL}, 0, 0, S_SPB15}, // S_SPB14 + {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_SPB16}, // S_SPB15 + {SPR_SPBM, 8, 1, {NULL}, 0, 0, S_SPB17}, // S_SPB16 + {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_SPB18}, // S_SPB17 + {SPR_SPBM, 8, 1, {NULL}, 0, 0, S_SPB19}, // S_SPB18 + {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_SPB20}, // S_SPB19 + {SPR_SPBM, 8, 1, {NULL}, 0, 0, S_SPB1}, // S_SPB20 {SPR_SPBM, 8, 175, {NULL}, 0, 0, S_NULL}, // S_SPB_DEAD {SPR_THNS, FF_FULLBRIGHT|9, 2, {NULL}, 0, 0, S_LIGHTNINGSHIELD2}, // S_LIGHTNINGSHIELD1 diff --git a/src/info.h b/src/info.h index 8acf73ba8..383dfd29f 100644 --- a/src/info.h +++ b/src/info.h @@ -275,7 +275,6 @@ enum actionnum A_ITEMPOP, A_JAWZCHASE, A_JAWZEXPLODE, - A_SPBCHASE, A_SSMINESEARCH, A_SSMINEEXPLODE, A_LANDMINEEXPLODE, @@ -547,7 +546,6 @@ void A_ChangeHeight(); void A_ItemPop(); void A_JawzChase(); void A_JawzExplode(); -void A_SPBChase(); void A_SSMineSearch(); void A_SSMineExplode(); void A_LandMineExplode(); diff --git a/src/k_objects.h b/src/k_objects.h index cc80d2555..7561c5d6e 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -15,4 +15,7 @@ void Obj_ShrinkGunRemoved(mobj_t *gun); boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim); void Obj_CreateShrinkPohbees(player_t *owner); +/* SPB */ +void Obj_SPBThink(mobj_t *spb); + #endif/*k_objects_H*/ diff --git a/src/objects/Sourcefile b/src/objects/Sourcefile index 94f7dd25b..4f0dacad7 100644 --- a/src/objects/Sourcefile +++ b/src/objects/Sourcefile @@ -1,2 +1,3 @@ hyudoro.c shrink.c +spb.c diff --git a/src/objects/spb.c b/src/objects/spb.c new file mode 100644 index 000000000..c5ae94efe --- /dev/null +++ b/src/objects/spb.c @@ -0,0 +1,777 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2022 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file shrink.c +/// \brief Shrink laser item code. + +#include "../doomdef.h" +#include "../doomstat.h" +#include "../info.h" +#include "../k_kart.h" +#include "../k_objects.h" +#include "../m_random.h" +#include "../p_local.h" +#include "../r_main.h" +#include "../s_sound.h" +#include "../g_game.h" +#include "../z_zone.h" +#include "../k_waypoint.h" +#include "../k_respawn.h" + +//#define SPB_SEEKTEST + +#define SPB_SLIPTIDEDELTA (ANG1 * 3) +#define SPB_STEERDELTA (ANGLE_90 - ANG10) +#define SPB_DEFAULTSPEED (FixedMul(mapobjectscale, K_GetKartSpeedFromStat(5) * 2)) + +enum +{ + SPB_MODE_SEEK, + SPB_MODE_CHASE, + SPB_MODE_WAIT, +}; + +#define spb_mode(o) ((o)->extravalue1) +#define spb_modetimer(o) ((o)->extravalue2) + +#define spb_nothink(o) ((o)->threshold) +#define spb_lastplayer(o) ((o)->lastlook) +#define spb_speed(o) ((o)->movefactor) +#define spb_pitch(o) ((o)->movedir) + +#define spb_curwaypoint(o) ((o)->cusval) + +#define spb_owner(o) ((o)->target) +#define spb_chase(o) ((o)->tracer) + +static void SpawnSPBTrailRings(mobj_t *spb) +{ + if (leveltime % (spb_mode(spb) != SPB_MODE_SEEK ? 6 : 3) == 0) + { + mobj_t *ring = P_SpawnMobjFromMobj(spb, + -FixedDiv(spb->momx, spb->scale), + -FixedDiv(spb->momy, spb->scale), + -FixedDiv(spb->momz, spb->scale) + (24*FRACUNIT), + MT_RING + ); + + ring->threshold = 10; + ring->fuse = 35*TICRATE; + + ring->colorized = true; + ring->color = SKINCOLOR_RED; + } +} + +static void SpawnSPBDust(mobj_t *spb) +{ + // The easiest way to spawn a V shaped cone of dust from the SPB is simply to spawn 2 particles, and to both move them to the sides in opposite direction. + mobj_t *dust; + fixed_t sx; + fixed_t sy; + fixed_t sz = spb->floorz; + angle_t sa = spb->angle - ANG1*60; + INT32 i; + + if (spb->eflags & MFE_VERTICALFLIP) + { + sz = spb->ceilingz; + } + + if ((leveltime & 1) && abs(spb->z - sz) < FRACUNIT*64) // Only every other frame. Also don't spawn it if we're way above the ground. + { + // Determine spawning position next to the SPB: + for (i = 0; i < 2; i++) + { + sx = 96 * FINECOSINE(sa >> ANGLETOFINESHIFT); + sy = 96 * FINESINE(sa >> ANGLETOFINESHIFT); + + dust = P_SpawnMobjFromMobj(spb, sx, sy, 0, MT_SPBDUST); + dust->z = sz; + + dust->momx = spb->momx/2; + dust->momy = spb->momy/2; + dust->momz = spb->momz/2; // Give some of the momentum to the dust + + P_SetScale(dust, spb->scale * 2); + + dust->color = SKINCOLOR_RED; + dust->colorized = true; + + dust->angle = spb->angle - FixedAngle(FRACUNIT*90 - FRACUNIT*180*i); // The first one will spawn to the right of the spb, the second one to the left. + P_Thrust(dust, dust->angle, 6*dust->scale); + + K_MatchGenericExtraFlags(dust, spb); + + sa += ANG1*120; // Add 120 degrees to get to mo->angle + ANG1*60 + } + } +} + +// Spawns SPB slip tide. To be used when the SPB is turning. +// Modified version of K_SpawnAIZDust. Maybe we could merge those to be cleaner? + +// dir should be either 1 or -1 to determine where to spawn the dust. + +static void SpawnSPBSliptide(mobj_t *spb, INT32 dir) +{ + fixed_t newx; + fixed_t newy; + mobj_t *spark; + angle_t travelangle; + fixed_t sz = spb->floorz; + + if (spb->eflags & MFE_VERTICALFLIP) + { + sz = spb->ceilingz; + } + + travelangle = K_MomentumAngle(spb); + + if ((leveltime & 1) && abs(spb->z - sz) < FRACUNIT*64) + { + newx = P_ReturnThrustX(spb, travelangle - (dir*ANGLE_45), 24*FRACUNIT); + newy = P_ReturnThrustY(spb, travelangle - (dir*ANGLE_45), 24*FRACUNIT); + + spark = P_SpawnMobjFromMobj(spb, newx, newy, 0, MT_SPBDUST); + spark->z = sz; + + spark->colorized = true; + spark->color = SKINCOLOR_RED; + + spark->flags = MF_NOGRAVITY|MF_PAIN; + P_SetTarget(&spark->target, spb); + + spark->angle = travelangle + (dir * ANGLE_90); + P_SetScale(spark, (spark->destscale = spb->scale*3/2)); + + spark->momx = (6*spb->momx)/5; + spark->momy = (6*spb->momy)/5; + + K_MatchGenericExtraFlags(spark, spb); + } +} + +// Used for seeking and when SPB is trailing its target from way too close! +static void SpawnSPBSpeedLines(mobj_t *spb) +{ + mobj_t *fast = P_SpawnMobjFromMobj(spb, + P_RandomRange(-24, 24) * FRACUNIT, + P_RandomRange(-24, 24) * FRACUNIT, + (spb->info->height / 2) + (P_RandomRange(-24, 24) * FRACUNIT), + MT_FASTLINE + ); + + P_SetTarget(&fast->target, spb); + fast->angle = K_MomentumAngle(spb); + + fast->color = SKINCOLOR_RED; + fast->colorized = true; + + K_MatchGenericExtraFlags(fast, spb); +} + +static fixed_t SPBDist(mobj_t *a, mobj_t *b) +{ + return P_AproxDistance(P_AproxDistance( + a->x - b->x, + a->y - b->y), + a->z - b->z + ); +} + +static void SPBTurn( + fixed_t destSpeed, angle_t destAngle, + fixed_t *editSpeed, angle_t *editAngle, + fixed_t lerp, SINT8 *returnSliptide) +{ + INT32 delta = destAngle - *editAngle; + fixed_t dampen = FRACUNIT; + + // Slow down when turning; it looks better and makes U-turns not unfair + dampen = FixedDiv((180 * FRACUNIT) - AngleFixed(abs(delta)), 180 * FRACUNIT); + + *editSpeed = FixedMul(destSpeed, dampen); + + delta = FixedMul(delta, lerp); + + // Calculate sliptide effect during seeking. + if (returnSliptide != NULL) + { + INT32 sliptide = (abs(delta) > SPB_SLIPTIDEDELTA); + + if (delta < 0) + { + sliptide = -sliptide; + } + + *returnSliptide = sliptide; + } + + *editAngle += delta; +} + +static void SetSPBSpeed(mobj_t *spb, fixed_t xySpeed, fixed_t zSpeed) +{ + spb->momx = FixedMul(FixedMul( + xySpeed, + FINECOSINE(spb->angle >> ANGLETOFINESHIFT)), + FINECOSINE(spb_pitch(spb) >> ANGLETOFINESHIFT) + ); + + spb->momy = FixedMul(FixedMul( + xySpeed, + FINESINE(spb->angle >> ANGLETOFINESHIFT)), + FINECOSINE(spb_pitch(spb) >> ANGLETOFINESHIFT) + ); + + spb->momz = FixedMul( + zSpeed, + FINESINE(spb_pitch(spb) >> ANGLETOFINESHIFT) + ); +} + +static void SPBSeek(mobj_t *spb, player_t *bestPlayer) +{ + const fixed_t desiredSpeed = SPB_DEFAULTSPEED; + + waypoint_t *curWaypoint = NULL; + waypoint_t *destWaypoint = NULL; + + fixed_t dist = INT32_MAX; + + fixed_t destX = spb->x; + fixed_t destY = spb->y; + fixed_t destZ = spb->z; + angle_t destAngle = spb->angle; + angle_t destPitch = 0U; + + fixed_t xySpeed = desiredSpeed; + fixed_t zSpeed = desiredSpeed; + SINT8 sliptide = 0; + + fixed_t steerDist = INT32_MAX; + mobj_t *steerMobj = NULL; + + size_t i; + + spb_lastplayer(spb) = -1; // Just make sure this is reset + + if (bestPlayer == NULL + || bestPlayer->mo == NULL + || P_MobjWasRemoved(bestPlayer->mo) == true + || bestPlayer->mo->health <= 0 + || (bestPlayer->respawn.state != RESPAWNST_NONE)) + { + // No one there? Completely STOP. + spb->momx = spb->momy = spb->momz = 0; + + if (bestPlayer == NULL) + { + spbplace = -1; + } + + return; + } + + // Found someone, now get close enough to initiate the slaughter... + P_SetTarget(&spb_chase(spb), bestPlayer->mo); + spbplace = bestPlayer->position; + + dist = SPBDist(spb, spb_chase(spb)); + +#ifdef SPB_SEEKTEST // Easy debug switch + (void)dist; +#else + if (dist <= (1024 * spb_chase(spb)->scale)) + { + S_StartSound(spb, spb->info->attacksound); + spb_mode(spb) = SPB_MODE_CHASE; // TARGET ACQUIRED + spb_modetimer(spb) = 7*TICRATE; + spb_speed(spb) = desiredSpeed; + return; + } +#endif + + // Move along the waypoints until you get close enough + if (spb_curwaypoint(spb) == -1) + { + // Determine first waypoint. + curWaypoint = K_GetBestWaypointForMobj(spb); + spb_curwaypoint(spb) = (INT32)K_GetWaypointHeapIndex(curWaypoint); + } + else + { + curWaypoint = K_GetWaypointFromIndex( (size_t)spb_curwaypoint(spb) ); + } + + destWaypoint = bestPlayer->nextwaypoint; + + if (curWaypoint != NULL) + { + fixed_t waypointDist = INT32_MAX; + fixed_t waypointRad = INT32_MAX; + + CONS_Printf("Moving towards waypoint... (%d)\n", K_GetWaypointID(curWaypoint)); + destX = curWaypoint->mobj->x; + destY = curWaypoint->mobj->y; + destZ = curWaypoint->mobj->z; + + waypointDist = R_PointToDist2(spb->x, spb->y, destX, destY) / mapobjectscale; + waypointRad = max(curWaypoint->mobj->radius / mapobjectscale, DEFAULT_WAYPOINT_RADIUS); + + if (waypointDist <= waypointRad) + { + boolean pathfindsuccess = false; + + if (destWaypoint != NULL) + { + // Go to next waypoint. + const boolean huntbackwards = false; + const boolean useshortcuts = K_GetWaypointIsShortcut(destWaypoint); // If the player is on a shortcut, use shortcuts. No escape. + + path_t pathtoplayer = {0}; + + pathfindsuccess = K_PathfindToWaypoint( + curWaypoint, destWaypoint, + &pathtoplayer, + useshortcuts, huntbackwards + ); + + if (pathfindsuccess == true) + { + if (pathtoplayer.numnodes > 1) + { + curWaypoint = (waypoint_t *)pathtoplayer.array[1].nodedata; + CONS_Printf("NEW: Proper next waypoint (%d)\n", K_GetWaypointID(curWaypoint)); + } + else if (destWaypoint->numnextwaypoints > 0) + { + curWaypoint = destWaypoint->nextwaypoints[0]; + CONS_Printf("NEW: Forcing next waypoint (%d)\n", K_GetWaypointID(curWaypoint)); + } + else + { + curWaypoint = destWaypoint; + CONS_Printf("NEW: Forcing destination (%d)\n", K_GetWaypointID(curWaypoint)); + } + + Z_Free(pathtoplayer.array); + } + } + + if (pathfindsuccess == true && curWaypoint != NULL) + { + // Update again + spb_curwaypoint(spb) = (INT32)K_GetWaypointHeapIndex(curWaypoint); + destX = curWaypoint->mobj->x; + destY = curWaypoint->mobj->y; + destZ = curWaypoint->mobj->z; + } + else + { + CONS_Printf("FAILURE, no waypoint (pathfind unsuccessful)\n"); + spb_curwaypoint(spb) = -1; + destX = spb_chase(spb)->x; + destY = spb_chase(spb)->y; + destZ = spb_chase(spb)->z; + } + } + } + else + { + CONS_Printf("FAILURE, no waypoint (no initial waypoint)\n"); + spb_curwaypoint(spb) = -1; + destX = spb_chase(spb)->x; + destY = spb_chase(spb)->y; + destZ = spb_chase(spb)->z; + } + + destAngle = R_PointToAngle2(spb->x, spb->y, destX, destY); + destPitch = R_PointToAngle2(0, spb->z, P_AproxDistance(spb->x - destX, spb->y - destY), destZ); + + SPBTurn(desiredSpeed, destAngle, &xySpeed, &spb->angle, FRACUNIT/8, &sliptide); + SPBTurn(desiredSpeed, destPitch, &zSpeed, &spb_pitch(spb), FRACUNIT/8, NULL); + + SetSPBSpeed(spb, xySpeed, zSpeed); + + // see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh! + steerDist = 1536 * mapobjectscale; + + for (i = 0; i < MAXPLAYERS; i++) + { + fixed_t ourDist = INT32_MAX; + INT32 ourDelta = INT32_MAX; + + if (playeringame[i] == false || players[i].spectator == true) + { + // Not in-game + continue; + } + + if (players[i].mo == NULL || P_MobjWasRemoved(players[i].mo) == true) + { + // Invalid mobj + continue; + } + + ourDelta = AngleDelta(spb->angle, R_PointToAngle2(spb->x, spb->y, players[i].mo->x, players[i].mo->y)); + if (ourDelta > SPB_STEERDELTA) + { + // Check if the angle wouldn't make us LOSE speed. + continue; + } + + ourDist = R_PointToDist2(spb->x, spb->y, players[i].mo->x, players[i].mo->y); + if (ourDist < steerDist) + { + steerDist = ourDist; + steerMobj = players[i].mo; // it doesn't matter if we override this guy now. + } + } + + // different player from our main target, try and ram into em~! + if (steerMobj != NULL && steerMobj != spb_chase(spb)) + { + P_Thrust(spb, R_PointToAngle2(spb->x, spb->y, steerMobj->x, steerMobj->y), spb_speed(spb) / 4); + } + + if (sliptide != 0) + { + // 1 if turning left, -1 if turning right. + // Angles work counterclockwise, remember! + SpawnSPBSliptide(spb, sliptide); + } + else + { + // if we're mostly going straight, then spawn the V dust cone! + SpawnSPBDust(spb); + } + + // Always spawn speed lines while seeking + SpawnSPBSpeedLines(spb); + + // Spawn a trail of rings behind the SPB! + SpawnSPBTrailRings(spb); +} + +static void SPBChase(mobj_t *spb, player_t *bestPlayer) +{ + fixed_t baseSpeed = 0; + fixed_t maxSpeed = 0; + fixed_t desiredSpeed = 0; + + fixed_t range = INT32_MAX; + fixed_t cx = 0, cy = 0; + + fixed_t dist = INT32_MAX; + angle_t destAngle = spb->angle; + angle_t destPitch = 0U; + fixed_t xySpeed = 0; + fixed_t zSpeed = 0; + + mobj_t *chase = NULL; + player_t *chasePlayer = NULL; + + spb_curwaypoint(spb) = -1; // Reset waypoint + + chase = spb_chase(spb); + + if (chase == NULL || P_MobjWasRemoved(chase) == true || chase->health <= 0) + { + P_SetTarget(&spb_chase(spb), NULL); + spb_mode(spb) = SPB_MODE_WAIT; + spb_modetimer(spb) = 55; // Slightly over the respawn timer length + return; + } + + if (chase->hitlag) + { + // If the player is frozen, the SPB should be too. + spb->hitlag = max(spb->hitlag, chase->hitlag); + return; + } + + baseSpeed = SPB_DEFAULTSPEED; + range = (160 * chase->scale); + + // Play the intimidating gurgle + if (S_SoundPlaying(spb, spb->info->activesound) == false) + { + S_StartSound(spb, spb->info->activesound); + } + + // Maybe we want SPB to target an object later? IDK lol + chasePlayer = chase->player; + if (chasePlayer != NULL) + { + UINT8 fracmax = 32; + UINT8 spark = ((10 - chasePlayer->kartspeed) + chasePlayer->kartweight) / 2; + fixed_t easiness = ((chasePlayer->kartspeed + (10 - spark)) << FRACBITS) / 2; + + spb_lastplayer(spb) = chasePlayer - players; // Save the player num for death scumming... + spbplace = chasePlayer->position; + + chasePlayer->pflags |= PF_RINGLOCK; // set ring lock + + if (P_IsObjectOnGround(chase) == false) + { + // In the air you have no control; basically don't hit unless you make a near complete stop + baseSpeed = (7 * chasePlayer->speed) / 8; + } + else + { + // 7/8ths max speed for Knuckles, 3/4ths max speed for min accel, exactly max speed for max accel + baseSpeed = FixedMul( + ((fracmax+1) << FRACBITS) - easiness, + K_GetKartSpeed(chasePlayer, false, false) + ) / fracmax; + } + + if (chasePlayer->carry == CR_SLIDING) + { + baseSpeed = chasePlayer->speed/2; + } + + // Be fairer on conveyors + cx = chasePlayer->cmomx; + cy = chasePlayer->cmomy; + + // Switch targets if you're no longer 1st for long enough + if (bestPlayer != NULL && chasePlayer->position <= bestPlayer->position) + { + spb_modetimer(spb) = 7*TICRATE; + } + else + { + if (spb_modetimer(spb) > 0) + { + spb_modetimer(spb)--; + } + + if (spb_modetimer(spb) <= 0) + { + spb_mode(spb) = SPB_MODE_SEEK; // back to SEEKING + } + } + } + + dist = P_AproxDistance(P_AproxDistance(spb->x - chase->x, spb->y - chase->y), spb->z - chase->z); + + desiredSpeed = FixedMul(baseSpeed, FRACUNIT + FixedDiv(dist - range, range)); + + if (desiredSpeed < baseSpeed) + { + desiredSpeed = baseSpeed; + } + + maxSpeed = (baseSpeed * 3) / 2; + if (desiredSpeed > maxSpeed) + { + desiredSpeed = maxSpeed; + } + + if (desiredSpeed < 20 * chase->scale) + { + desiredSpeed = 20 * chase->scale; + } + + if (chasePlayer != NULL && chasePlayer->carry == CR_SLIDING) + { + // Hack for current sections to make them fair. + desiredSpeed = min(desiredSpeed, chasePlayer->speed / 2); + } + + destAngle = R_PointToAngle2(spb->x, spb->y, chase->x, chase->y); + destPitch = R_PointToAngle2(0, spb->z, P_AproxDistance(spb->x - chase->x, spb->y - chase->y), chase->z); + + // Modify stored speed + if (desiredSpeed > spb_speed(spb)) + { + spb_speed(spb) += (desiredSpeed - spb_speed(spb)) / TICRATE; + } + else + { + spb_speed(spb) = desiredSpeed; + } + + SPBTurn(spb_speed(spb), destAngle, &xySpeed, &spb->angle, FRACUNIT, NULL); + SPBTurn(spb_speed(spb), destPitch, &zSpeed, &spb_pitch(spb), FRACUNIT, NULL); + + SetSPBSpeed(spb, xySpeed, zSpeed); + spb->momx += cx; + spb->momy += cy; + + // Spawn a trail of rings behind the SPB! + SpawnSPBTrailRings(spb); + + // Red speed lines for when it's gaining on its target. A tell for when you're starting to lose too much speed! + if (R_PointToDist2(0, 0, spb->momx, spb->momy) > (16 * R_PointToDist2(0, 0, chase->momx, chase->momy)) / 15 // Going faster than the target + && xySpeed > 20 * mapobjectscale) // Don't display speedup lines at pitifully low speeds + { + SpawnSPBSpeedLines(spb); + } +} + +static void SPBWait(mobj_t *spb) +{ + player_t *oldPlayer = NULL; + + spb->momx = spb->momy = spb->momz = 0; // Stoooop + spb_curwaypoint(spb) = -1; // Reset waypoint + + if (spb_lastplayer(spb) != -1 + && playeringame[spb_lastplayer(spb)] == true) + { + oldPlayer = &players[spb_lastplayer(spb)]; + } + + if (oldPlayer != NULL + && oldPlayer->spectator == false + && oldPlayer->exiting > 0) + { + spbplace = oldPlayer->position; + oldPlayer->pflags |= PF_RINGLOCK; + } + + if (spb_modetimer(spb) > 0) + { + spb_modetimer(spb)--; + } + + if (spb_modetimer(spb) <= 0) + { + if (oldPlayer != NULL) + { + if (oldPlayer->mo != NULL && P_MobjWasRemoved(oldPlayer->mo) == false) + { + P_SetTarget(&spb_chase(spb), oldPlayer->mo); + spb_mode(spb) = SPB_MODE_CHASE; + spb_modetimer(spb) = 7*TICRATE; + spb_speed(spb) = SPB_DEFAULTSPEED; + } + } + else + { + spb_mode(spb) = SPB_MODE_SEEK; + spb_modetimer(spb) = 0; + spbplace = -1; + } + } +} + +void Obj_SPBThink(mobj_t *spb) +{ + mobj_t *ghost = NULL; + player_t *bestPlayer = NULL; + UINT8 bestRank = UINT8_MAX; + size_t i; + + if (spb->health <= 0) + { + return; + } + + indirectitemcooldown = 20*TICRATE; + + ghost = P_SpawnGhostMobj(spb); + ghost->fuse = 3; + + if (spb_owner(spb) != NULL && P_MobjWasRemoved(spb_owner(spb)) == false && spb_owner(spb)->player != NULL) + { + ghost->color = spb_owner(spb)->player->skincolor; + ghost->colorized = true; + } + + if (spb_nothink(spb) > 0) + { + // Doesn't think yet, when it initially spawns. + spb_lastplayer(spb) = -1; + spb_curwaypoint(spb) = -1; + spbplace = -1; + + P_InstaThrust(spb, spb->angle, SPB_DEFAULTSPEED); + + spb_nothink(spb)--; + } + else + { + // Find the player with the best rank + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *player = NULL; + + if (playeringame[i] == false) + { + // Not valid + continue; + } + + player = &players[i]; + + if (player->spectator == true || player->exiting > 0) + { + // Not playing + continue; + } + + /* + if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + { + // No mobj + continue; + } + + if (player->mo <= 0) + { + // Dead + continue; + } + + if (player->respawn.state != RESPAWNST_NONE) + { + // Respawning + continue; + } + */ + + if (player->position < bestRank) + { + bestRank = player->position; + bestPlayer = player; + } + } + + switch (spb_mode(spb)) + { + case SPB_MODE_SEEK: + default: + SPBSeek(spb, bestPlayer); + break; + + case SPB_MODE_CHASE: + SPBChase(spb, bestPlayer); + break; + + case SPB_MODE_WAIT: + SPBWait(spb); + break; + } + } + + // Clamp within level boundaries. + if (spb->z < spb->floorz) + { + spb->z = spb->floorz; + } + else if (spb->z > spb->ceilingz - spb->height) + { + spb->z = spb->ceilingz - spb->height; + } +} diff --git a/src/p_enemy.c b/src/p_enemy.c index d5f999624..6fbbab61a 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -311,7 +311,6 @@ void A_ChangeHeight(mobj_t *actor); void A_ItemPop(mobj_t *actor); void A_JawzChase(mobj_t *actor); void A_JawzExplode(mobj_t *actor); -void A_SPBChase(mobj_t *actor); void A_SSMineSearch(mobj_t *actor); void A_SSMineExplode(mobj_t *actor); void A_LandMineExplode(mobj_t *actor); @@ -13408,516 +13407,6 @@ void A_JawzExplode(mobj_t *actor) return; } -static void SpawnSPBTrailRings(mobj_t *actor) -{ - I_Assert(actor != NULL); - - if (leveltime % 6 == 0) - { - if (leveltime % (actor->extravalue1 == 2 ? 6 : 3) == 0) // Extravalue1 == 2 is seeking mode. Because the SPB is about twice as fast as normal in that mode, also spawn the rings twice as often to make up for it! - { - mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momy, - actor->z - actor->momz + (24*mapobjectscale), MT_RING); - ring->threshold = 10; - ring->fuse = 35*TICRATE; - ring->colorized = true; - ring->color = SKINCOLOR_RED; - } - } -} - -// Spawns the V shaped dust. To be used when the SPB is going mostly forward. -static void SpawnSPBDust(mobj_t *mo) -{ - // The easiest way to spawn a V shaped cone of dust from the SPB is simply to spawn 2 particles, and to both move them to the sides in opposite direction. - mobj_t *dust; - fixed_t sx; - fixed_t sy; - fixed_t sz = mo->floorz; - angle_t sa = mo->angle - ANG1*60; - INT32 i; - - if (mo->eflags & MFE_VERTICALFLIP) - sz = mo->ceilingz; - - if (leveltime & 1 && abs(mo->z - sz) < FRACUNIT*64) // Only ever other frame. Also don't spawn it if we're way above the ground. - { - // Determine spawning position next to the SPB: - for (i=0; i < 2; i++) - { - sx = mo->x + FixedMul((mo->scale*96), FINECOSINE((sa)>>ANGLETOFINESHIFT)); - sy = mo->y + FixedMul((mo->scale*96), FINESINE((sa)>>ANGLETOFINESHIFT)); - - dust = P_SpawnMobj(sx, sy, sz, MT_SPBDUST); - dust->momx = mo->momx/2; - dust->momy = mo->momy/2; - dust->momz = mo->momz/2; // Give some of the momentum to the dust - P_SetScale(dust, mo->scale*2); - dust->colorized = true; - dust->color = SKINCOLOR_RED; - P_InitAngle(dust, mo->angle - FixedAngle(FRACUNIT*90 - FRACUNIT*180*i)); // The first one will spawn to the right of the spb, the second one to the left. - P_Thrust(dust, dust->angle, 6*dust->scale); - - K_MatchGenericExtraFlags(dust, mo); - - sa += ANG1*120; // Add 120 degrees to get to mo->angle + ANG1*60 - } - } -} - -// Spawns SPB slip tide. To be used when the SPB is turning. -// Modified version of K_SpawnAIZDust. Maybe we could merge those to be cleaner? - -// dir should be either 1 or -1 to determine where to spawn the dust. - -static void SpawnSPBAIZDust(mobj_t *mo, INT32 dir) -{ - fixed_t newx; - fixed_t newy; - mobj_t *spark; - angle_t travelangle; - fixed_t sz = mo->floorz; - - if (mo->eflags & MFE_VERTICALFLIP) - sz = mo->ceilingz; - - travelangle = K_MomentumAngle(mo); - if (leveltime & 1 && abs(mo->z - sz) < FRACUNIT*64) - { - newx = mo->x + P_ReturnThrustX(mo, travelangle - (dir*ANGLE_45), FixedMul(24*FRACUNIT, mo->scale)); - newy = mo->y + P_ReturnThrustY(mo, travelangle - (dir*ANGLE_45), FixedMul(24*FRACUNIT, mo->scale)); - spark = P_SpawnMobj(newx, newy, sz, MT_AIZDRIFTSTRAT); - spark->colorized = true; - spark->color = SKINCOLOR_RED; - spark->flags = MF_NOGRAVITY|MF_PAIN; - P_SetTarget(&spark->target, mo); - - P_InitAngle(spark, travelangle+(dir*ANGLE_90)); - P_SetScale(spark, (spark->destscale = mo->scale*3/2)); - - spark->momx = (6*mo->momx)/5; - spark->momy = (6*mo->momy)/5; - - K_MatchGenericExtraFlags(spark, mo); - } -} - -// Used for seeking and when SPB is trailing its target from way too close! -static void SpawnSPBSpeedLines(mobj_t *actor) -{ - mobj_t *fast = P_SpawnMobj(actor->x + (P_RandomRange(-24,24) * actor->scale), - actor->y + (P_RandomRange(-24,24) * actor->scale), - actor->z + (actor->height/2) + (P_RandomRange(-24,24) * actor->scale), - MT_FASTLINE); - - P_SetTarget(&fast->target, actor); - P_InitAngle(fast, K_MomentumAngle(actor)); - fast->color = SKINCOLOR_RED; - fast->colorized = true; - K_MatchGenericExtraFlags(fast, actor); -} - - -void A_SPBChase(mobj_t *actor) -{ - player_t *player = NULL; - player_t *scplayer = NULL; // secondary target for seeking - UINT8 i; - UINT8 bestrank = UINT8_MAX; - fixed_t dist; - angle_t hang, vang; - fixed_t wspeed, xyspeed, zspeed; - fixed_t pdist = 1536<threshold) // Just fired, go straight. - { - actor->lastlook = -1; - actor->cusval = -1; - spbplace = -1; - P_InstaThrust(actor, actor->angle, wspeed); - return; - } - - // Find the player with the best rank - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || players[i].exiting) - continue; // not in-game - - /*if (!players[i].mo) - continue; // no mobj - - if (players[i].mo->health <= 0) - continue; // dead - - if (players[i].respawn.state != RESPAWNST_NONE) - continue;*/ // respawning - - if (players[i].position < bestrank) - { - bestrank = players[i].position; - player = &players[i]; - } - } - - // lastlook = last player num targetted - // cvmem = stored speed - // cusval = next waypoint heap index - // extravalue1 = SPB movement mode - // extravalue2 = mode misc option - - if (actor->extravalue1 == 1) // MODE: TARGETING - { - actor->cusval = -1; // Reset waypoint - - if (actor->tracer && actor->tracer->health) - { - fixed_t defspeed = wspeed; - fixed_t range = (160*actor->tracer->scale); - fixed_t cx = 0, cy =0; - - // Play the intimidating gurgle - if (!S_SoundPlaying(actor, actor->info->activesound)) - S_StartSound(actor, actor->info->activesound); - - // Maybe we want SPB to target an object later? IDK lol - if (actor->tracer->player) - { - UINT8 fracmax = 32; - UINT8 spark = ((10-actor->tracer->player->kartspeed) + actor->tracer->player->kartweight) / 2; - fixed_t easiness = ((actor->tracer->player->kartspeed + (10-spark)) << FRACBITS) / 2; - - actor->lastlook = actor->tracer->player-players; // Save the player num for death scumming... - actor->tracer->player->pflags |= PF_RINGLOCK; // set ring lock - - if (actor->tracer->hitlag) - { - // If the player is frozen through no fault of their own, the SPB should be too. - actor->hitlag = actor->tracer->hitlag; - } - - if (!P_IsObjectOnGround(actor->tracer)) - { - // In the air you have no control; basically don't hit unless you make a near complete stop - defspeed = (7 * actor->tracer->player->speed) / 8; - } - else - { - // 7/8ths max speed for Knuckles, 3/4ths max speed for min accel, exactly max speed for max accel - defspeed = FixedMul(((fracmax+1)<tracer->player, false, false)) / fracmax; - } - - // Be fairer on conveyors - cx = actor->tracer->player->cmomx; - cy = actor->tracer->player->cmomy; - - // Switch targets if you're no longer 1st for long enough - if (actor->tracer->player->position <= bestrank) - actor->extravalue2 = 7*TICRATE; - else if (actor->extravalue2-- <= 0) - actor->extravalue1 = 0; // back to SEEKING - - spbplace = actor->tracer->player->position; - } - - dist = P_AproxDistance(P_AproxDistance(actor->x-actor->tracer->x, actor->y-actor->tracer->y), actor->z-actor->tracer->z); - - wspeed = FixedMul(defspeed, FRACUNIT + FixedDiv(dist-range, range)); - if (wspeed < defspeed) - wspeed = defspeed; - if (wspeed > (3*defspeed)/2) - wspeed = (3*defspeed)/2; - if (wspeed < 20*actor->tracer->scale) - wspeed = 20*actor->tracer->scale; - if (actor->tracer->player->carry == CR_SLIDING) - wspeed = actor->tracer->player->speed/2; - // ^^^^ current section: These are annoying, and grand metropolis in particular needs this. - - hang = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); - vang = R_PointToAngle2(0, actor->z, dist, actor->tracer->z); - - // Modify stored speed - if (wspeed > actor->cvmem) - actor->cvmem += (wspeed - actor->cvmem) / TICRATE; - else - actor->cvmem = wspeed; - - { - // Smoothly rotate horz angle - angle_t input = hang - actor->angle; - boolean invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - // Slow down when turning; it looks better and makes U-turns not unfair - xyspeed = FixedMul(actor->cvmem, max(0, (((180<angle += input; - - // Smoothly rotate vert angle - input = vang - actor->movedir; - invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - // Slow down when turning; might as well do it for momz, since we do it above too - zspeed = FixedMul(actor->cvmem, max(0, (((180<movedir += input; - } - - actor->momx = cx + FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); - actor->momy = cy + FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); - actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); - - // Spawn a trail of rings behind the SPB! - SpawnSPBTrailRings(actor); - - // Red speed lines for when it's gaining on its target. A tell for when you're starting to lose too much speed! - if (R_PointToDist2(0, 0, actor->momx, actor->momy) > (actor->tracer->player ? (16*actor->tracer->player->speed)/15 - : (16*R_PointToDist2(0, 0, actor->tracer->momx, actor->tracer->momy))/15) // Going faster than the target - && xyspeed > K_GetKartSpeed(actor->tracer->player, false, false) / 4) // Don't display speedup lines at pitifully low speeds - SpawnSPBSpeedLines(actor); - - return; - } - else // Target's gone, return to SEEKING - { - P_SetTarget(&actor->tracer, NULL); - actor->extravalue1 = 2; // WAIT... - actor->extravalue2 = 52; // Slightly over the respawn timer length - return; - } - } - else if (actor->extravalue1 == 2) // MODE: WAIT... - { - actor->momx = actor->momy = actor->momz = 0; // Stoooop - actor->cusval = -1; // Reset waypoint - - if (actor->lastlook != -1 - && playeringame[actor->lastlook] - && !players[actor->lastlook].spectator - && !players[actor->lastlook].exiting) - { - spbplace = players[actor->lastlook].position; - players[actor->lastlook].pflags |= PF_RINGLOCK; - if (actor->extravalue2-- <= 0 && players[actor->lastlook].mo) - { - P_SetTarget(&actor->tracer, players[actor->lastlook].mo); - actor->extravalue1 = 1; // TARGET ACQUIRED - actor->extravalue2 = 7*TICRATE; - actor->cvmem = wspeed; - } - } - else - { - actor->extravalue1 = 0; // SEEKING - actor->extravalue2 = 0; - spbplace = -1; - } - } - else // MODE: SEEKING - { - waypoint_t *lastwaypoint = NULL; - waypoint_t *bestwaypoint = NULL; - waypoint_t *nextwaypoint = NULL; - waypoint_t *tempwaypoint = NULL; - - actor->lastlook = -1; // Just make sure this is reset - - if (!player || !player->mo || player->mo->health <= 0 || (player->respawn.state != RESPAWNST_NONE)) - { - // No one there? Completely STOP. - actor->momx = actor->momy = actor->momz = 0; - if (!player) - spbplace = -1; - return; - } - - // Found someone, now get close enough to initiate the slaughter... - P_SetTarget(&actor->tracer, player->mo); - spbplace = bestrank; - dist = P_AproxDistance(P_AproxDistance(actor->x-actor->tracer->x, actor->y-actor->tracer->y), actor->z-actor->tracer->z); - - /* - K_GetBestWaypointForMobj returns the waypoint closest to the object that isn't its current waypoint. While this is usually good enough, - in cases where the track overlaps, this means that the SPB will sometimes target a waypoint on an earlier/later portion of the track instead of following along. - For this reason, we're going to try and make sure to avoid these situations. - */ - - // Move along the waypoints until you get close enough - if (actor->cusval > -1 && actor->extravalue2 > 0) - { - // Previously set nextwaypoint - lastwaypoint = K_GetWaypointFromIndex((size_t)actor->cusval); - tempwaypoint = K_GetBestWaypointForMobj(actor); - // check if the tempwaypoint corresponds to lastwaypoint's next ID at least; - // This is to avoid situations where the SPB decides to suicide jump down a bridge because it found a COMPLETELY unrelated waypoint down there. - - if (K_GetWaypointID(tempwaypoint) == K_GetWaypointNextID(lastwaypoint) || K_GetWaypointID(tempwaypoint) == K_GetWaypointID(lastwaypoint)) - // either our previous or curr waypoint ID, sure, take it - bestwaypoint = tempwaypoint; - else - bestwaypoint = K_GetWaypointFromIndex((size_t)actor->extravalue2); // keep going from the PREVIOUS wp. - } - else - bestwaypoint = K_GetBestWaypointForMobj(actor); - - if (bestwaypoint == NULL && lastwaypoint == NULL) - { - // We have invalid waypoints all around, so use closest to try and make it non-NULL. - bestwaypoint = K_GetClosestWaypointToMobj(actor); - } - - if (bestwaypoint != NULL) - { - const boolean huntbackwards = false; - boolean useshortcuts = false; - - // If the player is on a shortcut, use shortcuts. No escape. - if (K_GetWaypointIsShortcut(player->nextwaypoint)) - { - useshortcuts = true; - } - - nextwaypoint = K_GetNextWaypointToDestination( - bestwaypoint, player->nextwaypoint, useshortcuts, huntbackwards); - } - - if (nextwaypoint == NULL && lastwaypoint != NULL) - { - // Restore to the last nextwaypoint - nextwaypoint = lastwaypoint; - } - - if (nextwaypoint != NULL) - { - const fixed_t xywaypointdist = P_AproxDistance( - actor->x - nextwaypoint->mobj->x, actor->y - nextwaypoint->mobj->y); - - hang = R_PointToAngle2(actor->x, actor->y, nextwaypoint->mobj->x, nextwaypoint->mobj->y); - vang = R_PointToAngle2(0, actor->z, xywaypointdist, nextwaypoint->mobj->z); - - actor->cusval = (INT32)K_GetWaypointHeapIndex(nextwaypoint); - actor->extravalue2 = (INT32)K_GetWaypointHeapIndex(bestwaypoint); // save our last best, used above. - } - else - { - // continue straight ahead... Shouldn't happen. - hang = actor->angle; - vang = 0U; - } - - { - // Smoothly rotate horz angle - angle_t input = hang - actor->angle; - boolean invert = (input > ANGLE_180); - INT32 turnangle; - - if (invert) - input = InvAngle(input); - - input = FixedAngle(AngleFixed(input)/8); - - // Slow down when turning; it looks better and makes U-turns not unfair - xyspeed = FixedMul(wspeed, max(0, (((180<angle += input; - - // If input is small enough, spawn dust. Otherwise, spawn a slip tide! - turnangle = AngleFixed(input)/FRACUNIT; - - // The SPB is really turning if that value is >= 3 and <= 357. This looks pretty bad check-wise so feel free to change it for something that isn't as terrible. - if (turnangle >= 3 && turnangle <= 357) - SpawnSPBAIZDust(actor, turnangle < 180 ? 1 : -1); // 1 if turning left, -1 if turning right. Angles work counterclockwise, remember! - else - SpawnSPBDust(actor); // if we're mostly going straight, then spawn the V dust cone! - - SpawnSPBSpeedLines(actor); // Always spawn speed lines while seeking - - // Smoothly rotate vert angle - input = vang - actor->movedir; - invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - input = FixedAngle(AngleFixed(input)/8); - - // Slow down when turning; might as well do it for momz, since we do it above too - zspeed = FixedMul(wspeed, max(0, (((180<movedir += input; - } - - actor->momx = FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); - actor->momy = FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); - actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); - - // see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh! - for (i=0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || players[i].exiting) - continue; // not in-game - - if (R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y) < pdist) - { - pdist = R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y); - scplayer = &players[i]; // it doesn't matter if we override this guy now. - } - } - - // different player from our main target, try and ram into em~! - if (scplayer && scplayer != player) - { - pangle = actor->angle - R_PointToAngle2(actor->x, actor->y,scplayer->mo->x, scplayer->mo->y); - // check if the angle wouldn't make us LOSE speed... - if ((INT32)pangle/ANG1 >= -80 && (INT32)pangle/ANG1 <= 80) // allow for around 80 degrees - { - // Thrust us towards the guy, try to screw em up! - P_Thrust(actor, R_PointToAngle2(actor->x, actor->y, scplayer->mo->x, scplayer->mo->y), actor->movefactor/4); // not too fast though. - } - } - - // Spawn a trail of rings behind the SPB! - SpawnSPBTrailRings(actor); - - if (dist <= (1024*actor->tracer->scale) && !(actor->flags2 & MF2_AMBUSH)) // Close enough to target? Use Ambush flag to disable targetting so I can have an easier time testing stuff... - { - S_StartSound(actor, actor->info->attacksound); - actor->extravalue1 = 1; // TARGET ACQUIRED - actor->extravalue2 = 7*TICRATE; - actor->cvmem = wspeed; - } - } - - // Finally, no matter what, the spb should not be able to be under the ground, or above the ceiling; - if (actor->z < actor->floorz) - actor->z = actor->floorz; - else if (actor->z > actor->ceilingz - actor->height) - actor->z = actor->ceilingz - actor->height; - - return; -} - void A_SSMineSearch(mobj_t *actor) { fixed_t dis = INT32_MAX; diff --git a/src/p_mobj.c b/src/p_mobj.c index 0505d6bda..042cdad01 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6915,8 +6915,10 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->threshold--; break; case MT_SPB: - indirectitemcooldown = 20*TICRATE; - /* FALLTHRU */ + { + Obj_SPBThink(mobj); + break; + } case MT_BALLHOG: { mobj_t *ghost = P_SpawnGhostMobj(mobj);