Mancubus fireball clipping
From DoomWiki.org
In the Doom engine, it is possible for small, fast-moving fireballs to clip through walls. A wall, pillar, or other solid object usually blocks the fireballs, but not always; from time to time, a fireball will fly straight through an apparently permanent feature, possibly ambushing an unwary player on the other side. This is likely to happen for a fireball traveling south or west, and still possible though unlikely for a fireball traveling north or east.
Though this is most often noticed with the mancubus because of its projectile's fast speed and unusually small width, it can be noticed with other monsters, often imps, when playing in Nightmare! or with the -fast parameter on. The problem is worse in some console ports due to a lower tic rate meaning that projectiles are checked less often for clipping; in Doom 64, the Hell knight, baron of Hell, and nightmare imp all suffer the problem frequently.
Contents
Technical explanation[edit]
The mancubus fireball mobj is defined with these two properties relevant for this bug:
{ // MT_FATSHOT ... 20*FRACUNIT, // speed 6*FRACUNIT, // radius ... }
Its radius is 6 map units (for a width of 12) and it moves at 20 units per tic.
When a fireball moves, it has its old position on one tic, and a new position on the next tic, calculated from its old position and speed. When moving to a new position, its hitbox (the square defined by its radius) may never have occupied the space between the old and new positions, thus skipping collision detection of anything present in that in-between space.
As an example, this may happen when moving due south; if the north side of a mancubus fireball's hitbox lies at map unit 100, its south side at 88 (for a width of 12), a west-east-spanning wall linedef at map unit 81, then the fireball can have its speed applied to move once such that its hitbox's north side lies at map unit 80, its bottom side at 68. At no point did the wall ever lie between the hitbox's north and south sides, and thus it never collided and passed through.
Direction sensitivity[edit]
This bug behaves differently depending on the cardinal direction that the fireball was shot towards into a wall.
If the fireball is shot due north or east, it will be traveling in a positive direction, and is subject to correct collision detection such that the fireball has a greater chance of exploding when hitting a wall. If it is instead shot south or west, it will be traveling in a negative direction with faulty collision detection with a high chance of it passing through walls.
Function P_XYMovement from p_mobj.c
xmove = mo->momx; ymove = mo->momy; do { // MAXMOVE/2 is equal to 15 map units if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2) { ptryx = mo->x + xmove/2; ptryy = mo->y + ymove/2; xmove >>= 1; ymove >>= 1; } else { ptryx = mo->x + xmove; ptryy = mo->y + ymove; xmove = ymove = 0; } if (!P_TryMove (mo, ptryx, ptryy)) { ... } } while (xmove || ymove);
If the northwards or eastwards momentum exceeds 15 map units, both momentum directions are halved, leading to a half movement step, which is used for checking if the position between the old and new positions is valid. Afterwards, the rest of the momentum is applied to check the whole step. This makes collision detection more accurate by avoiding large whole steps leaping over things that should be collided with.
The bug in this code is that southwards and westwards momentum is ignored when halving steps; only positive directions are checked. As a result, negative directions (west and south) never have half steps.
This particular bug is fixed in the MBF source port, but even there, small and fast projectiles may still pass through walls. ZDoom and ports descended from it additionally fix the larger issue by forcing projectiles that are moving too quickly versus their size to make multiple smaller moves in each tic.
Positive direction clipping[edit]
It is impossible for a fireball traveling due north or east to clip through a wall; when it moves due north or east to a new position, its momentum in any direction exceeds 15 causing a half step to be checked, necessarily detecting any wall between its old and new positions.
Using the example from earlier with the wall at coordinate 81: if the south side of the fireball hitbox is at coordinate 68, its north side is at 80, and it moves to a new position where its south side is at 88 and its north is at 100, then a half step will be checked; its intermediate position, the south side at 78 and north side at 90, overlaps the wall, causing a collision. It additionally overlaps the old and new positions, preventing any gaps in collision detection.
It is however possible for a fireball traveling diagonally in positive directions to clip through walls.
For example, for a fireball moving at an angle of 45°, it will have a momentum vector of (cos(45°)×20, sin(45°)×20) = ~(14.142, 14.142), which points in a wholly positive direction and is subject to this check, but it is below the momentum threshold of 15, and gets no half steps.
Using the same positive direction example, and rounding the northwards momentum down to 14 for simplicity, the fireball hitbox moves with its south side at 68 and north side at 80 to a new position with its south side at 82 and its north side at 94, again never overlapping the wall (though coming considerably closer) and passing straight through.