#include "stdafx.h"

#include "UI.h"

#include <stdio.h>

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

void ProcessPortraitTimer7(RN targetObj, const TIMER *pTimer);
void info(char *, unsigned int);


extern i32 deleteDuplicateTimers;

#ifdef _MSVC_INTEL
#pragma warning(disable:4710)
#endif

void TraceCellflags(i32 x, i32 y, char *msg)
{
  if (TimerTraceActive)
  fprintf(GETFILE(TraceFile),"%s in %02x,%02x - new value = %02x\n",
             msg, x, y, d.LevelCellFlags[x][y]);
}

//#ifdef _DEBUG

void printHexLocation(FILE *f, i32 lev, i32 x, i32 y)
{
  fprintf(f,"%02x(%02x,%02x)",lev,x,y);
}

char *action(i32 act)
{
  if (act == 0)return "Set   ";
  if (act == 1)return "Clear ";
  if (act == 2)return "Toggle";
  return "??????";
}

void TraceTimer(TIMER *pTimer, i32 index, char *msg)
{
  char Msg[15];
  i32 len;
  if (!TimerTraceActive) return;
  memset(Msg,' ',14);
  Msg[14] = 0;
  len=strlen(msg);
  if (len>14) len=14;
  memcpy(Msg,msg,len);
  fprintf(GETFILE(TraceFile),"%06x timer %s %03x %02x "
                    "DLY=%04x "
                    "%02x %02x %02x %02x %02x %02x %08x",
            d.Time & 0xffffff,
            Msg, index, pTimer->timerTime>>24,
            (pTimer->timerTime & 0xffffff) - d.Time,
            pTimer->Function(),pTimer->timerUByte5,pTimer->timerUByte6,
            pTimer->timerUByte7,pTimer->timerUByte8,pTimer->timerUByte9,
            d.RandomNumber);
  switch (pTimer->Function())
  {
  case 6:
      fprintf(GETFILE(TraceFile)," %s  0x%02x in DB at ",
                  action(pTimer->timerUByte9),1<<pTimer->timerUByte8);
      printHexLocation(GETFILE(TraceFile),
                       pTimer->timerTime>>24,
                       pTimer->timerUByte6,
                       pTimer->timerUByte7);
      fprintf(GETFILE(TraceFile),"%02x",pTimer->timerUByte8);
    break;
  case TT_FALSEWALL:
    fprintf(GETFILE(TraceFile)," %s CELLFLAG 0x04 at %02x,%02x",
                action(pTimer->timerUByte9),pTimer->timerUByte6,pTimer->timerUByte7);
    break;
  case 8:
    fprintf(GETFILE(TraceFile)," %s CELLFLAG 0x08 at %02x,%02x",
                action(pTimer->timerUByte9),pTimer->timerUByte6,pTimer->timerUByte7);
    break;
  };
  fprintf(GETFILE(TraceFile),"\n");
}
//#else //_DEBUG
//void TraceTimer(TIMER *, i32 , char *)
//{
//}
//#endif //_DEBUG

void TIMER::swapTimerWord6(void)
{
  *(i16 *)(&timerUByte6) = LE16(*(i16 *)(&timerUByte6));
}


void TIMER::swapTimerObj8(void)
{
  *(i16 *)(&timerUByte8) = LE16(*(i16 *)(&timerUByte8));
}

void TIMER::swapTimerObj6(void)
{
  *(i16 *)(&timerUByte6) = LE16(*(i16 *)(&timerUByte6));
}


void TIMER::swapTimerWord8(void)
{
  *(i16 *)(&timerUByte8) = LE16(*(i16 *)(&timerUByte8));
}


//*********************************************************
//
//*********************************************************
//   TAG00e17a
void ProcessObjectFromMissile(RN missile, RN *pDest, i32 mapX, i32 mapY)
{ //(void)
  RN objD6;
  RN objD7;
  DB14     *DB14A3;
  DBCOMMON *pdb_4;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  DB14A3 = GetRecordAddressDB14(missile);
  objD6 = DB14A3->flyingObject();
  if (!objD6.IsMagicSpell()&&(objD6.dbType() != dbCLOUD))
  {
    if (pDest != NULL)
    {
      objD7 = *pDest;
      if (objD7 == RNeof)
      {
        pdb_4 = GetCommonAddress(objD6);
        pdb_4->link(RNeof);
        *pDest = objD6;
      }
      else
      {
        AddObjectToRoom(objD6, objD7, -1, 0, NULL);
      };
    }
    else
    {
      MoveObject(
                 RN(objD6).pos(missile.pos()),
                 -2,
                 0,
                 mapX,
                 mapY,
                 NULL,
                 NULL);
    };
  };
  //DB14A3->link(RNnul);
  DeleteDBEntry(DB14A3);
}

//*********************************************************
//
//*********************************************************
//   TAG00e962
void MissileTimer(TIMER *pTimer)
{//(void)
  // A thrown Screamer Slice passes this way, too.
  // A fireball is represented by a timer entry with:
  //    function = 48 then 49.
  //    obj6     = Record Name  (and position in cell)
  //    word8    = mapX (bits 0-4)
  //               mapY (bits 5-9)
  //               direction of travel (bits 10-11)
  //               delta energy (bits 12-15)
  //        The object is in database 0x0e (8 bytes)
  //
  dReg D0;
  RN objD0;
  DIRECTION missileDirection;
  TIMER timer;
  DB14 *pDB14A2;
  i32 origMapY=-999;
  i32 origMapX=-999;
  i32 curMapY; // Set to current position then moved.
  i32 curMapX; // Set to current position then moved.
  i32 curPos;
  bool leavingThisCell;
  RN  objD7;
  RN  objMissile;
  i32 nextCellFlags;
  i32 deltaEnergy; // From timer entry
  i32 newX, newY, newDir;
  i32 saveTimerIndex;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  memmove((pnt)&timer, (pnt)pTimer, 10); // 5 words = 10 bytes
  objMissile = timer.timerObj6();
  objD7 = objMissile;
  ASSERT(objMissile.dbType() == dbMISSILE,"missile");
  pDB14A2 = GetRecordAddressDB14(objMissile);
  curMapX = BITS0_4(timer.timerWord8()); //mapX
  curMapY = BITS5_9(timer.timerWord8()); //mapY
  if (timer.Function() == TT_Missile0)
  {
    timer.Function(TT_Missile);
  }
  else
  {
    curPos = objD7.pos();
    if (   (d.LoadedLevel == d.partyLevel)
        && (curMapX == d.partyX)
        && (curMapY == d.partyY) )
    {
      // Did missile strike someone?
      saveTimerIndex = GetRecordAddressDB14(objD7)->timerIndex();
      D0W = CheckMissileEncounter(
                   -2,  // Check party members
                   curMapX,
                   curMapY,
                   curPos,
                   objD7,
                   NULL);
      if (D0W != 0) return; // Missile is discarded.  Gone!
    };
    objD0 = FindFirstMonster(curMapX, curMapY);
    if (objD0 != RNeof)
    {
      saveTimerIndex = GetRecordAddressDB14(objMissile)->timerIndex();
      D0W = CheckMissileEncounter(
             -1,  // check monsters???
             curMapX,
             curMapY,
             curPos,
             objMissile,
             NULL);
      if (D0W != 0) return;
    };
    deltaEnergy = BITS12_15(timer.timerWord8());
    if (pDB14A2->rangeRemaining() <= deltaEnergy)
    {
      RemoveObjectFromRoom(objD7=objMissile, curMapX, curMapY, NULL);
      ProcessObjectFromMissile(objMissile, NULL, curMapX, curMapY);
      return;
    };
    pDB14A2->rangeRemaining(
                  pDB14A2->rangeRemaining() - deltaEnergy);
    if (pDB14A2->damageRemaining() < deltaEnergy)
    {
      pDB14A2->damageRemaining(0);
    }
    else
    {
      pDB14A2->damageRemaining(
                    pDB14A2->damageRemaining() - deltaEnergy);
    };
  };
  missileDirection = BITS10_11(timer.timerWord8());
  objD7 = timer.timerObj6();
  curPos = objD7.pos();
  leavingThisCell =   (missileDirection==curPos)
                   || (((missileDirection+1)&3) == curPos);
  if (leavingThisCell)
  {
    origMapX = curMapX; // mapX
    origMapY = curMapY; // mapY
    curMapX += d.DeltaX[missileDirection];
    curMapY += d.DeltaY[missileDirection];
    nextCellFlags = GetCellFlags(curMapX, curMapY);
        //Cell directly ahead.
    if (   (nextCellFlags>>5)
        && (((nextCellFlags>>5)!=roomFALSEWALL) || (nextCellFlags&5)) )
    {
      if ((nextCellFlags>>5) != roomSTAIRS) goto tag00eb90;
      if ((d.LevelCellFlags[origMapX][origMapY]>>5) != roomSTAIRS )
                              goto tag00eb90;
    };
    // Did missile strike wall?
    saveTimerIndex = GetRecordAddressDB14(objD7)->timerIndex();
    D0W = CheckMissileEncounter(
          nextCellFlags>>5,  //Cell type
          origMapX,
          origMapY,
          curPos,
          objD7,
          NULL);
    if (D0W != 0) return; // Missile is gone.
  };
tag00eb90:
  if ((missileDirection&1) == (curPos&1))
  {
    curPos -= 1;
  }
  else
  {
    curPos += 1;
  };
  //D4W &= 3;
  // (Later PRS 12/02) objD7.pos(curPos);
  if(leavingThisCell)
  {
    objD7.pos(curPos);
    MoveObject(objD7, origMapX, origMapY, curMapX, curMapY, NULL, NULL);
    newX = d.NewX & 31; //Set to newX by MoveObject
    timer.timerWord8() &= 0xffe0;
    timer.timerWord8() |= newX;   // New mapX to timer entry
    newY = d.NewY & 31; //Set to newY by MoveObject
    timer.timerWord8() &= 0xfc1f;
    timer.timerWord8() |= newY << 5; // New mapY to timer entry
    newDir = d.NewDir & 3; //Set to new Direction by MoveObject
    timer.timerWord8() &= 0xf3ff;
    timer.timerWord8() |= newDir<<10; //Set new direction in timer entry
    objD7.pos(d.NewPos); //Set new position in object. 
    timer.timerTime &= 0xffffff;
    D0L |= uw(d.NewLevel) << 24;
    timer.timerTime |= (d.NewLevel<<24);
  }
  else
  {
    D0W = GetCellFlags(curMapX, curMapY);
    if ((D0W >> 5) == roomDOOR) // cell type 4
    {
      // Did missile strike door
      saveTimerIndex = GetRecordAddressDB14(objMissile)->timerIndex();
      D0W = CheckMissileEncounter(
             4,
             curMapX,
             curMapY,
             curPos,
             objMissile,
             NULL);
      if (D0W != 0) return;//Missile is gone.
    };
    objD7.pos(curPos);
    RemoveObjectFromRoom(objD7, curMapX, curMapY, NULL);
    AddObjectToRoom(objD7, RN(RNempty), curMapX , curMapY, NULL);
  };
  timer.timerTime++;
  timer.timerObj6() = objD7;
  pDB14A2->timerIndex(SetTimer(&timer));
}


