#include "stdafx.h"

#include "UI.h"

#include <stdio.h>

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

static int testdelay = 500;

//  TAG00ac9e
i16 DetermineAttackOrdinal(
              i32 attackX,
              i32 attackY,
              i32 partyX,
              i32 partyY,
              i32 charPos)
{
  dReg D6;
  RN  objD5;
  DB4 *DB4A3;
  ui8  attackOrder[4];
  i32 i, pos;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  objD5 = FindFirstMonster(attackX, attackY);
  if (objD5 == RNeof)
  {
    return 0;
  };
  DB4A3 = GetRecordAddressDB4(objD5);
  DetermineAttackOrder(attackOrder,
                       attackX, attackY,
                       partyX, partyY, charPos);
  for (i=0;;i++)
  {
    pos = attackOrder[i];

    D6W = sw(OrdinalOfMonsterAtPosition(DB4A3, pos));
    if (D6W != 0) return D6W;
  };
}

//*********************************************************
//
//*********************************************************
//  TAG00f058
bool IsCellFluxcage(i32 mapX, i32 mapY)
{
  dReg  D0;
  ROOMTYPE rtD6;
  RN  objD7;
  DB15  *DB15A0;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  rtD6 = (ROOMTYPE)(GetCellFlags(mapX, mapY) >> 5);//room type
  if ((rtD6==roomSTONE) || (rtD6==roomSTAIRS))
  {
    return false;
  };
  for (objD7 = FindFirstObject(mapX, mapY);
       objD7 != RNeof;
       objD7 = GetDBRecordLink(objD7) )
  {
    if (objD7.dbType() != dbCLOUD) continue;
    DB15A0 = GetRecordAddressDB15(objD7);
    D0W = sw(DB15A0->type());
    if (D0W == 50) return true;
//
//
  };
  return false;
}

//*********************************************************
//
//*********************************************************
// TAG00f0e8
RN IsLordChaosHere(i32 mapX, i32 mapY)
{
  RN objD7;
  DB4 *DB4A3;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  objD7 = FindFirstMonster(mapX, mapY);
  if (objD7 == RNeof) return RNempty;
  DB4A3 = GetRecordAddressDB4(objD7);
  if (DB4A3->monsterType() != mon_LordChaos) return RNempty;
  return objD7;
}

bool legalXY(int x, int y)
{
  if ( (y < 0) || (y >= d.height) ) return false;
  if ( (x < 0) || (x >= d.width)  ) return false;
  return true;
};

bool IsCellWall(int x, int y)
{
  i32 cf;
  cf = (GetCellFlags(x, y) & 255) >> 5;
  return (cf==roomSTONE) || (cf==roomFALSEWALL);
}

bool IsCellOpenOrPit(int x, int y)
{
  i32 cf;
  cf = (GetCellFlags(x, y) & 255) >> 5;
  return   (cf == roomOPEN) ||(cf == roomPIT);
}

bool IsCellOccupied(int x, int y)
{
  if (FindFirstMonster(x, y) != RNeof) return true;
  if (   (d.LoadedLevel == d.partyLevel)
      && (d.partyX == x)
      && (d.partyY == y) ) return true;
  return false;
}

//*********************************************************
//
//*********************************************************
//   TAG00f340
RESTARTABLE _Fusion(const i32 attackX, const i32 attackY)
{//(void)
  static dReg D0, D4, D5, D6, D7;
  static RN  obj_16;
  static int i_14[4];
  static i16 w_6;
  static i16 w_4;
  static i16 w_2;
  int i, j, temp;
  int direction[4], x1[4], y1[4], x2[4], y2[4];
  bool fluxCageOrWall[4], empty1[4], empty2[4];
  RESTARTMAP
    RESTART(3)
  END_RESTARTMAP
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D7W = sw(attackX);
  D6W = sw(attackY);
  if (D7W < 0) RETURN;
                 //I think this is what the programmer
                 //wanted.  But it appears that the variable
                 //was unsigned and therefore impossible to
                 //be less than zero.
  if (D7W >= d.width) RETURN;
  if (D6W < 0) RETURN;
  if (D6W >= d.height) RETURN;
  TAG00dea8(RN(RNDispellMissile), 0xff, D7W, D6W, 0xff);
  obj_16 = IsLordChaosHere(D7W, D6W);
  if (obj_16 == RNempty) RETURN;
#define NewFusion
#ifdef NewFusion
  for (i=0; i<4; i++) direction[i] = i; // Initially north, east, south, west
  // Now scramble the four directions
  for (i=3; i>0; i--)
  {
    j = STRandom(i+1);
    temp = direction[i];
    direction[i] = direction[j];
    direction[j] = temp;
  };
  for (i=0; i<4; i++) //for each direction in scrambled order
  {
    x1[i] = D7W + d.DeltaX[direction[i]];
    y1[i] = D6W + d.DeltaY[direction[i]];
    x2[i] = D7W + 2*d.DeltaX[direction[i]];
    y2[i] = D6W + 2*d.DeltaY[direction[i]];
    fluxCageOrWall[i]
      = IsCellFluxcage(x1[i], y1[i]) || IsCellWall(x1[i], y1[i]);
    empty1[i] =   IsCellOpenOrPit(x1[i], y1[i])
               && !IsCellOccupied(x1[i], y1[i]);
    empty2[i] =   legalXY(x2[i], y2[i])
               && IsCellOpenOrPit(x2[i], y2[i])
               && !IsCellOccupied(x2[i], y2[i])
               && !IsCellFluxcage(x2[i], y2[i]);
  };
  // Now try to move Lord Chaos to an immediately adjacent cell.
  for (i=0; i<4; i++)
  {
    if (!fluxCageOrWall[i] && empty1[i])
    {
      D0W = MoveObject(obj_16, D7W, D6W, x1[i], y1[i]);
      if (D0W == 0)
      {
        TAG00af1a(x1[i], y1[i]);
        RETURN;
      };
    };
  };
  // If no fluxcage or wall is in the way try moving him two squares.
  for (i=0; i<4; i++)
  {
    if (!fluxCageOrWall[i] && empty2[i])
    {
      D0W = MoveObject(obj_16, D7W, D6W, x2[i], y2[i]);
      if (D0W == 0)
      {
        TAG00af1a(x2[i], y2[i]);
        RETURN;
      };
    };
  };
  // Sorry, Mr. Chaos.  You are toast.
#else  // NewFusion
  i_14[0] = IsCellFluxcage(D7W-1, D6W) ? 1 : 0;
  i_14[1] = IsCellFluxcage(D7W+1, D6W) ? 1 : 0;
  i_14[2] = IsCellFluxcage(D7W, D6W-1) ? 1 : 0;
  i_14[3] = IsCellFluxcage(D7W, D6W+1) ? 1 : 0;
  for (w_2 = sw(i_14[0] + i_14[1] + i_14[2] + i_14[3]);
       w_2 < 4;
       w_2++) // Examine each non-fluxcage cells in random order.
  {
    D5W = D7W;
    D4W = D6W;
    w_4 = (i16)STRandom0_3();
    for (w_6 = 4; w_6!=0; w_6--)
    { // Look in four directions from Lord Chaos starting with random direction in w_4.
      D0L = i_14[w_4];
      if (D0W == 0)
      {
        i_14[w_4] = 1;
        switch (w_4)
        {
        case 0: D5W--; break; //west
        case 1: D5W++; break; //east
        case 2: D4W--; break; //north
        case 3: D4W++; break; //south
        }; //switch
        break;
      };
      w_4 = sw((w_4 + 1) & 3);
//
    };
    if (OpenTeleporterPitOrDoor(D5W, D4W))
    if ( (D5W != d.partyX) || (D4W != d.partyY) )
    if (FindFirstMonster(D5W, D4W) == RNeof) // Don't put Lord Chaos on top
                                             // of another monster!
    {
      D0W = MoveObject(obj_16, D7W, D6W, D5W, D4W);
      if (D0W == 0)
      {
        TAG00af1a(D5W, D4W);
        RETURN;
      };
    };
//
  };
#endif // NewFusion
  FusionSequence(_3_); //TAG01fefc
  RETURN;
}

