#include "stdafx.h"

#include "UI.h"

#include <stdio.h>

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

void info(char *, unsigned int);

//*********************************************************
//
//*********************************************************
//  TAG0091e6
i32 GetObjectWeight(RN item)
{ // Note...recursive for chests.
  RN objD7;
  i32 totalWeight = 0;
  i32 index;
  if (item == RNnul) return totalWeight;
  switch (item.dbType())
  {
  case dbWEAPON:
    {
      DB5 *pDB5 = GetRecordAddressDB5(item);
      index = pDB5->weaponType();
      totalWeight = d.weapons[index].weight;
      break;
    };
  case dbCLOTHING:
    {
      DB6 *pDB6 = GetRecordAddressDB6(item);
      index = pDB6->clothingType();
      totalWeight = d.ClothingDesc[index].Weight();
      break;
    };
  case dbMISC:
    {
      DB10 *pDB10 = GetRecordAddressDB10(item);
      index = pDB10->word2 & 0x7f;
      totalWeight = ub(d.Byte8946[index]);
      if ((pDB10->word2&0x7f) == 1)
      {
        totalWeight += 2 * BITS14_15(pDB10->word2);
      };
      break;
    };
  case dbCHEST: 
    {
      totalWeight = 50; // weight of chest itself
      DB9 *pDB9 = GetRecordAddressDB9(item);
      RN objContents = pDB9->contents();
      while (objContents != RNeof)
      {
        totalWeight += GetObjectWeight(objContents);
        objContents = GetDBRecordLink(objContents);

      };
      break;
    };
  case dbPOTION:
    {
      DB8 *pDB8 = GetRecordAddressDB8(item);
      if (pDB8->potionType() == potion_EmptyFlask)
      {
        totalWeight = 1;
      }
      else
      {
        totalWeight = 3;
      };
      break;
    };
  case dbSCROLL:
      totalWeight = 1;
      break;
  };
  return totalWeight;
}

//*********************************************************
//
//*********************************************************
// TAG00a16e
RN ConstructPredefinedMissile(ui16 missileType)
{
  WEAPONTYPE wpnD6 = weapon_Rock; //To keep the compiler quiet.
  MISCTYPE miscD6 = misc_GorCoin; //To keep the compiler quiet
  RN RND4;
  DBTYPE dbTypeD5;
  DB5 *wpnA3;
  DB10 *miscA3;
  //D7W = P1;
  //dbTypeD5 = dbWEAPON;
  if ( (missileType >= 4) && (missileType <= 7)) missileType = 4; 
  switch (missileType)
  {
  case 54:
    wpnD6 = weapon_Rock;
    dbTypeD5 = dbWEAPON;
    break;
  case 128:
    miscD6 = misc_Boulder;
    dbTypeD5 = dbMISC;
    break;
  case 51:
    wpnD6 = weapon_Arrow;
    dbTypeD5 = dbWEAPON;
    break;
  case 52:
    wpnD6 = weapon_Slayer;
    dbTypeD5 = dbWEAPON;
    break;
  case 55:
    wpnD6 = weapon_PoisonDart;
    dbTypeD5 = dbWEAPON;
    break;
  case 56:
    wpnD6 = weapon_ThrowingStar;
    dbTypeD5 = dbWEAPON;
    break;
  case 32:
    wpnD6 = weapon_Dagger;
    dbTypeD5 = dbWEAPON;
    break;
  case 4:
    wpnD6 = weapon_Torch;
    dbTypeD5 = dbWEAPON;
    break;
  default:
    return RNnul;
  }; //switch (P1);
  RND4 = db.FindEmptyDBEntry(dbTypeD5, false);
  if (RND4 == RNnul) return RNnul;
  switch (dbTypeD5)
  {
    //The Atari code took advantage of the fact that the WEAPIN and MISC
    //records happen to look similar.  I will split things up.
  case dbWEAPON:
    wpnA3 = GetRecordAddressDB5(RND4);
    wpnA3->weaponType(wpnD6);
    if (missileType == 4)  // A torch
    {
      if (wpnA3->litTorch() != 0)
      {
        wpnA3->charges(15);
      };
    };
    break;
  case dbMISC:
    miscA3 = GetRecordAddressDB10(RND4);
    miscA3->miscType(miscD6);
    break;
  default:
    SystemError((UI16)0xa222);
  }; //switch (dbTypeD5)
  return RND4;
}

//*********************************************************
//
//*********************************************************
//   TAG012a74
void CreateTimer(TIMERTYPE function,
                 i32 b6,
                 i32 b7,
                 i32 b8,
                 i32 b9,
                 i32 time)
{
  TIMER timer;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  timer.timerTime = (d.LoadedLevel << 24) | time;
  timer.Function(function);
  timer.timerUByte5 = 0;
  timer.timerUByte6 = (ui8)b6;
  timer.timerUByte7 = (ui8)b7;
  timer.timerUByte8 = (ui8)b8;
  timer.timerUByte9 = (ui8)b9;
  SetTimer(&timer);
}

//*********************************************************
//
//*********************************************************
//   TAG012ac0
void SkillIncreaser(i32 skillNumber, i32 adjustment, i32 P3)
{//(void)
  dReg D7;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  if (P3 != 0)
  { // apply adjustment to party member touching actuator
    if (d.HandChar != -1)
    {
      AdjustSkills(d.HandChar, skillNumber, adjustment);
    };
    return;
  };
  // (P3 == 0) //divide adjustment among entire party
  adjustment = sw(adjustment / d.NumCharacter) ;
  for (D7W=0; D7W<d.NumCharacter; D7W++)
  {
    if (d.CH16482[D7W].HP() != 0)
    {
      AdjustSkills(D7W, skillNumber, adjustment);
    };
//
//
  };
}

