Merge branch 'fuck-you-loops' into 'master'

Fix inaccuracies on loop exit + smoother entry

See merge request KartKrew/Kart!1552
This commit is contained in:
Oni 2023-10-15 21:36:54 +00:00
commit 2e1983dd7f
10 changed files with 167 additions and 53 deletions

View file

@ -471,6 +471,7 @@ typedef struct {
fixed_t revolution, min_revolution, max_revolution;
angle_t yaw;
vector3_t origin;
vector2_t origin_shift;
vector2_t shift;
boolean flip;
} sonicloopvars_t;

View file

@ -7847,6 +7847,15 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->mo->sprzoff += player->cameraOffset;
}
if (player->loop.radius)
{
// Offset sprite Z position so wheels touch top of
// hitbox when rotated 180 degrees.
// TODO: this should be generalized for pitch/roll
angle_t pitch = FixedAngle(player->loop.revolution * 360) / 2;
player->mo->sprzoff += FixedMul(player->mo->height, FSIN(pitch));
}
K_UpdateOffroad(player);
K_UpdateDraft(player);
K_UpdateEngineSounds(player); // Thanks, VAda!
@ -10502,21 +10511,7 @@ static void K_KartSpindash(player_t *player)
P_InstaThrust(player->mo, player->mo->angle, thrust);
}
if (!player->tiregrease)
{
UINT8 i;
for (i = 0; i < 2; i++)
{
mobj_t *grease;
grease = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_TIREGREASE);
P_SetTarget(&grease->target, player->mo);
grease->angle = K_MomentumAngle(player->mo);
grease->extravalue1 = i;
K_ReduceVFX(grease, player);
}
}
player->tiregrease = 2*TICRATE;
K_SetTireGrease(player, 2*TICRATE);
player->spindash = 0;
S_ReducedVFXSound(player->mo, sfx_s23c, player);
@ -12470,4 +12465,23 @@ boolean K_Cooperative(void)
return false;
}
void K_SetTireGrease(player_t *player, tic_t tics)
{
if (!player->tiregrease)
{
UINT8 i;
for (i = 0; i < 2; i++)
{
mobj_t *grease;
grease = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_TIREGREASE);
P_SetTarget(&grease->target, player->mo);
grease->angle = K_MomentumAngle(player->mo);
grease->extravalue1 = i;
K_ReduceVFX(grease, player);
}
}
player->tiregrease = tics;
}
//}

View file

