diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 898fcc789..8bd399a1a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 p_floor.c p_inter.c p_lights.c + p_loop.c p_map.c p_maputl.c p_mobj.c diff --git a/src/p_local.h b/src/p_local.h index bf847fbca..12362a548 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -193,6 +193,10 @@ boolean P_AutoPause(void); void P_ElementalFire(player_t *player, boolean cropcircle); void P_SpawnSkidDust(player_t *player, fixed_t radius, boolean sound); +void P_HaltPlayerOrbit(player_t *player); +void P_ExitPlayerOrbit(player_t *player); +boolean P_PlayerOrbit(player_t *player); + void P_MovePlayer(player_t *player); void P_PlayerThink(player_t *player); void P_PlayerAfterThink(player_t *player); diff --git a/src/p_loop.c b/src/p_loop.c new file mode 100644 index 000000000..e2d838f31 --- /dev/null +++ b/src/p_loop.c @@ -0,0 +1,180 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2023 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 p_loop.c +/// \brief Sonic loop physics + +#include "doomdef.h" +#include "d_player.h" +#include "k_kart.h" +#include "p_local.h" +#include "p_setup.h" +#include "p_slopes.h" +#include "r_main.h" + +static inline angle_t +get_pitch (fixed_t revolution) +{ + return FixedAngle((revolution & FRACMASK) * 360); +} + +static inline fixed_t +get_shift_curve (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); +} + +void P_HaltPlayerOrbit(player_t *player) +{ + // see P_PlayerOrbit + player->mo->flags &= ~(MF_NOCLIPHEIGHT); + + player->loop.radius = 0; +} + +void P_ExitPlayerOrbit(player_t *player) +{ + sonicloopvars_t *s = &player->loop; + + angle_t pitch = get_pitch(s->revolution); + angle_t yaw = s->yaw; + + fixed_t co, si; + fixed_t speed; + + if (s->radius < 0) + { + pitch += ANGLE_180; + } + + co = FCOS(pitch); + si = FSIN(pitch); + + speed = FixedMul(co, player->speed); + + P_InstaThrust(player->mo, yaw, speed); + + player->mo->momz = FixedMul(si, player->speed); + + if (speed < 0) + { + yaw += ANGLE_180; + } + + // excludes only extremely vertical angles + if (abs(co) * 4 > abs(si)) + { + P_SetPlayerAngle(player, yaw); + } + + if (s->flip) + { + player->mo->eflags ^= MFE_VERTICALFLIP; + player->mo->flags2 ^= MF2_OBJECTFLIP; + + P_SetPitchRoll(player->mo, + pitch + ANGLE_180, s->yaw); + } + + // tiregrease gives less friction, extends momentum + player->tiregrease = TICRATE; + + P_HaltPlayerOrbit(player); +} + +boolean P_PlayerOrbit(player_t *player) +{ + sonicloopvars_t *s = &player->loop; + + angle_t pitch; + + fixed_t xy, z; + fixed_t xs, ys; + + fixed_t step, th, left; + + fixed_t grav; + + if (s->radius == 0) + { + return false; + } + + grav = abs(P_GetMobjGravity(player->mo)); + + // Lose speed on the way up. revolution = 0.5 always + // points straight up. + if (abs(s->revolution & FRACMASK) < FRACUNIT/2) + { + player->speed -= grav; + } + else + { + player->speed += 4 * grav; + } + + pitch = get_pitch(s->revolution); + + xy = FixedMul(abs(s->radius), FSIN(pitch)); + z = FixedMul(abs(s->radius), -(FCOS(pitch))); + + th = get_shift_curve(s); + + xs = FixedMul(s->shift.x, th); + ys = FixedMul(s->shift.y, th); + + xs += FixedMul(xy, FCOS(s->yaw)); + ys += FixedMul(xy, FSIN(s->yaw)); + + P_MoveOrigin(player->mo, + s->origin.x + xs, + s->origin.y + ys, + s->origin.z + z); + + // Match rollangle to revolution + P_SetPitchRoll(player->mo, + s->radius < 0 ? (ANGLE_180 + pitch) : pitch, + s->yaw); + + // circumfrence = (2r)PI + step = FixedDiv(player->speed, + FixedMul(s->radius, M_TAU_FIXED)); + + left = (s->max_revolution - s->revolution); + + if (abs(left) < abs(step)) + { + P_ExitPlayerOrbit(player); + + return false; + } + + // If player slows down by too much, throw them out of + // the loop in a tumble. + if (player->speed < player->mo->scale) + { + P_HaltPlayerOrbit(player); + K_StumblePlayer(player); + + return false; + } + + s->revolution += step; + + // We need to not clip the ground. It sucks but setting + // this flag is the only way to do that. + player->mo->flags |= (MF_NOCLIPHEIGHT); + + return true; +} diff --git a/src/p_map.c b/src/p_map.c index 1f7051c10..8c5751d2a 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2674,6 +2674,22 @@ fixed_t P_GetThingStepUp(mobj_t *thing, fixed_t destX, fixed_t destY) return maxstep; } +static boolean P_UsingStepUp(mobj_t *thing) +{ + if (thing->flags & MF_NOCLIP) + { + return false; + } + + // orbits have no collision + if (thing->player && thing->player->loop.radius) + { + return false; + } + + return true; +} + static boolean increment_move ( mobj_t * thing, @@ -2734,7 +2750,7 @@ increment_move // copy into the spechitint buffer from spechit spechitint_copyinto(); - if (!(thing->flags & MF_NOCLIP)) + if (P_UsingStepUp(thing)) { // All things are affected by their scale. fixed_t maxstep = P_GetThingStepUp(thing, tryx, tryy); diff --git a/src/p_mobj.c b/src/p_mobj.c index b9fa47a36..1fb36e854 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3979,7 +3979,8 @@ static void P_CheckFloatbobPlatforms(mobj_t *mobj) static void P_SquishThink(mobj_t *mobj) { if (!(mobj->flags & MF_NOSQUISH) && - !(mobj->eflags & MFE_SLOPELAUNCHED)) + !(mobj->eflags & MFE_SLOPELAUNCHED) && + !(mobj->player && mobj->player->loop.radius != 0)) { K_Squish(mobj); } @@ -4002,7 +4003,8 @@ static void P_PlayerMobjThinker(mobj_t *mobj) // Zoom tube if ((mobj->player->carry == CR_ZOOMTUBE && mobj->tracer && !P_MobjWasRemoved(mobj->tracer)) - || mobj->player->respawn.state == RESPAWNST_MOVE) + || mobj->player->respawn.state == RESPAWNST_MOVE + || mobj->player->loop.radius != 0) { P_HitSpecialLines(mobj, mobj->x, mobj->y, mobj->momx, mobj->momy); P_UnsetThingPosition(mobj); diff --git a/src/p_user.c b/src/p_user.c index 223af8f47..7826d4071 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4227,6 +4227,11 @@ void P_PlayerThink(player_t *player) P_DoZoomTube(player); player->rmomx = player->rmomy = 0; } + else if (player->loop.radius != 0) + { + P_PlayerOrbit(player); + player->rmomx = player->rmomy = 0; + } else { // Move around.