//*********************************************************
//
//*********************************************************
//   TAG012b3c
void PerformLocalAction(i16 localActionType, 
                        i32 mapX, 
                        i32 mapY, 
                        i32 mapPos)
{ //(void)
  //Actuator with bit 11 set in word4
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  if (localActionType == 10)
  {
    SkillIncreaser(8, 300, mapPos == -1 ? 0 : 1);
  }
  else
  {
    d.DelayedActuatorAction = localActionType;
    d.DelayedActuatorActionX = sw(mapX);
    d.DelayedActuatorActionY = sw(mapY);
    d.DelayedActuatorActionPos = sw(mapPos);
  };
}

//*********************************************************
//
//*********************************************************
// TAG012d80
RN FindObjectOfTypeAtPosition(i32 mapX,
                              i32 mapY,
                              ui16 pos,
                              OBJECTTYPE objectType)
{
  OBJECTTYPE otD0, otD6;
  RN objD5;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  
  otD6 = objectType;
  objD5 = FindFirstDrawableObject(mapX, mapY);
  while (objD5 != RNeof)
  {
    otD0 = GetBasicObjectType(objD5);
    if (otD0 == otD6)
    {
      if ((pos==-1) || (objD5.pos() == pos)) return objD5;
    };
      objD5 = GetDBRecordLink(objD5);
  };
  return RN(RNnul);
}

//*********************************************************
//
//*********************************************************
//  TAG012eac
i16 TouchWall(i32 mapX,i32 mapY,i32 touchedPosition)
{//(i16)
  dReg D0, D1, D4, D6;
  RN objD0, objD5;
  OBJECTTYPE otD0;
  DBCOMMON *dbA3;
  DB3 *DB3A2;
  RN currentListObject;
  OBJECTTYPE ot_38;
  DBCOMMON *pdb_28;
  RN  firstListObject;
  RN  previousListObject;
  RN  obj_20;
  RN  objectInHand;
  i16 remainingActuators[4];
  i16 LOCAL_8;
  i16 actuatorValue;
  i16 actuatorType;
  i16 actuatorPos;
  ACTUATORACTION actuatorAction;
  bool characterAdded;
  bool activePortrait;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D4L = 0xbaddadee;
  LOCAL_8 = 0;
  ASSERT(RememberToPutObjectInHand == -1);
  objectInHand = d.objectInHand;
  for (actuatorPos=0; actuatorPos<4; actuatorPos++)
  {
    remainingActuators[actuatorPos] = 0;

  };
  currentListObject = FindFirstObject(mapX, mapY);
  firstListObject = currentListObject;
  while (currentListObject != RNeof)
  {
    D6W = sw(currentListObject.dbType());
    if (D6W == dbACTUATOR)
    {
      D0W = currentListObject.pos();
      remainingActuators[D0W]++;
    }
    else
    {
      if (D6W >= dbMONSTER)
      {
        break;
      };
    };
    currentListObject = GetDBRecordLink(currentListObject);

  };
  characterAdded = false; //No Champion Portraits yet encounterd.
  for ( currentListObject=previousListObject=firstListObject;
        (currentListObject != RNeof);
        previousListObject=currentListObject, 
             currentListObject=GetDBRecordLink(currentListObject))
  {
    D6W = sw(currentListObject.dbType());
    if (D6W == dbACTUATOR) // Actuator
    {
      actuatorPos = currentListObject.pos();
      remainingActuators[actuatorPos]--;
      DB3A2 = GetRecordAddressDB3(currentListObject);
      actuatorType = DB3A2->actuatorType();
      if (actuatorType == 0) continue;
      if (d.HandChar == -1)//owner of cursor
      {
        if (actuatorType != 127) continue;
      };
      if (actuatorPos != touchedPosition) continue;
      actuatorValue = DB3A2->value();
      actuatorAction = DB3A2->action();
      switch (actuatorType)
      {
      case 1:
          D4W = 0;
          if (DB3A2->action() == actuatorAction_CONSTPRESSURE)
                                     continue;
          break;
      case 2:
          D4W = d.EmptyHanded;
          D1W = DB3A2->normallyClosed();
          D4W = (D1W!=D4W);
          break;
      case 17:
      case 11:
          D0W = remainingActuators[actuatorPos];
          if (D0W != 0) continue;
          // Note fall-through
      case 3:
      case 4:
        //Fallthrough for types 11 and 17
          ot_38 = GetBasicObjectType(objectInHand);
          D0W = (actuatorValue==ot_38);
          D1W = DB3A2->normallyClosed();
          D4W = (D1W==D0W);
            
          if ( (D4W==0) && (actuatorType==17) )
          {
            if (previousListObject == currentListObject) break;
            pdb_28 = GetCommonAddress(previousListObject);
            pdb_28->link(DB3A2->link());
            //DB3A2->link(RNnul);
            DeleteDBEntry(DB3A2);
            currentListObject = previousListObject;
          };
          if ( (D4W==0) && (actuatorType==11) )
          {
            PerformLocalAction(2, mapX, mapY, actuatorPos);
          };
          break;
      case 12:
          D0W = remainingActuators[actuatorPos];
          if (D0W != 0) continue;
          D4W = (d.EmptyHanded==0);
          if (D4W == 0)
          {
            PerformLocalAction(2, mapX, mapY, actuatorPos);
          };
          break;    
      case 13: // A torch holder was type 13.
          if (d.EmptyHanded)
          {
            objectInHand = FindObjectOfTypeAtPosition(
                               mapX, mapY, actuatorPos, 
                               OBJECTTYPE(actuatorValue));
            if (objectInHand == RN(RNnul)) continue;
            RemoveObjectFromRoom(objectInHand, mapX, mapY);
            TakeObject(objectInHand, 1);
          }
          else
          {
            otD0 = GetBasicObjectType(objectInHand);
            if (otD0 != actuatorValue) continue;
            objD0 = FindObjectOfTypeAtPosition(
                     mapX, mapY, actuatorPos, 
                     OBJECTTYPE(actuatorValue));
            if (objD0 != RNnul) continue;
            RemoveObjectFromHand();
            objectInHand.pos(actuatorPos);
            AddObjectToRoom(
                 objectInHand,
                 RN(RNempty),
                 mapX,
                 mapY);
            objectInHand = RN(RNnul);
          };
          PerformLocalAction(2, mapX, mapY, actuatorPos);
          D4W = 1;
          if (  (actuatorAction!=actuatorAction_CONSTPRESSURE)
              ||(d.EmptyHanded!=0))
          {
            D4W = 0;
          };
          break;   
      case 16:
          D0W = remainingActuators[actuatorPos];
          if (D0W != 0) continue;
          obj_20 = FindFirstDrawableObject(mapX, mapY);
          otD0 = GetBasicObjectType(objectInHand);
          if ((otD0!=actuatorValue) || (obj_20 == RNnul)) continue;
          RemoveObjectFromRoom(obj_20, mapX, mapY);
          RemoveObjectFromHand();
          objectInHand.pos(actuatorPos); //04Nov2002 PRS
          AddObjectToRoom(
                 objectInHand, 
                 RN(RNempty), 
                 mapX, 
                 mapY);
          TakeObject(obj_20, 1);
          D4W = 0;
          break;
      
      case 127:
          if (characterAdded) continue; //Only 1 please!
          activePortrait = 
                (DB3A2->State() & PORTRAIT_InActive) == 0;
          if (!activePortrait) continue;
          AddCharacter(actuatorValue);
          characterAdded = true;
          continue;

      default: continue;
      }; // switch (actuatorType)
      if (actuatorAction == actuatorAction_CONSTPRESSURE)
      {
        if (D4W != 0)
        {
          actuatorAction = actuatorAction_CLEAR;
        }
        else
        {
          actuatorAction = actuatorAction_SET;
        };
        D4W = 0;
      };
      if (D4W == 0)
      {
        LOCAL_8 = 1;
        if (DB3A2->audible() != 0)
        {
          QueueSound(1, d.partyX, d.partyY, 1);
        };
        if (   (d.EmptyHanded==0) 
            && (   (actuatorType==4)
                 ||(actuatorType==11)
                 ||(actuatorType==17)) )
        {
          dbA3 = GetCommonAddress(objectInHand);
          //dbA3->link(RNnul);
          RemoveObjectFromHand();
          //I reversed these two lines so that the object
          //will still exist when we delete it from the
          //player's hand.  So it has the proper weight!!!
          DeleteDBEntry(dbA3);
          objectInHand = RN(RNempty);
        }
        else
        {
          if ( (d.EmptyHanded!=0) && (actuatorType==12) )
          {
            objectInHand = ConstructPredefinedMissile(actuatorValue);
            if (objectInHand != RNnul)
            {
              TakeObject(objectInHand, 1);
            };
          };
        };
        QueueSwitchAction(DB3A2, 
                          actuatorAction, 
                          mapX, mapY, actuatorPos);
      };
      continue;
    };
    if (D6W >= dbMONSTER) break;

    // continue;
  }; // for (D7W != -2)
  RotateActuatorList();
  return (LOCAL_8);
}


