mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
SPB in its own file + better waypoint movement
This commit is contained in:
parent
8f38555a86
commit
5767315787
8 changed files with 805 additions and 536 deletions
|
|
@ -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"},
|
||||
|
|
|
|||
40
src/info.c
40
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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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*/
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
hyudoro.c
|
||||
shrink.c
|
||||
spb.c
|
||||
|
|
|
|||
777
src/objects/spb.c
Normal file
777
src/objects/spb.c
Normal 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;
|
||||
}
|
||||
}
|
||||
511
src/p_enemy.c
511
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<<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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue