RingRacers/src/objects/jawz.c
2024-04-05 10:26:52 -04:00

315 lines
7.3 KiB
C

// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2024 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2024 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 jawz.c
/// \brief Jawz 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"
#include "../k_collide.h"
#include "../k_specialstage.h"
#define MAX_JAWZ_TURN (ANGLE_90 / 15) // We can turn a maximum of 6 degrees per frame at regular max speed
#define jawz_speed(o) ((o)->movefactor)
#define jawz_selfdelay(o) ((o)->threshold)
#define jawz_dropped(o) ((o)->flags2 & MF2_AMBUSH)
#define jawz_droptime(o) ((o)->movecount)
#define jawz_retcolor(o) ((o)->cvmem)
#define jawz_stillturn(o) ((o)->cusval)
#define jawz_owner(o) ((o)->target)
#define jawz_chase(o) ((o)->tracer)
static void JawzChase(mobj_t *th, boolean grounded)
{
fixed_t thrustamount = 0;
fixed_t frictionsafety = (th->friction == 0) ? 1 : th->friction;
fixed_t topspeed = jawz_speed(th);
if (jawz_chase(th) != NULL && P_MobjWasRemoved(jawz_chase(th)) == false)
{
if (jawz_chase(th)->health > 0)
{
const angle_t targetangle = R_PointToAngle2(
th->x, th->y,
jawz_chase(th)->x, jawz_chase(th)->y
);
angle_t angledelta = th->angle - targetangle;
mobj_t *ret = NULL;
if (gametyperules & GTR_CIRCUIT)
{
const fixed_t distbarrier = FixedMul(
512 * mapobjectscale,
FRACUNIT + ((gamespeed-1) * (FRACUNIT/4))
);
const fixed_t distaway = P_AproxDistance(
jawz_chase(th)->x - th->x,
jawz_chase(th)->y - th->y
);
if (distaway < distbarrier)
{
if (jawz_chase(th)->player != NULL)
{
fixed_t speeddifference = abs(
topspeed - min(
jawz_chase(th)->player->speed,
K_GetKartSpeed(jawz_chase(th)->player, false, false)
)
);
topspeed = topspeed - FixedMul(speeddifference, FRACUNIT - FixedDiv(distaway, distbarrier));
}
}
}
if (angledelta != 0)
{
angle_t turnSpeed = MAX_JAWZ_TURN;
boolean turnclockwise = true;
// MAX_JAWZ_TURN gets stronger the slower the top speed of jawz
if (topspeed < jawz_speed(th))
{
if (topspeed == 0)
{
turnSpeed = ANGLE_180;
}
else
{
fixed_t anglemultiplier = FixedDiv(jawz_speed(th), topspeed);
turnSpeed += FixedAngle(FixedMul(AngleFixed(turnSpeed), anglemultiplier));
}
}
if (angledelta > ANGLE_180)
{
angledelta = InvAngle(angledelta);
turnclockwise = false;
}
if (angledelta > turnSpeed)
{
angledelta = turnSpeed;
}
if (turnclockwise == true)
{
th->angle -= angledelta;
}
else
{
th->angle += angledelta;
}
}
ret = P_SpawnMobjFromMobj(jawz_chase(th), 0, 0, 0, MT_PLAYERRETICULE);
ret->old_x = jawz_chase(th)->old_x;
ret->old_y = jawz_chase(th)->old_y;
ret->old_z = jawz_chase(th)->old_z;
P_SetTarget(&ret->target, jawz_chase(th));
ret->frame |= ((leveltime % 10) / 2) + 5;
ret->color = jawz_retcolor(th);
ret->renderflags = (ret->renderflags & ~RF_DONTDRAW) | (th->renderflags & RF_DONTDRAW);
ret->hitlag = 0; // spawns every tic, so don't inherit player hitlag
}
else
{
P_SetTarget(&jawz_chase(th), NULL);
}
}
if (jawz_chase(th) == NULL || P_MobjWasRemoved(jawz_chase(th)) == true)
{
mobj_t *newChase = NULL;
player_t *owner = NULL;
th->angle = K_MomentumAngle(th);
if ((jawz_owner(th) != NULL && P_MobjWasRemoved(jawz_owner(th)) == false)
&& (jawz_owner(th)->player != NULL))
{
owner = jawz_owner(th)->player;
}
newChase = K_FindJawzTarget(th, owner, ANGLE_90);
if (newChase != NULL)
{
P_SetTarget(&jawz_chase(th), newChase);
}
}
if (jawz_stillturn(th) > 0)
{
// When beginning to chase your own owner,
// we should turn but not thrust quite yet.
return;
}
if (grounded == true)
{
const fixed_t currentspeed = R_PointToDist2(0, 0, th->momx, th->momy);
if (currentspeed >= topspeed)
{
// Thrust as if you were at top speed, slow down naturally
thrustamount = FixedDiv(topspeed, frictionsafety) - topspeed;
}
else
{
const fixed_t beatfriction = FixedDiv(currentspeed, frictionsafety) - currentspeed;
// Thrust to immediately get to top speed
thrustamount = beatfriction + FixedDiv(topspeed - currentspeed, frictionsafety);
}
P_Thrust(th, th->angle, thrustamount);
}
}
static boolean JawzSteersBetter(void)
{
return !!!(gametyperules & GTR_CIRCUIT);
}
void Obj_JawzThink(mobj_t *th)
{
mobj_t *ghost = P_SpawnGhostMobj(th);
boolean grounded = P_IsObjectOnGround(th);
if (th->fuse > 0 && th->fuse <= TICRATE)
{
th->renderflags ^= RF_DONTDRAW;
}
if (jawz_dropped(th))
{
if (grounded && (th->flags & MF_NOCLIPTHING))
{
th->momx = 1;
th->momy = 0;
S_StartSound(th, th->info->deathsound);
th->flags &= ~MF_NOCLIPTHING;
}
return;
}
if (jawz_owner(th) != NULL && P_MobjWasRemoved(jawz_owner(th)) == false
&& jawz_owner(th)->player != NULL)
{
ghost->color = jawz_owner(th)->player->skincolor;
ghost->colorized = true;
}
if (JawzSteersBetter() == true)
{
th->friction = max(0, 3 * th->friction / 4);
}
JawzChase(th, grounded);
K_DriftDustHandling(th);
/* todo: UDMFify
if (P_MobjTouchingSectorSpecialFlag(th, ?))
{
K_DoPogoSpring(th, 0, 1);
}
*/
if (jawz_selfdelay(th) > 0)
{
jawz_selfdelay(th)--;
}
if (jawz_stillturn(th) > 0)
{
jawz_stillturn(th)--;
}
if (leveltime % TICRATE == 0)
{
S_StartSound(th, th->info->activesound);
}
}
void Obj_JawzThrown(mobj_t *th, fixed_t finalSpeed, SINT8 dir)
{
INT32 lastTarg = -1;
player_t *owner = NULL;
if (jawz_owner(th) != NULL && P_MobjWasRemoved(jawz_owner(th)) == false
&& jawz_owner(th)->player != NULL)
{
lastTarg = jawz_owner(th)->player->lastjawztarget;
jawz_retcolor(th) = jawz_owner(th)->player->skincolor;
owner = jawz_owner(th)->player;
}
else
{
jawz_retcolor(th) = SKINCOLOR_KETCHUP;
}
if (dir == -1)
{
// Thrown backwards, init self-chase
P_SetTarget(&jawz_chase(th), jawz_owner(th));
// Stop it here.
th->momx = 0;
th->momy = 0;
// Slow down the top speed.
finalSpeed = FixedMul(finalSpeed, 4*FRACUNIT/5);
// Set a fuse.
th->fuse = RR_PROJECTILE_FUSE;
// Stay still while you turn towards the player
jawz_stillturn(th) = ANGLE_180 / MAX_JAWZ_TURN;
}
else
{
if ((lastTarg >= 0 && lastTarg < MAXPLAYERS)
&& playeringame[lastTarg] == true)
{
player_t *tryPlayer = &players[lastTarg];
if (tryPlayer->spectator == false)
{
P_SetTarget(&jawz_chase(th), tryPlayer->mo);
}
}
// Sealed Star: target the UFO immediately. I don't
// wanna fuck with the lastjawztarget stuff, so just
// do this if a target wasn't set.
if (jawz_chase(th) == NULL || P_MobjWasRemoved(jawz_chase(th)) == true)
{
P_SetTarget(&jawz_chase(th), K_FindJawzTarget(th, owner, ANGLE_90));
}
}
S_StartSound(th, th->info->activesound);
jawz_speed(th) = finalSpeed;
}