Hitscan attacks hit invisible barriers in large open areas
From DoomWiki.org
This article or section is a stub. Please help the Doom Wiki by adding to it. |
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.