//*********************************************************
// Timer function 25 (0x19)
//*********************************************************
//   TAG00ecca
void ProcessTT_25(TIMER *pTimer)
{ //(void)
  //An open door spell???  Other missiles??
  dReg         D0, D1, D4, D5, D6, D7;
  aReg         A0, A1;
  DB15        *DB15A2;
  TIMER        timer; //26
  i16          w_16;
  MONSTERTYPE  mt_14=mon_undefined;
  i16          w_14;
  ui16         uw_12;
  RN           obj_10;
  MONSTERDESC *pmtDesc=NULL;
  ITEM16      *pi16A0;
  DB4         *pDB4_4=NULL;
  i32          damage;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D7W = pTimer->timerUByte6; //x
  D6W = pTimer->timerUByte7; //y
  DB15A2 = GetRecordAddressDB15(pTimer->timerObj8());
  D4W = D6W;
  D4W = (UI8)(d.LevelCellFlags[D7W][D6W]); // Cell flag
  D4W = sw(BITS5_15(D4W));
  //SET(D0W, d.LoadedLevel==d.partyLevel);
  //if (D0B != 0)
  //{
    //SET(D0W, D7W == d.partyX);
  //};
  //if (D0B != 0)
  //{
    //SET(D0W, D6W == d.partyY);
  //};
  if (   (d.LoadedLevel==d.partyLevel)
      && (D7W==d.partyX)
      && (D6W==d.partyY) ) w_16=1; else w_16=0;
  //w_16 = D0W & 1;
  obj_10 = FindFirstMonster(D7W, D6W);
  if (obj_10 != RNeof)
  {
    pDB4_4 = GetRecordAddressDB4(obj_10);
    mt_14 = pDB4_4->monsterType();
    pmtDesc = &d.MonsterDescriptor[mt_14];
  };
  uw_12 = uw(0xff80 + (DB15A2->cloudType()));
  if (uw_12 == 0xff87) //Poison cloud
  {
    D0L = Smaller(DB15A2->value()/32, 4);
    D1L = STRandomBool();
    D5W = sw(Larger(1, D0W + D1W));
  }
  else
  {
    D5W = sw(DB15A2->value()/2 + 1);
    //D0L = STRandom() & 0xffff;
    D5W = sw(D5W + STRandom(D5W) + 1);
  };
  switch (uw_12)
  {
  case 0xff82:
      D5W = sw(D5W / 2);
      if (D5W == 0) break;
      // Note fall-through.
  case 0xff80:
      if (D4W == 4)
      {
        HitDoor(D7W, D6W, D5W, 1, 0);
      };
      break;

  case RNDispellMissile:
      if (obj_10 != RNeof)
      {
        if (pmtDesc->nonMaterial())
        {
          if ( (mt_14==mon_Zytaz) && (d.LoadedLevel==d.partyLevel) )
          {
            w_14 = sw(D5W/8);
            D5W = sw(D5W - w_14);
            w_14 = sw(2*w_14 + 1);
            D4W = pDB4_4->numMonM1(); //#monsters-1
            do
            {
              D0W = sw(pDB4_4->groupIndex());
              ASSERT(D0W < d.MaxITEM16,"maxitem16");
              pi16A0 = &d.Item16[D0W];
              if (pi16A0->singleMonsterStatus[D4W].TestAttacking())
              {
                damage = STRandom(w_14);
                DamageMonster(
                     pDB4_4,
                     D4W,
                     D7W,
                     D6W,
                     sw(damage + STRandom0_3() + D5W),
                     1,
                     false,
                     NULL);
                // The following did not work because the debug and release
                // versions called the two Random functions in a different order!
                //DamageMonster(
                //     pDB4_4,
                //     D4W,
                //     D7W,
                //     D6W,
                //     sw(STRandom0_3() + STRandom(w_14) + D5W),
                //     1);
              };
              D4W--;
            } while (D4W >= 0);
          }
          else
          {
            DamageMonsterGroup(pDB4_4, D7W, D6W, D5W, 1, false, NULL);
          };
        };
      };
      break;
  case 0xffe4:
      //D3W = (DB15A2->type() + 1) & 0x7f;
      DB15A2->cloudType((CLOUDTYPE)(DB15A2->cloudType()+1));
      QueueSound(5, D7W, D6W,1);
      goto tag00f020;
  case 0xffa8:
      if (DB15A2->value() > 55)
      {
        //.W = DB15A2->value();
        //.W = D0W - 40;
        //D3W <<= 8;
        DB15A2->value(DB15A2->value()-40);
        goto tag00f020;
      };
      break;
  case 0xff87: //Poison Cloud
      if (w_16 != 0)
      {
        DamageAllCharacters(D5W, 0, 0);
      }
      else
      {
        if (obj_10 != RNeof)
        {
          D5W = TAG00bbbe(mt_14, D5W);
          if (D5W != 0)
          {
            D0W = DamageMonsterGroup(
                      pDB4_4,
                      D7W,
                      D6W,
                      D5W,
                      1,
                      false,
                      NULL);
            if (D0W != 2)
            {
              if (D5W > 2)
              {
                ProcessTimers29to41(D7W, D6W, TT_M3, 0);
              };
            };
          };
        };
      };
      if (DB15A2->value() < 6) break;
      //D3W = (BITS8_15(dbA2->word(2)) - 3) & 0xff;
      //D3W <<= 8;
      DB15A2->value(DB15A2->value()-3);
tag00f020:
      A1 = (pnt)pTimer;
      A0 = (pnt)&timer;
      MoveWords(A0, A1, 4); // 10 bytes
      timer.timerTime++;
      SetTimer(&timer);
      return;
  }; // end of switch statement
  RemoveObjectFromRoom(pTimer->timerObj8(),
                       D7W,
                       D6W,
                       NULL);
  //DB15A2->link(RNnul);
  DeleteDBEntry(DB15A2);
}

//*********************************************************
//
//*********************************************************
//  TAG00f132
bool OpenTeleporterPitOrDoor(i32 mapX, i32 mapY)
{
  i32 cf;
  cf = (GetCellFlags(mapX, mapY) & 255) >> 5;
  return   (cf == roomOPEN)
         ||(cf == roomTELEPORTER)
         ||(cf == roomPIT)
         ||(cf == roomDOOR);
}


AllocateTimers::AllocateTimers(void)
{
  m_index = NULL;
  m_timers = NULL;
  m_size = 0;
}

AllocateTimers::~AllocateTimers(void)
{
  if (m_index != NULL) UI_free(m_index);
  if (m_timers != NULL) UI_free(m_timers);
  m_index = NULL;
  m_timers = NULL;
  m_size = 0;
}

void AllocateTimers::Allocate(i32 numEnt)
{
  i32 i;
  if (m_size >= numEnt) return;
  m_index = (pnt)UI_realloc(m_index, numEnt*sizeof(d.timerQue[0]), 0xffff);
  if (m_index == NULL)
  {
    die(0x41fb,"Cannot allocate timer index");
  };
  m_timers = (pnt)UI_realloc(m_timers,numEnt*sizeof(d.Timers[0]), 0xffff);
  if (m_timers == NULL)
  {
    die(0x41fb,"Cannot allocate timers");
  };
  d.timerQue = (i16 *)m_index;
  d.Timers = (TIMER *)m_timers;
  for (i=m_size; i<numEnt; i++)
  {
    memset(d.Timers+i, 0, sizeof(d.Timers[i]));
    d.timerQue[i] = 0;
    d.Timers[i].Function(TT_EMPTY);
  };
  m_size = numEnt;
  d.MaxTimers = (i16)m_size;
}