//*********************************************************
//
//*********************************************************
// TAG01576a
RN RemoveObjectFromHand(void)
{//(RN)
  dReg D1;
  CHARDESC *pcA0;
  RN objD7;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  d.EmptyHanded = 1;
  ASSERT(RememberToPutObjectInHand == -1);
  objD7 = d.objectInHand;
  if (objD7 == RNnul) return (objD7);
  d.objectInHand = RN(RNnul);
  d.ObjectType16574 = -1;
  STHideCursor();
  ClearHeldObjectName();
  TAG002572();
  STShowCursor();
  if (d.HandChar == -1) return (objD7); //owner of cursor
  D1L = GetObjectWeight(objD7);
  pcA0 = &d.CH16482[d.HandChar];//owner of cursor
  DEBUGLOAD(pcA0, D1W, -1, objD7);
  pcA0->load = sw(pcA0->load - D1W);
  ASSERT(pcA0->load < MAXLOAD);
  pcA0->word48 |= 0x200;
  DrawCharacterState(d.HandChar);//owner of cursor
  return (objD7);
}


//*********************************************************
//
//*********************************************************
i16 TAG016382(RN object)
{
  dReg D6, D7;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D7W = sw(GetObjectWeight(object) / 2);
  D6W = ApplyLimits(1, D7W, 10);
  while ((D7W -= 10) > 0)
  {
    D6W = sw(D7W / 2);
//
  };
  return D6W;
}

//*********************************************************
//
//*********************************************************
//  TAG017570
i16 ThrowByCharacter(i32 chIdx,i16 hand,i16 LeftOrRight,
                     bool record, i32 x, i32 y, i32 f)
{//(i16)
  dReg D5, D6, D7;
  WEAPONDESC *weaponDescA2;
  CHARDESC   *pcA3=NULL;
  RN objD4;
  i16 w_4;
  RN  obj_2;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  w_4 = 0;
  if (hand < 0)
  {
    if (d.EmptyHanded != 0) return 0;
    objD4 = RemoveObjectFromHand();
    pcA3 = &d.CH16482[chIdx];
    obj_2 = pcA3->possessions[1]; // Save contents of fighting hand
    pcA3->possessions[1] = objD4; //put obect into fighting hand.
    hand = 1;
    w_4 = 1;
  };
  if (w_4 != 0)
  {
    if (record)
    {
      RecordFile.Record(x, y, f);
    };
    D7W = (i16)DetermineThrowingDistance(chIdx, hand);
    pcA3->possessions[hand] = obj_2; 
  }
  else
  {
    if (d.CH16482[chIdx].possessions[hand] != RNnul)
    {
      if (record)
      {
        RecordFile.Record(x, y, f);
      };
    };
    D7W = (i16)DetermineThrowingDistance(chIdx, hand);
    objD4 = RemovePossession(chIdx, hand);
    if (objD4 == RNnul) return 0;
  };
  QueueSound(16, d.partyX, d.partyY, 1);
  AdjustStamina(chIdx, TAG016382(objD4));
  DisableCharacterAction(chIdx, 4);
  D6W = 8;
  D5W = 1;
  //D0W = ;
  if (objD4.dbType() == dbWEAPON)
  {
    D6W += 4;
    weaponDescA2 = TAG0099d2(objD4);
    if (weaponDescA2->uByte1 <= 12)
    {
      D5W = weaponDescA2->uByte3;
      D6W = sw(D6W + D5W/4);
    };
  };
  AdjustSkills(chIdx, 10, D6W);
  D7W = sw(D7W + D5W);
  D5W = (i16)DetermineMastery(chIdx, 10); // Skill #10
  D7W = sw(D7W + STRandom(16) + D7W/2 + D5W);
  D6W = ApplyLimits(40, 8*D5W + (STRandom()&31), 200);
  D5W = sw(Larger(5, 11 - D5W));
  LaunchMissile(
                objD4,
                d.partyX,
                d.partyY,
                (d.partyFacing+LeftOrRight)&3,
                d.partyFacing,
                D7W,
                D6W,
                D5W);
  d.Word11712 = 4;
  d.Word11714 = d.partyFacing;
  DrawCharacterState(chIdx);
  return (1);
}