@ -235,6 +235,8 @@ boolean K_Cooperative(void);
// lat: used for when the player is in some weird state where it wouldn't be wise for it to be overwritten by another object that does similarly wacky shit.
boolean K_isPlayerInSpecialState(player_t *p);
void K_SetTireGrease(player_t *player, tic_t tics);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -12,7 +12,7 @@ target_sources(SRB2SDL2 PRIVATE
ufo.c
monitor.c
item-spot.c
loops.c
loops.cpp
drop-target.c
ring-shooter.c
audience.c

View file

@ -10,6 +10,8 @@
/// \file loop-endpoint.c
/// \brief Sonic loops, start and end points
#include <optional>
#include "../doomdef.h"
#include "../k_kart.h"
#include "../taglist.h"
@ -149,6 +151,81 @@ get_binary_direction
}
}
static std::optional<vector2_t>
intersect
( const mobj_t * anchor,
const mobj_t * toucher)
{
struct Line
{
angle_t a;
vector2_t o;
angle_t k = AbsAngle(a);
Line(vector2_t o_, angle_t a_) : a(a_), o(o_) {}
bool vertical() const { return k == ANGLE_90; }
fixed_t m() const
{
// tangent table is offset 90 degrees
return FTAN(a - ANGLE_90);
}
fixed_t b() const
{
return o.y - FixedMul(o.x, m());
}
fixed_t y(fixed_t x) const
{
return FixedMul(m(), x) + b();
}
};
if (toucher->momx == 0 && toucher->momy == 0)
{
// undefined angle
return {};
}
Line a({toucher->x, toucher->y},
R_PointToAngle2(0, 0, toucher->momx, toucher->momy));
Line b({anchor->x, anchor->y}, anchor->angle + ANGLE_90);
if (a.k == b.k)
{
// parallel lines do not intersect
return {};
}
vector2_t v;
auto v_intersect = [&v](Line &a, Line &b)
{
if (a.vertical())
{
return false;
}
v.x = b.o.x;
v.y = a.y(v.x);
return true;
};
if (!v_intersect(a, b) && !v_intersect(b, a))
{
// untested!
v.x = FixedDiv(a.b() - b.b(), b.m() - a.m());
v.y = a.y(v.x);
}
return v;
}
mobj_t *
Obj_FindLoopCenter (const mtag_t tag)
{
@ -226,8 +303,8 @@ Obj_LoopEndpointCollide
fixed_t radius;
/* predict movement for a smooth transition */
const fixed_t px = toucher->x + toucher->momx;
const fixed_t py = toucher->y + toucher->momy;
const fixed_t px = toucher->x + 2 * toucher->momx;
const fixed_t py = toucher->y + 2 * toucher->momy;
SINT8 flip;
@ -251,6 +328,11 @@ Obj_LoopEndpointCollide
if (!P_MobjWasRemoved(anchor_other(anchor)))
{
set_shiftxy(player, anchor);
vector2_t i = intersect(anchor, toucher)
.value_or(vector2_t {px, py});
s->origin_shift = {i.x - px, i.y - py};
}
flip = get_binary_direction(pitch, toucher);
@ -276,4 +358,7 @@ Obj_LoopEndpointCollide
/* cancel the effects of K_Squish */
toucher->spritexscale = FRACUNIT;
toucher->spriteyscale = FRACUNIT;
toucher->momx = 0;
toucher->momy = 0;
}

View file

@ -24,15 +24,11 @@ get_pitch (fixed_t revolution)
}
static inline fixed_t
get_shift_curve (const sonicloopvars_t *s)
normal_revolution (const sonicloopvars_t *s)
{
const angle_t th = get_pitch(FixedDiv(
s->revolution - s->min_revolution,
s->max_revolution - s->min_revolution));
// XY shift is transformed on wave scale; less movement
// at start and end of rotation, more halfway.
return FSIN((th / 2) - ANGLE_90);
return FixedDiv(
s->revolution - s->min_revolution,
s->max_revolution - s->min_revolution);
}
void P_HaltPlayerOrbit(player_t *player)
@ -88,7 +84,7 @@ void P_ExitPlayerOrbit(player_t *player)
}
// tiregrease gives less friction, extends momentum
player->tiregrease = TICRATE;
K_SetTireGrease(player, 3*TICRATE);
P_HaltPlayerOrbit(player);
}
@ -98,8 +94,9 @@ boolean P_PlayerOrbit(player_t *player)
sonicloopvars_t *s = &player->loop;
angle_t pitch;
angle_t pitch_normal;
fixed_t xy, z;
fixed_t r, xy, z;
fixed_t xs, ys;
fixed_t step, th, left;
@ -125,15 +122,29 @@ boolean P_PlayerOrbit(player_t *player)
}
pitch = get_pitch(s->revolution);
pitch_normal = get_pitch(normal_revolution(s) / 2);
xy = FixedMul(abs(s->radius), FSIN(pitch));
z = FixedMul(abs(s->radius), -(FCOS(pitch)));
r = abs(s->radius) -
FixedMul(player->mo->radius, abs(FSIN(pitch)));
th = get_shift_curve(s);
xy = FixedMul(r, FSIN(pitch));
z = FixedMul(abs(s->radius), -(FCOS(pitch))) -
FixedMul(player->mo->height, FSIN(pitch / 2));
// XY shift is transformed on wave scale; less movement
// at start and end of rotation, more halfway.
th = FSIN(pitch_normal - ANGLE_90);
xs = FixedMul(s->shift.x, th);
ys = FixedMul(s->shift.y, th);
// Interpolate 0-1 over entire rotation.
th = FSIN(pitch_normal / 2);
xs += FixedMul(s->origin_shift.x, th);
ys += FixedMul(s->origin_shift.y, th);
xs += FixedMul(xy, FCOS(s->yaw));
ys += FixedMul(xy, FSIN(s->yaw));
@ -153,13 +164,18 @@ boolean P_PlayerOrbit(player_t *player)
left = (s->max_revolution - s->revolution);
if (abs(left) < abs(step))
if (left == 0)
{
P_ExitPlayerOrbit(player);
return false;
}
if (abs(left) < abs(step))
{
step = left;
}
// If player slows down by too much, throw them out of
// the loop in a tumble.
if (player->speed < player->mo->scale)

