|
Wow, explosions in Doom are awesome right? Everyone knows the "bug" (it's intentional) in which explosions don't actually explode in a circle, instead being in a square shape which measures distance by only 1 vector, either x or y distance depending on which is larger.
But did you know that Id software fucked up their method of detecting collision too?
This entry will assume you know how the blockmap works, as this whole issue is related to the blockmap iterator. Let's look at a segment of P_RadiusAttack.
dist = (distance+MAXRADIUS)<<FRACBITS;
yh = P_GetSafeBlockY(spot->y + dist - bmaporgy);
yl = P_GetSafeBlockY(spot->y - dist - bmaporgy);
xh = P_GetSafeBlockX(spot->x + dist - bmaporgx);
xl = P_GetSafeBlockX(spot->x - dist - bmaporgx);
for (y=yl ; y<=yh ; y++)
for (x=xl ; x<=xh ; x++)
P_BlockThingsIterator (x, y, PIT_RadiusAttack );
FRACUNIT is one decimal point number, <<FRACBITS converts an integer to a fixed-point digit, by multiplying it by 65536.
MAXRADIUS = 32 * FRACUNIT
distance is an integer storing the distance. It is not in fixed point, which is why it gets converted.
P_BlockThingsIterator goes through blocks in the blockmap and finds what things are in that block.
P_GetSafeBlockY and P_GetSafeBlockX are functions that take a coordinate and find what block that position belongs to.
The for loops are supposed to loop through the bottom left block, to the top right block, left to right, then bottom to top.
If you are a decino viewer, which if you're reading doom code ramblings you probably are, then you probably know about the bug in which a thing can be read multiple times and take the damage of the eplosion multiple times, but that's not what I'm talking about today.
MAXRADIUS is used as a rough estimate for finding overlapping things across blocks, it checks 32 map units left, right up and down from the calling things position to see if it's near the edge of a block, if it is then it checks that block too to try and avoid missing collision (we all know how that went, it should ideally be the size of the calling thing + the maximum size of a thing, that being the spider mastermind (128 * FRACUNIT).
There's a slight miscalculation here, in the line (distance+MAXRADIUS)<<FRACBITS;
We know that MAXRADIUS is 32 * FRACUNIT, and <<FRACBITS multiplies an int to a FRACUNIT, but we are adding distance and MAXRADIUS BEFORE we scale it up!
The intention was clearly to convert the integer distance to fixed point, then add, but instead we also multiply the MAXRADIUS value!
What effect does this have?
MAXRADIUS * 65536 * 65536 = 137438953472.
fixed_t can store a maximum value of 4294967296.
This causes the value to overflow.
32 times.
and it wraps back around to MAXRADIUS<<FRACBITS = 0.
So it was completely useless.
And now the game does not even bother accounting for extra width.
Does this have any effect? Not particularly. But you could make a map with a thing that's on the very edge of a block and make it impossible to place a missile any closer than 128 map units, make it an arachnotron and the player would be very confused!
Oh and also if you think that the missile deals less damage if it hits the other edge of a target you're only partially correct.
dist -= thing->radius;
if (dist > 0)
dist = 0;
It subtracts the thing's radius first so the damage only depends on the distance the rocket's centre is from the very edge.
Thanks for your attention, tune in next time when I discuss my hatred for DSDA-Doom and Nyan-Doom's "Disable infinite height things" code and how I rewrote it all in one day and fixed all the bugs it had while also making it less cluttered and (probably) less slow, while also explaining why I commented out every line of the old code.
Also, this'll be a reocurring thing, but of course I rewrote this code to not ever attack the same thing twice while also fixing the inability to hit enemies sometimes, and the thing where the rocket can randomly deal less or more damage based on how the edge of the hitbox is aligned when actually colliding. Look forward to much much more in the future.
|
|