Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Hitbox-based hitscan collision #133

Merged
merged 6 commits into from
Mar 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## New Features

- **_Hitbox-based Hitscan Collision_** setting
- **_Bounded Voxel Rendering_** setting
- **Recoil-pitch scale percent** setting
- **_'IDDET'_ cheat**, to find exits in the automap
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ For these settings, their CVAR names are provided alongside the _CFG-only_ label
- **Tweaked _Stretch Short Skies_ algorithm**
- **_Black Fade_ screen wipe**
- **Extended _Level Brightness_ range:** [-8, 8]
- **_Hitbox-based Hitscan Collision_** setting
- **_"Direct + Auto"_ mode for Vertical Aiming**
- **_Direct Vertical Aiming_ for melee attacks**
- **_Move Over/Under Things_** setting [partially p.f. Crispy Doom, DSDA-Doom]
Expand Down
5 changes: 5 additions & 0 deletions src/g_game.c
Original file line number Diff line number Diff line change
Expand Up @@ -3866,7 +3866,7 @@
{
while (rewind_depth <= keyframe_index)
{
Z_Free(keyframe_list_head->frame);

Check warning on line 3869 in src/g_game.c

View workflow job for this annotation

GitHub Actions / Clang-Tidy

src/g_game.c:3869:12 [clang-analyzer-core.NullDereference]

Access to field 'frame' results in a dereference of a null pointer (loaded from variable 'keyframe_list_head')

if (keyframe_list_head->next)
{
Expand Down Expand Up @@ -6180,6 +6180,11 @@

M_BindBool("blockmapfix", &blockmapfix, NULL, false, ss_comp, wad_no,
"Fix blockmap bug (improves hit detection)");

// [Nugget] Hitbox-based hitscan collision
M_BindBool("hitbox_hitscan", &hitbox_hitscan, NULL, false, ss_comp, wad_no,
"Hitbox-based hitscan collision (fixes melee against large things)");

M_BindBool("checksight12", &checksight12, NULL, false, ss_comp, wad_no,
"Fast blockmap-based line-of-sight calculation");

Expand Down
4 changes: 4 additions & 0 deletions src/mn_setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -2615,6 +2615,10 @@ setup_menu_t comp_settings1[] = {
{"Improved Hit Detection", S_ONOFF | S_STRICT, M_X, M_SPC,
{"blockmapfix"}},

// [Nugget] Hitbox-based hitscan collision
{"Hitbox-based Hitscan Collision", S_ONOFF | S_CRITICAL, M_X, M_SPC,
{"hitbox_hitscan"}},

{"Fast Line-of-Sight Calculation", S_ONOFF | S_STRICT, M_X, M_SPC,
{"checksight12"}, .action = P_UpdateCheckSight},

Expand Down
213 changes: 179 additions & 34 deletions src/p_maputl.c
Original file line number Diff line number Diff line change
Expand Up @@ -645,58 +645,203 @@ boolean PIT_AddLineIntercepts(line_t *ld)
return true; // continue
}

// [Nugget] Hitbox-based hitscan collision /----------------------------------

boolean hitbox_hitscan;

static const inline boolean CheckPointsOnSides(mobj_t *const thing,
const fixed_t x1, const fixed_t y1,
const fixed_t x2, const fixed_t y2)
{
if ( P_PointOnDivlineSide(x1, y1, &trace)
!= P_PointOnDivlineSide(x2, y2, &trace))
{
divline_t dl = {
.x = x1,
.dx = x2 - x1,
.y = y1,
.dy = y2 - y1
};

const fixed_t frac = P_InterceptVector(&trace, &dl);

if (frac >= 0)
{
check_intercept();

intercept_p->frac = frac;
intercept_p->isaline = false;
intercept_p->d.thing = thing;
InterceptsOverrun(intercept_p - intercepts, intercept_p);
intercept_p++;

return true;
}
}

return false;
}

// [Nugget] -----------------------------------------------------------------/

//
// PIT_AddThingIntercepts
//
// killough 5/3/98: reformatted, cleaned up

boolean PIT_AddThingIntercepts(mobj_t *thing)
{
fixed_t x1, y1;
fixed_t x2, y2;
int s1, s2;
divline_t dl;
fixed_t frac;
// [Nugget] Hitbox-based hitscan collision
if (CASUALPLAY(hitbox_hitscan))
{
#define CHECK_POINTS_ON_LEFT() CheckPointsOnSides(thing, lx, by, lx, ty)
#define CHECK_POINTS_ON_RIGHT() CheckPointsOnSides(thing, rx, by, rx, ty)
#define CHECK_POINTS_ON_BOTTOM() CheckPointsOnSides(thing, lx, by, rx, by)
#define CHECK_POINTS_ON_TOP() CheckPointsOnSides(thing, lx, ty, rx, ty)

const fixed_t lx = thing->x - thing->radius,
rx = thing->x + thing->radius,
by = thing->y - thing->radius,
ty = thing->y + thing->radius;

signed char hside = (trace.x > rx)
- (trace.x < lx);

// check a corner to corner crossection for hit
if ((trace.dx ^ trace.dy) > 0)
signed char vside = (trace.y > ty)
- (trace.y < by);

if (hside == -1) // Left quadrant
{
x1 = thing->x - thing->radius;
y1 = thing->y + thing->radius;
x2 = thing->x + thing->radius;
y2 = thing->y - thing->radius;
if (vside == -1) // Bottom-left quadrant
{
if (CHECK_POINTS_ON_LEFT() || CHECK_POINTS_ON_BOTTOM())
{
if (!CHECK_POINTS_ON_RIGHT()) { CHECK_POINTS_ON_TOP(); }
}
}
else if (vside == 0) // Middle-left quadrant
{
if (CHECK_POINTS_ON_LEFT())
{
if (!CHECK_POINTS_ON_RIGHT())
{
if (!CHECK_POINTS_ON_BOTTOM()) { CHECK_POINTS_ON_TOP(); }
}
}
}
else { // vside == 1 | Top-left quadrant
if (CHECK_POINTS_ON_LEFT() || CHECK_POINTS_ON_TOP())
{
if (!CHECK_POINTS_ON_RIGHT()) { CHECK_POINTS_ON_BOTTOM(); }
}
}
}
else
else if (hside == 0) // Middle quadrant
{
x1 = thing->x - thing->radius;
y1 = thing->y - thing->radius;
x2 = thing->x + thing->radius;
y2 = thing->y + thing->radius;
if (vside == -1) // Bottom-middle quadrant
{
if (CHECK_POINTS_ON_BOTTOM())
{
if (!CHECK_POINTS_ON_TOP())
{
if (!CHECK_POINTS_ON_LEFT()) { CHECK_POINTS_ON_RIGHT(); }
}
}
}
else if (vside == 0) // Middle-middle quadrant
{
if (!CHECK_POINTS_ON_LEFT())
{
if (!CHECK_POINTS_ON_RIGHT())
{
if (!CHECK_POINTS_ON_BOTTOM()) { CHECK_POINTS_ON_TOP(); }
}
}
}
else { // vside == 1 | Top-middle quadrant
if (CHECK_POINTS_ON_TOP())
{
if (!CHECK_POINTS_ON_BOTTOM())
{
if (!CHECK_POINTS_ON_LEFT()) { CHECK_POINTS_ON_RIGHT(); }
}
}
}
}
else { // hside == 1 | Right quadrant
if (vside == -1) // Bottom-right quadrant
{
if (CHECK_POINTS_ON_RIGHT() || CHECK_POINTS_ON_BOTTOM())
{
if (!CHECK_POINTS_ON_LEFT()) { CHECK_POINTS_ON_TOP(); }
}
}
else if (vside == 0) // Middle-right quadrant
{
if (CHECK_POINTS_ON_RIGHT())
{
if (!CHECK_POINTS_ON_LEFT())
{
if (!CHECK_POINTS_ON_BOTTOM()) { CHECK_POINTS_ON_TOP(); }
}
}
}
else { // vside == 1 | Top-right quadrant
if (CHECK_POINTS_ON_RIGHT() || CHECK_POINTS_ON_TOP())
{
if (!CHECK_POINTS_ON_LEFT()) { CHECK_POINTS_ON_BOTTOM(); }
}
}
}
}
else
{
fixed_t x1, y1;
fixed_t x2, y2;
int s1, s2;
divline_t dl;
fixed_t frac;

// check a corner to corner crossection for hit
if ((trace.dx ^ trace.dy) > 0)
{
x1 = thing->x - thing->radius;
y1 = thing->y + thing->radius;
x2 = thing->x + thing->radius;
y2 = thing->y - thing->radius;
}
else
{
x1 = thing->x - thing->radius;
y1 = thing->y - thing->radius;
x2 = thing->x + thing->radius;
y2 = thing->y + thing->radius;
}

s1 = P_PointOnDivlineSide (x1, y1, &trace);
s2 = P_PointOnDivlineSide (x2, y2, &trace);

if (s1 == s2)
return true; // line isn't crossed
s1 = P_PointOnDivlineSide (x1, y1, &trace);
s2 = P_PointOnDivlineSide (x2, y2, &trace);

dl.x = x1;
dl.y = y1;
dl.dx = x2-x1;
dl.dy = y2-y1;
if (s1 == s2)
return true; // line isn't crossed

frac = P_InterceptVector (&trace, &dl);
dl.x = x1;
dl.y = y1;
dl.dx = x2-x1;
dl.dy = y2-y1;

if (frac < 0)
return true; // behind source
frac = P_InterceptVector (&trace, &dl);

if (frac < 0)
return true; // behind source

check_intercept(); // killough
check_intercept(); // killough

intercept_p->frac = frac;
intercept_p->isaline = false;
intercept_p->d.thing = thing;
InterceptsOverrun(intercept_p - intercepts, intercept_p);
intercept_p++;
intercept_p->frac = frac;
intercept_p->isaline = false;
intercept_p->d.thing = thing;
InterceptsOverrun(intercept_p - intercepts, intercept_p);
intercept_p++;
}

return true; // keep going
}
Expand Down
8 changes: 7 additions & 1 deletion src/p_maputl.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,16 @@ typedef struct {
} d;
} intercept_t;

// [Nugget]
// [Nugget] /-----------------------------------------------------------------

// CVARs
extern boolean hitbox_hitscan; // Hitbox-based hitscan collision

void P_SaveIntercepts(void);
void P_RestoreIntercepts(void);

// [Nugget] -----------------------------------------------------------------/

typedef boolean (*traverser_t)(intercept_t *in);

fixed_t P_AproxDistance(fixed_t dx, fixed_t dy);
Expand Down