//*********************************************************
// Called when we hit monster with sword
//*********************************************************
i16 TAG00f930(CHARDESC *pChar,         //8
              i32      chIdx,          //12
              DB4      *pMonster,      //14
              i16      P4,             //18
              i32      attackX,        //20
              i32      attackY,        //22
              i16      P7,             //24
              i16      P8,             //26
              i32      skillNumber,    //28
              bool     vorpalOrDisrupt,
              char     *traceID)
{        
  dReg          D0, D1, D3, D4, D6, D7;
  CHARDESC     *pchA3;
  DB4          *DB4A2;
  MONSTERDESC  *pmtDesc;
  OBJECTTYPE    ot_6;
  i16           w_2;
  bool          luckyHit = false;
  i32           throwingDistance;
  i32           StaminaDec;
  i32           ranResult;
  i32           levelDifficulty;
  D6L = 0x1ccccccc; // Avoid compiler warning.
  if (traceID!=NULL)
  {
    fprintf(GETFILE(TraceFile),
          "%sEntering TAG00f930(char=%d,monster,P4=%d,P7=%d,P8=%d,skillNumber=%d)\n",
          traceID, chIdx, P4, P7, P8, skillNumber);
  };
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  pchA3 = pChar;
  DB4A2 = pMonster;
  if (chIdx >= d.NumCharacter) return 0;
  if (pchA3->HP == 0)
  {
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sCharacter is dead.  Return 0\n",traceID);
    };
    return 0;
  };
  levelDifficulty = d.pCurLevelDesc->experienceMultiplier();
  if (traceID!=NULL)
  {
    fprintf(GETFILE(TraceFile),"%slevelDifficulty = %d\n",
                      traceID, levelDifficulty);
  };
  pmtDesc = &d.MonsterDescriptor[DB4A2->monsterType()];
  ot_6 = GetModifiedObjectType(pchA3->possessions[1]); //weapon hand
  // We modified the calling sequence to separate the
  // vorpalOrDisrupt parameter from parameter P7
  if (   (!pmtDesc->nonMaterial())
      || vorpalOrDisrupt  )
  {
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%spi26 word2 &0x40 == 0  or  vorpalOrDisrupt\n",traceID);
    };
    D0L = Quickness(pchA3,nextTracePrefix(traceID));
    D1L = STRandom(32);
    D3W = pmtDesc->uByte8[4];
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%scharacter's quickenss = %d\n",traceID,D0L);
      fprintf(GETFILE(TraceFile),"%srequired quickness = (pi26.uByte8[4]=%d) + (random(32)=%d) + (levelDifficulty=%d) - 16 = %d\n",
                               traceID, D3W, D1W, levelDifficulty, D1W+D3W+levelDifficulty-16);
    };
    D1W = sw(D1W + D3W + levelDifficulty - 16);

    if (   (D0W <= D1W)       // if quickness too small
        && ((luckyHit = (STRandom0_3() == 0)) == false)   // and no luck
        && (TAG016476(pchA3, 75 - P7, nextTracePrefix(traceID)) == 0)  )
    {
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sAttack failed.  Quickness too small. Luck(1/4) failed.  TAG16476(75-(P7=%d) returned 0.  Return 0\n",traceID);
      };
      AdjustStamina(chIdx, StaminaDec=STRandomBool() + 2);
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sDecrementStamina(2+random(1)=%d)\n",traceID,StaminaDec);
        fprintf(GETFILE(TraceFile),"%sTAG00f930 Returning 0\n",traceID);
      };
      DrawCharacterState(chIdx);
      return 0;
    };
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sAttack was successful because ",traceID);
      if (D0W>D1W)
      {
        fprintf(GETFILE(TraceFile), "Character's quickness is great enough\n");
      }
      else if (luckyHit)
      {
        fprintf(GETFILE(TraceFile),"Lucky Hit (1 chance in 4)\n");
      }
      else
      {
        fprintf(GETFILE(TraceFile),"TAG016476 returned non-zero value\n");
      };
    };
      // It is good to get here, I think
    throwingDistance = DetermineThrowingDistance(chIdx, 1);
    D7W = sw(throwingDistance);
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile), "%sD7W = Throwing distance=%d\n",traceID,D7W);
    };
    if (throwingDistance != 0)  /////goto tag00faa2;
    {
      D7W = sw(D7W + (STRandom() & 0xffff) % (D7W/2 + 1));
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sD7W = D7W+random(D7W/2+1) = %d\n",traceID,D7W);
      };
      D7W = sw(D7W * P8 / 32);
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sD7W = D7W*(P8=%d)/32 = %d\n",traceID,P8,D7W);
      };
      D4W = sw((ranResult=STRandom(32)) + pmtDesc->uByte8[0] + levelDifficulty);
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sD4W = (i26->uByte8[0]=%d) + (levelDifficulty=%d) + (random(32)=%d) = %d\n",
                     traceID,pmtDesc->uByte8[0],levelDifficulty,ranResult,D4W);
      };
      if (ot_6 == obj_DiamondEdge)
      {
        D4W = sw(D4W - D4W/4);
        if (traceID!=NULL)
        {
          fprintf(GETFILE(TraceFile),"%sDiamond Edge so subtract 25% from D4W --> %d\n",traceID,D4W);
        };
      }
      else
      {
        if (ot_6 == obj_Executioner)
        {
          D4W = sw(D4W - D4W/8);
          if (traceID!=NULL)
          {
            fprintf(GETFILE(TraceFile),"%sExecutioner so subtract 12.5% from D4W --> %d\n",traceID,D4W);
          };
        };
      };
    // D4 is perhaps like the monster's Armor effectiveness???
      D7W = sw(D7W + (ranResult=STRandom(32)) - D4W);
      D6W = D7W;  // chance of hitting?????
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sD7W = D7W + (random(32)=%d) - (D4W=%d) = %d\n",
                           traceID,ranResult, D4W, D7W);
        fprintf(GETFILE(TraceFile),"%sD6W = D7W = %d\n", traceID, D6W);
      };
    }; ///  }}}}}}
    if ((throwingDistance==0)||(D6W <= 1))
    {
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sAttack may fail because ",traceID);
        if (throwingDistance==0)
        {
          fprintf(GETFILE(TraceFile), "throwingDistance = 0.\n");
        }
        else
        {
          fprintf(GETFILE(TraceFile),"D6W < 2.\n");
        };
      };
      D7W = (i16)STRandom0_3();
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sD7W = random(4) = %d\n",traceID,D7W);
      };
      if (D7W == 0)
      {
        if (traceID!=NULL)
        {
          fprintf(GETFILE(TraceFile),"%sAttack failed because ",traceID);
          fprintf(GETFILE(TraceFile)," we were not Lucky (D7W==0)\n");
        };
        AdjustStamina(chIdx, StaminaDec=STRandomBool() + 2);
        if (traceID!=NULL)
        {
          fprintf(GETFILE(TraceFile),"%sDecrementStamina(2+random(1)=%d)\n",traceID,StaminaDec);
          fprintf(GETFILE(TraceFile),"%sTAG00f930 Returning 0\n",traceID);
        };
        DrawCharacterState(chIdx);
        return 0;
      };
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sWe were lucky.  D7W is non-zero\n",traceID);
      };
      D1W = sw(STRandom(16));
      ASSERT(D6L != 0x1ccccccc);
      D6W = sw(D6W + D1W);
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sD6W += (random(16)=%d) --> %d\n",
                           traceID, D1W, D6W);
      };
      if (   (D6W > 0)
          || (STRandomBool() != 0)  )
      {
        if (traceID!=NULL)
        {
          fprintf(GETFILE(TraceFile),"%sWe are going to recompute D7W because ",traceID);
          if (D6W>0)
          {
            fprintf(GETFILE(TraceFile), "D6W > 0\n");
          }
          else
          {
            fprintf(GETFILE(TraceFile),"we got lucky (1/2)\n");
          };
        };
        D7W = sw(D7W + (ranResult=STRandom0_3()));
        if (traceID!=NULL)
        {
          fprintf(GETFILE(TraceFile),"%sD7W += (random(3)=%d) --> %d\n",
                            traceID, ranResult, D7W);
        };
        if (STRandom0_3() == 0)
        {
          if (traceID!=NULL)
          {
            fprintf(GETFILE(TraceFile),"%sWe are going to recompute D7W because we got lucky(1/4)\n",traceID);
          };
          D7W = sw(D7W + Larger(0, (ranResult=STRandom(16)) + D6W));
          if (traceID!=NULL)
          {
            fprintf(GETFILE(TraceFile),"%sD7W += max((D6W=%d),(random(16)=%d))-->%d\n",
                                traceID,D6W, ranResult,D7W);
          };
        };
      };
    };
    D7W /= 2;
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sDivide D7W by 2 --> %d\n",traceID,D7W);
    };
    if (D7W == 0) D0W = 0;
    else D0W = sw((STRandom() & 0xffff) % D7W);
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sD0W=random(D7W) --> %d\n", traceID, D0W);
    };
    D1W = (i16)(ranResult=STRandom0_3());
    D7W = sw(D7W + D1W + D0W);
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile), "%sD7W += (random(4)=%d) + (D0W=%d) --> %d\n",
                          traceID, ranResult, D0W, D7W);
    };
    if (D7W != 0) D7W = sw(D7W + (ranResult=(STRandom() & 0xffff) % D7W));
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile), "%sD7W += (random(D7W)=%d)--> %d\n",
                          traceID, ranResult, D7W);
    };
    D7W /= 4;
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sDivide D7W by 4 --> %d\n", traceID, D7W);
    };
    D7W = sw(D7W + (ranResult=STRandom0_3()) + 1);
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sD7W += (random(4)=%d) + 1 --> %d\n",
                                traceID, ranResult, D7W);
    };
//
//
    if (traceID!=NULL)
    {
      if (    (ot_6 == obj_VorpalBlade)
           && !pmtDesc->nonMaterial() )
      {
        fprintf(GETFILE(TraceFile),"%sDivide D7W by 2 because VorpalBlade and i26 Word2 bit 6 = 0 --> %d\n",
                           traceID, D7W/2);
      };
    };
    if (   (ot_6 == obj_VorpalBlade  )
        && (!pmtDesc->nonMaterial()  )
        && ((D7W = sw(D7W/2)) == 0)  )
    {
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sAttack failed because Vorpal, i26 word 2 bit 6=0, and D7W=0\n",traceID);
      };
      AdjustStamina(chIdx, StaminaDec=STRandomBool() + 2);
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sDecrementStamina(2+random(1)=%d)\n",traceID,StaminaDec);
        fprintf(GETFILE(TraceFile),"%sTAG00f930 Returning 0\n",traceID);
      };
      DrawCharacterState(chIdx);
      return 0;
    };
    D0W = sw((ranResult=STRandom(64)));
    D1W = sw(DetermineMastery(chIdx, skillNumber, traceID==NULL?NULL:traceID-2));
    if (D0W < D1W)
    {
      D7W += 10;
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sAdd 10 to D7W because (Mastery(skill=%d)=%d) > (random(64)=%d) --> %d\n",
                           traceID,skillNumber,D1W, ranResult, D7W);
      };
    };
    w_2 = DamageMonster(DB4A2, P4, attackX, attackY, D7W, 1);
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sw_2 = DamageMonster(D7W=%d) --> %d\n",
                         traceID,D7W, w_2);
    };
    D0W = sw((D7W * pmtDesc->word16_8_11() / 16) + 3);
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sD0W = ((D7W=%d) * (Bits8_11(i26.word16)=%d)/16) + 3 --> %d\n",
                         traceID,D7W, pmtDesc->word16_8_11(),D0W);
    };
    AdjustSkills(chIdx, skillNumber, D0W);
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sAdjustSkills((skill=%d), (D0W=%d))\n",
                         traceID,skillNumber, D0W);
    };
    AdjustStamina(chIdx, (ranResult=STRandom0_3()) + 4);
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sDecrement Stamina by random(4)+4 = %d\n",
                         traceID, ranResult+4);
    };
  }
  else
  {
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%spi26 word2 &0x40 != 0  and  w_4 == 0\n",traceID);
    };
    D7W = 0;
    w_2 = 0;
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sD7W = 0\n+++w_2 = 0\n",traceID);
    };
    AdjustStamina(chIdx, (ranResult=STRandomBool()) + 2);
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sDecrement Stamina by random(2)+2 = %d\n",
                         traceID, ranResult+2);
    };
  };
  DrawCharacterState(chIdx);
  if (w_2 != 0)
  {
    ProcessTimers29to41(attackX, attackY, timerFunctionM1, 0);
  };
  if (traceID!=NULL)
  {
    fprintf(GETFILE(TraceFile),"%sTAG00f930 returning D7W = %d\n",traceID,D7W);
  };
  return (D7W);
}


//*********************************************************
//
//*********************************************************
//   TAG01adaa
RESTARTABLE _FlashAttackDamage(const i32 value)
{//(void)
  static dReg D0, D5, D6, D7;
  static aReg A2, A3;
  static RectPos *pR_10;
  static i8 b_6[6];
  static i32 damage;
  RESTARTMAP
    RESTART(1)
  END_RESTARTMAP
  damage = value;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  STHideCursor();
  d.UseByteCoordinates = 0;
  FillRectangle(d.LogicalScreenBase,
                (RectPos *)d.Word20,
                0,
                160);
  if (damage < 0)
  {
    if (damage == -1)
    {
      D5W = 242;
      A2 = "CAN'T REACH";
    }
    else
    {
      D5W = 248;
      A2 = "NEED AMMO";
    };
    TextOutToScreen(D5W, 100, 4, 0, A2);
  }
  else
  {
    if (damage > 40)
    {
      pR_10 = &d.wRectPos20202;
      A3 = GetGraphicAddress(14);
      D6W = 48;
    }
    else
    {
      if (damage > 15)
      {
        D7W = 2;
        D5W = 64;
        D6W = 32;
        pR_10 = &d.wRectPos20226;
      }
      else
      {
        D7W = 3;
        D5W = 42;
        D6W = 24;
        pR_10 = &d.wRectPos20234;
      };
      D0W = TAG022c90(D7W);
      if (D0W == 0)
      {
        A2 = GetGraphicAddress(14);
        ShrinkBLT(A2,
                  A3 = TAG022d38(D7W),
                  96,
                  45,
                  D5W,
                  37,
                  d.Byte156);
        TAG022d5e(D7W);
      }
      else
      {
        A3 = TAG022d38(D7W);
      };
    };
    BLT2Screen(A3, pR_10, D6W, -1);
    D7W = 5;
    D5W = 274;
    b_6[5] = 0;
    do
    {
    D0W = sw((damage % 10) + '0');
    D7W--;
    b_6[D7W] = D0B;
    D5W -= 3;
    damage = sw(damage/10);
    } while (damage != 0);
    TextOutToScreen(D5W, 100, 4, 0, &b_6[D7W]);
  };
  VBLDelay(_1_,15);
  STShowCursor();
  RETURN;
}



//*********************************************************
//  case  atk_WARCRY:
//  case  atk_CALM:
//  case  atk_BRANDISH:
//  case  atk_BLOWHORN:
//  case  atk_CONFUSE
//*********************************************************
//  TAG01bac2
i16 WarCryEtc(i32 chIdx,            //@8
              ATTACKTYPE attackType,//@10
              i32 attackX,          //@12
              i32 attackY,          //@14
              char *tracePrefix)
{//(i16)
  dReg        D0, D1, D4;
  i32         skillIncrement;
  i32         requiredMastery;
  DB4         *DB4A3;
  MONSTERDESC *pmtDesc;
  i32         effectiveMastery;
  ITEM16      *pi16_4;
  i32         Mastery;
  i32         temp;
  skillIncrement = 0xccccc;
  requiredMastery = 0xccccc;
  Mastery = 0xccccc;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D4W = 0;
  if (AttackTraceActive)
  {
    fprintf(GETFILE(TraceFile),
            "%sEntering 'WarCryEtc' for skill 14(WARCRY, CALM, BRANDISH, BLOWHORN).  Setting D4W = %d\n",
            tracePrefix,
            D4W);
  };
  if (d.MonsterUnderAttack == RNeof) return (D4W);
  switch (attackType)
  {
  case atk_WARCRY:
    Mastery = 3;
    skillIncrement = 12;
    if (AttackTraceActive)
    {
      fprintf(GETFILE(TraceFile),
             "%sAttack = Warcry....Set Mastery = %d, Set skillIncrement = %d\n",
             tracePrefix,
             Mastery,
             skillIncrement);
    };
    break;
  case atk_CALM:
    Mastery = 7;
    skillIncrement = 35;
    break;
  case atk_BRANDISH:
    Mastery = 6;
    skillIncrement = 30;
    break;
  case atk_BLOWHORN:
    Mastery = 6;
    skillIncrement = 20;
    if (AttackTraceActive)
    {
      fprintf(GETFILE(TraceFile),
              "%sAttack = BlowHorn....Set Mastery = %d, Set skillIncrement = %d\n",
              tracePrefix,
              Mastery,
              skillIncrement);
    };
    break;
  case atk_CONFUSE:
    Mastery = 12;
    skillIncrement = 45;
    break;
  }; // switch ()
  ASSERT(Mastery != 0xccccc);
  Mastery = sw(Mastery + sw(temp = DetermineMastery(chIdx, 14)));
  if (AttackTraceActive)
  {
    fprintf(GETFILE(TraceFile),
           "%sIncrement Mastery by (DetermineMastery(14)=%d) --> %d\n",
           tracePrefix,
           temp,
           Mastery);
  };
  DB4A3 = GetRecordAddressDB4(d.MonsterUnderAttack);
  pmtDesc = &d.MonsterDescriptor[DB4A3->monsterType()];
  effectiveMastery = sw((STRandom()&0xffff) % Mastery);
  requiredMastery = pmtDesc->bravery();
  if (AttackTraceActive)
  {
    fprintf(GETFILE(TraceFile),
            "%seffectiveMastery = Random(Mastery) --> %d\n",
            tracePrefix,
            effectiveMastery);
    fprintf(GETFILE(TraceFile),
            "%srequiredMastery = i26.bravery() --> %d\n",
            tracePrefix,
            requiredMastery);
  };
  if ( (requiredMastery > effectiveMastery) || (requiredMastery == 15) )
  {
    ASSERT(skillIncrement != 0xccccc);
    skillIncrement /= 2;
    if (AttackTraceActive)
    {
      fprintf(GETFILE(TraceFile),
              "%sAttack Failed and Divide skillIncrement by 2 because ",
              tracePrefix);
      if (requiredMastery > effectiveMastery)
            fprintf(GETFILE(TraceFile),"requiredMastery > effectiveMastery\n");
      else fprintf(GETFILE(TraceFile), "requiredMastery == 15\n");
    };
  }
  else
  {
    ASSERT (DB4A3->groupIndex() < d.MaxITEM16);
    pi16_4 = &d.Item16[DB4A3->groupIndex()];
    if (DB4A3->fear() == 6)
    {
      TAG00b02c(pi16_4, attackX, attackY);
      TAG00af1a(attackX, attackY);
    };
    DB4A3->fear(5);
    D0L = 0;
    D0W = sw(4 * (16-requiredMastery));
    D1L = 0; D1B = pmtDesc->timePerMove();//uByte6;
    D0W = sw(D0L / D1W);
    pi16_4->uByte5 = D0B;
    D4W = 1;
  };
  AdjustSkills(chIdx, 14, skillIncrement);
  return (D4W);
}

//*********************************************************
//
//*********************************************************
i16 TAG01bc2a(i32 chIdx,
              CHARDESC *pChar,
              ATTACKTYPE attackType,
              i32 attackX,
              i32 attackY,
              i32 skillNumber,
              char *traceID)
{//(i16)
  dReg D0, D5, D6, D7;
  OBJECTTYPE otD0;
  bool vorpalOrDisrupt;
  D6L = 0xccccc;
  if (traceID!=NULL)
  {
    fprintf(GETFILE(TraceFile),
            "%sEnter TAG01bc2a (attackType=%d, skillNumber=%d)\n",
            traceID, attackType, skillNumber);
  };
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  QueueSound(16, d.partyX, d.partyY, 1);
  if (d.MonsterUnderAttack == RNeof)
  {
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sNo monster in attacked square.  Return 0\n",traceID);
    };
    return 0;
  };
  D5W = DetermineAttackOrdinal(
                   attackX, attackY,
                   d.partyX, d.partyY,
                   pChar->position);
  if (D5W==0)
  {
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sNo monster can be attacked. Return 0.\n",traceID);
    };
    return 0;
  };
  D0W = sw((pChar->position + 4 - pChar->facing) & 3);
  D6W = 0;
  switch (D0W)
  {
  case 2:
    D6W = 2;
    // Note fallthrough
  case 3:
    D6W++;
    D0W = sw(CharacterAtPosition((pChar->position + D6W) & 3));
    if (D0W != -1)
    {
      d.Word20264 = -1;
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),"%sAttempt to attack through party member.  Return 0\n",traceID);
      };
      return 0;
    };
  }; // switch (D0W)
  if (attackType == atk_DISRUPT)
  {
    if (!d.MonsterUnderAttack.NonMaterial())
    {
      if (traceID!=NULL)
      {
        fprintf(GETFILE(TraceFile),
              "%sDISRUPT but monsterDesc bit 6 word2 = 0. Return 0\n",traceID);
      };
      return 0;
    };
  };
  D6W = (UI8)(d.Byte20002[attackType]);
       //Lots of zeroes with scattered 20s, 30s, 40s
       //block,chop,punch,kick,stab1,hit,swing,stab2,thrust,
       //jab,parry,hack,berzerk,disrupt,melee,slash,
       //cleave,bash,stun.  Appears to be things that
       //require strength or physical action.
  D7W = (UI8)(d.Byte19958[attackType]);
       //Scattered non-zero values.  Block, chop, punch,
       //kick, stab, hit, swng, stab, thrust, jab, parry,
       //hack, berzerk, disrupt, melee, slach, cleave,
       //bash, stun,
  if (traceID!=NULL)
  {
    fprintf(GETFILE(TraceFile),"%sD6W=Byte20002[attackType=%d]=%d\n",
                      traceID, attackType, D6W);
    fprintf(GETFILE(TraceFile),"%sD7W=Byte19958[attackType=%d]=%d\n",
                      traceID, attackType, D7W);
  };
  otD0 = GetModifiedObjectType(pChar->possessions[1]);
  vorpalOrDisrupt = false;
  if ( (otD0==obj_VorpalBlade) || (attackType==atk_DISRUPT) )
  {
    vorpalOrDisrupt = true;
    if (traceID!=NULL)
    {
      fprintf(GETFILE(TraceFile),"%sVorpalBlade or DISRUPT.\n",traceID);
    };
  };
  if (traceID!=NULL)
  {
    fprintf(GETFILE(TraceFile), "%sCalling TAG00f930(character, monster, D5W-1, D6W, D7W, skillNumber)\n",traceID);
  };
  D0W = TAG00f930(
            pChar,
            chIdx,
            GetRecordAddressDB4(d.MonsterUnderAttack),
            sw(D5W-1),
            attackX,
            attackY,
            D6W,
            D7W,
            skillNumber,
            vorpalOrDisrupt,
            nextTracePrefix(traceID));
  d.Word20264 = D0W;
  if (traceID!=NULL)
  {
    fprintf(GETFILE(TraceFile),"%sWord20264 = Result of calling TAG00f930 = %d\n", traceID, D0W);
    fprintf(GETFILE(TraceFile),"%sReturn 1\n",traceID);
  };
  return (1);
}

//*********************************************************
//
//*********************************************************
i16 TAG01bd5e(CHARDESC *pChar, i32 P2, i32 P3, i32 P4)
{
  dReg D6, D7;
  TIMER timer;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  D7W = sw(P3);
  D6W = 1;
  if (P4 != 0)
  {
    if (pChar->mana == 0) return 0;
    if (pChar->mana < 4)
    {
      D7W /= 2;
      pChar->mana = 0;
      D6W = 0;
    }
    else
    {
      pChar->mana -= 4;
    };
  };
  timer.timerWord6() = sw(D7W / 32);
  if (P2 != 0)
  {
    timer.timerFunction = timerFunction77;
    if (d.Word13274 > 50)
    {
      timer.timerWord6() /= 4;
    };
    d.Word13274 = sw(d.Word13274 + timer.timerWord6());
  }
  else
  {
    timer.timerFunction = timerFunction78;
    if (d.Word13276 > 50)
    {
      timer.timerWord6() /= 4;
    };
    d.Word13276 = sw(d.Word13276 + timer.timerWord6());
  };
  timer.timerUByte5 = 0;
  timer.timerTime = (d.Time+(UI16)(D7W)) | (d.partyLevel << 24);
  //timer.uByte6 = (ui8)timer.word6(); // Value expected in first byte
  // Not So!!!  We use the whole word.
  SetTimer(&timer);
  MarkAllChar1000();
  return D6W;
}

//*********************************************************
//
//*********************************************************
//   TAG01be82
void DecrementChargesRemaining(CHARDESC *pChar)
{//(void)
  RN   objD7;
  DB5  *DB5A3;
  DB6  *DB6A3;
  DB10 *DB10A3;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  objD7 = pChar->possessions[1];
  //dbA3 = GetRecordAddress(objD7);
  switch (objD7.dbType())
  {
  case dbWEAPON:
      DB5A3 = GetRecordAddressDB5(objD7);
      if (DB5A3->charges() != 0)
      {
        //D3W = BITS10_13(DB4A3->word(2)) - 1;
        DB5A3->charges(DB5A3->charges() - 1);
      };
      break;
  case dbCLOTHING:
      DB6A3 = GetRecordAddressDB6(objD7);
      if (DB6A3->charges() != 0)
      {
        //D3W = BITS9_12(DB6A3->word(2)) - 1;
        DB6A3->charges(DB6A3->charges() - 1);
      };
      break;
  case dbMISC:
      DB10A3 = GetRecordAddressDB10(objD7);
      if (DB10A3->value() != 0)
      {
        //D3W = BITS14_15(DB10A3->word(2)) - 1;
        DB10A3->value(DB10A3->value() - 1);
      };
      break;
  }; //switch(db)
  DrawEightHands();
}

//*********************************************************
//
//*********************************************************
//   TAG01bf70
void SetCharToPartyFacing(CHARDESC *pChar)
{
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  if (pChar->facing != d.partyFacing)
  {
    pChar->facing = ub(d.partyFacing);
    pChar->word48 |= 0x400;
  };
}

//*********************************************************
//
//*********************************************************
//  TAG01bf9a
RESTARTABLE _Attack(const i32 chIdx, const ATTACKTYPE attackType)
{//(i16)
  static dReg D0, D4, D5, D6;
  static ROOMTYPE rtD0;
  static RN   rnD5;
  static CHARDESC *pChar;
  static DBCOMMON *dbA2;
  static DB5      *DB5A2;
  static DB10     *DB10A2;
  static i32 attackX, attackY;
  static i16   w_42;
  static TIMER timer_32;
  static WEAPONDESC  *pWeapon_22;
  static WEAPONDESC  *pWeapon_18;
  static i32 skillNumber; //w_12
  // w_8 = attackY
  // w_6 = attackX
  static i32 w_2, w_10, experienceGained;
  static i32 neededMana_4;
  static RN  obj_4;
  static i32 wpnByte1_4;
  static i32 neededHP_4;
  RESTARTMAP
    RESTART(8)
  END_RESTARTMAP
  D4L = 0xccccc;
  neededMana_4 = 0xccc;//Compiler says not initialized.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  //D7W = sw(chIdx);
  if (chIdx >= d.NumCharacter)
  {
    RETURN_i16(0);
  };
  pChar = &d.CH16482[chIdx];
  // Weapon hand = 1;
  dbA2 = GetCommonAddress(pChar->possessions[1]);
  if (pChar->HP == 0) RETURN_i16(0);

  attackX = d.partyX;
  attackY = d.partyY;
  attackX += d.DeltaX[pChar->facing];
  attackY += d.DeltaY[pChar->facing];
  if (TimerTraceActive || AttackTraceActive)
  {
    fprintf(GETFILE(TraceFile),"%s attacks from %02x(%02x,%02x)"
                      " to %02x(%02x,%02x)\n",
            d.CH16482[chIdx].name,
            d.partyLevel,d.partyX,d.partyY,
            d.partyLevel,attackX, attackY);
  };
  if (AttackTraceActive)
  {
    i32 i;
    i = attackType;
    char *pAName;
    for (pAName = (char *)d.AttackNames; i != 0; i--)
    {
      while ( *(pAName++) != 0) {}; // Skip one string
    };

    fprintf(GETFILE(TraceFile),"Attack type = %d = %s\n",
                  attackType, pAName);
  };
  d.MonsterUnderAttack = FindFirstMonster(attackX, attackY);
  w_2 = (UI8)(d.Byte19914[attackType]); // small integer (2,6,12)
  skillNumber = (UI8)(d.SkillNumber[attackType]);
        //small intgers (largest 18)
  if (TimerTraceActive || AttackTraceActive)
  {
    fprintf(GETFILE(TraceFile), "Skill number required = %02d\n", skillNumber);
  };
  w_10 = (UI8)(d.Byte20046[attackType]) + STRandomBool();
        //small integers mostly 2,4,3 largest 40
  if (AttackTraceActive)
  {
    fprintf(GETFILE(TraceFile),"Byte20046[attackType=%d] = %d\n",
                      attackType, d.Byte20046[attackType]);
    fprintf(GETFILE(TraceFile),"w_10 = Byte20046[attackType=%d] + Random(1) = %d\n",
                      attackType, w_10);
  };
  experienceGained = (UI8)(d.experienceForAttacking[attackType]);
        //small integers.  largest 35.
  if (AttackTraceActive)
  {
    fprintf(GETFILE(TraceFile), "experiencedGained = Byte20178[attackType=%d] = %d\n",
                        attackType, experienceGained);
  };
  D6W = GetCellFlags(attackX, attackY);
  D5W = 1;
//
  if (   (skillNumber == 16)  // All the Wizard skills
      || (skillNumber == 17)
      || (skillNumber == 18)
      || (skillNumber == 19)
      || (skillNumber ==  3) )
  {
    D4W = sw(7-Smaller(6, DetermineMastery(chIdx,         //chIdx
      skillNumber,initialTracePrefix(AttackTraceActive))));
    if (AttackTraceActive)
    {
      fprintf(GETFILE(TraceFile),"Skill %d is Wizard skill.  Mastery=%d\n",
            skillNumber, DetermineMastery(chIdx,skillNumber));
      fprintf(GETFILE(TraceFile),"D4W = 7-min(6,Mastery) = %d\n",D4W);
    };
  };
  switch (attackType)
  {
  case atk_LIGHTNING:
      neededMana_4 = 180;
      rnD5 = RNLightning;
      goto tag01c11a;
  case atk_DISPELL:
      neededMana_4 = 150;
      rnD5 = RNDispellMissile;
      goto tag01c11a;
  case atk_FIREBALL:
      neededMana_4 = 150;
      goto tag01c116;
  case atk_SPIT:
      neededMana_4 = 250;
tag01c116:
      rnD5 = RNFireball;
tag01c11a:
      SetCharToPartyFacing(pChar);
      ASSERT(D4L != 0xccccc); // Uninitialized value
      if (pChar->mana < D4W)
      {
        neededMana_4 = Larger(2, (pChar->mana * neededMana_4) / D4W);
        D4W = pChar->mana;
      };
      ASSERT(neededMana_4 != 0xccc); //uninitialized value
      D5W = TAG0174ec(chIdx, rnD5, neededMana_4, D4W);
      if (D5W == 0)
      {
        experienceGained /= 2;
      };
      DecrementChargesRemaining(pChar);
      break;

  case atk_BASH:
  case atk_HACK:
  case atk_BERZERK:
  case atk_KICK:
  case atk_SWING:
  case atk_CHOP:
    if (TimerTraceActive)
    {
      fprintf(GETFILE(TraceFile), "Attack BASH/HACK/BERZERK/KICK/SWING/CHOP\n");
    }
    if (RoomType(D6W) == roomDOOR) //
    {
      if ((D6W & 7) == 4) // Is it a breakable door?
      {
        QueueSound(16, d.partyX, d.partyY, 1);
        w_2 = 6;
        HitDoor(attackX,
                attackY,
                DetermineThrowingDistance(chIdx, 1),
                0,  //Not a ZO spell
                2); //delay
        QueueSound(4, d.partyX, d.partyY, 2);
        break;
      };
    };
    // NOTE!  Fallthrough.
  case atk_DISRUPT:
  case atk_JAB:
  case atk_PARRY:
  case atk_STAB2:
  case atk_STAB1:
  case atk_STUN:
  case atk_THRUST:
  case atk_MELEE:
  case atk_SLASH:
  case atk_CLEAVE:
  case atk_PUNCH:
      D5W = TAG01bc2a(
                chIdx,
                pChar,
                attackType,
                attackX,
                attackY,
                skillNumber,
                initialTracePrefix(AttackTraceActive));
      if (D5W == 0)
      {
        experienceGained /= 2;
        w_2 /= 2;
      };
      break;
  case atk_CONFUSE:
      DecrementChargesRemaining(pChar);
      // *** NOTE   Fallthrough  *****
  case  atk_WARCRY:
  case  atk_CALM:
  case  atk_BRANDISH:
  case  atk_BLOWHORN:
    D5W = WarCryEtc(chIdx, attackType, attackX, attackY,initialTracePrefix(AttackTraceActive));
    break;

  case atk_SHOOT:
      if (pChar->possessions[0] == RNnul) goto tag01c35a;
      if (pChar->possessions[0].dbType() != dbWEAPON) goto tag01c35a;

      //I guess the following is safe.  We could not have
      //selected 'SHOOT' if we did not have the proper
      //weapon in the weapon hand.
      DB5A2 = dbA2->CastToDB5(); //We will crash if not weapon
      pWeapon_18 = &d.weapons[DB5A2->weaponType()];
      pWeapon_22 = TAG0099d2(pChar->possessions[0]);
      D4W = pWeapon_18->uByte1;
      wpnByte1_4 = pWeapon_22->uByte1;
      if ( (D4W >= 16) && (D4W <= 31) )
      {
        if (wpnByte1_4 != 10)
        {
          goto tag01c35a;
        };
        D4W -= 16;
      }
      else
      {
        if ( (D4W >= 32) && (D4W <= 47) )
        {
          if (wpnByte1_4 != 11)
          {
tag01c35a:
            d.Word20264 = (unsigned)0xfffe;
            experienceGained = 0;
            D5W = 0;
            break;
          };
          D4W -= 32;
        };
      };
      SetCharToPartyFacing(pChar);
      obj_4 = RemovePossession(chIdx, 0);
      QueueSound(16, d.partyX, d.partyY, 1);
      w_42 = sw(DetermineMastery(chIdx, 11));
      LaunchObject(
                   pChar,
                   obj_4,
                   pWeapon_18->uByte3 + pWeapon_22->uByte3,
                   2 * (pWeapon_18->word4 + w_42),
                   D4W );
      break;
  case atk_FLIP:
      if (STRandomBool() != 0)
      {
        PrintWithSubstitution("IT COMES UP HEADS");
      }
      else
      {
        PrintWithSubstitution("IT COMES UP TAILS");
      };
      break;
  case atk_SPELLSHIELD:
  case atk_FIRESHIELD:
      D0W = TAG01bd5e(pChar,
                       attackType==atk_SPELLSHIELD ? 1 : 0,
                       0x118,
                       1);
      if (D0W == 0)
      {
        experienceGained /= 4;
        w_2 /= 2;
      }
      else
      {
        DecrementChargesRemaining(pChar);
      };
      break;
  case atk_INVOKE:
      neededMana_4 = STRandom(128) + 100;
      switch (STRandom(6))
      {
      case 0: rnD5 = RNff86; break;
      case 1: rnD5 = RNPoisonCloud; break;
      case 2: rnD5 = RNDispellMissile; break;
      default:rnD5 = RNFireball; break;
      };
      goto tag01c11a;
  case atk_FLUXCAGE:
    SetCharToPartyFacing(pChar);
    TAG00f182(attackX, attackY);
    break;
  case atk_FUSE:
    SetCharToPartyFacing(pChar);
    //w_6 = d.partyX;
    //w_8 = d.partyY;
    attackX = d.partyX + d.DeltaX[d.partyFacing];
    attackY = d.partyY + d.DeltaY[d.partyFacing];
    Fusion(_8_,attackX, attackY);
    break;
  case atk_HEAL:
    neededHP_4 = pChar->maxHP - pChar->HP;
    if (neededHP_4 > 0)
    {
      if (pChar->mana != 0)
      {
        D4W = sw(Smaller(10, DetermineMastery(chIdx, 13)));
        experienceGained = 2;
        do
        {
          D6W = sw(Smaller(neededHP_4, D4W));
          pChar->HP = sw(pChar->HP + D6W);
          experienceGained += 2;
          pChar->mana -= 2;
          if (pChar->mana <0) break;
            neededHP_4 -= D6W;
        } while (neededHP_4 != 0);
        if (pChar->mana < 0)  pChar->mana = 0;
        pChar->word48 |= 0x100;
        D5W = 1;
      };
    };
    break;
  case atk_WINDOW:
    D4W = sw(STRandom(DetermineMastery(chIdx, skillNumber) + 8) + 5);
    timer_32.timerUByte5 = 0;
    timer_32.timerFunction = timerFunction73;
    timer_32.timerTime = ((d.partyLevel << 24) | d.Time) + D4W;
    SetTimer(&timer_32);
    d.SeeThruWalls++;
    DecrementChargesRemaining(pChar);
    break;
  case atk_CLIMBDOWN:
    attackX = d.partyX;
    attackY = d.partyY;
    attackX += d.DeltaX[d.partyFacing];
    attackY += d.DeltaY[d.partyFacing];
    rtD0 = RoomType(GetCellFlags(attackX, attackY));

    if (   (rtD0 == roomPIT)
        && (FindFirstMonster(attackX, attackY) == RNeof) )
    {
      d.SupressPitDamage = 1;
      MoveObject(RN(RNnul), d.partyX, d.partyY, attackX, attackY);
      d.SupressPitDamage = 0;
    }
    else
    {
      w_2 = 0;
    };
    break;
  case atk_FREEZELIFE:
    D4W = 0;
    if (dbA2->IsDBType(dbMISC))
    {
      DB10A2 = dbA2->CastToDB10();
      if (DB10A2->miscType() == misc_MagicalBoxA) D4W = 30;
      if (DB10A2->miscType() == misc_MagicalBoxB) D4W = 125;
    };
    if (D4W != 0)
    {
      RemovePossession(chIdx, 1);
      //DB10A2->link(RNnul);//The box is gone.
      DeleteDBEntry(DB10A2);
    }
    else
    {
      D4W = 70;
      DecrementChargesRemaining(pChar);
    };
    d.uByte13271 = ub(Smaller(200, d.uByte13271 + D4W));
    break;
  case atk_LIGHT:
    d.Brightness = sw(d.Brightness + d.Word1074[2]);
    SetBrightnessTimer(-2, 0x9c4);
    DecrementChargesRemaining(pChar);
    break;
  case atk_THROW:
      SetCharToPartyFacing(pChar);
      if (   (pChar->position == ((d.partyFacing + 1)&3))
          || (pChar->position == ((d.partyFacing + 2)&3)) )
      {
        D0W = 1; //right side
      }
      else
      {
        D0W = 0; //left side
      };
      D5W = ThrowByCharacter(chIdx, 1, D0W);
      if (D5W != 0)
      {
        ASSERT(pChar->busyTimer != -1);
        d.Timers[pChar->busyTimer].timerUByte6 = 2;
      };
      break;
  }; //switch ()
  if (w_2 != 0)
  {
    DisableCharacterAction(chIdx, w_2);
  };
  if (w_10 != 0)
  {
    AdjustStamina(chIdx, w_10);
  };
  if (experienceGained != 0)
  {
    AdjustSkills(chIdx, skillNumber, experienceGained);
  };
  if (TimerTraceActive)
  {
    fprintf(GETFILE(TraceFile), " Increase skill by %02x\n", experienceGained);
  };
  DrawCharacterState(chIdx);
  RETURN_i16(D5W);
}

