RingRacers/src/objects/ball-switch.cpp
2023-09-26 11:40:29 -04:00

241 lines
5.8 KiB
C++

#include "../k_objects.h"
#include "../doomdef.h"
#include "../info.h"
#include "../p_local.h"
#include "../r_main.h"
#include "../k_hitlag.h"
#define ball_pad(o) ((o)->target)
#define ball_instawhipped(o) ((o)->extravalue1) // see instawhip collide
#define ball_cooldown(o) ((o)->cvmem)
#define ball_activedefer(o) ((o)->extravalue2)
#define ball_activator(o) ((o)->tracer)
namespace
{
struct BallSwitch_Pad : mobj_t
{
statenum_t Anim() const { return static_cast<statenum_t>(this->state - states); }
void Anim(statenum_t n)
{
if (Anim() != n)
{
P_SetMobjState(this, n);
}
}
void Spawned()
{
renderflags |= RF_FLOORSPRITE|RF_NOSPLATBILLBOARD|RF_SLOPESPLAT|RF_NOSPLATROLLANGLE;
}
void Tick(boolean active)
{
if (active == true)
{
Anim(S_BALLSWITCH_PAD_ACTIVE);
}
else
{
Anim(S_BALLSWITCH_PAD);
}
}
};
struct BallSwitch_Ball : mobj_t
{
BallSwitch_Pad *Pad() const { return static_cast<BallSwitch_Pad *>( ball_pad(this) ); }
void Pad(BallSwitch_Pad *n) { P_SetTarget(&ball_pad(this), n); }
statenum_t Anim() const { return static_cast<statenum_t>(this->state - states); }
void Anim(statenum_t n)
{
if (Anim() != n)
{
P_SetMobjState(this, n);
}
}
INT32 Cooldown() const { return ball_cooldown(this); }
void Cooldown(INT32 n) { ball_cooldown(this) = n; }
boolean Active() const { return (ball_cooldown(this) != 0); }
boolean DeferActivation() const { return ball_activedefer(this); }
mobj_t *Activator() const { return ball_activator(this); }
void DeferActivation(boolean n, mobj_t *src)
{
ball_activedefer(this) = n;
P_SetTarget(&ball_activator(this), src);
}
SINT8 IntSign(int value) const
{
if (value > 0)
{
return 1;
}
if (value < 0)
{
return -1;
}
return 0;
}
void Spawned()
{
Pad( static_cast<BallSwitch_Pad *>( P_SpawnMobjFromMobj(this, 0, 0, 0, MT_BALLSWITCH_PAD) ) );
Pad()->Spawned();
this->z += Pad()->height * P_MobjFlip(this);
}
void Tick()
{
if (P_MobjWasRemoved(Pad()) == true)
{
P_RemoveMobj(this);
return;
}
ball_instawhipped(this) = 0;
if (DeferActivation() == true)
{
P_ActivateThingSpecial(this, Activator());
Cooldown(-1); // maybe later?
DeferActivation(false, nullptr);
}
fixed_t ourZ = P_GetMobjFeet(this);
fixed_t theirZ = P_GetMobjHead(Pad());
fixed_t dist = P_AproxDistance(P_AproxDistance(Pad()->x - this->x, Pad()->y - this->y), theirZ - ourZ);
fixed_t move = P_AproxDistance(P_AproxDistance(this->momx, this->momy), this->momz);
constexpr INT32 accelScale = 4;
if (dist < accelScale * this->scale && move < accelScale * this->scale)
{
P_SetOrigin(this, Pad()->x, Pad()->y, theirZ);
this->momx = this->momy = this->momz = 0;
}
else
{
static constexpr const INT32 accel[2] = { FRACUNIT*3/4, FRACUNIT*3/16 };
constexpr fixed_t frict = FRACUNIT*99/100;
this->momx = FixedMul(this->momx, frict);
this->momy = FixedMul(this->momy, frict);
this->momz = FixedMul(this->momz, frict);
SINT8 xSign = IntSign(Pad()->x - this->x);
SINT8 ySign = IntSign(Pad()->y - this->y);
SINT8 zSign = IntSign(theirZ - ourZ);
boolean xAway = (IntSign(this->momx) == xSign);
boolean yAway = (IntSign(this->momy) == ySign);
boolean zAway = (IntSign(this->momz) == zSign);
this->momx += FixedMul(accel[xAway], accelScale * this->scale) * xSign;
this->momy += FixedMul(accel[yAway], accelScale * this->scale) * ySign;
this->momz += FixedMul(accel[zAway], accelScale * this->scale) * zSign;
this->angle += FixedAngle(move * 2);
if (dist > this->radius * 2)
{
P_Thrust(this, this->angle, (move / accelScale) * 2 / 3);
}
}
if (Active() == true)
{
INT32 cool = Cooldown();
if (cool > 0)
{
Cooldown(cool - 1);
}
Anim(S_BALLSWITCH_BALL_ACTIVE);
}
else
{
Anim(S_BALLSWITCH_BALL);
}
Pad()->Tick(Active());
}
void Push(mobj_t *toucher, const fixed_t pushValue, const fixed_t repelValue)
{
fixed_t push = FixedMul(pushValue, toucher->scale);
fixed_t repel = FixedMul(repelValue, this->scale);
angle_t thrustAngle = R_PointToAngle2(toucher->x, toucher->y, this->x, this->y);
fixed_t thrustAngleCos = FINECOSINE(thrustAngle >> ANGLETOFINESHIFT);
fixed_t thrustAngleSin = FINESINE(thrustAngle >> ANGLETOFINESHIFT);
fixed_t thisZ = this->z + (this->height / 2);
fixed_t toucherZ = toucher->z + (toucher->height / 2);
angle_t thrustPitch = R_PointToAngle2(0, toucherZ, R_PointToDist2(toucher->x, toucher->y, this->x, this->y), thisZ);
fixed_t thrustPitchCos = FINECOSINE(thrustPitch >> ANGLETOFINESHIFT);
fixed_t thrustPitchSin = FINESINE(thrustPitch >> ANGLETOFINESHIFT);
this->momx += FixedMul(FixedMul(push, thrustAngleCos), thrustPitchCos);
this->momy += FixedMul(FixedMul(push, thrustAngleSin), thrustPitchCos);
this->momz += FixedMul(push, thrustPitchSin);
toucher->momx -= FixedMul(FixedMul(repel, thrustAngleCos), thrustPitchCos);
toucher->momy -= FixedMul(FixedMul(repel, thrustAngleSin), thrustPitchCos);
toucher->momz -= FixedMul(repel, thrustPitchSin);
}
void Touch(mobj_t *toucher)
{
Push(toucher, 4 << FRACBITS, 6 << FRACBITS);
}
void Hit(mobj_t *inflictor, mobj_t *source)
{
Push(inflictor, 64 << FRACBITS, 1 << FRACBITS);
K_SetHitLagForObjects(this, inflictor, source, 4, true);
if (Active() == false)
{
DeferActivation(true, source);
}
}
};
}; // namespace
void Obj_BallSwitchInit(mobj_t *mobj)
{
BallSwitch_Ball *ball = static_cast<BallSwitch_Ball *>(mobj);
ball->Spawned();
}
void Obj_BallSwitchThink(mobj_t *mobj)
{
BallSwitch_Ball *ball = static_cast<BallSwitch_Ball *>(mobj);
ball->Tick();
}
void Obj_BallSwitchTouched(mobj_t *mobj, mobj_t *toucher)
{
BallSwitch_Ball *ball = static_cast<BallSwitch_Ball *>(mobj);
ball->Touch(toucher);
}
void Obj_BallSwitchDamaged(mobj_t *mobj, mobj_t *inflictor, mobj_t *source)
{
BallSwitch_Ball *ball = static_cast<BallSwitch_Ball *>(mobj);
ball->Hit(inflictor, source);
}