AllocateTimers allocateTimers;
//*********************************************************
//
//*********************************************************
//   TAG00fd1c
void InitializeTimers(void)
{
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  dReg D7;
  TIMER *pA3;
  //d.Timers = (TIMER *)malloc((ui16)d.MaxTimers*(ui32)10, 1);
  //d.timerQue = (i16 *)allocateMemory(d.MaxTimers*2,1);
  allocateTimers.Allocate(d.MaxTimer());
  if (d.dungeonFormat != 0)
  {
    D7L = 0;
    for (pA3=allocateTimers.Timers(); D7W<d.MaxTimer(); D7W++, pA3++)
    {
      pA3->Function(TT_EMPTY);
    };

    //d.numTimer = 0;
    //d.FirstAvailTimer = 0;
    allocateTimers.Clear();
    SetWatchdogTimer();
  };
}

void EnlargeTimerQueue(i32 numEnt)
{
  allocateTimers.Allocate(numEnt);
}

//*********************************************************
//
//*********************************************************
bool TestTimerLess(TIMER *timer1, TIMER *timer2) // TAG00fd9e
{
  // return 1 if *p1 < *p2
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  dReg D1, D2;
  //A0 = (pnt)p1;
  //A1 = (pnt)p2;
  //D0L = 1;
  D2L = timer1->timerTime & 0xffffff; //D2=*p1
  D1L = timer2->timerTime & 0xffffff; //D1=*p2
  if (D2L < D1L) return true; // *p1 < *p2
  if (D2L > D1L) return false; // *p1 > *p2
  // Times are the same. Parameter messages come before everything else.
  if (timer1->Function() == TT_ParameterMessage)
  {
    if (timer2->Function() != TT_ParameterMessage) return true;
    // Both are Parameter messages; Compare sequence numbers.
    if (timer1->timerUByte5 < timer2->timerUByte5) return true;
    if (timer1->timerUByte5 > timer2->timerUByte5) return false;
    // Identical entries.  The smaller pointer comes first
    if (timer2 >= timer1) return true;
    return false;
  }
  else
  {
    if (timer2->Function() == TT_ParameterMessage) return false;
  };
  // Compare the first word.
  if (timer1->Function() > timer2->Function()) return true;
  if (timer1->Function() < timer2->Function()) return false;
  if (timer1->timerUByte5 > timer2->timerUByte5) return true;
  if (timer1->timerUByte5 < timer2->timerUByte5) return false;
  // Identical entries.  The smaller pointer comes first
  if (timer2 >= timer1) return true;
  return 0;
}

//  TAG00fdce
i16 FindTimerPosition(i32 P1)
{ // Find position of timer entry in timer queue.
  dReg D0, D1;
  i16 *pwA0;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  pwA0 = allocateTimers.timerQueue();
  D1W = d.MaxTimer();
  D0L = 0;
  do
  {
    if (P1 == *(pwA0++)) return D0W;
    D0W++;
  } while (D0W < D1W);
//
  return 0;
}

//   TAG00fdf0
void AdjustTimerPosition(i32 timerPosition)
{ // Current position of timer in queue.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  dReg D0, D4, D5, D6, D7;
//  pnt A3;
  TIMER *ptA3, *ptA0;
  i16 LOCAL_2;
  D7W = sw(timerPosition);
  D4W = d.NumTimer();
  //D4W = D0W;
  if (D4W == 1) return;
  D5W = d.TimerQue(D7W);
  ptA3 = d.pTimer(D5W); // New entry to insert
  LOCAL_2 = 0;
  while (D7W != 0) //Attempt to find entry
  { // Starting at middle word keep dividing the
    // que in half and moving the new entry down
    // as long as it is smaller than the one already
    // there.  Set local_2 if any such swap occurs.
    D6W = sw((D7W-1)>>1); // Locate middle item???
    D0W = d.TimerQue(D6W);
    ptA0 = d.pTimer(D0W);
    D0W = TestTimerLess(ptA3, ptA0); // Is new smaller
    if (D0W == 0) break;
    //D0W = d.timerQue[D6W];
    d.TimerQue(D7W, d.TimerQue(D6W));
    D7W = D6W; //The new empty spot
    LOCAL_2 = 1; //we made a swap


  };
  if (LOCAL_2 == 0)
  {
    D4W = sw((D4W-2)/2);
    while (D7W <= D4W)
    {
      D6W = sw(2*D7W + 1);
      D0W = sw(D6W+1);
      if (D0W < d.NumTimer())
      {
        //D0W = d.Pointer12998[D6W];
        //parm2 = &d.Pointer12994[D0W];
        //D0W = d.Pointer12998[D6W+1];
        //parm1 = &d.Pointer12994[D0W];
        D0W = TestTimerLess(d.pTimer(d.TimerQue(D6W+1)),
                            d.pTimer(d.TimerQue(D6W)));
        if (D0W != 0) D6W++;
      };
      D0W = TestTimerLess(d.pTimer(d.TimerQue(D6W)), ptA3);
      if (D0W == 0) break;
      D0W = d.TimerQue(D6W);
      d.TimerQue(D7W, D0W);
      D7W = D6W;


    }; //while
  };
  d.TimerQue(D7W, D5W);
}

//*********************************************************
//
//*********************************************************
//#define ASSERTTimer(x) (ASSERT(x))
#define ASSERTTimer(x)
bool CheckTimers(void)
{ // Return true if all is well.
  // We check that the queue is properly ordered.
  i32 n, N;
  N = d.NumTimer();
  for (i32 i=0; i<N; i++)
  {
    if (d.pTimer(d.TimerQue(i))->Function() == 0)
    {
      return false;
    };
    for (n=2*i+1; n<=2*i+2; n++)
    {
      if (n >= N) break;
      if (TestTimerLess(d.pTimer(d.TimerQue(n)),d.pTimer(d.TimerQue(i)))) 
      {
        return false;
      };
    };
  };
  return true;
}

//*********************************************************
//
//*********************************************************
//   TAG00ff4c
void DeleteTimer(i32 timerIndex)
{
  i32 QPos;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ASSERTTimer(CheckTimers());
  d.pTimer(timerIndex)->Function(TT_EMPTY);//  .WB8.bytes[0] = 0;
  if (timerIndex < d.FirstAvailTimer())
  {
    d.FirstAvailTimer(timerIndex);
  };
  d.DecrementNumTimer();
  //D5W = d.numTimer;
  if (d.NumTimer() != 0)
  {
    QPos = FindTimerPosition(timerIndex);
    if (QPos != d.NumTimer()) // If it was not the last entry
    {
      d.TimerQue(QPos, d.TimerQue(d.NumTimer()));
        // Move last entry into vacated position.
      AdjustTimerPosition(QPos);
    };
  };
  ASSERTTimer(CheckTimers());
}


//*********************************************************
//
//*********************************************************
//  TAG00ffbe
i16 SetTimer(TIMER *pNewTimer) //
{
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  //i32 D5,D7;
  dReg D0, D1, D5, D7;
  aReg A0, A1;
  //i16 *pwA0;
  TIMER *ptA0, *ptA2;
  ASSERTTimer(CheckTimers());
  //ptA3 = P1;
  if (d.NumTimer() >= d.MaxTimer() - 5)
  {
    if (d.MaxTimer() >= 5000)
    {
      die(45, "No empty Timer entries");
    };
    d.FirstAvailTimer(d.MaxTimer());
    EnlargeTimerQueue((d.MaxTimer()+10)*5/4);
  };
  if (   (pNewTimer->Function() >= TT_MAP_FIRST) 
      && (pNewTimer->Function() <= TT_MAP_LAST))
  {
    if (deleteDuplicateTimers != 0)
    {
      for (D7W=0, ptA2=allocateTimers.Timers(); D7W<d.MaxTimer(); D7W++, ptA2++)
      {
        if (   (ptA2->Function() >= TT_MAP_FIRST)
            && (ptA2->Function() <= TT_MAP_LAST)  )
        {
          if (pNewTimer->timerTime != ptA2->timerTime) continue;
          if (pNewTimer->timerWord6() != ptA2->timerWord6()) continue;
          if (ptA2->Function() == TT_STONEROOM)
          {
            if (ptA2->timerUByte8 != pNewTimer->timerUByte8) continue;
          };
          ptA2->timerUByte9 = pNewTimer->timerUByte9;
          TraceTimer(ptA2, D7W, "Byte9");
          ASSERTTimer(CheckTimers());
          return D7W;//goto tag010224;
        };
        if (ptA2->Function() != TT_1) continue;
        if (pNewTimer->timerTime != ptA2->timerTime) continue;
        if (pNewTimer->timerWord6() != ptA2->timerWord6()) continue;
        if (pNewTimer->timerUByte9 == 2)
        {
          D0W = sw(1 - ptA2->timerUByte9);
          pNewTimer->timerUByte9 = D0B;
          TraceTimer(pNewTimer,0,"Byte9");
        };
        DeleteTimer(D7W);
        TraceTimer(ptA2,D7W,"Delete");
        break;

      }; //for (D7W, A2)
    };
  }
  else
  {
    if (pNewTimer->Function() == TT_1) // goto tag010154;
    {
      for (D7W=0, ptA2=allocateTimers.Timers(); D7W<d.MaxTimer(); D7W++, ptA2++)
      {
        if (pNewTimer->timerTime != ptA2->timerTime) continue;
        if (pNewTimer->timerWord6() != ptA2->timerWord6()) continue;
        if (ptA2->Function() == TT_DOOR)
        {
          if (ptA2->timerUByte9 != 2)
          {
            ASSERTTimer(CheckTimers());
            TraceTimer(ptA2,D7W,"Unchanged");
            return D7W;
          };
          ptA2->timerUByte9 = ub(1 - pNewTimer->timerUByte9);
          TraceTimer(ptA2,D7W,"byte9");
          ASSERTTimer(CheckTimers());
          return D7W;
        };
        if (ptA2->Function() != TT_1) continue;
        ptA2->timerUByte9 = pNewTimer->timerUByte9;
        TraceTimer(ptA2, D7W, "Byte");
        ASSERTTimer(CheckTimers());
        return D7W;
  
      }; // for (D7W, uA2)
//goto tag0101c2;
    }
    else
    {
//tag010154:
      if (pNewTimer->Function() == TT_BASH_DOOR)
      {// goto tag0101c2;
        for (D7W=0, ptA2=allocateTimers.Timers(); D7W<d.MaxTimer(); D7W++, ptA2++)
        {
          if (pNewTimer->timerWord6() != ptA2->timerWord6()) continue;
          D0L = (pNewTimer->timerTime>>24)&0xff;
          D1L = (ptA2->timerTime>>24)&0xff;
//018e 6620                     BNE      $+34 (=0x0101b0)
          if (D1L != D0L) continue;
          if (ptA2->Function() != TT_1)
          {
            if (ptA2->Function() != TT_DOOR) continue;
          };
          DeleteTimer(D7W);
          TraceTimer(ptA2,D7W,"Delete");
//
        }; // for (D7W, A2)
      };
    };
  };
//tag0101c2:
  A1 = (pnt)pNewTimer;
  D5W = d.FirstAvailTimer();
  ASSERT(d.FirstAvailTimer() < d.MaxTimer(),"maxTimer");
  A0 = (pnt)d.pTimer(D5W);//A0 is next available timer entry
  MoveWords(A0, A1, 4); // Copy Timer to d.timers array.
  do
  {
    d.IncrementFirstAvailTimer(); //Try to find an empty entry for next time.
    if (d.FirstAvailTimer() == d.MaxTimer()) break;
    ptA0 = d.pTimer(d.FirstAvailTimer());
  } while (ptA0->Function() != TT_EMPTY);
  d.TimerQue(d.NumTimer(), D5W);
  d.IncrementNumTimer();
  AdjustTimerPosition(d.NumTimer()-1);
  TraceTimer(d.pTimer(D5W), D5W, "create");
  ASSERTTimer(CheckTimers());
  return D5W;
}