//*********************************************************
//
//*********************************************************
//  TAG01772e
i16 ThrowObjectInHand(i16 LeftOrRight,bool record, i32 x, i32 y, i32 f)
{//(i16)
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  if (d.HandChar == -1) return 0;//owner of cursor
  return ThrowByCharacter(d.HandChar, -1, LeftOrRight,record,x,y,f); //owner of cursor
}

//*********************************************************
//
//*********************************************************
//   TAG01a0c2
void TouchWallF1(void)
{//(void)
  i32 touchedX;
  i32 touchedY;
  touchedX = d.partyX;
  touchedY = d.partyY;
  touchedX += d.DeltaX[d.partyFacing];
  touchedY += d.DeltaY[d.partyFacing];
  if (touchedX < 0) return;
  if (touchedX >= d.width) return;
  if (touchedY < 0) return; 
  if (touchedY >= d.height) return;
  d.clockTick = TouchWall(touchedX, touchedY, (d.partyFacing+2)&3);
}

//*********************************************************
//
//*********************************************************
void TAG01a148(i16 P1)
{//(void)
  dReg D0, D1, D5, D6, D7;
  OBJECTTYPE otD0;
  RN objD4;
  bool levitated;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D7W = P1;
  if (d.HandChar == -1) return;//owner of cursor
  D6W = d.partyX;
  D5W = d.partyY;
  if (D7W >= 2) // If next square ahead of party
  {
    D1W = d.DeltaX[d.partyFacing];
    D6W = sw(D6W + D1W);
    D1W = d.DeltaY[d.partyFacing];  
    D5W = sw(D5W + D1W);
    objD4 = FindFirstMonster(D6W, D5W);
    if (objD4 != RNeof)
    {
      levitated = objD4.Levitating(); // /*, 4*/);
      if (!levitated)
      {
        D0W = OrdinalOfMonsterAtPosition(GetRecordAddressDB4(objD4),
                         (d.partyFacing+D7W)&3);
        if (D0W != 0) return;
      };
    };
  };
  objD4 = d.rn10574[D7W];
  otD0 = GetModifiedObjectType(objD4);
  if (otD0 != -1)
  {
    MoveObject(objD4, D6W, D5W, -1, 0);
    d.Word16580 = 1;
    TakeObject(objD4, 1);
  };
  d.clockTick = 1;
}

//*********************************************************
//
//*********************************************************
//   TAG01a228
void DropObject(i16 pos)
{//(void)
  // 0 = this room left, 1= this room right
  // 2 =                 3 = 
  // 4 = niche
  dReg D0, D1, D5, D6, D7;
  RN objD4;
  DB10     *DB10A3;
  TIMER timer_16;
  bool dropInAlcove;//w_6
  i16 w_4;
  OBJECTTYPE ot_2;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D7W = pos;
  if (d.HandChar == -1) return; //owner of cursor
  D6W = d.partyX;
  D5W = d.partyY;
  dropInAlcove = D7W==4;
  if (dropInAlcove) D7W = 2;
  if (D7W > 1)
  { // In next room.
    D6W = sw(D6W + d.DeltaX[d.partyFacing]);
    D5W = sw(D5W + d.DeltaY[d.partyFacing]);
  };
  w_4 = (I16)((d.partyFacing + D7W) & 3);
  objD4 = RemoveObjectFromHand();
  objD4.pos(w_4);//04Nov2002 prs
  MoveObject(
             objD4,
             -1,
             0,
             D6W,
             D5W);
  if (dropInAlcove != 0)
  {
    if (d.Word10534 != 0)
    {
      ot_2 = GetModifiedObjectType(objD4);
      if (ot_2 == obj_Bones_a) // bones??
      {
        DB10A3 = GetRecordAddressDB10(objD4);
        D0L = d.Time;
        D1L = d.partyLevel;
        D1L <<= 24;
        timer_16.timerTime = D0L | D1L;
        timer_16.Function(TT_13);
        timer_16.timerUByte5 = DB10A3->value();
        timer_16.timerUByte6 = D6B;
        timer_16.timerUByte7 = D5B;
        timer_16.timerUByte8 = (ui8)w_4;
        timer_16.timerUByte9 = 2;
        SetTimer(&timer_16);
      };
    };
  };
  d.clockTick = 1;
}