View file

@ -323,25 +323,7 @@ P_DoSpringEx
object->player->springstars = max(abs(vertispeed), horizspeed) / FRACUNIT / 2;
object->player->springcolor = starcolor;
// Less friction when hitting springs
if (!object->player->tiregrease)
{
UINT8 i;
for (i = 0; i < 2; i++)
{
mobj_t *grease;
grease = P_SpawnMobj(object->x, object->y, object->z, MT_TIREGREASE);
P_SetTarget(&grease->target, object);
grease->angle = K_MomentumAngle(object);
grease->extravalue1 = i;
K_ReduceVFX(grease, object->player);
}
}
if (object->player->tiregrease < greasetics)
{
object->player->tiregrease = greasetics;
}
K_SetTireGrease(object->player, max(object->player->tiregrease, greasetics));
}
}

View file

@ -674,6 +674,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEFIXED(save->p, players[i].loop.origin.x);
WRITEFIXED(save->p, players[i].loop.origin.y);
WRITEFIXED(save->p, players[i].loop.origin.z);
WRITEFIXED(save->p, players[i].loop.origin_shift.x);
WRITEFIXED(save->p, players[i].loop.origin_shift.y);
WRITEFIXED(save->p, players[i].loop.shift.x);
WRITEFIXED(save->p, players[i].loop.shift.y);
WRITEUINT8(save->p, players[i].loop.flip);
@ -1196,6 +1198,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].loop.origin.x = READFIXED(save->p);
players[i].loop.origin.y = READFIXED(save->p);
players[i].loop.origin.z = READFIXED(save->p);
players[i].loop.origin_shift.x = READFIXED(save->p);
players[i].loop.origin_shift.y = READFIXED(save->p);
players[i].loop.shift.x = READFIXED(save->p);
players[i].loop.shift.y = READFIXED(save->p);
players[i].loop.flip = READUINT8(save->p);

View file

@ -3289,7 +3289,15 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
dist -= FixedMul(11*dist/16, player->karthud[khud_boostcam]);
}
speed = P_AproxDistance(P_AproxDistance(mo->momx, mo->momy), mo->momz / 16);
if (player->loop.radius)
{
speed = player->speed;
}
else
{
speed = P_AproxDistance(P_AproxDistance(mo->momx, mo->momy), mo->momz / 16);
}
lag = FRACUNIT - ((FixedDiv(speed, speedthreshold) - FRACUNIT) * 2);
if (lag > FRACUNIT)
@ -4265,7 +4273,8 @@ void P_PlayerThink(player_t *player)
else if (player->loop.radius != 0)
{
P_PlayerOrbit(player);
player->rmomx = player->rmomy = 0;
player->rmomx = player->mo->momx;
player->rmomy = player->mo->momy;
}
else
{

View file

@ -139,6 +139,7 @@ void FM_Rotate(matrix_t *dest, angle_t angle, fixed_t x, fixed_t y, fixed_t z);
// FSIN(ANGLE_90) = FRACUNIT
#define FSIN(n) FINESINE(ANGLETOFINE(n))
#define FCOS(n) FINECOSINE(ANGLETOFINE(n))
#define FTAN(n) FINETANGENT(((n) >> ANGLETOFINESHIFT) & ((FINEANGLES / 2) - 1))
#ifdef __cplusplus
} // extern "C"