//*********************************************************
//
//*********************************************************
//   TAG01022c
void GetNextTimerEntry(TIMER *P1, ui32 *index)
{
  TIMER *ptA1;
  pnt A0;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  *index = d.TimerQue(0);
  ptA1 = d.pTimer(*index);
  A0 = (pnt)P1;
  MoveWords(A0, (pnt)ptA1, 4); // 5 words = 10 Bytes
  TraceTimer(d.pTimer(*index), *index, "Process");
  DeleteTimer(*index);
}


//*********************************************************
//
//*********************************************************
//  TAG010260
i16 CheckForTimerEvent(void)
{
  dReg D0, D1;
  bool zflag;
  D0W=d.NumTimer();
  zflag=false;
  if (D0W == 0) {zflag=true; goto tag01028c;};
  //A0=d.Pointer12998;
  D0W = d.TimerQue(0);
  //D0 = D0W * 10;
  //A0 += D0;
  //D0 = pA0->long0 & 0xffffff;
  D0L = d.pTimer(D0W)->timerTime & 0xffffff;
  D1L = d.Time;
  if ( D0L <= D1L ) D0B=1; else D0B=0;
  zflag = (D0B==0);
tag01028c:
  if (zflag) D0B=0; else D0B=1;
  if (D0B != 0)
  {
    if (d.Word11754 < 2) D0B=1; else D0B=0;
  };
  D0W &= 1;
  return D0W;
}

//*********************************************************
//
//*********************************************************
//   TAG0102a4
void ProcessTT_1(TIMER *pTimer)
{//(void)
  static dReg D0, D1, D4, D5, D6, D7;
  static CELLFLAG *cfA2;
  static i16  w_10;
  //static i16  w_8;
  static RN   obj_6;
  static DB0      *pDB0_4;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D6W = pTimer->timerUByte7;
  D7W = pTimer->timerUByte6;
  cfA2 = &d.LevelCellFlags[D7W][D6W];
  D4W = sw(*cfA2 & 7);
  if (D4W == 5) return;
  pTimer->timerTime++;
  D5W = pTimer->timerUByte9;
  if (D5W == 1)
  {
    pDB0_4 = FirstObjectRecordAddressDB0(D7W, D6W);
    w_10 = pDB0_4->mode(); //up/down or sideways
    if (   (d.LoadedLevel == d.partyLevel)
        && (D7W == d.partyX)
        && (D6W == d.partyY)
        && (D4W != 0)  )
    {
      if (d.NumCharacter > 0)
      {
        *cfA2 &= 0xf8; // Clear bits 0-2
        w_10 |= 8;
        D0W = DamageAllCharacters(5, 4, 2);
        if (D0W != 0)
        {
          QueueSound(soundDOOROPENING, D7W, D6W, 1);
        };
      };
      pTimer->timerTime++;
      SetTimer(pTimer);
      return;
    };
    obj_6 = FindFirstMonster(D7W, D6W);
    if (obj_6 != RNeof)
    {
      //w_8 = GetMonsterDescWord2(obj_6);
      if (!obj_6.NonMaterial())
      {
        if (w_10 != 0)
        {
          D1W = obj_6.VerticalSize();
        }
        else
        {
          D1W = 1;
        };
        if (D4W >= D1W)
        {
          D0W = DamageMonsterGroup(
                    GetRecordAddressDB4(obj_6),
                    D7W,
                    D6W,
                    5,
                    1,
                    false,
                    NULL);
          if (D0W != 2)
          {
            ProcessTimers29to41(D7W, D6W, TT_M3, 0);
          };
          if (D4W == 0)
          {
            D0W = 0;
          }
          else
          {
            D0W = sw(D4W - 1);
          };
          D4W = D0W;
          *cfA2 = ub((*cfA2 & 0xf8) | D4W);
          QueueSound(4, D7W, D6W, 1);
          pTimer->timerTime++;
          SetTimer(pTimer);
          return;
        };
      };
    };
  };
//
  if (   ((D5W==0) && (D4W==0))
      || ((D5W==1) && (D4W==4)) ) return;

  if (D5W == 0)
  {
    D0W = -1;
  }
  else
  {
    D0W = 1;
  };
  D4W = sw(D4W + D0W);
  *cfA2 = ub((*cfA2 & 0xf8) | D4W);
  QueueSound(2, D7W, D6W, 1);
//
//
//
  if (   ((D5W != 0) && (D4W != 4))
      || ((D5W == 0) && (D4W != 0))  )
  {
    SetTimer(pTimer);
  };
}

//*********************************************************
//
//*********************************************************
//   TAG0104bc
void ProcessTT_FALSEWALL(TIMER *pTimer) //function = 7
{
  dReg D5;
  CELLFLAG *pcfA2;
  RN    objD4;
  i32 mapX, mapY;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  mapY = pTimer->timerUByte7;
  mapX = pTimer->timerUByte6;
  {
    RN obj;
    DB3 *pDB3;
    i32 actuatorType;
    for (obj=FindFirstObject(d.LoadedLevel,mapX, mapY);
         obj != RNeof;
         obj = GetDBRecordLink(obj))
    {
      if (obj.dbType() != dbACTUATOR) continue;
      pDB3 = GetRecordAddressDB3(obj);
      actuatorType = pDB3->actuatorType();
      if (   (actuatorType == 127)
          && (obj.pos() == pTimer->timerUByte8))
      {
        ProcessPortraitTimer7(obj, pTimer);
        continue;
      };
    };
  };
  pcfA2 = &d.LevelCellFlags[mapX][mapY];
  D5W = pTimer->timerUByte9;//Action to perform
  if (D5W == 2) // if toggle
  {
    D5W = sw(*pcfA2 & 4);
    if (D5W != 0)
    {
      D5W = 1; // Clear if currently set
    }
    else
    {
      D5W = 0; //Set if currently clear.
    };
  };
  if (D5W == 1)
  {
//
//
    if (   (d.LoadedLevel == d.partyLevel)
        && (mapX == d.partyX)
        && (mapY == d.partyY) )
    {
      pTimer->timerTime++;
      SetTimer(pTimer); //Wait for party to leave
    }
    else
    {
      objD4 = FindFirstMonster(mapX, mapY);
      if (   (objD4 != RNeof)
          && objD4.NonMaterial() )
      {
        pTimer->timerTime++;
        SetTimer(pTimer); // Wait for Monsters to leave
      }
      else
      {
        *pcfA2 &= 0xfb; // clear 0x04 = ??
        TraceCellflags(mapX, mapY, "Clear 0x04");
      };
    };
  }
  else
  {
    *pcfA2 |= 0x04;
    TraceCellflags(mapX, mapY, "Set 0x04");
  };
}

