From 428106c0145d22645da3790a4f751d11c4284c70 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 13 Oct 2023 21:05:48 -0700 Subject: [PATCH] Loops: correct origin point over duration of loop - Players moving into a gate with a lot of momentum would offset the origin point from its intended position - Find intersection of player movement and gate - Correct origin point between loop entry and exit by interpolating the difference between player position on entry and intersection point --- src/d_player.h | 1 + src/objects/loops.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++- src/p_loop.c | 25 +++++++----- src/p_saveg.c | 4 ++ src/tables.h | 1 + 5 files changed, 109 insertions(+), 11 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index f4beaf95c..57b33bcf4 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -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; diff --git a/src/objects/loops.cpp b/src/objects/loops.cpp index 21d67c12f..c54103f9d 100644 --- a/src/objects/loops.cpp +++ b/src/objects/loops.cpp @@ -10,6 +10,8 @@ /// \file loop-endpoint.c /// \brief Sonic loops, start and end points +#include + #include "../doomdef.h" #include "../k_kart.h" #include "../taglist.h" @@ -149,6 +151,81 @@ get_binary_direction } } +static std::optional +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; } diff --git a/src/p_loop.c b/src/p_loop.c index e2d838f31..815a6aa61 100644 --- a/src/p_loop.c +++ b/src/p_loop.c @@ -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) @@ -98,6 +94,7 @@ boolean P_PlayerOrbit(player_t *player) sonicloopvars_t *s = &player->loop; angle_t pitch; + angle_t pitch_normal; fixed_t xy, z; fixed_t xs, ys; @@ -125,15 +122,25 @@ 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))); - th = get_shift_curve(s); + // 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)); diff --git a/src/p_saveg.c b/src/p_saveg.c index 685c7491a..dd8714cb1 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -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); diff --git a/src/tables.h b/src/tables.h index 1a1da82d3..61812cb2e 100644 --- a/src/tables.h +++ b/src/tables.h @@ -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"