#include "stdafx.h"

#include "UI.h"

#include <stdio.h>

#include "Objects.h"
#include "Dispatch.h"
#include "CSB.h"
#include "Data.h"


#ifdef _DEBUG
i32 timertrace[1000];
i32 timertraceindex=0;
void settrace(i32 n)
{
  if (n==-1)
  {
    while (timertraceindex>0) timertrace[--timertraceindex]=0;
  }
  else if (n==-2)
  {
    if (TimerTraceActive)
    {
      int i;
      for (i=0; i<timertraceindex; i++)
      {
        fprintf (GETFILE(TraceFile), "%4d %08x\n",i, timertrace[i]);
      };
    };
  }
  else if (timertraceindex>999)
  {
    return;
  }
  else
  {
    timertrace[timertraceindex++] = n;
  };
}
#endif

#ifdef _DEBUG
#define TIMERTRACE(n) settrace(n);
#else
#define TIMERTRACE(n)
#endif



//*********************************************************
//
//*********************************************************
// TAG00b1f0
RN CreateMonster(i32 value,   //8
                 i32 hitPointMultiplier, //10
                 i32 numMonM1,//12 Number Monsters-1??
                 i32 facing,  //14 P4
                 i32 mapX,    //16
                 i32 mapY)    //18
{// (RN)
  dReg         D0, D3, D4, D5, D6;
  RN           objD7;
  MONSTERDESC *pmtDesc;
  DB4         *DB4A3;
  i16          w_12;
  i16          w_2;
  MONSTERTYPE  mt;
  mt = (MONSTERTYPE)value;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//

  if (!MonsterAllowedOnLevel(RNnul, d.LoadedLevel, mt)) return(RNnul);
  if (   (d.ITEM16QueLen >= d.MaxITEM16-5)
      && (d.LoadedLevel == d.partyLevel)  ) return (RNnul);
  objD7=FindEmptyDB4Entry(false);
  if (objD7 == RNnul) return (RNnul);
  DB4A3 = GetRecordAddressDB4(objD7);
  DB4A3->possession(RNeof);
  D3W = 0;
  //30OCT02SETWBITS10_10(DB4A3->word14, uw(0));
  DB4A3->important(0);
  //30OCT02SETWBITS8_9(DB4A3->word14, P4);
  DB4A3->facing(facing);
  //30OCT02SETWBITS5_6(DB4A3->word14, numMonM1);
  DB4A3->numMonM1(numMonM1);
  w_2 = sw(numMonM1);
  if (w_2 != 0)
  {
    D4W = 0;// Added to avoid compiler warning. Not needed.
    D5W = STRandom0_3(); // Monster position????
  }
  else
  {
    D4W = 255;
    D5W = 0; //Added to avoid compiler warning. Not needed.
  };
  DB4A3->monsterType(value); // Item26 index
  ASSERT(DB4A3->monsterType() < 27);
  pmtDesc = &d.MonsterDescriptor[DB4A3->monsterType()];
  D6W = pmtDesc->uByte8[1];
  do
  {
    w_12 = sw(STRandom() % (D6W/4 + 1));
    D0W = sw(D6W * hitPointMultiplier + w_12);
    DB4A3->hitPoints6[numMonM1] = D0W;
    if (w_2 != 0)
    {
      D4W = SetMonsterPositionBits(
                D4W,     //old positions byte
                numMonM1, //monster number
                D5W++);  //monster position
      if (pmtDesc->horizontalSize() == 1)
      {
        D5W++;
      };
      D5W &= 3;
    };
  } while ((numMonM1--) != 0);
  DB4A3->groupIndex(D4W);
  // The following function call fixes DB4A3->uByte5.
  D0W = MoveObject(objD7, -1, 0, mapX, mapY);
  ASSERT(D0W != 0xccc);
  if (D0W != 0)
  {
    return (RNnul);
  }
  else
  {
    QueueSound(soundTELEPORT, mapX, mapY, 1);
    return (objD7);
  };
}

//*********************************************************
//
//*********************************************************
//   TAG00bc12
void StealFromCharacter(DB4 *pDB4, i16 chIdx)
{//(void)
  dReg D0, D1, D5, D6, D7;
  RN objD4;
  CHARDESC *pcA2;
  i16      w_2;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
  w_2 = 0;
  pcA2 = &d.CH16482[chIdx];
  D1W = sw(Quickness(pcA2));
  D7W = sw(100 - D1W);
  D5W = sw(STRandom(8));
  while ( (D7W>0) && (TAG016476(pcA2,D7W)==0) )
  {
    D6W = d.uByte13126[D5W];
    if (D6W == 13)
    {
      D6W = sw(D6W + STRandom(17));
    };
    objD4 = pcA2->possessions[D6W];
    if (objD4 != RNnul)
    {
      w_2 = 1;
      objD4 = RemovePossession(chIdx, D6W);
      if (pDB4->possession() == RNeof)
      {
        pDB4->possession(objD4);
      }
      else
      {
        AddObjectToRoom(objD4, pDB4->possession(), -1, 0);
      };
      DrawCharacterState(chIdx);
    };
    D5W = sw((D5W + 1) & 7);
    D7W -= 20;

  };
  D0W = sw(STRandom(8));
//
  if ( (STRandom(8)==0) || ((w_2!=0) && (STRandomBool()!=0)) )
  {
    D0W = sw(STRandom(64) + 20);
    ASSERT(pDB4->groupIndex() < d.MaxITEM16);
    d.Item16[pDB4->groupIndex()].uByte5 = D0B;
    //30OCT02SETWBITS0_3(pDB4->word14, 5);
    pDB4->fear(5);
  };
}

//*********************************************************
//
//*********************************************************
//   TAG00be5a
bool BlockedTypeA(i32 mapX, i32 mapY)
{ // return true if blocked to passage.
  // Certain monsters can go through certain doors.
  // Door types 3 & 4 are considered blocked as are
  // certain special doors on this level.
  dReg D0, D5;
  CELLFLAG cfD7;
  ROOMTYPE rtD6;
  DB0 *pDoor;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  cfD7 = d.LevelCellFlags[mapX][mapY];
  rtD6 = (ROOMTYPE)(cfD7>>5);
  if (rtD6 == roomDOOR)
  {
    pDoor = FirstObjectRecordAddressDB0(mapX, mapY); //Door
    D5W = (I16)(cfD7 & 7); // door type
    SET(D0W, D5W==3);
    if (D0W == 0)
    {
      SET(D0W, D5W==4);
    };
    if (D0W != 0)
    {
      D0W = d.DoorTOC[pDoor->doorType()];//one of two types on level
      SET(D0W, (D0W & 0x100) == 0);// The high order byte
    };
    D0W = (I16)(D0W & 1);
  }
  else
  {
    SET(D0W, rtD6==roomSTONE);
    if (D0W == 0)
    {
      SET(D0W, rtD6 == roomFALSEWALL);
      if (D0W != 0)
      {
        SET(D0W, (cfD7 & 4) == 0);
      };
    };
  };
  return D0W != 0;
}

//*********************************************************
//
//*********************************************************
//  TAG00bf00
bool StoneOrClosedDoor(i32 mapX, i32 mapY)
{
  dReg D0;
  CELLFLAG cfD7;
  ROOMTYPE rtD6;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  cfD7 = d.LevelCellFlags[mapX][mapY];
  rtD6 = (ROOMTYPE)(cfD7>>5);
  SET(D0W, rtD6==roomSTONE);
  if (D0W == 0)
  {
    SET(D0W, rtD6==roomFALSEWALL);
    if (D0W != 0)
    {
      SET(D0W, (cfD7&4)==0);
    };
  };
  return D0W & 1;
}