//*********************************************************
//
//*********************************************************
//   TAG010576
void ProcessTT_BASH_DOOR(TIMER *pTimer)
{ //When I bashed a door down.
  CELLFLAG *pCF;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  pCF = &d.LevelCellFlags[pTimer->timerUByte6][pTimer->timerUByte7];
  SETBBITS0_2(*pCF, 5); //Set door as bashed.
}

//*********************************************************
// Under certain circumstances this timer will queue
// a type 1 timer with all other parameters the same.
// It does nothing else.
// The following table has an 'X' for those cases in
// which a new timer is queued.
//
//                    Cell flag & 7
//                   0 1 2 3 4 5 6 7
// timer byte 9 = 0  . X X X X . X X
// timer byte 9 = 1  X X X X . . X X
// timer byte 9 = 2  . X X X . . X X
//*********************************************************
//   TAG0105ba
void ProcessTT_DOOR(TIMER *pTimer)
{
  dReg D7;
  CELLFLAG *cfA0;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  //D0B = pTimer->uByte7;
  cfA0 = &d.LevelCellFlags[pTimer->timerUByte6][pTimer->timerUByte7];
  D7W = sw(*cfA0 & 0x7); // Bottom 3 bits of cell flag
  if (D7W == 5) return;
  if (pTimer->timerUByte9 == 2)
  {
    if (D7W == 0) pTimer->timerUByte9 = 1;
    else pTimer->timerUByte9 = 0;
  }
  else
  {
    if (pTimer->timerUByte9 == 0)
    {
      if (D7W == 0)  return;
    }
    else
    {
      if (D7W == 4) return;
    };
  }
  pTimer->Function(TT_1);
  SetTimer(pTimer);
}


//*********************************************************
// Called when a timer function (type 6)
// is directed to an actuator of type 6.
// Or when a timer funtion type 5 or 6 is directed
// at an actuator of type 48 (My invention - PRS).
// SET increments the counter.  Anything else decrements
// it.  The actuator is considered 'pressed' if the count
// goes to zero.
// For some reason, you cannot increment a counter that
// is initially zero.
//*********************************************************
void OperateCounterActuator(const TIMER *pTimer, DB3 *pActuator)
{
  i32 counterContents;
  bool swpressed, swclosed;
  i32 targetX, targetY, targetPos;
  char *asciiAction;
  targetX   = pActuator->targetX();
  targetY   = pActuator->targetY();
  targetPos = pActuator->targetPos();
  counterContents = pActuator->value();

  if (counterContents == 0)
  {
    if (TimerTraceActive)
    {
      fprintf(GETFILE(TraceFile),"Value is zero so no action\n");
    };
  };
  if (counterContents > 0)
  {  // Cannot affect a counter that equals zero. (Why?)
    if (pTimer->timerUByte9 == 0) // Action to perform == SET
    {                        // implies increment.
      if (counterContents < 511)
      {
        counterContents++;
        asciiAction = "Increment";
      }
      else
      {
        asciiAction = "Max value so no increment";
      };
    }
    else
    {
      counterContents--;
      asciiAction = "Decrement";
    };

    pActuator->value(counterContents);
    if (TimerTraceActive)
    {
      fprintf(GETFILE(TraceFile),"%s newVal= %03x\n",
                        asciiAction, counterContents);
    };
    if (pActuator->action() == actuatorAction_CONSTPRESSURE)
    {
      swpressed = counterContents==0;
      swclosed = swpressed ^ pActuator->normallyClosed();
      if (TimerTraceActive)
      {
        fprintf(GETFILE(TraceFile), " ConstPressure %s\n",
                    swclosed?"Closed":"Open");
      };
      QueueSwitchAction(pActuator,
                        swclosed ? 0 : 1,
                        targetX,
                        targetY,
                        targetPos);
    }
    else
    {
      if (counterContents == 0)
      {
        if (TimerTraceActive)
        {
          fprintf(GETFILE(TraceFile)," Pressed %s\n",
                  pActuator->normallyClosed()?"NC":"NO");
        }
        QueueSwitchAction(pActuator,
                          pActuator->action(),
                          targetX,
                          targetY,
                          targetPos);
      };
    };
  };
}



//*********************************************************
//
//*********************************************************
//   TAG010642
void ProcessTT_OPENROOM(TIMER *pTimer)
{//(void)
  dReg D5, D6, D7;
  i32 targetX, targetY;
  RN  objD4;
  DB2 *pDB2_4;
  DB3 *pDB3;
  i32 w_10;
  TIMER timer;
  targetY = pTimer->timerUByte7;
  targetX = pTimer->timerUByte6;
  for (objD4 = FindFirstObject(targetX, targetY);
       objD4 != RNeof;
       objD4 = GetDBRecordLink(objD4) )
  {
    D7W = (I16)objD4.dbType();
    if (D7W == dbTEXT)
    {
      pDB2_4 = GetRecordAddressDB2(objD4);
      D6W = pDB2_4->show();
      switch (pTimer->timerUByte9)
      {
      case 0:  pDB2_4->show(1); break;
      case 1:  pDB2_4->show(0); break;
      case 2:  pDB2_4->show(pDB2_4->show() ^ 1);  break;
      };





      if (   (D6W==0)
          && (pDB2_4->show() != 0)
          && (d.LoadedLevel == d.partyLevel)
          && (targetX == d.partyX)
          && (targetY == d.partyY)      )
      {
        char text[1000];
        DecodeText(text, 
                   GetRecordAddressDB2(objD4), 
                   1, 
                   990);
        QuePrintLines(15, text);
      };
    } // End DB2 code
    else
    {
      if (D7W == dbACTUATOR) //DB3
      {
        pDB3 = GetRecordAddressDB3(objD4);
        if (pDB3->actuatorType() == 47)
        {
          LOCATIONREL locr;
          locr.l = d.LoadedLevel;
          locr.x = targetX;
          locr.y = targetY;
          locr.p = objD4.pos();
          ProcessDSATimer5(objD4, pTimer, locr);
          continue;
        };
        if (pDB3->actuatorType() == 48) //Counter
        {
          if (TimerTraceActive)
          fprintf(GETFILE(TraceFile), "Actuator6(counter) %03x %02x(%02x,%02x) ",
                     objD4.idx(),
                     d.LoadedLevel, targetX, targetY);

          OperateCounterActuator(pTimer, pDB3);
        };
        if (pDB3->actuatorType() == 6) // monster generator
        {
          D5W = pDB3->delay(); // # monsters or current value
          if ((D5W & 0x8) != 0)
          {
            D5W = sw(STRandom(D5W&7));
          }
          else
          {
            D5W -= 1; // modified delay
          };
          w_10 = pDB3->hitPointMultiplier();
          if (w_10 == 0)
          {
            w_10 = d.pCurLevelDesc->experienceMultiplier();
          };
          CreateMonster(
                        pDB3->value(),
                        w_10,
                        D5W,          //#monsters-1
                        STRandom0_3(), //facing??
                        targetX,
                        targetY,
                        pDB3->normallyClosed(),
                        pDB3->drawAsSize4()); //invisible
          if (pDB3->audible())
          {
            QueueSound(soundTELEPORT, targetX, targetY, 1);
          };
          if (pDB3->onceOnly())
          {
            pDB3->actuatorType(0);
          }
          else
          {
            w_10 = pDB3->disableTime();
            if (w_10 != 0)
            {
              pDB3->actuatorType(0);
              if (w_10 >= 128)
              {
                w_10 = 64 * (w_10 - 126);
              };
              timer.Function(TT_ReactivateGenerator);
              timer.timerTime = d.Time + w_10 + (d.LoadedLevel<<24);
              timer.timerUByte5 = 0;
              timer.timerUByte6 = ub(targetX);
              timer.timerUByte7 = ub(targetY);
              timer.timerObj8() = objD4;
              SetTimer(&timer);
            };
          };
        };
      };
    };
  }; // for ()
}

//*********************************************************
//
//*********************************************************
//   TAG0108a4
void ProcessTT_ReactivateGenerator(TIMER *pTimer)
{
  RN objD7;
  DB3 *DB3A2;
  // The original FTL code looked for the first actuator
  // with type == 0 and turned it into a monster generator.
  // We had a problem with this however.  If a 'once-only'
  // pressure pad was on the same square then, after the 
  // pad had been used once, its type is set to zero.  Then
  // when the timer regeneration timer came along it 
  // turned the pressure pad into a generator and that did 
  // not work well.  Now we store the generator's ID (RN id)
  // in the timer.  If we can find an actuator with that ID 
  // then we reactivate it.  Otherwise, we reactivate the 
  // first actuator with type==0.  That keeps us backward
  // compatible with old savegames.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  RN generatorID;
  RN firstTypeZero;
  generatorID = pTimer->timerObj8();
  firstTypeZero = RNeof;
  for (objD7 = FindFirstObject(pTimer->timerUByte6, pTimer->timerUByte7);
       objD7 != RNeof;
       objD7 = GetDBRecordLink(objD7) )
  {

    if (objD7.dbType() == dbACTUATOR)
    {
      DB3A2= GetRecordAddressDB3(objD7);
      if (DB3A2->actuatorType() != 0) continue;
      if (firstTypeZero == RNeof) firstTypeZero = objD7;
      if (objD7 == generatorID) break;
    };
//
//
  };
  if (objD7 == RNeof) objD7 = firstTypeZero;
  if (objD7 == RNeof) return;
  DB3A2 = GetRecordAddressDB3(objD7);
  DB3A2->actuatorTypeOR(6); // set two bits??? Generator = 6!
}