//*********************************************************
//
//*********************************************************
//  TAG01a34c
i32 TestThrow(i16 clickX, i16 clickY)
{//(i16)
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  if ( (clickY<47) || (clickY>102) )
  {
    return 0;
  };
  if (clickX <= 111)
  {
    if (clickX < 32) return 0;
    if (clickX < 64)
    {
      clickX = 63;
      if (d.CellTypeJustAhead == roomDOORFACING)
      {
        return 0;
      };
    }
    else
    {
      clickX = 111;
    };
    return ThrowObjectInHand(0,true,clickX,47,80);
  };
  if (clickX > 191) return 0;
  if (clickX > 163)
  {
    clickX = 191;
    if (d.CellTypeJustAhead == roomDOORFACING)
    {
      return 0;
    };
  }
  else
  {
    clickX = 163;
  };
  return ThrowObjectInHand(1,true,clickX,47,80);
}


//*********************************************************
//
//*********************************************************
//  TAG01a3d6
i16 TestInRectangle(ui8 *rectangle,i32 clickX, i32 clickY)
{
  ui8 *r = (ui8 *) rectangle;
  if (clickX < r[0]) return 0;
  if (clickX > r[1]) return 0;
  if (clickY < r[2]) return 0;
  if (clickY > r[3]) return 0;
  return 1;
}

//*********************************************************
//
//*********************************************************
//   TAG01a436
void HandleClickInViewport(i32 clickX, i32 clickY)
{//(void)
  dReg D0, D1, D5;
  DBCOMMON *dbA3;
  DB0      *DB0A3;
  CHARDESC *pcA0;
  i16 touchedY;
  i16 touchedX;
  i16 LOCAL_6;
  RN  obj_LOCAL_6;
  OBJECTTYPE ot_4;
  RN  LOCAL_2;
  touchedX = d.partyX;
  touchedY = d.partyY;
  D1W = d.partyFacing;
  D1W = d.DeltaX[D1W];
  touchedX = sw(touchedX + D1W); // Cell touched X
  D1W = d.DeltaY[d.partyFacing];
  touchedY = sw(touchedY + D1W); // Cell touched Y 
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  //D7W = sw(clickX);  //use clickX instead.
  //D6W = sw(clickY);  //use clickY instead.
  if (d.CellTypeJustAhead == roomDOORFACING)
  {
    if (d.HandChar == -1) 
    {
      RecordFile.Record(clickX, clickY, 80);
      return;//owner of cursor
    };
    D0W = d.EmptyHanded;
    if (D0W != 0)
    {
      DB0A3 = GetRecordAddressDB0(FindFirstDoor(touchedX, touchedY));
      D0W = DB0A3->doorSwitch();
      if (D0W != 0)
      {
        D0W = TestInRectangle(d.ViewportObjectButtons[5], 
                         clickX, 
                         clickY-33);
        if (D0W != 0)
        {
          RecordFile.Record((d.ViewportObjectButtons[5][0]+d.ViewportObjectButtons[5][1])/2,
                            (d.ViewportObjectButtons[5][2]+d.ViewportObjectButtons[5][3])/2+33,
                            80);
          d.clockTick = 1;
          QueueSound(1, d.partyX, d.partyY, 1);
          CreateTimer(TT_DOOR,
                      touchedX,
                      touchedY,
                      0,
                      2,
                      d.Time+1);
          return;
        };
      };
    }
    else
    {
      D0L = TestThrow(sw(clickX), sw(clickY));
      if (D0W != 0) return;
    };
  };
  D0W = d.EmptyHanded;
  if (D0W != 0)
  {
    for (D5W=0; D5W<6; D5W++)
    {
      D0W = TestInRectangle(d.ViewportObjectButtons[D5W], 
                             clickX, 
                             clickY-33);
      if (D0W != 0)
      {
        i32 middleX, middleY, btn;
        middleX = (d.ViewportObjectButtons[D5W][0]+d.ViewportObjectButtons[D5W][1])/2;
        middleY = (d.ViewportObjectButtons[D5W][2]+d.ViewportObjectButtons[D5W][3])/2+33;
        for (btn=0; btn<D5W; btn++)
        {
          if (TestInRectangle(d.ViewportObjectButtons[btn],
                              middleX,
                              middleY-33)) break;
        };
        if (btn == D5W)
        {
          RecordFile.Record(middleX, middleY, 80);
        }
        else
        {
          RecordFile.Record(clickX, clickY, 80);
        };
        if (D5W == 5)
        {
          if (d.FacingAlcove == 0)
          {
            TouchWallF1();
          };
        }
        else
        {
          TAG01a148(D5W);
        };
        return;
      };

    };
    {
      if (   (d.FacingAlcove == 0)
          && (d.CellTypeJustAhead == 0))
      {
        CELLFLAG cf;
        ROOMTYPE rt;
        cf = GetCellFlags(touchedX, touchedY);
        rt = (ROOMTYPE)(cf>>5);
        if (   (rt==roomSTONE) 
            || ((rt==roomFALSEWALL) && ((cf & 5) == 0)))
        {
          if (   (clickX >= 33)
              && (clickX <= 189)
              && (clickY >= 44)
              && (clickY <= 150))
          {
            QueueSound(4,d.partyX,d.partyY,0);
          };
        };
      };
    };
    RecordFile.Record(clickX, clickY, 80);
    return;
  };
  ASSERT(RememberToPutObjectInHand == -1);
  LOCAL_2 = d.objectInHand;
  dbA3 = GetCommonAddress(LOCAL_2); // (UNKNOWN *)
  if (d.CellTypeJustAhead == 0)
  {
    for (D5W=0; D5W<2; D5W++)
    {
      D0W = TestInRectangle(d.DropAreas+4*D5W, clickX, clickY);
      if (D0W != 0)
      {
        RecordFile.Record((d.DropAreas[4*D5W+0]+d.DropAreas[4*D5W+1])/2,
                          (d.DropAreas[4*D5W+2]+d.DropAreas[4*D5W+3])/2,
                          80);
        DropObject(D5W); // D5=position
        return;
      };
//
    };
    D0W = TestInRectangle(d.ViewportObjectButtons[5], 
                    clickX, 
                    clickY-33);
    if (D0W != 0)
    {
      RecordFile.Record((d.ViewportObjectButtons[5][0]+d.ViewportObjectButtons[5][1])/2,
                        (d.ViewportObjectButtons[5][2]+d.ViewportObjectButtons[5][3])/2+33,
                        80);
      if (d.FacingAlcove != 0)
      {
        DropObject(4); // 4 = position
      }
      else
      {
        if (d.Word10536 != 0)
        {
          ot_4 = GetModifiedObjectType(LOCAL_2);
          D0W = sw(GetObjectWeight(LOCAL_2));
          LOCAL_6 = D0W;
          obj_LOCAL_6 = LOCAL_2;
          if ( (ot_4 >= obj_Waterskin) && (ot_4 <= obj_Water) )
          {
            dbA3->CastToDB10()->value(3);
          }
          else
          {
            if (ot_4 == obj_EmptyFlask)
            {
              //D3W = 15 << 8;
              //dbA3->word(2) &= 0x80ff; // Bits 8-14
              //dbA3->word(2) |= D3W;
              dbA3->CastToDB8()->potionType(potion_WaterFlask);
            }
            else
            {
              goto tag01a69a;
            };
          };
          DrawEightHands();
          D1W = sw(GetObjectWeight(LOCAL_2));
          D1W = sw(D1W - LOCAL_6);
          pcA0 = &d.CH16482[d.HandChar];//owner of cursor
          DEBUGLOAD(pcA0, LOCAL_6, -1, obj_LOCAL_6);
          DEBUGLOAD(pcA0, GetObjectWeight(LOCAL_2), +1, LOCAL_2);
          pcA0->load = sw(pcA0->load + D1W);
          ASSERT(pcA0->load < MAXLOAD);
        };
tag01a69a:
        TouchWallF1();
      };
      return;
    };
    RecordFile.Record(clickX, clickY, 80);
    return;
  };
  D0L = TestThrow(sw(clickX), sw(clickY));
  if (D0W != 0) return;
  for (D5W=0; D5W<4; D5W++)
  {
    D0W = TestInRectangle(d.DropAreas+4*D5W, clickX, clickY);
    if (D0W != 0)
    {
      RecordFile.Record((d.DropAreas[4*D5W+0]+d.DropAreas[4*D5W+1])/2,
                        (d.DropAreas[4*D5W+2]+d.DropAreas[4*D5W+3])/2,
                        80);
      DropObject(D5W); // D5=position
      return;
    };
//
  };
  RecordFile.Record(clickX, clickY, 80);
}