//*********************************************************
//Generally, we try to follow a straight line between
// the party and the monster by keeping the slope
// of the line as constant as possible.  If we run into
// a wall (as determined by the provided function) we
// say it is impossible and return a zero.
// Else we return the distance to target. Orthogonal crow.
//*********************************************************
i16 TestDirectPathOpen(i32 mapX,    //8
                       i32 mapY,    //10
                       i32 partyX,  //12
                       i32 partyY,  //14
                       bool (*function)(i32,i32))
{
  dReg D0, D1, D4, D5, D6, D7;
  i16  w_12;
  bool DIAG_10;
  i16  w_8;
  i16  w_6;
  bool NS_4;
  i16  w_2;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D0W = sw(abs(mapX-partyX));
  D1W = sw(abs(mapY-partyY));
  if (D0W+D1W  <= 1) return 1;
  D5W = sw(abs(partyX - mapX));
  D4W = sw(abs(partyY - mapY));
  NS_4 = D5W < D4W; // Set if north/south
  DIAG_10 = D5W == D4W; // // set if perfect diagonal
  D5W = sw(partyX);
  D7W =(I16)((partyX <= mapX)  ? 1 : -1); // Y increment party to monster
  D4W = sw(partyY);
  D6W = (I16)(partyY <= mapY ? 1 : -1); // Increment party to monster
  if (NS_4) // If North/south
  {
    w_2 = sw(D4W - mapY); // partyY-mapY
    if (w_2 != 0)
    {
      D0W = sw(64 * (D5W - mapX) / w_2);
    }
    else
    {
      D0W = 128;
    };
  }
  else
  {
    w_2 = sw(D5W - mapX);
    if (w_2 != 0)
    {
      D0W = sw(64 * (D4W - mapY)/w_2); //partyY-mapY
    }
    else
    {
      D0W = 128;
    };
  };
  w_12 = D0W;
  do
  {
    if (DIAG_10) //perfect diagonal
    {


      if (    (    ((*function)(sw(D5W+D7W), D4W) != 0)
                && ((*function)(D5W, sw(D4W+D6W)) != 0))
           || ((*function)(D5W=sw(D5W+D7W), D4W=sw(D4W+D6W))!=0) )
      {
        return 0;
      };
    }
    else
    {
      if (NS_4) //North/south
      {
        w_2 = sw(D4W-mapY); //
        if (w_2 != 0)
        {
          D0W = sw(64 * (D5W + D7W - mapX)/w_2);
        }
        else
        {
          D0W = 128;
        };
      }
      else
      {
        w_2 = sw(D5W + D7W - mapX); // one step in x-direction vs mapX
        if (w_2 != 0) // if not in adjacent column
        {
          D0W = sw(64 * (D4W - mapY)/w_2); // new slope to monster
        }
        else
        {
          D0W = 128;
        };
      };
      w_2 = sw(D0W - w_12); // new slope minus original slope
      w_6 = sw(abs(w_2));
      if (NS_4)
      {
        w_2 = sw(D4W + D6W - mapY);
        if (w_2 != 0)
        {
          D1W = sw(64 * (D5W - mapX)/w_2);
        }
        else
        {
          D1W = 128;
        };
      }
      else
      {
        w_2 = sw(D5W - mapX); //#columns to monster
        if (w_2 != 0)
        {
          D1W = sw(64 * (D4W + D6W - mapY)/w_2); // new slope if we take y-step
        }
        else
        {
          D1W = 128;
        };
      };
      w_2 = sw(D1W - w_12);//y-step slope vs original slope
      w_8 = sw(abs(w_2));//difference in slope if we take y-step
      if (w_6 < w_8)
      {
        D5W = sw(D5W + D7W); // Assume party moves in x-direction
      }
      else
      {
        D4W = sw(D4W + D6W);//Assume party takes the y-step
      };
      D0W = (*function)(D5W, D4W);
      if (D0W != 0) // non-zero if blocked
      {
        if (w_6 != w_8) return 0;
        D0W = (*function)(D5W=sw(D5W+D7W), D4W=sw(D4W-D6W));
        if (D0W != 0) return 0;
      };
    };
    D0W = sw(abs(D5W - mapX));
    D1W = sw(abs(D4W - mapY));
  } while (D1W + D0W > 1);
  return DistanceBetweenPoints(mapX, mapY, partyX, partyY);
}

//*********************************************************
//
//*********************************************************
//  TAG00c202
i16 Blocked(DB4 *pDB4, i32 P2, i32 mapX, i32 mapY)
{
  dReg         D0, D4, D5, D6, D7;
  MONSTERDESC *pmtDesc;
  i16          w_18;
  i16          w_8[4];
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  pmtDesc = &d.MonsterDescriptor[pDB4->monsterType()];
  if (   (d.Invisible != 0)
      && !pmtDesc->seesInvisible() ) return 0;
  if (pmtDesc->word2Bit2()) goto tag00c2fe;
  //ASSERT(pDB4->uByte5 < d.MaxITEM16);
  if (pDB4->groupIndex() < d.MaxITEM16)
  {
    D4W = d.Item16[pDB4->groupIndex()].facings();
  }
  else
  {
    D4W = 0x7ccc; //designed to cause error!
  };

  if (P2 < 0)
  {
    D6W = 0;
    for (P2=pDB4->numMonM1(); //#monsters in group-1
         P2>=0;
         P2--)
    {
//      D7W = sw((D4W >> (2*P2)) & 3); //Direction this monster facing?
      D7W = sw(TwoBitNibble(D4W,P2)); //Direction this monster facing?
      D5W = D6W;
      while (D5W-- != 0)
      {
        D0W = w_8[D5W];
        if (D7W == D0W) break;
      };
      if (D5W == -1) // if we fell out bottom of loop.
      {
        w_8[D6W++] = D7W;
      };
      // for (P2) continue
//
    }; // while (P2 >= 0)
  }
  else
  {
//    w_8[0] = sw((D4W >> 2*P2) & 3);
    w_8[0] = sw(TwoBitNibble(D4W,P2));
    D6W = 1;
  };
  while (D6W-- != 0)
  {
    D0W = IsItInThisDirection(
                   w_8[D6W],
                   mapX,
                   mapY,
                   d.partyX,
                   d.partyY);
    if (D0W == 0) continue;
tag00c2fe: //***
    D5W = pmtDesc->sightDistance();
    if (!pmtDesc->canSeeInDark())
    {
      D5W = sw(D5W - d.CurrentPalette/2);
    };
    w_18 = sw(Larger(1, D5W));
    if (d.Word13022 > w_18) return 0; //Cannot see the party then zero.
    D0W = TestDirectPathOpen(
              mapX, mapY, d.partyX, d.partyY, BlockedTypeA);
    return D0W;
    //continue here
  }; // while (D6-- != 0)
  return 0;
}

//*********************************************************
//
//*********************************************************
//  TAG00c36c
i16 GetBestMonsterFacingP1(MONSTERDESC *pmtDesc, i32 mapX, i32 mapY)
{
  dReg D0, D6, D7;
  i16  w_10;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D7W = pmtDesc->word14_8_11();
  if (D7W == 0) return 0;

  if (   ((D7W+1)/2 >= d.Word13022)
      && (TestDirectPathOpen(mapX,
                             mapY,
                             d.partyX,
                             d.partyY,
                             StoneOrClosedDoor) != 0) )
  {
    d.Dir12956 = d.Word13026;
    return sw(d.Word13024 + 1);
  };
  D6W = TAG0169ec(mapX, mapY);
  if (D6W != 0)
  {
    w_10 = STRandom0_3();
    D0W = sw(w_10 + (UI8)(d.Byte13220[D6W-1]));
    if (D0W > 30 - 2*D7W)
    {
      return sw(GetDirectionMoved(
                     mapX,
                     mapY,
                     BITS0_4(d.Word13268[D6W]),
                     BITS5_9(d.Word13268[D6W]) ) + 1);
    };
  };
  return 0;
}