//*********************************************************
// Called from only one place.
// A Timer6 event was applied to an actuator
// of type 7, 8, 9, 10, 14, or 15.
//*********************************************************
//   TAG010910
void TriggerMissileLauncher(DB3 *pDB3, const TIMER *pTimer)
{//(void)
  //dReg D5;
  i32 actuatorTypeD4;
  RN  secondObject, firstObject;
  //i16 w_26;
  i16 objectPosition;
  bool T7or8or14; //w_14//actuator type 7, 8, or 14
  //i16 w_12;
  i16 energyRemaining;
  i16 missileType;
  i16 initialPosition;
  i16 timerMapY;
  i16 timerMapX;
  i32 launchX, launchY;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  timerMapX = pTimer->timerUByte6;
  timerMapY = pTimer->timerUByte7;
  //D5W = pTimer->timerUByte8; //position
  initialPosition = sw((pTimer->timerUByte8 + 2) & 3); //position
  actuatorTypeD4 = pDB3->actuatorType();
  missileType = pDB3->value();
  energyRemaining = sw(pDB3->energyRemaining());
  //w_12 = BITS12_15(pDB3->word6);
  T7or8or14 = (   (actuatorTypeD4==7)
               || (actuatorTypeD4==8)
               || (actuatorTypeD4==14));
  if ( (actuatorTypeD4 == 8) || (actuatorTypeD4==10) )
  {
    firstObject.CreateSpell(missileType);
    secondObject = firstObject;
  }
  else
  {
    if ( (actuatorTypeD4==14) || (actuatorTypeD4==15) )
    {
      for (firstObject =  FindFirstObject(timerMapX, timerMapY);
           firstObject != RNeof;//Original had RNnul;  // Why not RNeof??
           firstObject =  GetDBRecordLink(firstObject) )
      {
        ASSERT(firstObject != RNnul,"firstObj");
        objectPosition = firstObject.pos();
        if (firstObject.dbType() > dbACTUATOR)
        {
          if (objectPosition == pTimer->timerUByte8) break; //correct position
          if (objectPosition == ((pTimer->timerUByte8 + 1) & 3)) break; //correct position
        };
//
      };
      ASSERT(firstObject != RNnul,"firstObj");
      if (firstObject == RNeof) return;// original had RNnul) return;
      RemoveObjectFromRoom(firstObject,
                           timerMapX, timerMapY,
                           NULL);
      if (!T7or8or14)
      {
        for (secondObject = FindFirstObject(timerMapX, timerMapY);
             secondObject != RNeof;
             secondObject = GetDBRecordLink(secondObject) )
        {
          objectPosition = secondObject.pos();
          if (secondObject.dbType() > dbACTUATOR)
          {
            if (objectPosition == pTimer->timerUByte8) break; //correct position
            if (objectPosition == ((pTimer->timerUByte8+1) & 3)) break; //correct position
          };
//
//
        };
        ASSERT(secondObject != RNnul,"secondObj"); // Should this be RNeof?
        if (secondObject == RNeof)//changed from RNnul
        {
          T7or8or14 = 1;
        }
        else
        {
          RemoveObjectFromRoom(secondObject,
                               timerMapX,
                               timerMapY,
                               NULL);
        };
      };
    }
    else
    {
      firstObject = ConstructPredefinedMissile(missileType);
      if (firstObject == RNnul) return;
      if (!T7or8or14)
      {
        secondObject = ConstructPredefinedMissile(missileType);
        if (secondObject == RNnul) T7or8or14 = true;
      };
    };
  };
  if (T7or8or14)
  {
    //w_26 = STRandomBool();
    initialPosition = sw((initialPosition + STRandomBool()) & 3);
  };
  launchX = timerMapX + d.DeltaX[pTimer->timerUByte8]; //position
  launchY = timerMapY + d.DeltaY[pTimer->timerUByte8]; //position
  //d.MissileDelay = 1;
  LaunchMissile(
                firstObject,
                launchX, //x
                launchY, //y
                initialPosition,         //pos
                pTimer->timerUByte8,     //direction
                energyRemaining,    //range
                100,                //damage
                0,                  //decayRate
                true);
  if (!T7or8or14)
  {
    LaunchMissile(
                  secondObject,
                  launchX,
                  launchY,
                  (initialPosition + 1) & 3, //position
                  pTimer->timerUByte8,       //direction
                  energyRemaining, //range
                  100,             //damage
                  0,               //decayRate
                  true);
  };
  //d.MissileDelay = 0;
}

//********************************************************
//
// Mostly copied from TriggerMissileLauncher()
//
//********************************************************
void ThrowMissile(i32 missileType,
                  const LOCATIONREL& launchLocation,
                  i32 direction,
                  const LOCATIONREL& objectLocation,
                  i32 range,
                  i32 damage,
                  i32 decayRate)
{
  RN  objectToThrow;
  DBTYPE objType;
  objectToThrow = RNeof;
  i32 oldLevel;
  if (!launchLocation.IsValid()) return;
  switch (missileType & 0xffff)
  {
  case RNFireball:
  case RNPoison:
  case RNLightning:
  case RNDispellMissile:
  case RNZoSpell:
  case RNPoisonBolt:
  case RNPoisonCloud:
  case RNMonsterDeath:
    objectToThrow.CreateSpell(missileType & 0x7f);
    break;
  case 0:
    {
      if (!objectLocation.IsValid()) return;
      for (objectToThrow =  FindFirstObject(objectLocation);
           objectToThrow != RNeof;
           objectToThrow =  GetDBRecordLink(objectToThrow) )
      {
        objType = objectToThrow.dbType();
        if (   (objType > dbMONSTER)
            && (objType <= dbMISC) )
        {
          if (objectLocation.p == objectToThrow.pos()) break;
        };
     
      };
      if (objectToThrow == RNeof) return;// original had RNnul) return;
      oldLevel = d.LoadedLevel;
      RemoveObjectFromRoom(objectToThrow,
                           objectLocation);
    };
    break;
  };
  if (objectToThrow == RNeof) return;
  //d.MissileDelay = 1;
  oldLevel = d.LoadedLevel;
  LoadLevel(launchLocation.l);
  LaunchMissile(
                objectToThrow,
                launchLocation.x, //x
                launchLocation.y, //y
                launchLocation.p, //pos
                direction,      //direction
                range,
                damage,
                decayRate,
                false);
  LoadLevel(oldLevel);
  //d.MissileDelay = 0;
}

static i32 NewState(i32 oldState,
                    i32 bitmask,
                    i32 responses,
                    i32 fieldStart)
{
  switch ((responses >> fieldStart) & 3)
  {
  case PORTRAIT_ResponseNone:
    return oldState;
  case PORTRAIT_ResponseClear:
    return oldState | bitmask;
    break;
  case PORTRAIT_ResponseSet:
    return oldState & ~bitmask;
    break;
  case PORTRAIT_ResponseToggle:
    return oldState ^= bitmask;
    break;
  default: return oldState; //impossible
  };
}

static void ProcessPortraitTimer6(
                     RN objPortrait, 
                     const TIMER *pTimer)
{
  i32 level;
  i32 responses;
  i32 timerFunction, timerX, timerY, timerPosition;
  i32 state;
  i32 inputMsgType;
  DB3 *pPortrait;
  pPortrait = GetRecordAddressDB3(objPortrait);
  level = d.LoadedLevel;
  timerFunction = pTimer->timerUByte9;
  timerPosition = pTimer->timerUByte8;
  timerY        = pTimer->timerUByte7;
  timerX        = pTimer->timerUByte6;
  state = pPortrait->State();
  inputMsgType = timerFunction;
  if (TimerTraceActive)
  {
    fprintf(GETFILE(TraceFile),"Portrait State %d MSG %d\n",
                      state, 
                      inputMsgType);
  };
  switch (inputMsgType)
  {
  case 0: //Set
    responses = pPortrait->SetResponses();
    break;
  case 1: //Clear
    responses = pPortrait->ClearResponses();
    break;
  case 2: //Toggle
    responses = pPortrait->ToggleResponses();
    break;
  default:
    return; //Impossible
  };
  state = NewState (state,
                   PORTRAIT_InActive,
                   responses,
                   PORTRAIT_ActiveResponse);
  state = NewState (state,
                   PORTRAIT_HidePortrait,
                   responses,
                   PORTRAIT_PortraitResponse);
  state = NewState (state,
                   PORTRAIT_HideGraphic,
                   responses,
                   PORTRAIT_GraphicResponse);
  pPortrait->State((ui16)state);
}

void ProcessPortraitTimer7(RN targetObj, const TIMER *pTimer)
{
  ProcessPortraitTimer6(targetObj, pTimer);
}