bool mouseQueueIsEmpty(void)
{
  return ((d.MouseQEnd + 1)%5) == d.MouseQStart;
}

bool GravityMove(MouseQueueEnt *ent)
{
  i32 axis, absdir, reldir;
  i32 i, delta, level, x, y;
  CELLFLAG cfh, cfd;
  ROOMTYPE rth, rtd;
  if (d.Time < nextGravityMoveTime) return false;
  ent->x = 0;
  ent->y = 0;
  for (i=2; i>=0; i--)
  {
    axis = moveHistory[i];
    delta = (moveHistory[3] & (1<<axis))?1:-1;
    level = d.partyLevel;
    x = d.partyX;
    y = d.partyY;
    switch (axis)
    {
    case 0: x += delta; break;
    case 1: y += delta; break;
    case 2: level += delta; break;
    };
    if (x < 0) continue;
    if (y < 0) continue;
    if (level < 0) continue;
    if (level >= d.dungeonDatIndex->NumLevel()) continue;
    if (x > d.pLevelDescriptors[level].LastColumn()) continue;
    if (y > d.pLevelDescriptors[level].LastRow()) continue;
    cfh = *GetCellFlagsAddress(d.partyLevel,d.partyX,d.partyY);
    rth = RoomType(cfh);
    cfd = *GetCellFlagsAddress(level,x,y);
    rtd = RoomType(cfd);
    switch (axis)
    {
    case 0:
    case 1:
      switch (rtd)
      {
      case roomPIT:
      case roomOPEN: break;
      default: continue;
      };
      break;
    case 2:
      switch (delta)
      {
      case +1: if (rth != roomPIT) continue; break;
      case -1: if (rtd != roomPIT) continue; break;
      };
      break;
    };
    // It appears that gravity will cause another move.
    if (FindFirstMonster(level, x, y) != RNeof) continue;
    switch (axis)
    {
    case 0: absdir = (delta==1)?1:3; break;
    case 1: absdir = (delta==1)?2:0; break;
    default: absdir = (delta==1)?5:4; break;
    };
    switch (absdir)
    {
    case 0:
    case 1:
    case 2:
    case 3:
      reldir = (absdir - d.partyFacing) & 3;
      switch (reldir)
      {
      case 0: ent->num = 3; break;
      case 1: ent->num = 4; break;
      case 2: ent->num = 5; break;
      case 3: ent->num = 6; break;
      };
      break;
    case 4:
      ent->num = 0x1125; break;
    case 5:
      ent->num = 0x1124; break;
    };
    return true;
  };
  gravityMove = false;
  return false;
}


