From b9615d7225f7c24be4685897aaafa40a25da8508 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 13 Oct 2023 20:25:32 -0700 Subject: [PATCH 1/8] objects/loop.c -> objects/loops.cpp --- src/objects/CMakeLists.txt | 2 +- src/objects/{loops.c => loops.cpp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/objects/{loops.c => loops.cpp} (100%) diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index db3656d9e..1e99c2f0f 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -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 diff --git a/src/objects/loops.c b/src/objects/loops.cpp similarity index 100% rename from src/objects/loops.c rename to src/objects/loops.cpp From 428106c0145d22645da3790a4f751d11c4284c70 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 13 Oct 2023 21:05:48 -0700 Subject: [PATCH 2/8] 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" From 107809d876cd6434f76d4aa76284a2cca2ca457a Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 13 Oct 2023 21:17:03 -0700 Subject: [PATCH 3/8] Loops: guarantee exit at exact intended angle --- src/p_loop.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/p_loop.c b/src/p_loop.c index 815a6aa61..12ef3d714 100644 --- a/src/p_loop.c +++ b/src/p_loop.c @@ -160,13 +160,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) From 2c1cce17b238175eab11776e479c12f9ec45b27d Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 13 Oct 2023 21:17:43 -0700 Subject: [PATCH 4/8] Loops: fix momentum cut on grounded exit --- src/p_user.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index ed8f1a215..3af27cf0e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4265,7 +4265,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 { From c87ffeba29fb4c6fa07921eabff3c7fed9257f3c Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 13 Oct 2023 21:20:52 -0700 Subject: [PATCH 5/8] Loops: try to position player so hitbox edge touches loop radius Large enough momentum will still position the player beyond the loop radius initially. This is intentional so that there is no jerk when entering the loop. Without a more sophisticated algorithm to revolve asymmetrically, this is the best I can do. The player is guaranteed to exit at the correct position, though. --- src/k_kart.c | 9 +++++++++ src/p_loop.c | 10 +++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 8ea9d92ab..9635ad966 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -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! diff --git a/src/p_loop.c b/src/p_loop.c index 12ef3d714..b0aeb7a79 100644 --- a/src/p_loop.c +++ b/src/p_loop.c @@ -96,7 +96,7 @@ boolean P_PlayerOrbit(player_t *player) 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; @@ -124,9 +124,13 @@ boolean P_PlayerOrbit(player_t *player) pitch = get_pitch(s->revolution); pitch_normal = get_pitch(normal_revolution(s) / 2); + r = abs(s->radius) - + FixedMul(player->mo->radius, abs(FSIN(pitch))); - xy = FixedMul(abs(s->radius), FSIN(pitch)); - z = FixedMul(abs(s->radius), -(FCOS(pitch))); + 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. From 0baad7b863cd821e0205c7de3932b46cdcc9183c Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 14 Oct 2023 19:28:20 -0700 Subject: [PATCH 6/8] Loops: lag camera correctly --- src/p_user.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 3af27cf0e..17f04c76f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -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) From 905592003d0fe5f281402c6863f454f0f42be14a Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 14 Oct 2023 19:29:09 -0700 Subject: [PATCH 7/8] Add K_SetTireGrease, spawn spring grease VFX and set tiregrease --- src/k_kart.c | 35 ++++++++++++++++++++--------------- src/k_kart.h | 2 ++ src/p_loop.c | 2 +- src/p_map.c | 20 +------------------- 4 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 9635ad966..b65553acc 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10511,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); @@ -12479,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; +} + //} diff --git a/src/k_kart.h b/src/k_kart.h index af773c953..7d0379760 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -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 diff --git a/src/p_loop.c b/src/p_loop.c index b0aeb7a79..e8b7e44bb 100644 --- a/src/p_loop.c +++ b/src/p_loop.c @@ -84,7 +84,7 @@ void P_ExitPlayerOrbit(player_t *player) } // tiregrease gives less friction, extends momentum - player->tiregrease = TICRATE; + K_SetTireGrease(player, TICRATE); P_HaltPlayerOrbit(player); } diff --git a/src/p_map.c b/src/p_map.c index dd23deae7..9bd4a141a 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -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)); } } From 27609b008fb49c9fb6d4204d4c3f9878d92be976 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 14 Oct 2023 19:29:27 -0700 Subject: [PATCH 8/8] Loops: triple exit spring grease --- src/p_loop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_loop.c b/src/p_loop.c index e8b7e44bb..bc1454a5d 100644 --- a/src/p_loop.c +++ b/src/p_loop.c @@ -84,7 +84,7 @@ void P_ExitPlayerOrbit(player_t *player) } // tiregrease gives less friction, extends momentum - K_SetTireGrease(player, TICRATE); + K_SetTireGrease(player, 3*TICRATE); P_HaltPlayerOrbit(player); }