From DoomWiki.org

Under construction icon-yellow.svgThis article or section is a stub. Please help the Doom Wiki by adding to it.

The "Anywhere Moo Bug"[1] is a mathematical error in a tracer-based algorithm, which used the BLOCKMAP to do monster Line-of-sight (LOS) checks. This bug caused monsters to occasionally see the player through an arbitrary number of walls in Doom v1.2, and persisted into Heretic, Hexen, and some versions of ZDoom. It was eventually "fixed" by swapping the original algorithm with a BSP-based one. However, the performance penalty necessitated the creation of REJECT tables to speed up LOS checks, which brought along its own errors, due to the blind spots in Doom's REJECT tables.

The bug received its name by James "Quasar" Haley, who noted the bug was best demonstrated with boss monsters, most notably the cyberdemon and maulotaur, due to their wake sounds not decreasing in volume with distance.

Code[edit]

The corrected code, copied from ZDoom, is below:

for (count = 0 ; count < 100 ; count++)
  {
    if (flags & PT_ADDLINES)
    {
      AddLineIntercepts(mapx, mapy);
    }
    
    if (flags & PT_ADDTHINGS)
    {
      AddThingIntercepts(mapx, mapy, btit);
    }
        
    if (mapx == xt2 && mapy == yt2)
    {
      break;
    }

    // [RH] Handle corner cases properly instead of pretending they don't exist.
    switch ((((yintercept >> FRACBITS) == mapy) << 1) | ((xintercept >> FRACBITS) == mapx))
    {
    case 0:   // neither xintercept nor yintercept match!
      count = 100;  // Stop traversing, because somebody screwed up.
      break;

    case 1:   // xintercept matches
      xintercept += xstep;
      mapy += mapystep;
      break;

    case 2:   // yintercept matches
      yintercept += ystep;
      mapx += mapxstep;
      break;

    case 3:   // xintercept and yintercept both match
      // The trace is exiting a block through its corner. Not only does the block
      // being entered need to be checked (which will happen when this loop
      // continues), but the other two blocks adjacent to the corner also need to
      // be checked.
      if (flags & PT_ADDLINES)
      {
        AddLineIntercepts(mapx + mapxstep, mapy);
        AddLineIntercepts(mapx, mapy + mapystep);
      }
      
      if (flags & PT_ADDTHINGS)
      {
        AddThingIntercepts(mapx + mapxstep, mapy, btit);
        AddThingIntercepts(mapx, mapy + mapystep, btit);
      }
      xintercept += xstep;
      yintercept += ystep;
      mapx += mapxstep;
      mapy += mapystep;
      break;
    }
  }

In the original code, "Case 3" was missing. Quoting Christoph Oelckers (Graf Zahl): "When case 3 is not there the trace will get lost with no chance of ever getting back on track which would cause an endless loop. That's where the 'for' loop came in. Apparently id had problems with the code hanging but never found the real cause so they just put a safety net around their broken algorithm so that it can terminate."[2]

References[edit]

  1. James Haley (20 August 2003). "cam_sight.cpp of The Eternity Engine Source Code." Retrieved 29 November 2016.
  2.  (11 October 2009). "Is it possible to... (Heretic source) - Doomworld Forums." Retrieved 29 November 2016.