//*********************************************************
//
//*********************************************************
bool TAG00c442(MONSTERDESC *pmtDesc,   //8
               i32 mapX,     //12
               i32 mapY,     //14
               i32 direction,//16
               i16 P5)       //18
{ // This function expects d.Obj13020 to be the RN of
  // a monster.  After loading a new game, d.Obj13020 may
  // be zero.
  // And even during an extended game play, Obj13020 will
  // sometimes point to the wrong monster.
  // So what do we do about that?  To be compatible
  // with the initial Atari version, we should deal with the
  // zero.  To be correct, we should fix d.Obj13020 so that
  // it reflects the current monster.  So that is what I
  // have done.
  dReg           D0, D1, D4, D5, D6, D7;
  CELLFLAG      *pcfA0;
  DB0           *DB0A2;
  DB1           *DB1A2;
  DB15          *pDB15A2;
  RN             obj_2;
  D4L = 0xccccc;
  D5L = 0xccccc;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  TIMERTRACE(0xc442);
  d.Byte13030[direction] = 1;
  d.Obj13040 = RNeof;
  d.Word13042 = 0;
  d.Word13044 = 0;
  if (pmtDesc->timePerMove() == 255) return 0;

  UpdateXY(direction, 1, 0, &mapX, &mapY);//One step forward
  D7W = sw(mapX);
  D6W = sw(mapY);
  SET(D0B, D7W>=0);
  if (D0B != 0)
  {
    SET(D0B, D7W < d.width);
  };
  if (D0B != 0)
  {
    SET(D0B, D6W >= 0);
    if (D0B != 0)
    {
      SET(D0B, D6W < d.height);
    };
  };
  if (D0B != 0)
  {
    pcfA0 = d.LevelCellFlags[D7W]; // column pointer
    D5W = pcfA0[D6W]; // Get this room's CELLFLAG
    //D5W = D0W
    D4W = sw(D5W >> 5); // The type of room.
    SET(D0B, D4W != roomSTONE);
  };
  if (D0B != 0)
  {
    SET(D0B, D4W != roomSTAIRS);
  };
  if (D0B != 0)
  {
    SET(D0B, D4W != roomPIT);
    if (D0B == 0)
    {
      D0W = (I16)(D5W & 1);
      if (D0W != 0) D0W = P5;
      SET(D0B, D0W != 0);
    };
    if (D0B == 0)
    {
      SET(D0B, (D5W&8)==0);
    };
    if (D0B == 0) D0L = pmtDesc->levitating()?1:0;
    SET(D0B, D0W != 0);
  };
  if (D0B != 0)
  {
    SET(D0B, D4W != roomFALSEWALL);
    if (D0B == 0)
    {
      D0W = (I16)(D5W & 4);
    }
    SET(D0W, D0B != 0);
    if (D0B == 0)
    {
      D0W = (I16)(D5W & 1);
      if (D0W != 0) D0W = P5;
      SET(D0B, D0W != 0);
    };
  };
  SET(D0B, D0B == 0);
  d.Word13038 = (I16)(D0W & 1);
  if (d.Word13038 != 0) return 0;
  if (pmtDesc->invincible())
  {
    for (obj_2 =  FindFirstObject(D7W, D6W);
         obj_2 != RNeof;
         obj_2 =  GetDBRecordLink(obj_2))
    {

      if (obj_2.dbType() != dbCLOUD) continue;
      pDB15A2 = GetRecordAddressDB15(obj_2);
      if (pDB15A2->type() != 50) continue;
      d.Byte13034[direction] = 1;
      d.Word13036++;
      return 0;
//
//
    };
  };
  for (;;)
  {
    ASSERT(D4L != 0xccccc);
    ASSERT(D5L != 0xccccc);
    if (D4W != roomTELEPORTER) break;
    if ((D5W & 8)==0) break;//break if teleporter inactive
    if (pmtDesc->word16_12_15() < 10) break;
    DB1A2 = FirstObjectRecordAddressDB1(D7W, D6W);
    if (DB1A2->what() == 0) break;//if not monsters
	// See comment at top of this function about d.Obj13020.  It may
	// be zero and it may be wrong.  I have patched the Atari code
  // ProcessTimer29To41 to always put the current monster
  // in Obj13020.  This seemed the 'right' thing to do.
    if (MonsterAllowedOnLevel(d.Obj13020, DB1A2->destLevel()))
            break;
    d.Word13038 = 1;
    return 0;
  };
  SET(D0B, d.LoadedLevel == d.partyLevel);
  if (D0B != 0)
  {
    SET(D0B, D7W == d.partyX);
  };
  if (D0B != 0)
  {
    SET(D0W, D6W == d.partyY);
  };
  d.Word13044 = (I16)(D0W & 1);
  if (d.Word13044 != 0) return 0;
  for (;;)
  {
    if (D4W != roomDOOR) break;//
    DB0A2 = FirstObjectRecordAddressDB0(D7W, D6W);
    D0W = (I16)(D5W & 7);
    D1W = BITS5_5(DB0A2->mode()); //Up-down or sideways
    if (D1W != 0)
    {
      D1W = pmtDesc->verticalSize();
    }
    else
    {
      D1W = 1;
    };
    if (D0W <= D1W) break;
    if ((D5W & 7) == 5) break;
    if (pmtDesc->nonMaterial()) break;
    d.Word13042 = 1;
    return 0;
  };
  d.Obj13040 = FindFirstMonster(D7W, D6W);
  SET(D0W, d.Obj13040 == RNeof);
  return D0W & 1;
}

//*********************************************************
//
//*********************************************************
i16 TAG00c70c(MONSTERDESC *pmtDesc,i32 P2,i32 P3,i16 P4)
{
  dReg D0, D7;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  for (D7W = 0; D7W < 4; D7W++)
  {
    if (d.Byte13030[D7W] != 0) continue;
    D0W = TAG00c442(pmtDesc, P2, P3, D7W, P4);
    if (D0W != 0) return sw(D7W + 1);
//
//
  };
  return 0;
}

//*********************************************************
//
//*********************************************************
i16 TAG00c756(MONSTERDESC *pmtDesc,  //8
              i32 mapX,              //12
              i32 mapY,              //14
              i32 direction)         //16
{
  dReg D7;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D7W = sw(direction);
  ASSERT(D7W < 4);
  if (d.Byte13034[D7W] != 0)
  {
    return 0;
  };
  mapX = sw(mapX + d.DeltaX[D7W]);
  mapY = sw(mapY + d.DeltaY[D7W]);
  return TAG00c442(pmtDesc, mapX, mapY, D7W, 0);
}

//*********************************************************
//
//*********************************************************
//   TAG00c7bc
void TurnMonster(ITEM16 *pI16,
                 i32 preferredDirection,
                 i32 monsterIndex,  // within group
                 bool P4)
{ //P4 means to treat the monsters a pairs.  For example,
  //worms, which occupy two corners, must turn together.
  dReg D0, D5;
  i32 newDir;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  //D7W = preferredDirection;
  //D6W = monsterIndex;
  D5W = P4;
  if (P4)
  {
    if (   (d.Time == d.Time13130)
        && (pI16 == d.pI1613134) ) return;
  };
  D5W = pI16->facings(); // Old facing byte
//  D0W = sw(D5W >> (2*monsterIndex)); // Current facing
  D0W = sw(TwoBitNibble(D5W,monsterIndex)); // Current facing
  D0W = (I16)((D0W - preferredDirection) & 3); // relative to preference
  if (D0W == 2) // if facing away from party
  {
    newDir = ((STRandom() & 2) + preferredDirection + 1) & 3;
    D5W = SetMonsterPositionBits(
             D5W,  //old facing byte
             monsterIndex,
             newDir); //
  }
  else
  {
    newDir = preferredDirection;
    D5W = SetMonsterPositionBits(
               D5W,  //old directions byte
               monsterIndex,
               newDir); //new direction
  };
  if (P4)
  {
    D5W = SetMonsterPositionBits(
                     D5W,             //old directions byte
                     monsterIndex ^ 1, //monster number
                     newDir);          //facing direction
    d.Time13130 = d.Time;
    d.pI1613134 = pI16;
  };
  pI16->facings() = D5B;
}

//*********************************************************
//
//*********************************************************
//   TAG00c86c
void TurnMonsterGroup(ITEM16 *pI16,
                      i32 PreferredDirection,
                      i32 numMonster,
                      i32 P4)
{ // P4 means monsters should be treated as pairs.
  // This particularly applies to worms that occupy
  // two corners of the room.  They must turn together.
  i32 monsterIndex;
  bool bD6;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  monsterIndex = numMonster;
  bD6 = ( (monsterIndex!=0) && (P4!=0) );
  if (bD6) monsterIndex--;
  do
  {
//
    if ( (monsterIndex == 0) || (STRandomBool() != 0) )
    {

      TurnMonster(pI16,
                  PreferredDirection,
                  monsterIndex,
                  bD6);
    };
    monsterIndex--;
  } while (monsterIndex >= 0);
}

