Hitscan attacks hit invisible barriers in large open areas

From DoomWiki.org

Revision as of 17:29, 14 April 2018 by Axdoomer (talk | contribs) (Explain why it always occurs more often with linedef #0. Explain why it may still occur even if linedef #0 is short.)


Under construction icon-yellow.svgThis article or section is a stub. Please help the Doom Wiki by adding to it.
Spider Mastermind's attacks are blocked by an invisible barrier

Sometimes hitscans may hit an invisible barrier. This is caused by the fact that Doom has some difficulty handling the collision of bullets against very long walls. This bug happens more frequently on levels where there are walls that are more than 2048 units long.

Technical explanation of the hitscan collision error

When a wall is very long, the engine may incorrectly evaluate the position of the wall.

The technical explanation as how it occurs is yet unknown, but a particular case is know that mappers should avoid: linedef #0 should always be shorter than 2048 units long. This greatly reduces the occurrence of the bug, because essentially all blockmap generating utilities will include linedef #0 in each block of the blockmap. Although this is the most common cause of this bug, it may also occur when a hitscan crosses a block that contains another long linedef.

Technical explanation of the most common case

To understand this case, the structure of the blockmap must be first understood. Each block of the blockmap contains a list (blocklist) of linedefs that touches or may touch the block. The blocklist always starts with the value 0 and ends with -1. Most utilities will include the number 0 at the start of the list although it's not a technical necessity and the Doom engine will not skip it.

This is the essential part of the function P_BlockLinesIterator from p_maputl.c:

   offset = y*bmapwidth+x;
   
   offset = *(blockmap+offset);
   
   for ( list = blockmaplump+offset ; *list != -1 ; list++)
   {
       ld = &lines[*list];
       
       if (ld->validcount == validcount)
           continue; 	// line has already been checked
       
       ld->validcount = validcount;
       
       if ( !func(ld) )
           return false;
   }
   return true;	// everything was checked

The loop will iterate over the list and it starts at the beginning of the blocklist which has the value 0 until it reaches the end. Each element on the list is the number of a linedef, but since the iteration starts on the first element which is always 0, then linedef #0 is constantly checked each time the function is called. Since it's done whenever the player fires, the bug is more inclined to occur on a level where this linedef is too long.

This case can be fixed by adding +1 to blockmaplump+offset, but hitscan errors may still occur against other walls which are too long. A good habit for map developers is to break long walls into linedefs smaller than 1024 units long, which will also reduce the visibility of the long wall error at the same time.

Demo files