void A_SPBChase(mobj_t *actor)
{
player_t *player = NULL;
UINT8 i;
UINT8 bestrank = UINT8_MAX;
fixed_t dist;
angle_t hang, vang;
fixed_t wspeed, xyspeed, zspeed;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_SPBChase", actor))
return;
#endif
// Default speed
wspeed = actor->movefactor;
if (actor->threshold) // Just fired, go straight.
{
actor->lastlook = -1;
spbplace = -1;
P_InstaThrust(actor, actor->angle, wspeed);
actor->flags &= ~MF_NOCLIPTHING; // just in case.
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].kartstuff[k_respawn])
continue;*/ // respawning
if (players[i].kartstuff[k_position] < bestrank)
{
bestrank = players[i].kartstuff[k_position];
player = &players[i];
}
}
if (actor->extravalue1 == 1) // MODE: TARGETING
{
if (actor->tracer && actor->tracer->health)
{
fixed_t defspeed = wspeed;
fixed_t range = (160*actor->tracer->scale);
fixed_t cx = 0, cy =0;
// we're tailing a player, now's a good time to regain our damage properties
actor->flags &= ~MF_NOCLIPTHING;
// 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...
if (!P_IsObjectOnGround(actor->tracer) /*&& !actor->tracer->player->kartstuff[k_pogospring]*/)
{
// 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)) / 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->kartstuff[k_position] <= bestrank)
actor->extravalue2 = 7*TICRATE;
else if (actor->extravalue2-- <= 0)
actor->extravalue1 = 0; // back to SEEKING
spbplace = actor->tracer->player->kartstuff[k_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->pflags & PF_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));
// 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)/4) // Don't display speedup lines at pitifully low speeds
{
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);
fast->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy);
//fast->momx = (3*actor->momx)/4;
//fast->momy = (3*actor->momy)/4;
//fast->momz = (3*actor->momz)/4;
fast->color = SKINCOLOR_RED;
fast->colorized = true;
K_MatchGenericExtraFlags(fast, 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
// don't hurt players that have nothing to do with this:
actor->flags |= MF_NOCLIPTHING;
if (actor->lastlook != -1
&& playeringame[actor->lastlook]
&& !players[actor->lastlook].spectator
&& !players[actor->lastlook].exiting)
{
spbplace = players[actor->lastlook].kartstuff[k_position];
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
{
actor->lastlook = -1; // Just make sure this is reset
if (!player || !player->mo || player->mo->health <= 0 || player->kartstuff[k_respawn])
{
// 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...
// don't hurt players that have nothing to do with this:
actor->flags |= MF_NOCLIPTHING;
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);
hang = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
vang = R_PointToAngle2(0, actor->z, dist, actor->tracer->z);
{
// 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(wspeed, 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(wspeed, max(0, (((180<<FRACBITS) - AngleFixed(input)) / 90) - FRACUNIT));
input = FixedAngle(AngleFixed(input)/4);
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));
if (dist <= (3072*actor->tracer->scale)) // Close enough to target?
{
S_StartSound(actor, actor->info->attacksound); // Siren sound; might not need this anymore, but I'm keeping it for now just for debugging.
actor->extravalue1 = 1; // TARGET ACQUIRED
actor->extravalue2 = 7*TICRATE;
actor->cvmem = wspeed;
}
}
return;
}