//*********************************************************
//
//*********************************************************
i16 TAG00c8c0(DB4 *pDB4, //8
              i32 mapX,  //12
              i32 mapY,  //14
              i16 P4)    //16 //MonsterIndex within group
{ //(i16)
  //Called to compute last parameter of call
  // to NextMonsterUpdateTime.
  //Return 0 if (no live character or ???).
  //      else 1;
  dReg         D4, D5, D6;
  RN           objD7;
  MONSTERTYPE  mtD7;
  ITEM16       item16_20;
  DB4         *DB4A3;
  MONSTERDESC *pmtDesc;
  CHARDESC    *pch_4;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  DB4A3 = pDB4;
  d.Long12950 = d.Time;
  ASSERT(DB4A3->groupIndex() < d.MaxITEM16);
  memmove(&item16_20, &d.Item16[DB4A3->groupIndex()], 16);
  mtD7 = DB4A3->monsterType();
  pmtDesc = &d.MonsterDescriptor[mtD7];
  D6W = d.Word13024;
  D5W = item16_20.positions();
  if (D5W == 255)
  {
    D5W = STRandomBool();
  }
  else
  {
//    D5W = sw(((((D5W >> (2*P4)) & 3) + 5 - D6W) & 2) / 2);
    D5W = sw((((TwoBitNibble(D5W,P4)) + 5 - D6W) & 2) / 2);
    // 1 if CCW turn is good?????
  };
  D5W = (I16)((D5W + D6W) & 3);

  if (    (pmtDesc->word14_12_15() > 1) // ie: 3, 4, or 6
       && ((d.Word13022 > 1) || (STRandomBool() != 0))   )
  {
    if ( (mtD7 == mon_Vexirk) || (mtD7 == mon_LordChaos) )
    {
      if (STRandomBool() != 0)
      {
        objD7 = RNFireball;
      }
      else
      {
        switch (STRandom0_3())
        {
        case 0: objD7 = RNDispellMissile; break;

        case 1: objD7 = RNLightning; break;

        case 2: objD7 = RNPoisonCloud; break;

        case 3: objD7 = RNZoSpell; break;
        }; //switch()
      };
    }
    else
    {
      switch (mtD7)
      {
      case mon_SlimeDevil:
          objD7 = RNPoison; break;
      case mon_FlyingEye:
          if (STRandom(8) != 0) objD7 = RNLightning;
          else objD7 = RNZoSpell;
          break;
      case mon_Zytaz:
          if (STRandomBool() != 0) objD7 = RNPoisonCloud;
          else objD7 = RNFireball;
          break;

      case mon_Demon:
      case mon_Dragon:
          objD7 = RNFireball;
          break;
      }; // switch
    };
    D4W = sw(pmtDesc->uByte8[4]/4 + 1);
    D4W = sw(D4W + STRandom(D4W));
    D4W = sw(D4W + STRandom(D4W));
    LaunchMissile(
                  objD7,       //parameter
                  mapX,
                  mapY,
                  D5W,        //position??
                  d.Word13024, //direction???
                  ApplyLimits(20, D4W, 255),
                  pmtDesc->uByte8[4],
                  8);
  }
  else
  {
    if (pmtDesc->word2Bit4())
    {
      D5W = STRandom0_3();
      for (D4W = 0;
           (D4W<4) && (d.CH16482[D5W].HP==0);
           D4W++)
      { //look for live character.
//
      };
      if (D4W == 4) return 0;
    }
    else
    {
      D5W = GetCharacterToDamage(mapX, mapY, D5W);
      if (D5W < 0) return 0;
    };
    if (mtD7 == mon_Giggler)
    {
      StealFromCharacter(DB4A3, D5W);
    }
    else
    {
      D4W = sw(1 + TAG00f6ee(DB4A3, D5W));
      pch_4 = &d.CH16482[D5W];
      if (D4W > pch_4->uByte41)
      {
        pch_4->uByte41 = (UI8)(D4W);
        pch_4->facing3 = (UI8)((D6W+2) & 3);
      };
    };
  };
  D4W = pmtDesc->uByte1;
  if (D4W)
  {
    D4W--;
    QueueSound(d.uByte9672[D4W],mapX,mapY,1);
  };
  return (1);
}

//*********************************************************
//
//*********************************************************
void TAG00cb68(TIMER *pTimer,ui32 time)
{
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  if (time < (pTimer->timerTime & 0xffffff))
  {
    pTimer->timerFunction -= timerFunction37-timerFunction32;
    pTimer->timerUByte8 = (UI8)((pTimer->timerTime & 0xffffff) - time);
    pTimer->timerTime = time | (pTimer->timerTime & 0xff000000);
  }
  else
  {
    pTimer->timerUByte8 = (UI8)(time - (pTimer->timerTime & 0xffffff));
  };
  SetTimer(pTimer);
}



