mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
It zooms out, pans to the side and toward the player.
Loop Center thing:
- arg2: zoom-out speed in tics (zooms out when entering the loop)
- arg3: zoom-in speed in tics (zooms in when exiting the loop)
- arg4: zoom-out distance in fracunits (multiply by 65536)
- arg5: angle to pan to the side of the loop in degrees fracunits (multiply by 65536)
- This will be flipped depending on where the camera was
facing before entering the loop.
- arg6: panning speed in degrees fracunits (multiply by 65536)
- arg7: panning acceleration in tics (camera gradually pans to side of loop)
- arg8: panning deceleration in tics (camera gradually pans back to normal)
376 lines
7.4 KiB
C++
376 lines
7.4 KiB
C++
// DR. ROBOTNIK'S RING RACERS
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2023 by James R.
|
|
// 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 loop-endpoint.c
|
|
/// \brief Sonic loops, start and end points
|
|
|
|
#include <optional>
|
|
|
|
#include "../doomdef.h"
|
|
#include "../k_kart.h"
|
|
#include "../taglist.h"
|
|
#include "../p_local.h"
|
|
#include "../p_setup.h"
|
|
#include "../p_spec.h"
|
|
#include "../r_main.h"
|
|
#include "../k_objects.h"
|
|
|
|
#define end_anchor(o) ((o)->target)
|
|
|
|
#define center_max_revolution(o) ((o)->threshold)
|
|
#define center_alpha(o) ((o)->target)
|
|
#define center_beta(o) ((o)->tracer)
|
|
|
|
static inline boolean
|
|
center_has_flip (const mobj_t *center)
|
|
{
|
|
return (center->flags2 & MF2_AMBUSH) == MF2_AMBUSH;
|
|
}
|
|
|
|
static inline void
|
|
center_set_flip
|
|
( mobj_t * center,
|
|
boolean mode)
|
|
{
|
|
center->flags2 = (center->flags2 & ~(MF2_AMBUSH)) |
|
|
((mode != false) * MF2_AMBUSH);
|
|
}
|
|
|
|
#define anchor_center(o) ((o)->target)
|
|
#define anchor_other(o) ((o)->tracer)
|
|
#define anchor_type(o) ((o)->reactiontime)
|
|
|
|
static void
|
|
set_shiftxy
|
|
( player_t * player,
|
|
const mobj_t * a)
|
|
{
|
|
const mobj_t *b = anchor_other(a);
|
|
|
|
const fixed_t dx = (b->x - a->x);
|
|
const fixed_t dy = (b->y - a->y);
|
|
|
|
const angle_t th =
|
|
(R_PointToAngle2(0, 0, dx, dy) - a->angle);
|
|
|
|
const fixed_t adj = FixedMul(
|
|
abs(FCOS(AbsAngle(th - ANGLE_90))),
|
|
FixedHypot(dx, dy)) / 2;
|
|
|
|
vector2_t *xy = &player->loop.shift;
|
|
|
|
xy->x = FixedMul(FSIN(a->angle), adj);
|
|
xy->y = FixedMul(FCOS(a->angle), adj);
|
|
}
|
|
|
|
static void
|
|
measure_clock
|
|
( const mobj_t * center,
|
|
const mobj_t * anchor,
|
|
angle_t * pitch,
|
|
fixed_t * radius)
|
|
{
|
|
const fixed_t dx = (anchor->x - center->x);
|
|
const fixed_t dy = (anchor->y - center->y);
|
|
const fixed_t dz = (anchor->z - center->z);
|
|
|
|
/* Translate the anchor point to be along a center line.
|
|
This makes the horizontal position one dimensional
|
|
relative to the center point. */
|
|
const fixed_t xy = (
|
|
FixedMul(dx, FCOS(anchor->angle)) +
|
|
FixedMul(dy, FSIN(anchor->angle)));
|
|
|
|
/* The 3d position of the anchor point is then reduced to
|
|
two axes and can be measured as an angle. */
|
|
*pitch = R_PointToAngle2(0, 0, xy, dz) + ANGLE_90;
|
|
*radius = FixedHypot(xy, dz);
|
|
}
|
|
|
|
static void
|
|
crisscross
|
|
( mobj_t * anchor,
|
|
mobj_t ** target_p,
|
|
mobj_t ** other_p)
|
|
{
|
|
P_SetTarget(target_p, anchor);
|
|
|
|
if (!P_MobjWasRemoved(*other_p))
|
|
{
|
|
P_SetTarget(&anchor_other(anchor), *other_p);
|
|
P_SetTarget(&anchor_other(*other_p), anchor);
|
|
}
|
|
}
|
|
|
|
static boolean
|
|
moving_toward_gate
|
|
( const player_t * player,
|
|
const mobj_t * anchor,
|
|
angle_t pitch)
|
|
{
|
|
const fixed_t
|
|
x = player->mo->momx,
|
|
y = player->mo->momy,
|
|
z = player->mo->momz,
|
|
|
|
zx = FixedMul(FCOS(anchor->angle), z),
|
|
zy = FixedMul(FSIN(anchor->angle), z),
|
|
|
|
co = abs(FCOS(pitch)),
|
|
si = abs(FSIN(pitch)),
|
|
|
|
dx = FixedMul(co, x) + FixedMul(si, zx),
|
|
dy = FixedMul(co, y) + FixedMul(si, zy);
|
|
|
|
return AngleDelta(anchor->angle,
|
|
R_PointToAngle2(0, 0, dx, dy)) < ANG60;
|
|
}
|
|
|
|
static SINT8
|
|
get_binary_direction
|
|
( angle_t pitch,
|
|
mobj_t * toucher)
|
|
{
|
|
const fixed_t si = FSIN(pitch);
|
|
|
|
if (abs(si) < abs(FCOS(pitch)))
|
|
{
|
|
// pitch = 0 points downward so offset 90 degrees
|
|
// clockwise so 180 occurs at horizon
|
|
return ((pitch + ANGLE_90) < ANGLE_180) ? 1 : -(1);
|
|
}
|
|
else
|
|
{
|
|
return intsign(si) * P_MobjFlip(toucher);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
INT32 i;
|
|
|
|
TAG_ITER_THINGS(tag, i)
|
|
{
|
|
mapthing_t *mt = &mapthings[i];
|
|
|
|
if (mt->type == mobjinfo[MT_LOOPCENTERPOINT].doomednum)
|
|
{
|
|
return mt->mobj;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
Obj_InitLoopEndpoint
|
|
( mobj_t * end,
|
|
mobj_t * anchor)
|
|
{
|
|
P_SetTarget(&end_anchor(end), anchor);
|
|
}
|
|
|
|
void
|
|
Obj_InitLoopCenter (mobj_t *center)
|
|
{
|
|
center_max_revolution(center) = center->thing_args[1] * FRACUNIT / 360;
|
|
center_set_flip(center, center->thing_args[0]);
|
|
}
|
|
|
|
void
|
|
Obj_LinkLoopAnchor
|
|
( mobj_t * anchor,
|
|
mobj_t * center,
|
|
UINT8 type)
|
|
{
|
|
P_SetTarget(&anchor_center(anchor), center);
|
|
|
|
anchor_type(anchor) = type;
|
|
|
|
if (!P_MobjWasRemoved(center))
|
|
{
|
|
switch (type)
|
|
{
|
|
case TMLOOP_ALPHA:
|
|
crisscross(anchor,
|
|
¢er_alpha(center),
|
|
¢er_beta(center));
|
|
break;
|
|
|
|
case TMLOOP_BETA:
|
|
crisscross(anchor,
|
|
¢er_beta(center),
|
|
¢er_alpha(center));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Obj_LoopEndpointCollide
|
|
( mobj_t * end,
|
|
mobj_t * toucher)
|
|
{
|
|
player_t *player = toucher->player;
|
|
sonicloopvars_t *s = &player->loop;
|
|
sonicloopcamvars_t *cam = &s->camera;
|
|
|
|
mobj_t *anchor = end_anchor(end);
|
|
mobj_t *center = anchor ? anchor_center(anchor) : NULL;
|
|
|
|
angle_t pitch;
|
|
fixed_t radius;
|
|
|
|
/* predict movement for a smooth transition */
|
|
const fixed_t px = toucher->x + 2 * toucher->momx;
|
|
const fixed_t py = toucher->y + 2 * toucher->momy;
|
|
|
|
SINT8 flip;
|
|
|
|
if (P_MobjWasRemoved(center))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (player->loop.radius != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
measure_clock(center, anchor, &pitch, &radius);
|
|
|
|
if (!moving_toward_gate(player, anchor, pitch))
|
|
{
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
s->yaw = anchor->angle;
|
|
|
|
s->origin.x = center->x - (anchor->x - px);
|
|
s->origin.y = center->y - (anchor->y - py);
|
|
s->origin.z = center->z;
|
|
|
|
s->radius = radius * flip;
|
|
s->revolution = AngleFixed(pitch) / 360;
|
|
|
|
s->min_revolution = s->revolution;
|
|
s->max_revolution = s->revolution +
|
|
center_max_revolution(center) * flip;
|
|
|
|
s->flip = center_has_flip(center);
|
|
|
|
cam->enter_tic = leveltime;
|
|
cam->exit_tic = INFTICS;
|
|
|
|
cam->zoom_out_speed = center->thing_args[2];
|
|
cam->zoom_in_speed = center->thing_args[3];
|
|
cam->dist = center->thing_args[4] * FRACUNIT;
|
|
cam->pan = FixedAngle(center->thing_args[5] * FRACUNIT);
|
|
cam->pan_speed = center->thing_args[6] * FRACUNIT;
|
|
cam->pan_accel = center->thing_args[7];
|
|
cam->pan_back = center->thing_args[8];
|
|
|
|
player->speed =
|
|
3 * (player->speed + toucher->momz) / 2;
|
|
|
|
/* cancel the effects of K_Squish */
|
|
toucher->spritexscale = FRACUNIT;
|
|
toucher->spriteyscale = FRACUNIT;
|
|
|
|
toucher->momx = 0;
|
|
toucher->momy = 0;
|
|
}
|