//*********************************************************
//
//*********************************************************
//   TAG010b9a
RESTARTABLE _ProcessTT_STONEROOM(TIMER *pTimer, ui32 index)
{//(void)
  static dReg D4, D5;
  static RN   objAtTarget;
  static DB3 *pDB3A2;
  static i32  targetPos;
  static i32  targetY;
  static i32  targetX;
  static i32  objectDB;
  static i32  actuatorType;
  static DB2  *pDB2_4 = NULL;
  const static char *sPressed, *sClosed;
  RESTARTMAP
    RESTART(1)
    RESTART(2)
  END_RESTARTMAP
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  if (pTimer->Function() == TT_ParameterMessage)
  {
    pDSAparameters[0] = expool.Read((EDT_MessageParameters<<24) + index,pDSAparameters+1, 100);
    pTimer->Function(TT_STONEROOM);
  };
  targetY = pTimer->timerUByte7;
  targetX = pTimer->timerUByte6;
  
  targetPos = pTimer->timerUByte8;
  for (objAtTarget = FindFirstObject(targetX, targetY);
       objAtTarget != RNeof;
       objAtTarget = GetDBRecordLink(objAtTarget))
  {
    objectDB = objAtTarget.dbType();

    if (   (objectDB == dbTEXT) // Text of some sort?
        && (objAtTarget.pos() == pTimer->timerUByte8)   )
    {
      pDB2_4 = GetRecordAddressDB2(objAtTarget);
      if (pTimer->timerUByte9 == 2)
      { // Toggle bit 0 of word 2
        //D0W = pDB2_4->word2 & 1;
        //D3W = D0W==0 ? 1 : 0;
        //pDB2_4->word2 = (pDB2_4->word2 & 0xfffe) | D3W;
        pDB2_4->show(pDB2_4->show() ^ 1);
      }
      else
      { //Set bit 0 if function==0  else clear bit 0
        //D3W = pTimer->uByte9 == 0 ? 1 : 0;
        //pDB2_4->word2 = (pDB2_4->word2 & 0xfffe) | D3W;
        pDB2_4->show((pTimer->timerUByte9 & 1) == 0);
      };
    }
    else
    {
      if (objectDB == dbACTUATOR)
      {
        pDB3A2 = GetRecordAddressDB3(objAtTarget);
        actuatorType = pDB3A2->actuatorType();
        if (actuatorType == 47)
        {
          DSAVARS dsaVars;
          LOCATIONREL locr;
          locr.l = d.LoadedLevel;
          locr.x = targetX;
          locr.y = targetY;
          locr.p = objAtTarget.pos();
          ProcessDSATimer6(objAtTarget, pTimer, locr, false, &dsaVars);
          continue;
        };
        if (   (actuatorType == 127)
            && (objAtTarget.pos() == pTimer->timerUByte8))
        {
          ProcessPortraitTimer6(objAtTarget, pTimer);
          continue;
        };
        D4W = pDB3A2->value();
        if ( (actuatorType == 6)||(actuatorType == 48) ) 
        {    // Counter
          if (TimerTraceActive)
          fprintf(GETFILE(TraceFile), "Actuator6(counter) %03x %02x(%02x,%02x) ",
                     objAtTarget.idx(),
                     d.LoadedLevel, targetX, targetY);

          OperateCounterActuator(pTimer, pDB3A2);
        }
        else
        {
          if (actuatorType == 5)
          {
            i32 oldState, newState, targetState;
            bool oldMatch, newMatch;
            i32 bitmask, messageType;
            oldState = D4W & 0xf;
            bitmask = D5W = sw(1 << pTimer->timerUByte8);
            messageType = pTimer->timerUByte9;
            if (pTimer->timerUByte9 == 2)
            {

//
//
//
              D4W ^= D5W;
            }
            else
            {
              if (pTimer->timerUByte9 != 0)
              {
                D4W &= ~D5W;
              }
              else
              {
                D4W |= D5W;
              };
            };
            pDB3A2->value(D4W);
            D5W = (UI16)(D4W & 15);
            if (D5W == BITS4_7(D4W)) D5W=1; else D5W=0;
            //SET(D5W, D5W == BITS4_7(D4W));
            //D5W &= 1; // Set if actuator is 'pressed'
            sPressed = D5W?"Pressed":"Released";
            if (pDB3A2->normallyClosed()) D5W ^= 1;
            D5W &= 1;
            sClosed = D5W?"Closed":"Open";
            if (TimerTraceActive)
            fprintf(GETFILE(TraceFile), "Actuator5 %03x %02x(%02x,%02x) "
                               "new value = %03x, %s, %s\n",
                       objAtTarget.idx(),
                       d.LoadedLevel, targetX, targetY,
                       pDB3A2->value(), sPressed, sClosed);
            newState = pDB3A2->value() & 0xf;
            targetState = (pDB3A2->value() >> 4) & 0xf;
            oldMatch = oldState == targetState;
            newMatch = newState == targetState;
            if (ExtendedFeaturesVersion != '@')
            {
              switch (pDB3A2->andOrEdge())
              {
              case 0: //Always
                break; 
              case 1: //only if input changes
                if (oldState == newState) continue;
                break;
              case 2: //Only if output changes
                if (oldMatch == newMatch) continue;
                break;
              case 3: //Only if input change and not output change
                if (oldState == newState) continue;
                if (oldMatch != newMatch) continue;
                break;
              case 4: //Only if SET message
                if (messageType != 0) continue;
                break;
              case 5: //Only if CLEAR message
                if (messageType != 1) continue;
                break;
              case 6: //Only if bit is set by message
                if (oldState == newState) continue;
                if (oldState & bitmask) continue;
                break;
              case 7: //Only if bit is cleared by message
                if (oldState == newState) continue;
                if (newState & bitmask) continue;
                break;
              };
            };
            if (pDB3A2->action() == actuatorAction_CONSTPRESSURE)
            {
              QueueSwitchAction(pDB3A2,
                                D5W ^ 1,
                                targetX,
                                targetY,
                                objAtTarget.pos());
                                //targetPos);
            }
            else
            {
              if (D5W != 0)
              {
                QueueSwitchAction(pDB3A2,
                                  pDB3A2->action(),
                                  targetX,
                                  targetY,
                                  objAtTarget.pos());
                                  //targetPos);
              };
            };
          }
          else
          {

            if (   (   ((actuatorType>=7) && (actuatorType<=10))
                    || (actuatorType==14)
                    || (actuatorType==15) )
                && (objAtTarget.pos() == pTimer->timerUByte8) )
            {
              if (TimerTraceActive)
              fprintf(GETFILE(TraceFile), "Actuator5 %03x (%02x,%02x) ",
                         objAtTarget.idx(),
                         targetX, targetY);
              TriggerMissileLauncher(pDB3A2, pTimer);
              if (pDB3A2->onceOnly()) pDB3A2->actuatorType(0);
            }
            else
            {
              if (actuatorType == 18)
              {
                d.CanRestartFromSavegame = 0;
                d.Word11694 = 1;
                VBLDelay(_2_,300); //about 5 seconds
                ShowCredits(_1_,1); // We won the game with 4 corbums in FulYa pit.
              };
            };
          };
        };
      };
    };
    

  };
  RotateActuatorList();
  RETURN;
}

//*********************************************************
//
//*********************************************************
//   TAG0110a6
void ProcessTT_TELEPORTER(TIMER *pTimer)
{//(void)
  CELLFLAG *pcf;
  i32 mapX, mapY;
  mapY = pTimer->timerUByte7;
  mapX = pTimer->timerUByte6;
  pcf = &d.LevelCellFlags[mapX][mapY];
  if (pTimer->timerUByte9 == tmrAct_TOGGLE)
  {
    if (*pcf & 0x08) pTimer->timerUByte9 = tmrAct_CLEAR;
    else pTimer->timerUByte9 = tmrAct_SET;
  };
  if (pTimer->timerUByte9 == tmrAct_SET)
  {
    *pcf |= 8;
    WiggleEverything(mapX, mapY);//So they will fall through
                                 //new pit (or whatever)
  }
  else
  {
    *pcf &= 0xfff7;
  };
}

//*********************************************************
// This is done after activating a pit, for example.
// Everthing needs to be wiggled (moved 0 spaces) so that
// they will fall through the newly activated pit,
// teleporter, etc.
//*********************************************************
//   TAG010e98
void WiggleEverything(i32 mapX, i32 mapY)
{//(void)
  dReg D5;
  DB14  *pDB14A3;
  TIMER *ptA2;
  RN  objD4;
  i16 w_4;
  RN  obj_2;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
//
  if (   (d.LoadedLevel == d.partyLevel)
      && (mapX == d.partyX)
      && (mapY == d.partyY) )
  {
    MoveObject(RN(RNnul), mapX, mapY, mapX, mapY, NULL, NULL); //move party
    DrawEightHands();
  };
  objD4 = FindFirstMonster(mapX, mapY);
  if (objD4 != RNeof)
  {
    MoveObject(objD4, mapX, mapY, mapX, mapY, NULL, NULL);
  };
  objD4 = FindFirstDrawableObject(mapX, mapY);
  obj_2 = objD4;
  w_4 = 0;
  while ( objD4 != RNeof)
  {
    if (objD4.dbType() > dbMONSTER)
    {
      w_4++; // Count objects > type 4
    };
    objD4 = GetDBRecordLink(objD4);
  };
  for (objD4 = obj_2;
       ( (objD4!=RNeof) && (w_4!=0) );
       objD4 = obj_2)
  {
    w_4--;
    obj_2 = GetDBRecordLink(objD4);
    D5W = sw(objD4.dbType());
    if (D5W > dbMONSTER)
    {
      MoveObject(objD4, mapX, mapY, mapX, mapY, NULL, NULL);
    };
    if (D5W == dbMISSILE)
    {
      pDB14A3 = GetRecordAddressDB14(objD4);
      ptA2 = d.pTimer(pDB14A3->timerIndex());
      SETWBITS0_4(ptA2->timerWord8(), d.NewX);
      SETWBITS5_9(ptA2->timerWord8(), d.NewY);
      SETWBITS10_11(ptA2->timerWord8(), d.NewDir);
      ptA2->timerObj6() = objD4;
      ptA2->timerObj6().pos(d.NewPos);
      ptA2->timerTime = (ptA2->timerTime & 0xffffff) | (d.NewLevel << 24);
    }
    else
    {
      if (D5W != dbCLOUD) continue;
      D5W = 0;
      ptA2 = d.pTimer(0);
      for (D5W = 0;
           D5W < d.MaxTimer();
           D5W++, ptA2++)
      {  // Search entire array???? A bit ugly!
        if (    (ptA2->Function() != TT_25)
             && (ptA2->Function() != TT_24) )continue;
        if (objD4 != ptA2->timerObj8()) continue;
        ptA2->timerUByte6 = (ui8)d.NewX;
        ptA2->timerUByte7 = (ui8)d.NewY;
        ptA2->timerObj8() = objD4;
        ptA2->timerObj8().pos(d.NewPos);
        SETIBITS24_35(ptA2->timerTime, d.NewLevel);
//
//
      }; // for (D5W = each timer)
    };
    // continue
//
  }; // for (objD4)
}