//*********************************************************
//
//*********************************************************
//   TAG01a7b2
RESTARTABLE _HandleMouseEvents(const i32 delta)
{//(bool)
  static i32 lastTime=0;
  static bool thawCommand;
  RESTARTMAP
    RESTART(1)
    RESTART(2)
    RESTART(3)
    RESTART(4)
    RESTART(8)
    RESTART(10)
    RESTART(11)
    RESTART(14)
  END_RESTARTMAP
  static dReg D0, D6, D7;
  static i32 clickX, clickY;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  //i32 D6;
  //pnt A3;

  DiskMenuNeeded = false;

  if (delta == -999887)
  {
    //Initialize
    lastTime = 0;
    RETURN_BOOL(false);
  };
  
  d.MouseInterlock = 1;

  if (gravityMove)
  {
    d.MouseQStart = d.MouseQEnd = 0;
    if (!GravityMove(&d.MouseQueue[d.MouseQStart]))
    {
      d.MouseInterlock = 0;
      d.MouseQStart = 1;
      RETURN_BOOL(false);
    };
  };

  if (!gravityMove && PlayFile.IsOpen())
  {
    //STHideCursor();
    d.MouseQStart = d.MouseQEnd = 0;
    if (!PlayFile.Play(&d.MouseQueue[d.MouseQStart])) 
    {
      if (gameFrozen && PlayFile.IsEOF())
      {
        PlayFile.ReadEOF(); //Force close with next Play();
      };
      d.MouseInterlock = 0;
      d.MouseQStart = 1;
      RETURN_BOOL(false);
    };
  };

//  if (d.Time == 0x2b828)
//  {
//    i32 jjj321 = 1; //Reminds us to remove this nonsense
//    RN containedObject;
//    DB9 *pDB9 = GetRecordAddressDB9(RNVAL(0x2402));
//    containedObject = pDB9->contents(); //Gem of Ages
//    DBCOMMON *pDB = GetRecordAddress(containedObject);
//    pDB->link(RNeof);
//  };  
  D0W = d.MouseQEnd;
  D0W++;
  D7W = D0W; // D7 = End+1
  if (D0W > 4) D7W = 0;
  if (D7W == d.MouseQStart)
  { //The queue is empty
    d.MouseInterlock = 0;
    CauseFakeMouseClick();//If one is waiting
    RETURN_BOOL(false);
  };

  // There is something in the mouse queue.
  //
  // If the game is frozen then we discard
  // any mouse events that do not thaw it.
  //
  if (!PlayFile.IsOpen())
  {
    if (gameFrozen)
    {
      if (d.MouseQueue[d.MouseQStart].num != 148) //Restart clock
      {
        d.MouseQStart = sw((d.MouseQStart+1)%5);
        d.MouseInterlock = 0;
        CauseFakeMouseClick(); //if one is waiting
        RETURN_BOOL(false);
      };
    };
  }
  else
  {
    if (gameFrozen && PlayFile.IsEOF())
    {
      PlayFile.ReadEOF(); //Force close with next Play();
    };
  };
  // We limit mouse events to no more than one per 
  // clock tick.  This pretty much ensures that we
  // can record a session and play it back with identical
  // results.  The reasons that this works are a bit
  // complex and not too clear to me.  But it has
  // been very effective as a tool for reproducing
  // errors so that they can be more easily found and fixed.
  // It also makes things just slightly more sluggish
  // because you can only have 6 events per second.
  //
  thawCommand = 
           gameFrozen 
        && (d.MouseQueue[d.MouseQStart].num == 148);

  if (    !PlayFile.IsOpen()
       && (lastTime != 0) 
       && (d.Time < lastTime + delta)
       && (!thawCommand)
       /*&& (RecordFile.IsOpen())*/  ) 
  {
    d.MouseInterlock = 0;
    RETURN_BOOL(false);
  };

  
  

//D6 = d.Word16854 * 6;
//A0 = &d.Word16848 + D6W;
  D6W = d.MouseQueue[d.MouseQStart].num;
  if (  (D6W >= 3)
      &&(D6W <= 6)
      &&(  (d.Word11710!=0)
         ||(  (d.Word11712!=0)
             &&(((d.partyFacing+D6W-3)&3) == d.Word11714)
           )
        )
      )
  {
    if (PlayFile.IsOpen())
        PlayFile.Backspace(&d.MouseQueue[d.MouseQStart]);
    d.MouseInterlock = 0;
    CauseFakeMouseClick();    
    RETURN_BOOL(false);
  };
  
  if (d.MouseQueue[d.MouseQStart].num != 80)
  { // HandleClickInViewport will do its own recording.
    // This is because the active areas in the viewport depend on
    // the game's state and we want to use as few xy coordinates as
    // possible so that the logfile can be compressed nicely.
    RecordFile.Record(&d.MouseQueue[d.MouseQStart]);
  };
  clickX = d.MouseQueue[d.MouseQStart].x;
  clickY = d.MouseQueue[d.MouseQStart].y;
  if (PlayFile.IsOpen())
  {
//    d.CurCursorX = sw(clickX);
//    d.CurCursorY = sw(clickY);
//    d.NewMouseX  = sw(clickX);
//    d.NewMouseY  = sw(clickY);
  };
  d.MouseQStart = sw((d.MouseQStart + 1) % 5);
  //D0W = d.MouseQStart;
  //if (D0W > 4) d.MouseQStart = 0;
  d.MouseInterlock = 0;
  CauseFakeMouseClick();
  if ((D6W == 2) || (D6W == 1)) // Turns
  {
    TurnParty(_1_,D6W);
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (  ((D6W >= 3) && (D6W <= 6)) // Moves
      ||(D6W == 0x1124)    //Down
      ||(D6W == 0x1125))   //Up
  {
    MoveParty(_2_,D6W); // TAG01992e
    lastTime=d.Time;
    RETURN_BOOL(true);
  };

  if ((D6W >= 12) && (D6W <= 15))
  {
    D7W = D0W = sw(D6W - 12);
    if (   ((ui16)D0W<(ui16)d.NumCharacter) 
        && (d.PotentialCharacterOrdinal==0)) 
    {
      TAG019c34(D7W, clickX, clickY);
    };
    lastTime=d.Time;
    RETURN_BOOL(true);
  };

  if ((D6W >=125)&&(D6W<=128))
  {
    if (PlayFile.IsOpen())
    {
//      d.CurCursorX = sw(clickX);
//      d.CurCursorY = sw(clickY);
      d.NewMouseX  = sw(clickX);
      d.NewMouseY  = sw(clickY);
    };
    TAG0025a6(D6W-125);
    lastTime=d.Time;
    RETURN_BOOL(true);
  };

  if ((D6W>=28)&&(D6W<66))
  {
    if (d.HandChar != -1) //owner of cursor
    {
      HandleClothingClick(D6W-20);
    };
    lastTime=d.Time;
    RETURN_BOOL(true);
  };

  if ((D6W>=7) && (D6W<=11))
  {
    D7W = D0W = sw(D6W-7);

    if ((D7W==4) || (D7W<d.NumCharacter))
    {
      if (d.PotentialCharacterOrdinal==0)
      {
        TAG0192f4(D7W);
      };
    };
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 83)
  {
    if (d.HandChar != -1) //owner of cursor
    {
      TAG0192f4(d.HandChar);//owner of cursro
    };
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 100)
  {
    if ((d.PotentialCharacterOrdinal==0)&&(d.MagicCaster!=-1))
    {
      HandleMagicClick(_14_, clickX ,clickY);
    };
    lastTime=d.Time;
    RETURN_BOOL(true);
  };

  if (D6W == 111)
  {
    if (d.PotentialCharacterOrdinal==0)
    {
      TAG019fac(_3_,clickX, clickY);
    };
    lastTime=d.Time;
    RETURN_BOOL(true);
  };

  if (D6W == 70)
  {
    FeedCharacter(_10_);
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 71)
  {
    TAG0191e8(_4_);
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 80) // The Viewport while adventuring
  {
    if (d.ClockRunning != 0) HandleClickInViewport(clickX, clickY);
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 81)
  {
    TAG01a6ea(_11_, clickX, clickY);
    lastTime=d.Time;
    RETURN_BOOL(true);
  };

  if (d.Word11756!=0) {lastTime=d.Time; RETURN_BOOL(true);};
  if (d.Word11760!=0) {lastTime=d.Time; RETURN_BOOL(true);};


  if (D6W == 145)
  {
    if (d.PotentialCharacterOrdinal == 0)
    {
      if (d.SelectedCharacterOrdinal != 0)
      {
        TAG0192f4(4);
      };
      TAG02076e();
      d.PartySleeping = 1;
      DisplaySleepScreen();
      MarkViewportUpdated(2);
      d.Speed = 0;
      d.PrimaryButtonList = d.Buttons17796;
      d.SecondaryButtonList = NULL;
      d.pKeyXlate2 = (KeyXlate *)d.Byte18440;
      d.pKeyXlate1 = NULL;
      TAG01953a();
    };

    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 146)
  {
    WakeUp();
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 140)
  {
    if (   ((ui16)d.NumCharacter > 0) 
        && (d.PotentialCharacterOrdinal == 0))
    {
      //DisplayDiskMenu(_9_);
      //We cannot call 'DisplayDiskMenu' here because
      //it would result in recursive call to ourselves.
      //So we set the flag and return.  Everyone who
      //call us checks the flag and does the call to
      //'DisplayDiskMenu' for us.
      DiskMenuNeeded = true;
    };
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 147)
  {
    d.ClockRunning = 0;
    gameFrozen = true;
    TAG02076e();
    Flood(d.pViewportBMP, 0, 1904); // Flood with color 0
    TextOut_OneLine(d.pViewportBMP, 
                    112, 
                    81, 
                    69, 
                    4, 
                    0, 
                    "GAME FROZEN");
    MarkViewportUpdated(2);
    d.Pointer18982 = d.PrimaryButtonList;
    d.Pointer18986 = d.SecondaryButtonList;
    d.pKeyXlate18974 = d.pKeyXlate2;
    d.pKeyXlate18978 = d.pKeyXlate1;
    d.PrimaryButtonList = d.Buttons17832;
    d.SecondaryButtonList = NULL;
    d.pKeyXlate2 = (KeyXlate *)d.Byte18448;
    d.pKeyXlate1 = NULL;
    TAG01953a();
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 148)
  {
    d.ClockRunning = 1;
    gameFrozen = false;
    TAG0207cc();
    d.PrimaryButtonList = d.Pointer18982;
    d.SecondaryButtonList = d.Pointer18986;
    d.pKeyXlate2 = d.pKeyXlate18974;
    d.pKeyXlate1 = d.pKeyXlate18978;
    TAG01953a();
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 200)
  {
    d.dungeonFormat = 1;
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 201)
  {
    d.dungeonFormat = 0;
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if (D6W == 202)
  {
    TAG01f928(_8_);
    lastTime=d.Time;
    RETURN_BOOL(true);
  };
  if ((D6W >= 210) && (D6W <= 213))
  {
    d.Word11764 = sw(D6W - 209);
      lastTime=d.Time;
      RETURN_BOOL(true);
  };
  if (D6W == 215) d.PartyHasDied = 1;
  if (D6W == -1) // Changing marching order.
                 // Cursor moved outside of rectangle.
  {
    d.NewMouseX = d.NewMouseY = 0;
  };
  lastTime=d.Time;
  RETURN_BOOL(true);
}

//*********************************************************
//
//*********************************************************
//   TAG003026
void STShowCursor(void)
{
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  d.Word23118--; // Could this be a "hide cursor" count???
  if (d.Word23118==0) 
  {
    d.Word23134 = 1;
    d.Word23130 = 1;
  };
}


//*********************************************************
//
//*********************************************************
//   TAG002fd2
void STHideCursor(void)
{
  dReg D0;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D0W = d.Word23118;
  d.Word23118++; // Hide cursor counter???
  if (D0W == 0)
  {
    d.Word23138 = 1; // some sort of cursor flag
    d.Word23134 = 0; // Mouse something
    d.Word23130 = 0;
    d.Word23132 = 0;
    if (d.Word23136 != 0) // If cursor on screen
    {
      d.Word23136 = 0;
      RemoveCursor(); //TAG00289a();
    };
    d.Word23138 = 0;
  };
  //if (d.Word19218 != 12777)
  //{
  //  D0W = TAG0194c0(&d.Byte11678);
  //};
}