//*********************************************************
//
//*********************************************************
//   TAG00cbc6
void ProcessTimers29to41(i32 mapX,
                         i32 mapY,
                         TIMERFUNCTION timeFunc,
                         i16 timeUntilNextUpdate)
{//(void)
  ITEM16      staticItem16;
  dReg        D0, D1, D3, D4, D5;
  MONSTERTYPE mtD0;
  bool        boolD0;
  DB4         *DB4A3;
  ITEM16      *pI16A2;
  MONSTERDESC *pmtDesc;
  i16         w_110;
  i16         w_100;
  i16         w_90;
  i16         w_80;
  TIMER       timer_70;
  i32         i_60;
  bool        invincible; //w_56
  i16         w_54;
  i16         w_52;
  i16         w_50;
  i16         w_48;
  i16         w_46 = 0x7ddd;
  i16         w_44;
  i16         w_42;
  i16         w_40;
  i32         PreferredDirection;
  bool        bool_36;
  i16         w_34; // Blocked() = 0 or distance.
  i16         w_32;
  i16         w_30;
  RN          monster; //28
  MONSTERDESC mtDescLocal;
  i16         temp;
  i32         monIdx;
  i32 monsterDirection;
  // A timer event with function code 37 is put on
  // the queue when a character dies from lack of
  // food in the prison.  We had to fake byte 5 of
  // the entry.  We put a 255 into it.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  //D3L = 0xccccc; // Uninitialized value
  //D7W = mapX;
  //D6W = mapY;
  TIMERTRACE(-1);
  TIMERTRACE(d.RandomNumber);
  if (d.LoadedLevel != d.partyLevel)
  {
    TIMERTRACE(0xcbe0);
    D5L = timeFunc;
    if (timeFunc != timerFunction37)
    {
      if (timeFunc != timerFunction32)
      {
        if (timeFunc != timerFunction38)
        {
          if (timeFunc != timerFunction33)
          {
            return;
          };
        };
      };
    };
  };
  TIMERTRACE(0xcc02);
  monster = FindFirstMonster(mapX, mapY);
        //
		// The following three words can be patched to put
		// monster at d.Obj13020 for TAG00c442.  See the
		// comments at TAG00c442.
		// As follows:
		// 	00cc14  67e8	     beq     <cbfe>  // return via nearby long-jump
		// 	00cc16  3940 cd24  move.w  D0,-13020(A4)
		//
  if (monster == RNeof)
  {
    return;
  };
  d.Obj13020 = monster;
  DB4A3 = GetRecordAddressDB4(monster);
  //ASSERT(DB4A3->uByte5 != 0xff);
  TIMERTRACE(0xcc26);
  TIMERTRACE(DB4A3->monsterType());
  mtD0 = DB4A3->monsterType();

  pmtDesc = &d.MonsterDescriptor[mtD0];
  //A0 = (pnt)&Item26_26;
  memmove((pnt)&mtDescLocal, (pnt)pmtDesc, 26);
  //
  // The following three words can be patched to ensure
  // that w_34 is initialized.  Under rare circumstances,
  // w_34 does not get initialized.  This only causes
  // slightly erratic behaviour of the monster, but it makes
  // it nearly impossible to exactly match the PC code to
  // the Atari code.  So I have patched the Atari code
  // to set w_34 to a (relatively) large number.
  // In general, the uninitialized value is part of an
  // address and is a large, positive number.  As follows:
  //
  //    00cc4c  7618         moveq.l #24,D3     patch was 263c
  //    00cc4e  3d43 ffde    move.w  D3,-34(A6) patch was 0000 0018
  //
  w_34 = 24;
  timer_70.timerTime = d.Time | (d.LoadedLevel<<24);
  D3W = mtDescLocal.timePerMove();
  timer_70.timerUByte5 = ub(255 - D3W);
  timer_70.timerUByte6 = ub(mapX);
  timer_70.timerUByte7 = ub(mapY);
  timer_70.timerUByte8 = 0;// This was also patched into the
                      // Atari binary code.  Sometimes a
                      // timer entry was created with garbage
                      // in uByte8.  It only caused an undefined
                      // delay but made it impossible to exactly
                      // match my code against the Atari code.
                      // Patch:  Paul Stevens
                      //  cc6a  1d47 ffc0  move.b D7,-64(A6) patch was 3007
                      //  cc6e  1d46 ffc1  move.b D6,-63(A6) patch was ffc0
                      //  cc72  422e ffc2  clr.b  -62(A6)    patch was ffc1
  if (d.LoadedLevel == d.partyLevel) goto tag00cd46;
  TIMERTRACE(0xcc88);
  D5W = STRandom0_3();
  TIMERTRACE(0xcc94);
  D0W = TAG00c442(&mtDescLocal, mapX, mapY, D5W, 0);
  if (D0W == 0) goto tag00cd04;
  TIMERTRACE(0xcca0);
  w_30 = sw(mapX);
  w_32 = sw(mapY);
  w_30 = sw(w_30 + d.DeltaX[D5W]);
  w_32 = sw(w_32 + d.DeltaY[D5W]);
  TIMERTRACE(0xccce);
  TIMERTRACE(monster.ConvertToInteger());
  D0W = MoveObject(monster, mapX, mapY, w_30, w_32);
  ASSERT(D0W != 0xccc);
  if (D0W != 0)
  {
    return;
  };
  timer_70.timerUByte6 = ub(d.NewX);
  timer_70.timerUByte7 = ub(d.NewY);

tag00cd04:
  TIMERTRACE(0xcd04);
  timer_70.timerFunction = timerFunction37;
  D0W = sw(abs(d.LoadedLevel-d.partyLevel));
  D5W = sw(Larger(D0W << 4, 2*(UI8)(mtDescLocal.timePerMove())));

tag00cd30:
  TIMERTRACE(0xcd30);
  timer_70.timerTime += D5W;
  SetTimer(&timer_70);
  return;
tag00cd46:
  TIMERTRACE(0xcd46);
  invincible = mtDescLocal.invincible(); //Impervious?
  if (invincible)
  {
    if (d.Word11694 != 0) //Has the game been won?
    {
      return;
    };
    d.Word13036 = 0;
    d.Byte13034[0] = 0;
    d.Byte13034[1] = 0;
    d.Byte13034[2] = 0;
    d.Byte13034[3] = 0;
  };
  ASSERT(DB4A3->groupIndex() < d.MaxITEM16);
  pI16A2 = &d.Item16[DB4A3->groupIndex()];
  if (DB4A3->groupIndex() >= d.MaxITEM16)
    pI16A2 = &staticItem16; // Just so we won't clobber something.
  D3W = pI16A2->uByte4;
  w_54 = sw((d.Time&255) - D3W );
  if (w_54 < 0)
  {
    w_54 += 256;
  };
  w_52 = (UI8)(mtDescLocal.timePerMove());
  if (w_52 == 255)
  {
    w_52 = 100;
  };
  if ( (d.uByte13271 != 0) && (!invincible) )
  {
    TIMERTRACE(0xcdc0);
    if (timeFunc < timerFunction0)
    {
      return;
    };
    timer_70.timerFunction = ub(timeFunc);
    timer_70.timerUByte8 = ub(timeUntilNextUpdate);
    D5W = 4;
    goto tag00cd30;
  };
  TIMERTRACE(0xcde2);
  if (timeFunc < timerFunction0)
  {
    TIMERTRACE(0xcde8);
    switch (timeFunc)
    {
    case timerFunctionM3: timer_70.timerFunction = timerFunction29; break;
    case timerFunctionM2: timer_70.timerFunction = timerFunction30; break;
    case timerFunctionM1: timer_70.timerFunction = timerFunction31; break;
    default: SystemError((UI16)0xcdf0);
    };
    //timer_70.function = ub(timeFunc + 32);
//
    if (   (timeFunc == timerFunctionM1)
        || ((D5W = sw((w_52+2)/4 - w_54)) < 1) )
    {
      D5W = 1;
    };
    goto tag00cd30;
  };

  TIMERTRACE(0xce18);
  D4W = (I16)(DB4A3->fear());
  w_50 = (I16)(DB4A3->numMonM1());//Number of monsters-1
  w_48 = mtDescLocal.horizontalSize(); //Size of monster 1=two squares
  D5W = sw(mapX - d.partyX);
  w_30 = sw(abs(D5W));
  D5W = sw(mapY - d.partyY);
  w_32 = sw(abs(D5W));
  d.Word13016 = sw(mapX);
  d.Word13018 = sw(mapY);
  d.Obj13020 = monster;
  d.Byte13030[0] = d.Byte13030[1] =d.Byte13030[2] =d.Byte13030[3] =0;
  d.Word13022 = DistanceBetweenPoints(mapX, mapY, d.partyX, d.partyY);
  d.Word13024 = (I16)GetDirectionMoved(mapX, mapY, d.partyX, d.partyY);
  d.Word13026 = (I16)d.Dir12956;
  i_60 = 0;
  w_40 = 1;
  ASSERT(timeFunc >= timerFunction29);
  switch (timeFunc)
  {
    //timeFunc = sw(timeFunc-32);
  case timerFunction31:
      timeFunc = timerFunctionM1;
      TIMERTRACE(0xcedc);
      if ( (D4W != 6) && (D4W != 5) )
      {
        DeleteMonsterMovementTimers(mapX, mapY);
        goto tag00d0f0;
      };
      TIMERTRACE(0xcef6);
      pI16A2->uByte6 = ub(d.partyX);
      pI16A2->uByte7 = ub(d.partyY);
      return;

  case timerFunction30:
      TIMERTRACE(0xcf14);
      timeFunc = timerFunctionM2;
      if ( (D4W == 6) || (D4W == 5) )
      {
        return;
      };
      SET(D0B, D4W==3);
      if (D0B == 0)
      {
        SET(D0B, D4W==2);
      };
      D5W = (I16)(D0W & 1);
      if ( (D5W != 0) || (STRandom0_3() != 0) )
      {
        TIMERTRACE(0xcf50);
        D0W = Blocked(DB4A3, -1, mapX, mapY);
        if (D0W == 0)
        {
          TIMERTRACE(0xcd5c);
          bool_36 = false;
          w_46 = 0;
          goto tag00d42e;
        };
        if (D5W != 0)
        {
          return;
        };
        if (STRandom0_3() != 0)
        {
          return;
        };
      };
      w_46 = D4W == 6;
      bool_36 = false;
      goto tag00d27e;
  case timerFunction29:
      TIMERTRACE(0xcf86);
      timeFunc = timerFunctionM3;
      w_46 = D4W == 6;
      bool_36 = false;
      goto tag00d27e;
  }; //switch
  if (timeFunc < timerFunction37)
  {
    TIMERTRACE(0xcfa6);
    timer_70.timerFunction = ub(timeFunc+timerFunction37-timerFunction32);
    D0W = Blocked(DB4A3, -1, mapX, mapY);
    if (D0W != 0)
    {
      TIMERTRACE(0xcfc6);
      if ( (D4W != 6) && (D4W != 5) )
      {
        TIMERTRACE(0xcfd2);
        D0W = sw(abs(d.partyX - mapX));
        D1W = sw(abs(d.partyY - mapY));
        if (D0W + D1W <= 1) goto tag00d0f0;
        if ( ((D4W==0)||(D4W==3)) && (D4W!=7) ) goto tag00d21a;
      };
      TIMERTRACE(0xd018);
      pI16A2->uByte6 = ub(d.partyX);
      pI16A2->uByte7 = ub(d.partyY);
    };
    if (D4W == 6)
    {
      TIMERTRACE(0xd036);
      D5W = sw(timeFunc-33);
      D0W = (I16)(pI16A2->uByte12[D5W] & 0x80);
      i_60 = NextMonsterUpdateTime(pI16A2, D5W, D0W);
      goto tag00dcb8;
    };
    TIMERTRACE(0xd0580);
    if ( (w_30 > 3) || (w_32 > 3) )
    {
      TIMERTRACE(0xd068);
      i_60 = d.Time + BITS4_7(mtDescLocal.word20);
      goto tag00dcb8;
    };
    goto tag00dca0;
  };

  TIMERTRACE(0xd08a);
  w_40 = 0;
  if (timeUntilNextUpdate != 0)
  {
    TIMERTRACE(0xd094);
    i_60 = d.Time;
  };
  if (timeFunc != timerFunction37) goto tag00d870;
  TIMERTRACE(0xd0b6);
  if (   (D4W != 0)
      && (D4W != 2)
      && (D4W != 3) )goto tag00d490;
  w_34 = Blocked(DB4A3, -1, mapX, mapY);
  if (w_34 == 0) goto tag00d24a;
  TIMERTRACE(0xd0d0);
  if (w_34 > mtDescLocal.word14_12_15()) goto tag00d214;
  TIMERTRACE(0xd0e2);
  if ( (w_30 != 0) && (w_32 != 0) ) goto tag00d214;

tag00d0f0:
  TIMERTRACE(0xd0f0);
  if (timeFunc == -2)
  {
    TIMERTRACE(0xd0f8);
    DeleteMonsterMovementTimers(mapX, mapY);
  };
  pI16A2->uByte6 = ub(d.partyX);
  pI16A2->uByte7 = ub(d.partyY);
  D3W = 6;
  //30OCT02SETWBITS0_3(DB4A3->word14, D3W);
  DB4A3->fear(6);
  D5W = d.Word13024;

  for (monIdx = w_50; monIdx >= 0; monIdx--)
  {
    int coinflip=2;


    // Always turn monster 0 to the preferred direction.
    // Turn other monsters with probability of 50 percent.
    // You may well ask what is happening here.  The compiler optimization had
    // an error and treated (2*monIdx) as a loop constant.  So I could
    // not shift by (2*monIdx).  So I shifted twice by monIdx instead.
    // What grief!
//    monsterDirection = (pI16A2->facings() >> (2*monIdx)) & 3;
    monsterDirection = TwoBitNibble(pI16A2->facings(),monIdx);
    if (   (D5W != monsterDirection)

      && ((monIdx==0) || ((coinflip=STRandomBool())==0)) )
    {
      TIMERTRACE(0xd154);
//
      TurnMonster(pI16A2,
                  D5W,  //preferred direction
                  monIdx,  //monster index
                  (w_50 != 0) && (w_48 == 1));
      w_80 = STRandom0_3();
      D1L = d.Time;
      D3L = w_80 & 0xffff;
      D1L += D3L + 2;
      timer_70.timerTime = (timer_70.timerTime & 0xff000000) | D1L;
    }
    else
    {
      TIMERTRACE(0xd1a8);
      timer_70.timerTime = (timer_70.timerTime & 0xff000000) | (d.Time + 1);
    };
    if (w_40 != 0)
    {
      TIMERTRACE(0xd1c4);
      timer_70.timerTime += Smaller(STRandom0_3()+(UI8)(mtDescLocal.uByte7)/2,
                           timeUntilNextUpdate);
    };
    timer_70.timerFunction = ub(monIdx + 38);
    TAG00cb68(&timer_70, NextMonsterUpdateTime(pI16A2, (i16)monIdx, 0));
    //continue here
//
  }; // for (monIdx)
  return;

tag00d214:
  TIMERTRACE(0xd214);
  if (D4W != 2)
  {
tag00d21a:
  TIMERTRACE(0xd21a);
    D3W = 7;
    //30OCT02SETWBITS0_3(DB4A3->word14, D3W);
    DB4A3->fear(7);
    pI16A2->uByte6 = ub(d.partyX);
    pI16A2->uByte7 = ub(d.partyY);
    timer_70.timerTime++;
    goto tag00dc9a;
  };
  goto tag00d48c;

tag00d24a:
  TIMERTRACE(0xd24a);
  if (D4W != 0) goto tag00d48c;
  TIMERTRACE(0xd250);
  PreferredDirection =
       GetBestMonsterFacingP1(&mtDescLocal, mapX, mapY);
  if (PreferredDirection != 0)
  {
    TIMERTRACE(0xd264);
    PreferredDirection -= 1;
    w_42 = 0;
    goto tag00d55c;
  };
  bool_36 = false;
  if (STRandomBool() == 0) goto tag00d3cc;

tag00d27e:
  TIMERTRACE(0xd27e);
  D5W = STRandom0_3();
  D4W = D5W;
  w_44 = 0;

tag00d28a:
  TIMERTRACE(0xd28a);
  w_30 = sw(mapX);
  w_32 = sw(mapY);
  w_30 = sw(w_30 + d.DeltaX[D5W]);
  w_32 = sw(w_32 + d.DeltaY[D5W]);
  if (pI16A2->uByte8 == w_30)
  {
    TIMERTRACE(0xd2ca);
    if (pI16A2->uByte9 == w_32)
    {
      w_44 = STRandom0_3() == 0;
      TIMERTRACE(0xd2e2);
      if (w_44 == 0) goto tag00d36e;
    };
  };
  D0W = TAG00c442(&mtDescLocal, mapX, mapY, D5W, 0);
  TIMERTRACE(0xd2fe);
  if (D0W == 0) goto tag00d36e;
  //while (from d728)
  //while (from d630)

tag00d302:
  TIMERTRACE(0xd302);
  D4W = sw(w_52/2 - w_54);
  bool_36 = D4W <= 0;
  if (bool_36)
  {
    TIMERTRACE(0xd31c);
    D0W = MoveObject(monster, mapX, mapY, w_30, w_32);
    ASSERT(D0W != 0xccc);
    if (D0W != 0)
    {
      return;
    };
    timer_70.timerUByte6 = ub(d.NewX);
    timer_70.timerUByte7 = ub(d.NewY);
    pI16A2->uByte8 = ub(mapX);
    pI16A2->uByte9 = ub(mapY);
    pI16A2->uByte4 = (ui8)d.Time;
  }
  else
  {
    TIMERTRACE(0xd362);
    w_52 = D4W;
    w_54 = -1;
  };
  goto tag00d3cc;

tag00d36e:
  TIMERTRACE(0xd36e);
  if (d.Word13044 != 0)
  {
    TIMERTRACE(0xd374);
    if (timeFunc != -3)
    {
      TIMERTRACE(0xd37c);
      if ((timeUntilNextUpdate & 15)  != 5) goto tag00d0f0;
      TIMERTRACE(0xd38a);
      if (TAG00c70c(&mtDescLocal, mapX, mapY, 0)==0) goto tag00d0f0;
      TIMERTRACE(0xd3a0);
      if (STRandomBool() != 0) goto tag00d0f0;
      TIMERTRACE(0xd38a);
    };
    pI16A2->uByte6 = ub(d.partyX);
    pI16A2->uByte7 = ub(d.partyY);
  };
  D5W = (I16)((D5W + 1) & 3); // Different direction?
  if (D4W != D5W) goto tag00d28a;

tag00d3cc:
  TIMERTRACE(0xd3cc);
  if (   (bool_36 == false)
      && (w_54 != -1)
      && (invincible)
      && ((timeFunc == -3) || (STRandom0_3() == 0)))
  {
    TIMERTRACE(0xd3f0);
    PreferredDirection = STRandom0_3();
    d.Dir12956 = (DIRECTION)((PreferredDirection + 1) & 3);
    goto tag00d63c;
  };

  if (bool_36 == false)
  {
    TIMERTRACE(0xd40c);
    if (STRandom0_3() != 0)
    {
      TIMERTRACE(0xd414);
//
      ASSERT( (timeFunc==-3) || (w_34 != 0x7ddd) );
      //
      // This happened once when
      // I was bashing an oitu with a door.
      // Happened when I sent fireball at oitu. (timeFunc=-2)
      // Happened again when I bashed a demon with a door.
      // Notice that if (timeFunc==-3) then we unconditionally
      // make the same jump as we make conditionally here.
      //
      // Therefore, the fact that w_34 is undefined will
      // never do any harm (if timeFunc==-3).
      //
      // If too far away then don't turn towards party.
      if (w_34 > mtDescLocal.word14_8_11()) goto tag00d454;
    };
    if (timeFunc == -3) goto tag00d454;
  };

tag00d42e:
  TIMERTRACE(0xd42e);
  if ( (bool_36 == false) && (w_54 >= 0) )
  {
    TIMERTRACE(0xd43a);
    D5W = STRandom0_3();
  };
  TurnMonsterGroup(pI16A2,
                   D5W,   //preferred direction
                   w_50,   //number of monsters
                   w_48);  //must-turn-as-group flag (worms, eg)

tag00d454:
  TIMERTRACE(0xd454);
  if (timeFunc < -1)
  {
    TIMERTRACE(0xd45c);
    if (bool_36 == 0)
    {
      return;
    };
    ASSERT(w_46 != 0x7ddd);
    if (w_46 != 0)
    {
      TIMERTRACE(0xd46c);
      D3W = 7;
      //30OCT02SETWBITS0_3(DB4A3->word14, D3W);
      DB4A3->fear(7);
    };
    TAG00b02c(pI16A2, mapX, mapY);
  };

tag00d48c:
  TIMERTRACE(0xd48c);
  goto tag00d86c;
tag00d490:
  TIMERTRACE(0xd490);
  if (D4W != 7) goto tag00d746;
  w_34 = Blocked(DB4A3, -1, mapX, mapY);
  if (w_34 != 0)
  {
    TIMERTRACE(0xd4b0);
    if (w_34 <= mtDescLocal.word14_12_15())
    {
      TIMERTRACE(0xd4c0);
      if (w_30 == 0) goto tag00d0f0;
      if (w_32 == 0) goto tag00d0f0;
    };
tag00d4d0:
    TIMERTRACE(0xd4d0);
    w_52 = sw((w_52 + 1)/2);
    pI16A2->uByte6 = ub(d.partyX);
    w_30 = d.partyX;
    pI16A2->uByte7 = ub(d.partyY);
    w_32 = d.partyY;
  }
  else
  {
tag00d500:
  TIMERTRACE(0xd500);
    w_30 = pI16A2->uByte6;
    w_32 = pI16A2->uByte7;
    if (mapX == w_30)
    {
      if (mapY == w_32)
      {
        TIMERTRACE(0xd528);
        bool_36 = false;
        D3W = 0;
        //30OCT02SETWBITS0_3(DB4A3->word14,D3W);
        DB4A3->fear(0);
        goto tag00d42e;
      };
    };
  };
  w_42 = 1;

tag00d546:
  TIMERTRACE(0xd546);
  PreferredDirection = GetDirectionMoved(mapX, mapY, w_30, w_32);
  // Direction to approach party.
tag00d55c:
  TIMERTRACE(0xd55c + (PreferredDirection<<16));
  D5W = sw(PreferredDirection);
  D0W = TAG00c442(&mtDescLocal, mapX, mapY, D5W, w_42);
  if (D0W != 0) goto tag00d5fc;
  TIMERTRACE(0xd57e);
  D0W = (w_42 != 0) && (STRandomBool() != 0);
  D5W = sw((i32)d.Dir12956);
  D0W = TAG00c442(&mtDescLocal, mapX, mapY, D5W, D0W);
  if (D0W != 0) goto tag00d5fc;
  TIMERTRACE(0xd5ae);
  D5W = (I16)((D5W + 2) & 3);
  D0W = TAG00c442(&mtDescLocal, mapX, mapY, D5W, 0);
  if (D0W != 0) goto tag00d5fc;
  TIMERTRACE(0xd5d0);
  if (STRandom0_3() != 0) goto tag00d634;
  D5W = sw((PreferredDirection + 2) & 3);
  TIMERTRACE(0xd5d8);
  D0W = TAG00c442(&mtDescLocal, mapX, mapY, D5W, 0);
  if (D0W == 0) goto tag00d634;

tag00d5fc:
  TIMERTRACE(0xd5fc);
  w_30 = sw(mapX);
  w_32 = sw(mapY);
  w_30 = sw(w_30 + d.DeltaX[D5W]);
  w_32 = sw(w_32 + d.DeltaY[D5W]);
  goto tag00d302;

tag00d634:
  TIMERTRACE(0xd634);
  if (!invincible) goto tag00d72c;

tag00d63c:
  TIMERTRACE(0xd63c);
  TAG00c70c(&mtDescLocal, mapX, mapY, 0);
  D5W = sw(PreferredDirection);
  D0W = TAG00c756(&mtDescLocal, mapX, mapY, D5W);
  if (D0W != 0) goto tag00d6d6;
  TIMERTRACE(0xd66a);
  D5W = sw((i32)d.Dir12956);
  D0W = TAG00c756(&mtDescLocal, mapX, mapY, D5W);
  if (D0W != 0) goto tag00d6d6;
  TIMERTRACE(0xd686);
  if (d.Word13036 != 0)
  {
    TIMERTRACE(0xd686);
    D5W = (I16)((D5W + 2) & 3);
    D0W = TAG00c756(&mtDescLocal, mapX, mapY, D5W);
    if (D0W != 0) goto tag00d6d6;
  };
  TIMERTRACE(0xd6ac);
  if (d.Word13036 < 2) goto tag00d72c;
  TIMERTRACE(0xd6b4);
  D5W = sw((PreferredDirection + 2) & 3);
  D0W = TAG00c756(&mtDescLocal, mapX, mapY, D5W);
  if (D0W == 0) goto tag00d72c;

tag00d6d6:
  TIMERTRACE(0xd6d6);
  // Try two steps in direction D5W???
  w_30 = sw(mapX);
  w_32 = sw(mapY);
  w_30 = sw(w_30 + 2*d.DeltaX[D5W]);
  w_32 = sw(w_32 + 2*d.DeltaY[D5W]);
  QueueSound(17, w_30, w_32, 1);
  goto tag00d302;

tag00d72c:
  TIMERTRACE(0xd72c);
  TurnMonsterGroup(pI16A2, PreferredDirection, w_50, w_48);
  goto tag00d86c;

tag00d746:
  TIMERTRACE(0xd746);
  if (D4W != 5) goto tag00d86c;
tag00d74e:
  TIMERTRACE(0xd74e);
  w_42 = 1;
  w_34 = Blocked(DB4A3, -1, mapX, mapY);
  if (w_34 != 0)
  {
    TIMERTRACE(0xd76c);
    pI16A2->uByte6 = ub(d.partyX);
    w_30 = d.partyX;
    pI16A2->uByte7 = ub(d.partyY);
    w_32 = d.partyY;
    goto tag00d834;
  };
  pI16A2->uByte5--;
  if (pI16A2->uByte5 != 0) goto tag00d7ba;

tag00d79e:
  TIMERTRACE(0xd79e);
  bool_36 = false;
  D3W = 0;
  //30OCT02SETWBITS0_3(DB4A3->word14, D3W);
  DB4A3->fear(0);
  goto tag00d42e;

tag00d7ba:
  TIMERTRACE(0xd7ba);
  if (STRandomBool() != 0)
  {
    TIMERTRACE(0xd7c2);
    D0W = TAG00c70c(&mtDescLocal, mapX, mapY, 0);
    if (D0W == 0)
    {
      TIMERTRACE(0xd7d8);
      D0W = sw(abs(mapX - d.partyX));
      D1W = sw(abs(mapY - d.partyY));
      if (D1W + D0W <= 1) goto tag00d79e;
    };
    TIMERTRACE(0xd0808);
    w_30 = pI16A2->uByte10;
    w_32 = pI16A2->uByte11;
                  goto tag00d546;
  };
  TIMERTRACE(0xd820);
  w_30 = pI16A2->uByte6;
  w_32 = pI16A2->uByte7;
tag00d834:
  TIMERTRACE(0xd834);
  D0W = (I16)(i32)GetDirectionMoved(mapX, mapY, w_30, w_32);
  PreferredDirection = (D0W + 2) & 3;
  d.Dir12956 = (DIRECTION)((d.Dir12956+2)&3);//180 degrees clockwise
  w_52 = sw(w_52 - w_52/4);
  goto tag00d55c;

tag00d86c:
  TIMERTRACE(0xd86c);
  goto tag00dc7e;

tag00d870:
  TIMERTRACE(0xd870);
  if (D4W == 5)
  {
    TIMERTRACE(0xd876);
    if (w_50 != 0)
    {
      TIMERTRACE(0xd87c);
      TAG00b02c(pI16A2, mapX, mapY);
    }
    goto tag00d74e;
  };
  D4W = sw(timeFunc - 38);
  D0W = pI16A2->uByte12[D4W];
  if (D0W & 0x80)
  {
    TIMERTRACE(0xd8a6);
    i_60 = NextMonsterUpdateTime(pI16A2, D4W, 0);
    w_90 = STRandom0_3();
    D4W = (UI8)(mtDescLocal.uByte7);
    timer_70.timerTime += uw(D4W + w_90 - 1);
    if (D4W > 15)
    {
      TIMERTRACE(0xd8dc);
      D0W = sw((STRandom() & 7) - 2);
      timer_70.timerTime += D0W;//D0 can be negative.
    };
  }
  else
  {
    if (D4W > w_50)
    {
      return;
    };
    PreferredDirection = d.Word13024;
    w_34 = Blocked(DB4A3, D4W, mapX, mapY);
    if (w_34 != 0)
    {
      TIMERTRACE(0xd91c);
      pI16A2->uByte6 = ub(d.partyX);
      pI16A2->uByte7 = ub(d.partyY);
    };
//
//
    if (   (w_50 == 0)
        && (w_48 != 2)
        && (((D5W=(I16)((i16)STRandom())) & 0xc0) == 0) )
    {
      TIMERTRACE(0xd94c);
      if (pI16A2->positions() == 255) goto tag00d98c;
      if (D5W & 0x38)
      {
        TIMERTRACE(0xd960);
        pI16A2->positions() = 0xff;
        goto tag00d98c;
      };
      D0W = pI16A2->positions();
      if (D5W & 1)
      {
        TIMERTRACE(0xd97e);
        D1W = 1; // Turn right
      }
      else
      {
        TIMERTRACE(0xd980);
        D1W = -1; // Turn left
      };
      D5W = (I16)((D0W + D1W) & 3);
      // I think there is something wrong with D5 here.
tag00d98c:
      TIMERTRACE(0xd98c | (D5W<<16));
      if (   ((D5W & 0x38) == 0)
          && (w_34 != 1)
          && (w_48 == 0) )
      {
        TIMERTRACE(0xd9a2);
        D0W = TAG00e8ce(-1, mapX, mapY, pI16A2->positions());
        if (D0W != 0)
        {
          if (d.Word12958 == 2)
          {
            return;
          };
        };
        pI16A2->positions() = (UI8)(D5W & 3);
      };
    };
//
    if (   (w_34 != 0)
        && (  (mtDescLocal.word2Bit2())
//            ||(((pI16A2->facings() >> (2*D4W)) & 3) == PreferredDirection ) ))
            ||((TwoBitNibble(pI16A2->facings(),D4W)) == PreferredDirection ) ))
    {
      TIMERTRACE(0xd9fe);
      D5W = mtDescLocal.word14_12_15();
      if (   (w_34 <= D5W)
          && ((w_30 == 0)||(w_32 == 0))
          && (D5W <= (w_100=(I16)(STRandom(16) + 1))) )
      {
        TIMERTRACE(0xda38);
        if (D5W == 1)
        {
          TIMERTRACE(0xda40);
          //D5W = Item26_26.word2;
//
//
//
//
          if (  (   !mtDescLocal.word2Bit3()
                 || (STRandom0_3() == 0)
                 || !mtDescLocal.word2Bit4()
                )
              && (w_48 == 0)
              && (pI16A2->positions() != 255))
          {
            TIMERTRACE(0xda74);
//            D5W = (I16)((pI16A2->positions() >> (2*D4W)) & 3);
            D5W = (I16)(TwoBitNibble(pI16A2->positions(),D4W));
            if (PreferredDirection != D5W)
            {
              TIMERTRACE(0xda90);
              if (D5W != ((PreferredDirection + 1) & 3))
              {
                TIMERTRACE(0xdaa2);
//
                if ( (w_50 == 0) && (STRandomBool() != 0) )
                {
                  TIMERTRACE(0xdab0);
//                  pI16A2->uByte3 = 0xff;
                }
                else
                {
                  if ((PreferredDirection & 1) == (D5W & 1))
                  {
                    TIMERTRACE(0xdacc);
                    D5W--; // Turn left
                  }
                  else
                  {
                    TIMERTRACE(0xdaad0);
                    D5W++; //Turn right
                  };
                  D5W &= 3;
                  D0W = OrdinalOfMonsterAtPosition(DB4A3, D5W);

//
//
//
                  if (   (D0W==0)
                      || (  (STRandomBool()!=0)
                          &&((OrdinalOfMonsterAtPosition(DB4A3,D5W=(I16)((D5W+2)&3))))==0) )
                  {
                    TIMERTRACE(0xdb08);
                    D0W = TAG00e8ce(-1, mapX, mapY, pI16A2->positions());
                    if ((D0W!=0) && (d.Word12958==2))
                    {
                      return;
                    };
                    if (d.Word12958 != 1)
                    {
                      TIMERTRACE(0xdb36);
                      D0W = SetMonsterPositionBits(
                               pI16A2->positions(), //old positions byte
                               D4W,           //monster number
                               D5W);          //position
                      pI16A2->positions() = D0UB;
                    };
                  };
                };
                D0W = sw(mtDescLocal.timePerMove()/2 + STRandomBool());
                D0L = Larger(1, D0W);
                timer_70.timerTime += D0L;
                timer_70.timerFunction = ub(timeFunc);
                goto tag00dca0;
              };
            };
          };
        };
        TIMERTRACE(0xdb7a);
        temp = TAG00c8c0(DB4A3,mapX,mapY,D4W);
        i_60 = NextMonsterUpdateTime(
                           pI16A2,
                           D4W,
                           temp);
        w_110 = STRandomBool();
        D0L = ((mtDescLocal.word20 & 15) + w_110) & 0xffff;
        timer_70.timerTime += D0L;
      }
      else
      {
        D3W = 7;
        //ASSERT(D3L != 0xccccc);
        //30OCT02SETWBITS0_3(DB4A3->word14, D3W) ;
        DB4A3->fear(7);
        if (w_50 != 0)
        {
          TIMERTRACE(0xdbd4);
          TAG00b02c(pI16A2, mapX, mapY);
        };
        goto tag00d4d0;
      };
    }
    else
    {
      D0W = Blocked(DB4A3, -1, mapX, mapY);
      if (D0W != 0)
      {
        TIMERTRACE(0xdbfe);
        pI16A2->uByte6 = ub(d.partyX);
        pI16A2->uByte7 = ub(d.partyY);
        boolD0 = (w_50 != 0) & (w_48 == 1);
        TurnMonster(pI16A2,
                    PreferredDirection,
                    D4W,    //monster index
                    boolD0);
        timer_70.timerTime += 2;
        i_60 = timer_70.timerTime & 0xffffff;
      }
      else
      {
        TIMERTRACE(0xdc4a);
        D3W = 7;
        //30OCT02SETWBITS0_3(DB4A3->word14, D3W);
        DB4A3->fear(7);
        if (w_50 != 0)
        {
          TAG00b02c(pI16A2, mapX, mapY);
        };
        goto tag00d500;
      };
    };
  };
  timer_70.timerFunction = ub(timeFunc);
  goto tag00dcb8;

tag00dc7e:
  TIMERTRACE(0xdc7e);
  timer_70.timerTime += Larger(1, w_52 + STRandom0_3() - 1);

tag00dc9a:
  TIMERTRACE(0xdc9a);
  timer_70.timerFunction = timerFunction37;

tag00dca0:
  TIMERTRACE(0xdca0);
  if (i_60 == 0)
  {
    TIMERTRACE(0xdca6);
    i_60 = NextMonsterUpdateTime(pI16A2, -1, 0);
  };

tag00dcb8:
  TIMERTRACE(0xdcb8);
  if (w_40 != 0)
  {
    TIMERTRACE(0xdcbe);
    timer_70.timerTime += timeUntilNextUpdate & 0xffff;
  }
  else
  {
    i_60 += timeUntilNextUpdate & 0xffff;
  };
  TAG00cb68(&timer_70, i_60);
  return;
}