//*********************************************************
//
//*********************************************************
//   TAG01111c
void ProcessTT_PITROOM(TIMER *pTimer)
{//(void)
  dReg D0, D6, D7;
  CELLFLAG *pcfA2;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D6W = pTimer->timerUByte7;
  D7W = pTimer->timerUByte6;
  pcfA2 = &d.LevelCellFlags[D7W][D6W];
  if (pTimer->timerUByte9 == 2)
  {
    if (*pcfA2 & 8)
    {
      D0W = 1;
    }
    else
    {
      D0W = 0;
    };
    pTimer->timerUByte9 = D0B;
  };
  D0W = pTimer->timerUByte9;
  if (D0W == 0)
  {
    bool wasClosed = ((*pcfA2) & 0x8) == 0;
    *pcfA2 |= 0x08;
    if (wasClosed) WiggleEverything(D7W, D6W);//So they will fall through
                                 //new pit (or whatever)
  }
  else
  {
    *pcfA2 &= 0xfff7; // clear 0x08
  };
}

//*********************************************************
//
//*********************************************************
//   TAG011192
void ProcessTimer60and61(TIMER *pTimer)
{//(void)
  //Appears to be a pending monster generator.  A monster
  //was generated where a monster already existed.  Try
  //again later.  Or a monster teleported to such a place.
  dReg D6, D7;
  DB4 *pDB4A2;
  i16 w_2;
  RN object;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  w_2 = 0;
  D7W = pTimer->timerUByte6; // X
  D6W = pTimer->timerUByte7; // Y
  do
  {
//
//

    if (   (   (d.partyLevel != d.LoadedLevel)
              || (d.partyX     != D7W)
              || (d.partyY     != D6W) )
          && (FindFirstMonster(D7W, D6W) == RNeof) )
    { // Different position from party and no monsters here.
      if (pTimer->Function() == TT_61)
      {
        QueueSound(17, D7W, D6W, 1);
      };
      MoveObject(pTimer->timerObj8(), -1, 0, D7W, D6W, NULL, NULL);
      return;
    };
  // Same level, x, and y as party   -OR-  another monster is already there
    if (w_2 != 0) break;

    w_2 = 1;
    pDB4A2 = GetRecordAddressDB4(pTimer->timerObj8());
    if (pDB4A2->monsterType() != mon_LordChaos) break;

    if (STRandom0_3() != 0) break; // 25 percent chance of movement.

    switch (STRandom0_3()) // Which direction to move?
    {
    case 0:
        D7W--;
        break;

    case 1:
        D7W++;
        break;

    case 2:
        D6W--;
        break;

    case 3:
        D6W++;
        break;
    }; //End switch
  } while (OpenTeleporterPitOrDoor(D7W, D6W));





  pTimer->timerTime += 5;
  SetTimer(pTimer);
}

//*********************************************************
//
//*********************************************************
//   TAG011292
void EnableCharacterAction(i32 chIdx)
{
  dReg D0, D5, D6;
  CHARDESC *pChar;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  if (chIdx > d.NumCharacter) die(0x65cc);
  pChar = &d.CH16482[chIdx];
  pChar->busyTimer = -1;
  pChar->word48 &= 0xfff7;
  if (pChar->attackType != -1) //Attack type in progress
  {
    //pChar->word64
    //     = sw(pChar->word64 - d.Byte20090[(UI8)(pChar->attackType)]);
    // See definition of word64 for explanation.
    pChar->word64 = 0;
  };
  if (pChar->HP() != 0)
  {
    if (   (pChar->attackType==atk_SHOOT)
        && (pChar->possessions[0]==RNnul))
    {
      D6W = 12;
      D0W = TAG0153c2(chIdx, 1, D6W);
      if (D0W != 0)
      {
tag01130c:
        PlaceObjectOnCharacter(chIdx, RemovePossession(chIdx, D6W), 0);
      }
      else
      {
        for (D5W=0; D5W<3; D5W++)
        {
          D6W = sw(D5W+7);
          D0W = TAG0153c2(chIdx, 1, D6W);
          if (D0W != 0) goto tag01130c;
//
        };
      };
    };
    pChar->word48 |= 0x8000;
    DrawCharacterState(chIdx);
  };
  pChar->attackType = -1;
}

//*********************************************************
//
//*********************************************************
void TAG011366(i16 chIdx)
{ // Called when timer function 12 event occurs.
  CHARDESC *pcA3;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  pcA3 = &d.CH16482[chIdx];
  pcA3->timerIndex = -1;
  if (pcA3->HP() == 0) return;
  if (chIdx+1 == d.SelectedCharacterOrdinal)
  {
    STHideCursor();
    CharacterPortraitToStatusBox(chIdx);
    STShowCursor();
  }
  else
  {
    pcA3->word48 |= 0x80;
    DrawCharacterState(chIdx);
  };
}

//*********************************************************
//
//*********************************************************
//   TAG0113c4
void ProcessTT_13(TIMER *pTimer)
{ //(void)
  //I got here when I put bones in altar of VI.
  dReg D5, D6, D7;
  RN objD4;
  DB10 *DB10A2;
  i16 w_6;
  i16 w_4;
  OBJECTTYPE ot_2;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D7W = pTimer->timerUByte6;
  D6W = pTimer->timerUByte7;
  D5W = pTimer->timerUByte8;
  w_6 = pTimer->timerUByte5;
  w_4 = pTimer->timerUByte9;
  switch (pTimer->timerUByte9)
  {
  case 2:
      CreateCloud(RN(RNffe4), 0, D7W, D6W, D5W, NULL);
      pTimer->timerTime += 5;
tag011414:
      w_4--;
      pTimer->timerUByte9 = (ui8)w_4;
      SetTimer(pTimer);
      break;

  case 1:
      for (objD4 = FindFirstObject(D7W, D6W);
           objD4 != RNeof;
           objD4 = GetDBRecordLink(objD4) )
      {

        if (objD4.pos() != D5UW) continue;
        if (objD4.dbType() != dbMISC) continue;
        ot_2 = GetModifiedObjectType(objD4);
        if (ot_2 != obj_Bones_a) continue;
        DB10A2 = GetRecordAddressDB10(objD4);
        if (BITS14_15(DB10A2->word2) != w_6) continue;
        RemoveObjectFromRoom(objD4, D7W, D6W, NULL);
        //DB10A2->link(RNnul); // Free the database entry.
        DeleteDBEntry(DB10A2);
        pTimer->timerTime++;
        goto tag011414;
//
      };
      break;
  case 0:

      BringCharacterToLife(pTimer->timerUByte5);
      break;
  }; //case
}

//*********************************************************
//
//*********************************************************
//   TAG0114d0
void SetWatchdogTimer(void)
{
  TIMER LOCALa;
  //i32 LOCAL_10;
  //i8  LOCAL_6[6];
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  LOCALa.Function(TT_53);
  LOCALa.timerTime = d.WatchdogTime = d.Time + 300; // about 50 seconds
  SetTimer(&LOCALa); //TAG00ffbe
}

//*********************************************************
//
//*********************************************************
//   TAG0114fa
void ProcessLightLevelTimer(TIMER *pTimer)
{
  TIMER timer;
  i32 oldNumberOfSteps;
  i32 newNumberOfSteps;
  i32 changeInBrightness;
  bool negativeChange;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  oldNumberOfSteps = pTimer->timerWord6();
  if (oldNumberOfSteps == 0) return;
  negativeChange = oldNumberOfSteps < 0;
  if (negativeChange)
  {
    oldNumberOfSteps = -oldNumberOfSteps;
  };
  newNumberOfSteps = oldNumberOfSteps - 1;
  ASSERT(oldNumberOfSteps < 16,"steps");
  changeInBrightness = d.Word1074[oldNumberOfSteps] 
                           - d.Word1074[newNumberOfSteps];
  if (negativeChange)
  {
    changeInBrightness = sw(-changeInBrightness);
    newNumberOfSteps = sw(-newNumberOfSteps);
  };
  d.Brightness = sw(d.Brightness + changeInBrightness);
  if (newNumberOfSteps != 0)
  {
    timer.Function(TT_LIGHT);
    timer.timerWord6() = (i16)newNumberOfSteps;
    timer.timerTime = (d.Time+4) | (d.partyLevel << 24);;
    timer.timerUByte5 = 0;
    SetTimer(&timer);
  };
}