//*********************************************************
//
//*********************************************************
//           TAG01fed6
RESTARTABLE _ProcessTimersViewportAndSound(void)
{//(void)
  RESTARTMAP
    RESTART(1)
  END_RESTARTMAP
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ProcessTimers(_1_);
  DrawViewport(d.partyFacing, d.partyX, d.partyY);
  display();
  StartQueuedSound();
  d.Time++;
  GameTime = d.Time;
  RETURN;
}

//*********************************************************
//
//*********************************************************
//           TAG01fefc
RESTARTABLE _FusionSequence(void)
{ //(void)    Called by Fusion attack only.
  static dReg D0, D1, D5, D6;
  static i16  D7w;
  static RN    objD4;
  static DB15 *DB15A2;
  static DB4  *DB4A3;
  static i8    b_226[200];
  static RN    obj_26[8];
  static i8    b_9;
  static RN    obj_8;
  static i16   w_6;
  static i16   w_4;
  RESTARTMAP
    RESTART(1)
    RESTART(3)
    RESTART(4)
    RESTART(5)
    RESTART(7)
    RESTART(8)
    RESTART(12)
    RESTART(13)
    RESTART(14)
    RESTART(15)
    RESTART(16)
    RESTART(17)
    RESTART(18)
    RESTART(19)
    RESTART(20)
    RESTART(21)
    RESTART(22)
    RESTART(23)
    RESTART(29)
    RESTART(30)
  END_RESTARTMAP
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  d.Word11694 = 1;
  if (d.SelectedCharacterOrdinal != 0)
  {
    TAG0192f4(4);
  };
  WaitForButtonFlash();
  d.Brightness = 200;
  SelectPaletteForLightLevel();
  d.Word13278 = 100;
  d.Word13274 = 100;
  d.Word13276 = 100;
  MarkAllChar1000();
  ProcessTimersViewportAndSound(_1_);
  w_4 = sw(d.partyX + d.DeltaX[d.partyFacing]);
  w_6 = sw(d.partyY + d.DeltaY[d.partyFacing]);
  obj_8 = FindFirstMonster(w_4, w_6);
  DB4A3 = GetRecordAddressDB4(obj_8);
  DB4A3->hitPoints6[0] = 10000;
  SetMonsterPositionByte(DB4A3, 255, d.partyLevel);
  SetMonsterFacingByte(DB4A3, (d.partyFacing+2)&3, d.partyLevel);
  D7w = 1;
  D6W = d.partyX;
  D5W = d.partyY;
tag01ffd2:
  for (objD4 = FindFirstDrawableObject(D6W, D5W);
       objD4 != RNeof;
       objD4 = GetDBRecordLink(objD4) )
  {
    if (objD4.dbType() != dbCLOUD) continue;
    DB15A2 = GetRecordAddressDB15(objD4);
    if (DB15A2->type() != 50) continue;
    RemoveObjectFromRoom(objD4, D6W, D5W);
    goto tag01ffd2;
//
//
  };
  if (D7w != 0)
  {
    D7w = 0;
    D6W = w_4;
    D5W = w_6;
    goto tag01ffd2;
  };
  ProcessTimersViewportAndSound(_3_);
  for (D7w=55; D7w<=255; D7w+=40)
  {
    TAG00dea8(RN(RNFireball), D7w, w_4, w_6, 255);
    ProcessTimersViewportAndSound(_4_);//TAG01fed6
    if (testdelay) UI_Sleep(testdelay);
//
//
  };
  QueueSound(17, w_4, w_6, 1);
  DB4A3->monsterType(mon_25);
  ProcessTimersViewportAndSound(_5_);//TAG01fed6(_5_);
  if (testdelay) UI_Sleep(testdelay);
  for (D7w=55; D7w<=255; D7w+=40)
  {
    TAG00dea8(RN(RNDispellMissile), D7w, w_4, w_6, 255);
    ProcessTimersViewportAndSound(_7_);//TAG01fed6(_7_);
    if (testdelay) UI_Sleep(testdelay);
//
//
  };
  for (D6W=3; D6W!=0; D6W--)
  {
    for (D7w = 4; D7w!=0; D7w--)
    {
      QueueSound(17, w_4, w_6, 1);
      if (D7w & 1)
      {
        D0W = 25;
      }
      else
      {
        D0W = 23;
      };
      DB4A3->monsterType(D0W);
      for (D5W=sw(D6W-1); D5W!=0; D5W--)
      {
        ProcessTimersViewportAndSound(_8_);//TAG01fed6(_8_);
        if (testdelay) UI_Sleep(testdelay);
//
      };
//
//
    }; //D7W
//
//
  }; //D6W
  ProcessTimersViewportAndSound(_30_);//TAG01fed6(_7_);
  UI_Sleep(400);
  TAG00dea8(RN(RNFireball), 0xff, w_4, w_6, 255);
  ProcessTimersViewportAndSound(_12_);//TAG01fed6(_12_)
  UI_Sleep(800);
  TAG00dea8(RN(RNDispellMissile), 0xff, w_4, w_6, 255);
  ProcessTimersViewportAndSound(_15_);//TAG01fed6(_12_);
  UI_Sleep(800);
  DB4A3->monsterType(mon_GreyLord);
  ProcessTimersViewportAndSound(_13_);//TAG01fed6(_13_);
  if (testdelay) UI_Sleep(testdelay);
  d.Word1868 = 1;
  ProcessTimersViewportAndSound(_14_);//TAG01fed6(_14_);
  if (testdelay) UI_Sleep(testdelay);
  for (D7w=0; D7w<d.width; D7w++)
  {
    for (D6W=0; D6W<d.height; D6W++)
    {
      objD4 = FindFirstMonster(D7w, D6W);
      if (objD4 ==RNeof) continue;
      if ((D7w != w_4) || (D6W != w_6))
      {
        DeleteMonster(D7w, D6W);
      };
//
//
    };
//
//
  };
  ProcessTimersViewportAndSound(_16_);//TAG01fed6(_16_);
  if (testdelay) UI_Sleep(testdelay);
  objD4 = FindFirstObject(0,0);
  for (D7w=0; objD4!=RNeof; objD4 = GetDBRecordLink(objD4))
  {
    if (objD4.dbType() != dbTEXT) continue;
    obj_26[D7w++] = objD4;
//
//
  };
  b_9 = 'A';
  for (D5W=D7w; (D7w--)!=0;)
  {
    for (D6W=0; D6W<D5W; D6W++)
    {
      DecodeText(b_226, 
                 GetRecordAddressDB2(obj_26[D6W]), 
                 (unsigned)0x8001, 
                 200);
      D0B = b_226[1];
      D1B = b_9;
      if (D0B == D1B)
      {
        TAG00187e();
        b_226[1] = 10;
        PrintLines(_29_,15, b_226+1);
        ProcessTimersViewportAndSound(_17_);//TAG01fed6(_17_);
        VBLDelay(_18_,780);
        b_9++;
        break;
      };
//
//
    };
//
  };
  PrintLines(_22_,0, d.Byte1830); // Linefeed
  PrintLines(_21_,15, "THE END.");
  PrintLines(_23_,0, d.Byte1830); // Linefeed
  VBLDelay(_19_,600);
  d.CanRestartFromSavegame = 0;
  TAG01f9e6(_20_,1);
  RETURN;
}