//*********************************************************
//
//*********************************************************
i16 TAG00e8ce(i16 P1, i32 mapX, i32 mapY, ui16 position)
{//(i16)
  dReg D0, D5;
  RN  objD6;
  int saveTimerIndex;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  //D7W = position;
  D5W = 0;
  d.Word12958 = 0;
  do
  {
    for (objD6 = FindFirstObject(mapX, mapY);
         objD6 != RNeof;
         objD6 = GetDBRecordLink(objD6)  )
    {
      if (objD6.dbType() != dbMISSILE) continue;
      if (objD6.pos() != position) continue;
      saveTimerIndex = GetRecordAddressDB14(objD6)->timerIndex();
      D0W = CheckMissileEncounter(
                P1,    //party or monster
                mapX,
                mapY,
                position,
                objD6);//missile
      if (D0W == 0) continue;
      DeleteTimer(saveTimerIndex); //TAG00e156(objD6);
      D5W++;
      break;

//
    };
  } while ((objD6 != RNeof) && ((P1!=-1)||(d.Word12958!=2)));

//
  return (D5W);
}

//*********************************************************
// Distance as the (orthoganal) crow flies.
//*********************************************************
//  TAG00f4c8
i16 DistanceBetweenPoints(
              i32 X1,
              i32 Y1,
              i32 X2,
              i32 Y2)
{
  return sw(abs(X1-X2) + abs(Y1-Y2));
}
//*********************************************************
//
//*********************************************************
i16 TAG00f6ee(DB4 *pDB4,i16 chIdx)
{//(i16)
  dReg        D0, D1, D3, D4, D5, D6, D7;
  DB4        *DB4A3;
  CHARDESC   *chA2;
  MONSTERDESC mtDesc;
  i16         w_2;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  DB4A3 = pDB4;
  chA2 = &d.CH16482[chIdx];
  if (chIdx >= d.NumCharacter) return 0;
  if (chA2->HP == 0) return 0;

  if (d.PartySleeping != 0) WakeUp();
  w_2 = sw(2 * d.pCurLevelDesc->experienceMultiplier());
  memmove(&mtDesc, &d.MonsterDescriptor[DB4A3->monsterType()],26);
  // local copy of monster descriptor
  AdjustSkills(chIdx, 7, mtDesc.word16_8_11());
  if (d.PartySleeping == 0)
  {
    D0W = sw(Quickness(chA2));
    D1W = sw(STRandom(32));
    D3W = mtDesc.uByte8[4];
    D1W = sw(D1W + D3W + w_2 - 16);

    if ( (D0W >= D1W) && (STRandom0_3() != 0) ) return 0;
    // character is slower (or monster is lucky!)
    if (TAG016476(chA2, 60) != 0) return 0;
  };
  D6W = (i16)STRandom();
  if (D6W & 0x70)
  {
    D6W &= 15;
    D5W = mtDesc.word22();
    for (D4W=0; D6W > (D5W&15); D4W++) D5W >>= 4;
//

    D4W = d.uByte590[D4W];
  }
  else
  {
    D4W = (I16)(D6W & 1);
  };
  D3W = mtDesc.uByte8[2];
  D0W = sw(w_2 + STRandom(16) + D3W);
  D1W = sw(DetermineMastery(chIdx, 7));
  D7W = sw(D0W - 2*D1W);
  if (D7W <= 1)
  {
    if (STRandomBool() != 0) return 0;
    D7W = sw(STRandom0_3() + 2);
  };
  D7W /= 2;
  D0W = sw(STRandom(D7W));
  D1W = STRandom0_3();
  D7W = sw(D7W + D0W + D1W);
  D7W = sw(D7W + STRandom(D7W));
  D7W /= 4;
  D7W = sw(D7W + STRandom0_3() + 1);
  if (STRandomBool() != 0)
  {
    D7W = sw(D7W - (STRandom(D7W/2 + 1) - 1));
  };
  D7W = sw(DamageCharacter(chIdx, D7W, D4W, mtDesc.uByte22[2]));
  if (D7W != 0)
  {
    QueueSound(chIdx + 9, d.partyX, d.partyY, 2);
    D6W= mtDesc.uByte8[3];
    if (D6W != 0)
    {
      if (STRandomBool() != 0)
      {
        D6W = TAG016426(chA2, 4, D6W);
        // There was an impossible jump around the following function call.
        TAG017274(chIdx, D6W);
      };
    };
    return (D7W);
  };
  return (0);
}