SPB in its own file + better waypoint movement

This commit is contained in:
Sally Coolatta 2022-09-18 01:07:46 -04:00
parent 8f38555a86
commit 5767315787
8 changed files with 805 additions and 536 deletions

View file

@ -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"},

View file

@ -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

View file

@ -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();

View file

@ -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*/

View file

@ -1,2 +1,3 @@
hyudoro.c
shrink.c
spb.c

777
src/objects/spb.c Normal file
View file

@ -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;
}
}

View file

@ -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<<FRACBITS; // best player distance when seeking
angle_t pangle; // angle between us and the player
if (LUA_CallAction(A_SPBCHASE, actor))
return;
// Default speed
wspeed = FixedMul(mapobjectscale, K_GetKartSpeedFromStat(5)*2); // Go at twice the average speed a player would be going at!
if (actor->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)<<FRACBITS) - easiness, K_GetKartSpeed(actor->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<<FRACBITS) - AngleFixed(input)) / 90) - FRACUNIT));
input = FixedAngle(AngleFixed(input)/4);
if (invert)
input = InvAngle(input);
actor->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<<FRACBITS) - AngleFixed(input)) / 90) - FRACUNIT));
input = FixedAngle(AngleFixed(input)/4);
if (invert)
input = InvAngle(input);
actor->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<<FRACBITS) - AngleFixed(input)) / 90) - FRACUNIT));
if (invert)
input = InvAngle(input);
actor->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<<FRACBITS) - AngleFixed(input)) / 90) - FRACUNIT));
if (invert)
input = InvAngle(input);
actor->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;

View file

@ -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);