mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge branch 'gremlins-sweep-revelation' into 'master'
Line sweep to eliminate Gremlins II, also mitigate getting stuck inside slope enclosures Closes #393 See merge request KartKrew/Kart!1610
This commit is contained in:
commit
aac583f2eb
13 changed files with 1011 additions and 54 deletions
|
|
@ -64,6 +64,8 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
|
|||
p_tick.c
|
||||
p_user.c
|
||||
p_slopes.c
|
||||
p_sweep.cpp
|
||||
p_test.cpp
|
||||
tables.c
|
||||
r_bsp.cpp
|
||||
r_data.c
|
||||
|
|
|
|||
|
|
@ -806,6 +806,7 @@ consvar_t cv_numlaps = OnlineCheat("numlaps", "Map default").values(numlaps_cons
|
|||
|
||||
consvar_t cv_restrictskinchange = OnlineCheat("restrictskinchange", "Yes").yes_no().description("Don't let players change their skin in the middle of gameplay");
|
||||
consvar_t cv_spbtest = OnlineCheat("spbtest", "Off").on_off().description("SPB can never target a player");
|
||||
consvar_t cv_showgremlins = OnlineCheat("showgremlins", "No").yes_no().description("Show line collision errors");
|
||||
consvar_t cv_timescale = OnlineCheat(cvlist_timer)("timescale", "1.0").floating_point().min_max(FRACUNIT/20, 20*FRACUNIT).description("Overclock or slow down the game");
|
||||
consvar_t cv_ufo_follow = OnlineCheat("ufo_follow", "0").min_max(0, MAXPLAYERS).description("Make UFO Catcher folow this player");
|
||||
consvar_t cv_ufo_health = OnlineCheat("ufo_health", "-1").min_max(-1, 100).description("Override UFO Catcher health -- applied at spawn or when value is changed");
|
||||
|
|
|
|||
107
src/math/fixed.hpp
Normal file
107
src/math/fixed.hpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2023 by James Robert Roman
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef math_fixed_hpp
|
||||
#define math_fixed_hpp
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "traits.hpp"
|
||||
|
||||
#include "../m_fixed.h"
|
||||
|
||||
namespace srb2::math
|
||||
{
|
||||
|
||||
struct Fixed
|
||||
{
|
||||
static Fixed copysign(fixed_t x, fixed_t y) { return (x < 0) != (y < 0) ? -x : x; }
|
||||
static Fixed hypot(fixed_t x, fixed_t y) { return FixedHypot(x, y); }
|
||||
|
||||
constexpr Fixed() : val_(0) {}
|
||||
constexpr Fixed(fixed_t val) : val_(val) {}
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
|
||||
Fixed(T val) : val_(FloatToFixed(val)) {}
|
||||
|
||||
Fixed(const Fixed& b) = default;
|
||||
Fixed& operator=(const Fixed& b) = default;
|
||||
|
||||
fixed_t value() const { return val_; }
|
||||
int sign() const { return val_ < 0 ? -1 : 1; }
|
||||
|
||||
operator fixed_t() const { return val_; }
|
||||
explicit operator float() const { return FixedToFloat(val_); }
|
||||
|
||||
Fixed& operator+=(const Fixed& b)
|
||||
{
|
||||
val_ += b.val_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fixed& operator-=(const Fixed& b)
|
||||
{
|
||||
val_ -= b.val_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fixed& operator*=(const Fixed& b)
|
||||
{
|
||||
val_ = FixedMul(val_, b.val_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fixed& operator/=(const Fixed& b)
|
||||
{
|
||||
val_ = FixedDiv(val_, b.val_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fixed operator-() const { return -val_; }
|
||||
|
||||
#define X(op) \
|
||||
template <typename T> \
|
||||
Fixed operator op(const T& b) const { return val_ op b; } \
|
||||
Fixed operator op(const Fixed& b) const \
|
||||
{ \
|
||||
Fixed f{val_};\
|
||||
f op##= b;\
|
||||
return f;\
|
||||
} \
|
||||
template <typename T> \
|
||||
Fixed& operator op##=(const T& b) \
|
||||
{ \
|
||||
val_ op##= b; \
|
||||
return *this; \
|
||||
}
|
||||
|
||||
X(+)
|
||||
X(-)
|
||||
X(*)
|
||||
X(/)
|
||||
|
||||
#undef X
|
||||
|
||||
private:
|
||||
fixed_t val_;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Traits<Fixed>
|
||||
{
|
||||
static constexpr Fixed kZero = 0;
|
||||
static constexpr Fixed kUnit = FRACUNIT;
|
||||
|
||||
static constexpr auto copysign = Fixed::copysign;
|
||||
static constexpr auto hypot = Fixed::hypot;
|
||||
};
|
||||
|
||||
}; // namespace srb2::math
|
||||
|
||||
#endif/*math_fixed_hpp*/
|
||||
102
src/math/line_equation.hpp
Normal file
102
src/math/line_equation.hpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2023 by James Robert Roman
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef math_line_equation_hpp
|
||||
#define math_line_equation_hpp
|
||||
|
||||
#include "fixed.hpp"
|
||||
#include "line_segment.hpp"
|
||||
#include "vec.hpp"
|
||||
|
||||
namespace srb2::math
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct LineEquation
|
||||
{
|
||||
using vec2 = Vec2<T>;
|
||||
using line_segment = LineSegment<T>;
|
||||
|
||||
// Fixed-point: shift value by this amount during
|
||||
// multiplications and divisions to avoid overflows.
|
||||
static constexpr std::enable_if_t<std::is_same_v<T, Fixed>, fixed_t> kF = 1024; // fixed_t, not Fixed
|
||||
|
||||
LineEquation() {}
|
||||
LineEquation(const vec2& p, const vec2& d) : d_(d), m_(d.y / d.x), b_(p.y - (p.x * m())) {}
|
||||
LineEquation(const line_segment& l) : LineEquation(l.a, l.b - l.a) {}
|
||||
|
||||
const vec2& d() const { return d_; }
|
||||
T m() const { return m_; }
|
||||
T b() const { return b_; }
|
||||
T y(T x) const { return (m() * x) + b(); }
|
||||
|
||||
vec2 intersect(const LineEquation& q) const
|
||||
{
|
||||
T x = (b() - q.b()) / (q.m() - m());
|
||||
return {x, y(x)};
|
||||
}
|
||||
|
||||
protected:
|
||||
vec2 d_{};
|
||||
T m_{}, b_{};
|
||||
};
|
||||
|
||||
template <>
|
||||
inline LineEquation<Fixed>::LineEquation(const vec2& p, const vec2& d) :
|
||||
d_(d), m_((d.y / d.x) / kF), b_((p.y / kF) - (p.x * m_))
|
||||
{
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Fixed LineEquation<Fixed>::m() const
|
||||
{
|
||||
return m_ * kF;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Fixed LineEquation<Fixed>::b() const
|
||||
{
|
||||
return b_ * kF;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Fixed LineEquation<Fixed>::y(Fixed x) const
|
||||
{
|
||||
return ((m_ * x) + b_) * kF;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline LineEquation<Fixed>::vec2 LineEquation<Fixed>::intersect(const LineEquation& q) const
|
||||
{
|
||||
Fixed x = ((b_ - q.b_) / ((q.m_ - m_) * kF)) * kF;
|
||||
return {x, y(x)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct LineEquationX : LineEquation<T>
|
||||
{
|
||||
T x(T y) const { return (y - LineEquation<T>::b()) / LineEquation<T>::m(); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct LineEquationX<Fixed> : LineEquation<Fixed>
|
||||
{
|
||||
LineEquationX() {}
|
||||
LineEquationX(const vec2& p, const vec2& d) : LineEquation(p, d), w_((d.x / d.y) / kF), a_((p.x / kF) - (p.y * w_)) {}
|
||||
LineEquationX(const line_segment& l) : LineEquationX(l.a, l.b - l.a) {}
|
||||
|
||||
Fixed x(Fixed y) const { return ((w_ * y) + a_) * kF; }
|
||||
|
||||
protected:
|
||||
Fixed w_{}, a_{};
|
||||
};
|
||||
|
||||
}; // namespace srb2::math
|
||||
|
||||
#endif/*math_line_equation_hpp*/
|
||||
43
src/math/line_segment.hpp
Normal file
43
src/math/line_segment.hpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2023 by James Robert Roman
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef math_line_segment_hpp
|
||||
#define math_line_segment_hpp
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "vec.hpp"
|
||||
|
||||
namespace srb2::math
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct LineSegment
|
||||
{
|
||||
using vec2 = Vec2<T>;
|
||||
using view = std::pair<const vec2&, const vec2&>;
|
||||
|
||||
vec2 a, b;
|
||||
|
||||
LineSegment(vec2 a_, vec2 b_) : a(a_), b(b_) {}
|
||||
|
||||
template <typename U>
|
||||
LineSegment(const LineSegment<U>& b) : LineSegment(b.a, b.b) {}
|
||||
|
||||
bool horizontal() const { return a.y == b.y; }
|
||||
bool vertical() const { return a.x == b.x; }
|
||||
|
||||
view by_x() const { return std::minmax(a, b, [](auto& a, auto& b) { return a.x < b.x; }); }
|
||||
view by_y() const { return std::minmax(a, b, [](auto& a, auto& b) { return a.y < b.y; }); }
|
||||
};
|
||||
|
||||
}; // namespace srb2
|
||||
|
||||
#endif/*math_line_segment_hpp*/
|
||||
34
src/math/traits.hpp
Normal file
34
src/math/traits.hpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2023 by James Robert Roman
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef math_traits_hpp
|
||||
#define math_traits_hpp
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
namespace srb2::math
|
||||
{
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct Traits;
|
||||
|
||||
template <typename T>
|
||||
struct Traits<T, std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
{
|
||||
static constexpr T kZero = 0.0;
|
||||
static constexpr T kUnit = 1.0;
|
||||
|
||||
static T copysign(T x, T y) { return std::copysign(x, y); }
|
||||
static T hypot(T x, T y) { return std::hypot(x, y); }
|
||||
};
|
||||
|
||||
}; // namespace srb2::math
|
||||
|
||||
#endif/*math_traits_hpp*/
|
||||
84
src/math/vec.hpp
Normal file
84
src/math/vec.hpp
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2023 by James Robert Roman
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef math_vec_hpp
|
||||
#define math_vec_hpp
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "traits.hpp"
|
||||
|
||||
namespace srb2::math
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct Vec2
|
||||
{
|
||||
T x, y;
|
||||
|
||||
Vec2() : x{}, y{} {}
|
||||
Vec2(T x_, T y_) : x(x_), y(y_) {}
|
||||
Vec2(T z) : x(z), y(z) {}
|
||||
|
||||
template <typename U>
|
||||
Vec2(const Vec2<U>& b) : Vec2(b.x, b.y) {}
|
||||
|
||||
T magnitude() const { return Traits<T>::hypot(x, y); }
|
||||
Vec2 normal() const { return {-y, x}; }
|
||||
|
||||
#define X(op) \
|
||||
Vec2& operator op##=(const Vec2& b) \
|
||||
{ \
|
||||
x op##= b.x; \
|
||||
y op##= b.y; \
|
||||
return *this; \
|
||||
} \
|
||||
Vec2 operator op(const Vec2& b) const { return Vec2(x op b.x, y op b.y); } \
|
||||
|
||||
X(+)
|
||||
X(-)
|
||||
X(*)
|
||||
X(/)
|
||||
|
||||
#undef X
|
||||
|
||||
Vec2 operator-() const { return Vec2(-x, -y); }
|
||||
};
|
||||
|
||||
template <typename>
|
||||
struct is_vec2 : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_vec2<Vec2<T>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_vec2_v = is_vec2<T>::value;
|
||||
|
||||
#define X(op) \
|
||||
template <typename T, typename U, std::enable_if_t<!is_vec2_v<T>, bool> = true> \
|
||||
Vec2<T> operator op(const T& a, const Vec2<U>& b) \
|
||||
{ \
|
||||
return Vec2 {a} op Vec2<T> {b}; \
|
||||
} \
|
||||
template <typename T, typename U, std::enable_if_t<!is_vec2_v<U>, bool> = true> \
|
||||
Vec2<U> operator op(const Vec2<T>& a, const U& b) \
|
||||
{ \
|
||||
return Vec2<U> {a} op Vec2 {b}; \
|
||||
} \
|
||||
|
||||
X(+)
|
||||
X(-)
|
||||
X(*)
|
||||
X(/)
|
||||
|
||||
#undef X
|
||||
|
||||
}; // namespace srb2::math
|
||||
|
||||
#endif/*math_vec_hpp*/
|
||||
|
|
@ -387,9 +387,16 @@ struct tm_t
|
|||
// so missiles don't explode against sky hack walls
|
||||
line_t *ceilingline;
|
||||
|
||||
// set by PIT_CheckLine() for any line that stopped the PIT_CheckLine()
|
||||
// that is, for any line which is 'solid'
|
||||
line_t *blockingline;
|
||||
// P_CheckPosition: this position blocks movement
|
||||
boolean blocking;
|
||||
|
||||
// P_CheckPosition: set this before each call to
|
||||
// P_CheckPosition to enable a line sweep on collided
|
||||
// lines
|
||||
boolean sweep;
|
||||
|
||||
// sweep: max step up at tm.x, tm.y
|
||||
fixed_t maxstep;
|
||||
};
|
||||
|
||||
extern tm_t tm;
|
||||
|
|
@ -415,6 +422,7 @@ struct TryMoveResult_t
|
|||
boolean success;
|
||||
line_t *line;
|
||||
mobj_t *mo;
|
||||
vector2_t normal;
|
||||
};
|
||||
|
||||
boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *result);
|
||||
|
|
@ -422,6 +430,10 @@ boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff, T
|
|||
boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff, TryMoveResult_t *result);
|
||||
boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *result);
|
||||
|
||||
void P_TestLine(line_t *ld);
|
||||
void P_ClearTestLines(void);
|
||||
line_t *P_SweepTestLines(fixed_t ax, fixed_t ay, fixed_t bx, fixed_t by, fixed_t r, vector2_t *return_normal);
|
||||
|
||||
boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing);
|
||||
boolean P_IsLineTripWire(const line_t *ld);
|
||||
boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam);
|
||||
|
|
|
|||
190
src/p_map.c
190
src/p_map.c
|
|
@ -1770,7 +1770,6 @@ static BlockItReturn_t PIT_CheckCameraLine(line_t *ld)
|
|||
// could be crossed in either order.
|
||||
|
||||
// this line is out of the if so upper and lower textures can be hit by a splat
|
||||
tm.blockingline = ld;
|
||||
if (!ld->backsector) // one sided line
|
||||
{
|
||||
if (P_PointOnLineSide(mapcampointer->x, mapcampointer->y, ld))
|
||||
|
|
@ -1841,6 +1840,22 @@ boolean P_IsLineTripWire(const line_t *ld)
|
|||
return ld->tripwire;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//
|
||||
// PIT_CheckLine
|
||||
// Adjusts tm.floorz and tm.ceilingz as lines are contacted
|
||||
|
|
@ -1898,14 +1913,20 @@ static BlockItReturn_t PIT_CheckLine(line_t *ld)
|
|||
// could be crossed in either order.
|
||||
|
||||
// this line is out of the if so upper and lower textures can be hit by a splat
|
||||
tm.blockingline = ld;
|
||||
|
||||
{
|
||||
UINT8 shouldCollide = LUA_HookMobjLineCollide(tm.thing, tm.blockingline); // checks hook for thing's type
|
||||
UINT8 shouldCollide = LUA_HookMobjLineCollide(tm.thing, ld); // checks hook for thing's type
|
||||
if (P_MobjWasRemoved(tm.thing))
|
||||
return BMIT_CONTINUE; // one of them was removed???
|
||||
if (shouldCollide == 1)
|
||||
return BMIT_ABORT; // force collide
|
||||
{
|
||||
if (tm.sweep)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
tm.blocking = true; // force collide
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
else if (shouldCollide == 2)
|
||||
return BMIT_CONTINUE; // force no collide
|
||||
}
|
||||
|
|
@ -1914,15 +1935,55 @@ static BlockItReturn_t PIT_CheckLine(line_t *ld)
|
|||
{
|
||||
if (P_PointOnLineSide(tm.thing->x, tm.thing->y, ld))
|
||||
return BMIT_CONTINUE; // don't hit the back side
|
||||
return BMIT_ABORT;
|
||||
|
||||
if (tm.sweep)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
tm.blocking = true;
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
||||
if (P_IsLineBlocking(ld, tm.thing))
|
||||
return BMIT_ABORT;
|
||||
{
|
||||
if (tm.sweep)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
tm.blocking = true;
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
||||
// set openrange, opentop, openbottom
|
||||
P_LineOpening(ld, tm.thing, &open);
|
||||
|
||||
if (tm.sweep && P_UsingStepUp(tm.thing))
|
||||
{
|
||||
// copied from P_TryMove
|
||||
// TODO: refactor this into one place
|
||||
if (open.range < tm.thing->height)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
else if (tm.maxstep > 0)
|
||||
{
|
||||
if (tm.thing->z < open.floor)
|
||||
{
|
||||
if (open.floorstep > tm.maxstep)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
}
|
||||
else if (open.ceiling < tm.thing->z + tm.thing->height)
|
||||
{
|
||||
if (open.ceilingstep > tm.maxstep)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjust floor / ceiling heights
|
||||
if (open.ceiling < tm.ceilingz)
|
||||
{
|
||||
|
|
@ -2042,7 +2103,8 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *re
|
|||
tm.bbox[BOXLEFT] = x - tm.thing->radius;
|
||||
|
||||
newsubsec = R_PointInSubsector(x, y);
|
||||
tm.ceilingline = tm.blockingline = NULL;
|
||||
tm.ceilingline = NULL;
|
||||
tm.blocking = false;
|
||||
|
||||
// The base floor / ceiling is from the subsector
|
||||
// that contains the point.
|
||||
|
|
@ -2314,23 +2376,33 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *re
|
|||
|
||||
validcount++;
|
||||
|
||||
P_ClearTestLines();
|
||||
|
||||
// check lines
|
||||
for (bx = xl; bx <= xh; bx++)
|
||||
{
|
||||
for (by = yl; by <= yh; by++)
|
||||
{
|
||||
if (!P_BlockLinesIterator(bx, by, PIT_CheckLine))
|
||||
{
|
||||
blockval = false;
|
||||
}
|
||||
P_BlockLinesIterator(bx, by, PIT_CheckLine);
|
||||
}
|
||||
}
|
||||
|
||||
if (tm.blocking)
|
||||
{
|
||||
blockval = false;
|
||||
}
|
||||
|
||||
if (result != NULL)
|
||||
{
|
||||
result->line = tm.blockingline;
|
||||
result->line = NULL;
|
||||
result->mo = tm.hitthing;
|
||||
}
|
||||
else
|
||||
{
|
||||
P_ClearTestLines();
|
||||
}
|
||||
|
||||
tm.sweep = false;
|
||||
|
||||
return blockval;
|
||||
}
|
||||
|
|
@ -2379,7 +2451,7 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam)
|
|||
tm.bbox[BOXLEFT] = x - thiscam->radius;
|
||||
|
||||
newsubsec = R_PointInSubsector(x, y);
|
||||
tm.ceilingline = tm.blockingline = NULL;
|
||||
tm.ceilingline = NULL;
|
||||
|
||||
mapcampointer = thiscam;
|
||||
|
||||
|
|
@ -2753,22 +2825,6 @@ 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,
|
||||
|
|
@ -2821,7 +2877,29 @@ increment_move
|
|||
tryy = y;
|
||||
}
|
||||
|
||||
if (!P_CheckPosition(thing, tryx, tryy, result))
|
||||
if (P_UsingStepUp(thing))
|
||||
{
|
||||
tm.maxstep = P_GetThingStepUp(thing, tryx, tryy);
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
tm.sweep = true;
|
||||
}
|
||||
|
||||
boolean move_ok = P_CheckPosition(thing, tryx, tryy, result);
|
||||
|
||||
if (P_MobjWasRemoved(thing))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
result->line = P_SweepTestLines(thing->x, thing->y, x, y, thing->radius, &result->normal);
|
||||
}
|
||||
|
||||
if (!move_ok)
|
||||
{
|
||||
return false; // solid wall or thing
|
||||
}
|
||||
|
|
@ -3466,30 +3544,27 @@ static void P_HitSlideLine(line_t *ld)
|
|||
//
|
||||
// HitBounceLine, for players
|
||||
//
|
||||
static void P_PlayerHitBounceLine(line_t *ld)
|
||||
static void P_PlayerHitBounceLine(line_t *ld, vector2_t* normal)
|
||||
{
|
||||
INT32 side;
|
||||
angle_t lineangle;
|
||||
fixed_t movelen;
|
||||
fixed_t x, y;
|
||||
|
||||
side = P_PointOnLineSide(slidemo->x, slidemo->y, ld);
|
||||
lineangle = ld->angle - ANGLE_90;
|
||||
|
||||
if (side == 1)
|
||||
lineangle += ANGLE_180;
|
||||
|
||||
lineangle >>= ANGLETOFINESHIFT;
|
||||
|
||||
movelen = P_AproxDistance(tmxmove, tmymove);
|
||||
|
||||
if (slidemo->player && movelen < (15*mapobjectscale))
|
||||
movelen = (15*mapobjectscale);
|
||||
|
||||
x = FixedMul(movelen, FINECOSINE(lineangle));
|
||||
y = FixedMul(movelen, FINESINE(lineangle));
|
||||
if (!ld)
|
||||
{
|
||||
angle_t th = R_PointToAngle2(0, 0, tmxmove, tmymove);
|
||||
normal->x = -FCOS(th);
|
||||
normal->y = -FSIN(th);
|
||||
}
|
||||
|
||||
if (P_IsLineTripWire(ld))
|
||||
x = FixedMul(movelen, normal->x);
|
||||
y = FixedMul(movelen, normal->y);
|
||||
|
||||
if (ld && P_IsLineTripWire(ld))
|
||||
{
|
||||
tmxmove = x * 4;
|
||||
tmymove = y * 4;
|
||||
|
|
@ -3958,6 +4033,8 @@ papercollision:
|
|||
|
||||
static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
|
||||
{
|
||||
extern consvar_t cv_showgremlins;
|
||||
|
||||
fixed_t mmomx = 0, mmomy = 0;
|
||||
fixed_t oldmomx = mo->momx, oldmomy = mo->momy;
|
||||
|
||||
|
|
@ -3982,8 +4059,23 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
|
|||
slidemo = mo;
|
||||
bestslideline = result->line;
|
||||
|
||||
if (bestslideline == NULL)
|
||||
return;
|
||||
if (bestslideline == NULL && cv_showgremlins.value)
|
||||
{
|
||||
// debug
|
||||
mobj_t*x = P_SpawnMobj(mo->x, mo->y, mo->z, MT_THOK);
|
||||
x->frame = FF_FULLBRIGHT | FF_ADD;
|
||||
x->renderflags = RF_ALWAYSONTOP;
|
||||
x->color = SKINCOLOR_RED;
|
||||
|
||||
CONS_Printf(
|
||||
"GREMLIN: leveltime=%u x=%f y=%f z=%f angle=%f\n",
|
||||
leveltime,
|
||||
FixedToFloat(mo->x),
|
||||
FixedToFloat(mo->y),
|
||||
FixedToFloat(mo->z),
|
||||
AngleToFloat(R_PointToAngle2(0, 0, oldmomx, oldmomy))
|
||||
);
|
||||
}
|
||||
|
||||
if (mo->eflags & MFE_JUSTBOUNCEDWALL) // Stronger push-out
|
||||
{
|
||||
|
|
@ -3996,7 +4088,7 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
|
|||
tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
|
||||
}
|
||||
|
||||
if (P_IsLineTripWire(bestslideline))
|
||||
if (bestslideline && P_IsLineTripWire(bestslideline))
|
||||
{
|
||||
// TRIPWIRE CANNOT BE MADE NONBOUNCY
|
||||
K_ApplyTripWire(mo->player, TRIPSTATE_BLOCKED);
|
||||
|
|
@ -4014,7 +4106,7 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
|
|||
K_SpawnBumpEffect(mo);
|
||||
}
|
||||
|
||||
P_PlayerHitBounceLine(bestslideline);
|
||||
P_PlayerHitBounceLine(bestslideline, &result->normal);
|
||||
mo->eflags |= MFE_JUSTBOUNCEDWALL;
|
||||
|
||||
mo->momx = tmxmove;
|
||||
|
|
@ -4022,7 +4114,7 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
|
|||
mo->player->cmomx = tmxmove;
|
||||
mo->player->cmomy = tmymove;
|
||||
|
||||
if (!P_IsLineTripWire(bestslideline))
|
||||
if (!bestslideline || !P_IsLineTripWire(bestslideline))
|
||||
{
|
||||
if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove, true, NULL))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1671,7 +1671,7 @@ void P_XYMovement(mobj_t *mo)
|
|||
// blocked move
|
||||
moved = false;
|
||||
|
||||
if (LUA_HookMobjMoveBlocked(mo, tm.hitthing, tm.blockingline))
|
||||
if (LUA_HookMobjMoveBlocked(mo, tm.hitthing, result.line))
|
||||
{
|
||||
if (P_MobjWasRemoved(mo))
|
||||
return;
|
||||
|
|
@ -1679,7 +1679,7 @@ void P_XYMovement(mobj_t *mo)
|
|||
else if (P_MobjWasRemoved(mo))
|
||||
return;
|
||||
|
||||
P_PushSpecialLine(tm.blockingline, mo);
|
||||
P_PushSpecialLine(result.line, mo);
|
||||
|
||||
if (mo->flags & MF_MISSILE)
|
||||
{
|
||||
|
|
|
|||
271
src/p_sweep.cpp
Normal file
271
src/p_sweep.cpp
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2023 by James Robert Roman
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
#include "p_sweep.hpp"
|
||||
|
||||
using namespace srb2::math;
|
||||
using namespace srb2::sweep;
|
||||
|
||||
Result SlopeAABBvsLine::vs_slope(const line_segment& l) const
|
||||
{
|
||||
auto [a, b] = l.by_x(); // left, right
|
||||
LineEquation<unit> ql{l};
|
||||
unit ls = copysign(kUnit, ql.m());
|
||||
|
||||
auto hit = [&](const vec2& k, unit xr, unit x, const vec2& n) -> Contact
|
||||
{
|
||||
std::optional<vec2> k2;
|
||||
|
||||
if (l.horizontal())
|
||||
{
|
||||
// Horizontal line: create second contact point on opposite corner.
|
||||
// TODO: avoid duplicate point
|
||||
k2 = vec2(std::clamp(x + xr, a.x, b.x), k.y);
|
||||
}
|
||||
|
||||
return {time(x), n, k, k2};
|
||||
};
|
||||
|
||||
auto slide = [&](const vec2& k, const vec2& s) -> std::optional<Contact>
|
||||
{
|
||||
vec2 kf = k * s;
|
||||
vec2 r = r_ * s;
|
||||
vec2 p = k - r;
|
||||
|
||||
// Slide vertically along AABB left/right edge.
|
||||
unit f = q_.y(p.x) * s.y;
|
||||
|
||||
if (f - r_ > kf.y)
|
||||
{
|
||||
// Out of bounds detection.
|
||||
// This should never slide in front.
|
||||
// If it does, there was never a hit.
|
||||
return {};
|
||||
}
|
||||
|
||||
if (f + r_ < kf.y)
|
||||
{
|
||||
// Slid behind contact point.
|
||||
// Try sliding horizontally along AABB top/bottom
|
||||
// edge.
|
||||
|
||||
if (q_.m() == kZero)
|
||||
{
|
||||
// Sweep is horizontal.
|
||||
// It is impossible to slide against a line's
|
||||
// end by the X axis because the line segment
|
||||
// lies on that axis.
|
||||
return {};
|
||||
}
|
||||
|
||||
p.x = q_.x(p.y);
|
||||
f = p.x * s.x;
|
||||
|
||||
if (f - r_ > kf.x)
|
||||
{
|
||||
// Slid beyond contact point.
|
||||
return {};
|
||||
}
|
||||
|
||||
if (f + r_ < kf.x)
|
||||
{
|
||||
// Out of bounds detection.
|
||||
// This should never slide behind.
|
||||
// If it does, there was never a hit.
|
||||
return {};
|
||||
}
|
||||
|
||||
return hit(k, r.x, p.x, {kZero, -s.y});
|
||||
}
|
||||
|
||||
return hit(k, r.x, p.x, {-s.x, kZero});
|
||||
};
|
||||
|
||||
// xrs.x = x radius
|
||||
// xrs.y = x sign
|
||||
auto bind = [&](const vec2& k, const vec2& xrs, unit ns) -> std::optional<Contact>
|
||||
{
|
||||
if (k.x < a.x)
|
||||
{
|
||||
return slide(a, {xrs.y, ls});
|
||||
}
|
||||
|
||||
if (k.x > b.x)
|
||||
{
|
||||
return slide(b, {xrs.y, -ls});
|
||||
}
|
||||
|
||||
return hit(k, xrs.x, k.x + xrs.x, normal(l) * ns);
|
||||
};
|
||||
|
||||
if (ql.m() == q_.m())
|
||||
{
|
||||
// Parallel lines can only cross at the ends.
|
||||
vec2 s{kUnit, ls};
|
||||
return order(slide(a, s), slide(b, -s), ds_.x);
|
||||
}
|
||||
|
||||
vec2 i = ql.intersect(q_);
|
||||
|
||||
// Compare slopes to determine if ray is moving upward or
|
||||
// downward into line.
|
||||
// For a positive line, AABB top left corner hits the
|
||||
// line first if the ray is moving upward.
|
||||
// Swap diagonal corners to bottom right if moving
|
||||
// downward.
|
||||
unit ys = q_.m() * ds_.x < ql.m() * ds_.x ? -kUnit : kUnit;
|
||||
unit yr = r_ * ys;
|
||||
|
||||
// Swap left/right corners if line is negative.
|
||||
unit xr = yr * ls;
|
||||
|
||||
// Intersection as if ray were offset -r, +r.
|
||||
vec2 v = [&]
|
||||
{
|
||||
unit y = (q_.m() * xr) + yr;
|
||||
unit x = y / (ql.m() - q_.m());
|
||||
return vec2 {x, (x * q_.m()) + y};
|
||||
}();
|
||||
|
||||
// Find the intersection along diagonally oppposing AABB
|
||||
// corners.
|
||||
vec2 xrs{xr, ds_.x};
|
||||
return {bind(i + v, xrs, -ys), bind(i - v, -xrs, -ys)};
|
||||
}
|
||||
|
||||
// TODO: Comments. Bitch.
|
||||
Result SlopeAABBvsLine::vs_vertical(const line_segment& l) const
|
||||
{
|
||||
auto [a, b] = l.by_y(); // bottom, top
|
||||
|
||||
auto hit = [&](const vec2& p, std::optional<vec2> q, unit x, const vec2& n) -> Contact { return {time(x), n, p, q}; };
|
||||
|
||||
auto bind = [&](const vec2& k, const vec2& a, const vec2& b, const vec2& s, auto limit) -> std::optional<Contact>
|
||||
{
|
||||
vec2 r = r_ * s;
|
||||
vec2 af = a * s;
|
||||
unit kyf = k.y * s.y;
|
||||
|
||||
if (kyf + r_ < af.y)
|
||||
{
|
||||
if (q_.m() == kZero)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
unit x = q_.x(a.y - r.y);
|
||||
|
||||
if ((x * s.x) - r_ > af.x)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return hit(a, {}, x, {kZero, -s.y});
|
||||
}
|
||||
|
||||
// TODO: avoid duplicate point
|
||||
vec2 k2{k.x, limit(k.y - r.y, a.y)};
|
||||
unit byf = b.y * s.y;
|
||||
vec2 n{-s.x, kZero};
|
||||
|
||||
if (kyf + r_ > byf)
|
||||
{
|
||||
if (kyf - r_ > byf)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return hit(b, k2, k.x - r.x, n);
|
||||
}
|
||||
|
||||
return hit(vec2(k.x, k.y + r.y), k2, k.x - r.x, n);
|
||||
};
|
||||
|
||||
vec2 i{a.x, q_.y(a.x)};
|
||||
vec2 v{kZero, q_.m() * r_ * ds_.x * ds_.y};
|
||||
vec2 s = ds_ * ds_.y;
|
||||
|
||||
// Damn you, template overloads!
|
||||
auto min = [](unit x, unit y) { return std::min(x, y); };
|
||||
auto max = [](unit x, unit y) { return std::max(x, y); };
|
||||
|
||||
return order(bind(i - v, a, b, s, max), bind(i + v, b, a, -s, min), ds_.y);
|
||||
}
|
||||
|
||||
Result VerticalAABBvsLine::vs_slope(const line_segment& l) const
|
||||
{
|
||||
auto [a, b] = l.by_x(); // left, right
|
||||
LineEquation<unit> ql{l};
|
||||
|
||||
auto hit = [&](const vec2& k, unit xr, unit y, const vec2& n) -> Contact
|
||||
{
|
||||
std::optional<vec2> k2;
|
||||
|
||||
if (l.horizontal())
|
||||
{
|
||||
// Horizontal line: create second contact point on opposite corner.
|
||||
// TODO: avoid duplicate point
|
||||
k2 = vec2(std::clamp(x_ + xr, a.x, b.x), k.y);
|
||||
}
|
||||
|
||||
return {time(y), n, k, k2};
|
||||
};
|
||||
|
||||
auto bind = [&](const vec2& a, const vec2& b, const vec2& s) -> std::optional<Contact>
|
||||
{
|
||||
vec2 r = r_ * s;
|
||||
unit xf = x_ * s.x;
|
||||
|
||||
if (xf - r_ > b.x * s.x)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
unit axf = a.x * s.x;
|
||||
|
||||
if (xf - r_ < axf)
|
||||
{
|
||||
if (xf + r_ < axf)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return hit(a, r.x, a.y - r.y, {kZero, -s.y});
|
||||
}
|
||||
|
||||
vec2 i{x_, ql.y(x_)};
|
||||
vec2 v{r.x, ql.m() * r.x};
|
||||
vec2 k = i - v;
|
||||
return hit(k, r.x, k.y - r.y, normal(l) * -s.y);
|
||||
};
|
||||
|
||||
unit mys = copysign(kUnit, ql.m() * ds_.y);
|
||||
vec2 s{kUnit, ds_.y * mys};
|
||||
return order(bind(a, b, s), bind(b, a, -s), mys);
|
||||
}
|
||||
|
||||
Result VerticalAABBvsLine::vs_vertical(const line_segment& l) const
|
||||
{
|
||||
// Box does not overlap Y plane.
|
||||
if (x_ + r_ < l.a.x || x_ - r_ > l.a.x)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto [a, b] = l.by_y(); // bottom, top
|
||||
|
||||
auto hit = [&](const vec2& k, unit yr) -> Contact { return {time(k.y + yr), {kZero, -ds_.y}, k}; };
|
||||
|
||||
// Offset away from line ends.
|
||||
// Contacts are opposite when swept downward.
|
||||
return order(hit(a, -r_), hit(b, r_), ds_.y);
|
||||
}
|
||||
131
src/p_sweep.hpp
Normal file
131
src/p_sweep.hpp
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2023 by James Robert Roman
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef p_sweep_hpp
|
||||
#define p_sweep_hpp
|
||||
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
#include "math/fixed.hpp"
|
||||
#include "math/line_equation.hpp"
|
||||
#include "math/line_segment.hpp"
|
||||
#include "math/vec.hpp"
|
||||
|
||||
namespace srb2::sweep
|
||||
{
|
||||
|
||||
using unit = math::Fixed;
|
||||
using vec2 = math::Vec2<unit>;
|
||||
using line_segment = math::LineSegment<unit>;
|
||||
|
||||
struct Contact
|
||||
{
|
||||
unit z; // time
|
||||
vec2 n; // normal TODO REMOVE duplicate for each contact
|
||||
vec2 p; // contact point 1
|
||||
std::optional<vec2> q; // AABBvsLine: contact point 2
|
||||
};
|
||||
|
||||
struct Result
|
||||
{
|
||||
std::optional<Contact> hit, exit; // TODO result itself should be optional, not each contact
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct BaseAABBvsLine : protected srb2::math::Traits<unit>
|
||||
{
|
||||
public:
|
||||
Result operator()(const line_segment& l) const
|
||||
{
|
||||
auto derived = static_cast<const T*>(this);
|
||||
return l.vertical() ? derived->vs_vertical(l) : derived->vs_slope(l);
|
||||
}
|
||||
|
||||
protected:
|
||||
unit r_; // AABB radius
|
||||
vec2 ds_; // sweep direction signs
|
||||
|
||||
BaseAABBvsLine(unit r, const vec2& d, unit pz, unit dz) :
|
||||
r_(r), ds_(copysign(kUnit, d.x), copysign(kUnit, d.y)), t_(pz, dz) {}
|
||||
|
||||
unit time(unit x) const { return (x - t_.x) / t_.y; }
|
||||
|
||||
static Result order(std::optional<Contact>&& t1, std::optional<Contact>&& t2, unit s)
|
||||
{
|
||||
return s > kZero ? Result {t1, t2} : Result {t2, t1};
|
||||
}
|
||||
|
||||
static vec2 normal(const vec2& v)
|
||||
{
|
||||
// Normalize vector so that x is positive -- normal always points up.
|
||||
return v.normal() * (copysign(kUnit, v.x) / v.magnitude());
|
||||
}
|
||||
|
||||
static vec2 normal(const line_segment& l) { return normal(l.b - l.a); }
|
||||
|
||||
private:
|
||||
vec2 t_; // origin and length for calculating time
|
||||
};
|
||||
|
||||
}; // namespace detail
|
||||
|
||||
// Sweep can be represented as y = mx + b
|
||||
struct SlopeAABBvsLine : detail::BaseAABBvsLine<SlopeAABBvsLine>
|
||||
{
|
||||
SlopeAABBvsLine(unit r, const line_segment& l) : SlopeAABBvsLine(r, l.a, l.b - l.a) {}
|
||||
|
||||
Result vs_slope(const line_segment& l) const;
|
||||
Result vs_vertical(const line_segment& l) const;
|
||||
|
||||
private:
|
||||
math::LineEquationX<unit> q_;
|
||||
|
||||
SlopeAABBvsLine(unit r, const vec2& p, const vec2& d) : BaseAABBvsLine(r, d, p.x, d.x), q_(p, d) {}
|
||||
};
|
||||
|
||||
// Sweep is vertical
|
||||
struct VerticalAABBvsLine : detail::BaseAABBvsLine<VerticalAABBvsLine>
|
||||
{
|
||||
VerticalAABBvsLine(unit r, const line_segment& l) : VerticalAABBvsLine(r, l.a, l.b - l.a) {}
|
||||
|
||||
Result vs_slope(const line_segment& l) const;
|
||||
Result vs_vertical(const line_segment& l) const;
|
||||
|
||||
private:
|
||||
unit x_;
|
||||
|
||||
VerticalAABBvsLine(unit r, const vec2& p, const vec2& d) : BaseAABBvsLine(r, d, p.y, d.y), x_(p.x) {}
|
||||
};
|
||||
|
||||
struct AABBvsLine
|
||||
{
|
||||
AABBvsLine(unit r, const line_segment& l) :
|
||||
var_(l.vertical() ? var_t {VerticalAABBvsLine(r, l)} : var_t {SlopeAABBvsLine(r, l)})
|
||||
{
|
||||
}
|
||||
|
||||
Result operator()(const line_segment& l) const
|
||||
{
|
||||
Result rs;
|
||||
std::visit([&](auto& sweeper) { rs = sweeper(l); }, var_);
|
||||
return rs;
|
||||
}
|
||||
|
||||
private:
|
||||
using var_t = std::variant<SlopeAABBvsLine, VerticalAABBvsLine>;
|
||||
var_t var_;
|
||||
};
|
||||
|
||||
}; // namespace srb2::sweep
|
||||
|
||||
#endif/*p_sweep_hpp*/
|
||||
78
src/p_test.cpp
Normal file
78
src/p_test.cpp
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2023 by James Robert Roman
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "math/fixed.hpp"
|
||||
#include "p_sweep.hpp"
|
||||
|
||||
#include "p_local.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::vector<line_t*> g_lines;
|
||||
|
||||
};
|
||||
|
||||
void P_TestLine(line_t* ld)
|
||||
{
|
||||
g_lines.emplace_back(ld);
|
||||
}
|
||||
|
||||
line_t* P_SweepTestLines(fixed_t ax, fixed_t ay, fixed_t bx, fixed_t by, fixed_t r, vector2_t* return_normal)
|
||||
{
|
||||
using namespace srb2::math;
|
||||
using namespace srb2::sweep;
|
||||
|
||||
struct Collision
|
||||
{
|
||||
unit z;
|
||||
vec2 normal;
|
||||
line_t* ld;
|
||||
|
||||
bool operator<(const Collision& b) const { return z < b.z; }
|
||||
};
|
||||
|
||||
std::optional<Collision> collision;
|
||||
|
||||
LineSegment<Fixed> l{{ax, ay}, {bx, by}};
|
||||
AABBvsLine sweep{r, l};
|
||||
|
||||
for (line_t* ld : g_lines)
|
||||
{
|
||||
LineSegment<Fixed> ls{{ld->v1->x, ld->v1->y}, {ld->v2->x, ld->v2->y}};
|
||||
Result rs = sweep(ls);
|
||||
if (rs.hit)
|
||||
{
|
||||
if (!collision || rs.hit->z < collision->z)
|
||||
{
|
||||
collision = {rs.hit->z, rs.hit->n, ld};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_lines.clear();
|
||||
|
||||
if (!collision)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return_normal->x = Fixed {collision->normal.x};
|
||||
return_normal->y = Fixed {collision->normal.y};
|
||||
|
||||
return collision->ld;
|
||||
}
|
||||
|
||||
void P_ClearTestLines(void)
|
||||
{
|
||||
g_lines.clear();
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue