//---------------------------------------------------------------------------
// Character class
//---------------------------------------------------------------------------

// Macro to align a long to a 4-byte boundary
#define ALIGN(a)   ((a + 3) & 0xfffffffc)
#define ABS(x)     ((x) <  0 ? -(x) : (x))

#include <stdio.h>
#include <sys\types.h>
#include <libetc.h>
#include <libgte.h>
#include <libgpu.h>
#include <rand.h>
#include <libcd.h>
#include <memory.h>
#include "..\include\compile.h"
#include "..\include\db.h"
#include "..\include\readfile.h"
#include "..\include\bkgnd.h"
#include "..\include\alocvram.h"
#include "..\include\fonts.h"

void PlaySFX (int vabnbr, int sfxnbr, int voll, int volr);

extern u_long  FrameCounter;
extern int     LE1orLE2;
extern int     LevelSelected;
extern int     bkdvab;
extern int     PassFallen;
extern int     ActualScript;
extern int     HelicoScale;
extern int     Difficulty;
extern int     DynamicDifficulty;
extern int     BloodyMode;
extern int     WhichClut;
extern int     NightVision;
extern char    BossAirportDown;
extern char    BossAirportDead;
extern char    AirportEndCnt;
extern char    BossChemicalDead;
extern char    CarChaseTimerNear;
extern char    CarChaseTimerFar;
extern char    CarKilled[7];
extern u_short CarTimer[7];
extern char    MetalBoxUp;
extern short   SmallFireX;
extern short   SmallFireY;
extern int     debug;
extern int     YouLose;
extern char    BarrelUp;
extern char    BossStageCoachDead;

#ifndef __shootpoint
   #define __shootpoint
   typedef struct
   {
      int x;
      int y;
      int GunType;
      int Counter;
   } shootpnt;
#endif

#define ACTIONRUN       0
#define ACTIONJUMP      1
#define ACTIONRISE      2
#define ACTIONSHOOT1    3
#define ACTIONSHOOT2    4
#define ACTIONDIE1      5
#define ACTIONDIE2      6
#define ACTIONROLL      7
#define ACTIONDUCK      8
#define ACTIONWAIT      9
#define ACTIONRELOAD    10
#define ACTIONFALL      11
#define ACTIONIMHIT     12

#include "..\include\characte.h"

// Prototypes...
void NewInnocent (int deltay, int depth, Character *chara);
void NewExplosion (int coorx, int coory, int depth, int scal);
void AddBlood (int coorx, int coory, int typebld);
void AddFlash (shootpnt *shot);
void InitEnemyLife (Character *enemy);
inline void psx_memset (u_char *dst, u_char c, int n);
inline void psx_memcpy (u_char *dst, u_char *src, int n);

#include "..\include\links.h"

//-------------------------------------
// Constructor.
//-------------------------------------
Character::Character ()
{
   Death = 1;
   Origina = 0;
   ShotToDeath = 1;
   VramMemHandle = -1;
}

//-------------------------------------
// Initialize the class and load an
// animation file.
//-------------------------------------
int Character::Load (char filename[], u_long *memptr)
{
   char *memloc;
   int i, j, k, rot;
   int bigx = 0, bigy = 0;
   int numframe;

   SpecificDeath = -1;
   ShotToDeath = 0;
   Origina = 1;
   Projectile = 0;
   ActualProjectile = 0;
   Bossu = 0;
   CarState = 0;
   CarLoopX = 0;
   if ((ActualScript == 7) && (LE1orLE2 == 1) && (CharNbr < 12) && (CharNbr > 6))
      CarLoopY = 512;           // For barrels...
   else
      CarLoopY = 0;
   CarShake = 0;

   memloc = (char *)*memptr;

   if ((u_long)memloc & 3)
      memloc = (char *)(((u_long)memloc + 4) & 0xFFFFFFFC);

   //-------- Load the sprites --------
   #ifdef MESSAGEDUMPER
      DumpMessage ("Opening sprites file...\n");
   #endif
   if (OpenFile (filename) == -1)
      return (0);

   #ifdef MESSAGEDUMPER
      DumpMessage ("Read the sprites file header...\n");
   #endif
   // Reads the header of the sprites file
   if (ReadFile (memloc, 5) != 5)
   {
      CloseFile ();
      return (0);
   }

   // Checks to be sure it is a sprite file
   if ((memloc[0] != 'A') || (memloc[1] != 'N'))
   {
      CloseFile ();
      return (0);
   }

   NumSprite = memloc[2] + (memloc[3] * 256);
   compressed = memloc[4];

   // Reads the width of every sprite
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   if (ReadFile ((char *)memloc, NumSprite << 1) != NumSprite << 1)
   {
      CloseFile ();
      return (0);
   }
   SprW = (u_short *)memloc;
   memloc += NumSprite << 1;

   // Reads the height of every sprite
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   if (ReadFile ((char *)memloc, NumSprite << 1) != NumSprite << 1)
   {
      CloseFile ();
      return (0);
   }
   SprH = (u_short *)memloc;
   memloc += NumSprite << 1;

   // Reads the address of every sprite
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   if (ReadFile ((char *)memloc, NumSprite << 2) != NumSprite << 2)
   {
      CloseFile ();
      return (0);
   }
   SprAddress = (int *)memloc;
   memloc += NumSprite << 2;

   // Get a handle for each sprite
   SprHandle = (short *)memloc;
   for (i = 0; i < NumSprite; i++) {
      *((short *)memloc) = SprCacheHandle();
      (int)memloc += sizeof(short);
   }

   // Reads the map size of every sprite
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   if (ReadFile ((char *)memloc, NumSprite << 1) != NumSprite << 1)
   {
      CloseFile ();
      return (0);
   }
   SprMapSize = (u_short *)memloc;
   memloc += NumSprite << 1;

   // Reads the clut size
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   if (ReadFile (memloc, 2) != 2)
   {
      CloseFile ();
      return (0);
   }
   i = memloc[0] + (memloc[1] * 256);

   // Reads the clut
   if (ReadFile ((char *)memloc, i << 1) != i << 1)
   {
      CloseFile ();
      return (0);
   }
   ActualClut = (u_short *)memloc; // Allocate space for clut and B & W clut
   memloc += i << 1;               //
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   DestClut = (u_short *)memloc;   //
   memloc += i << 1;               //

   #ifdef MESSAGEDUMPER
      DumpMessage ("Read the sprites file hitmaps...\n");
   #endif
   // Reads the width of every hit map
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   if (ReadFile (memloc, NumSprite) != NumSprite)
   {
      CloseFile ();
      return (0);
   }
   SprHitW = memloc;
   memloc += NumSprite;

   // Reads the height of every hit map
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   if (ReadFile (memloc, NumSprite) != NumSprite)
   {
      CloseFile ();
      return (0);
   }
   SprHitH = memloc;
   memloc += NumSprite;

   // Calculating the size of all hit map
   i = 0;
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   SprHitMapAdr = (int *)memloc;
   memloc += NumSprite << 2;
   for (j = 0; j < NumSprite; j++)
   {
      SprHitMapAdr[j] = i + (int)memloc;
      i += (SprHitW[j] * SprHitH[j]);
   }

   // Reads the hit maps themselves
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   if (ReadFile ((char *)memloc, i) != i)
   {
      CloseFile ();
      return (0);
   }
   memloc = (char *)((int)memloc + i);
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);

   #ifdef MESSAGEDUMPER
      DumpMessage ("Calculating size of all sprites...\n");
   #endif
       // Calculating the size of all sprites
      //memloc += NumSprite << 1;
      //memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
      //i = 0;
      //rot = 0;
      //for (j = 0; j < NumSprite; j++)
      //{
      //   i += SprMapSize[j];
      //   rot += SprW[j] * SprH[j];
      //   rot = (rot + 3) & 0xFFFFFFFC;
      //}

      //for (j = 0; j < NumSprite; j++)
      //   SprAddress[j] += (int)memloc + rot;

      // Calculating the (compressed) size of all sprites
      i = 0;
      for (j = 0; j < NumSprite; j++) {
         i += SprMapSize[j];
         SprAddress[j] += (int)memloc;
      }

   #ifdef MESSAGEDUMPER
      DumpMessage ("Read the sprites themselves...\n");
   #endif

   // Reads the sprites themselves
   //if (ReadFile ((char *)(memloc + rot), i) != i)
   if (ReadFile (memloc, i) != i)
   {
      CloseFile ();
      return (0);
   }

   //#ifdef MESSAGEDUMPER
   //   DumpMessage ("Do some hard work...\n");
   //#endif
   //for (i = 0; i < NumSprite; i++)
   //{
   //   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   //   DecompressTexture (SprW[i], SprH[i], (u_char *)SprAddress[i],
   //                      (u_char *)memloc);
   //   SprAddress[i] = (int)memloc;
   //   memloc += SprW[i] * SprH[i];
   //   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   //}

   memloc = (char *)((int)memloc + i);
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);

   //-- Load the animation sequences --

   #ifdef MESSAGEDUMPER
      DumpMessage ("Read the animation sequences...\n");
   #endif
   // Reads the number of actions
   if (ReadFile (memloc, 1) != 1)
   {
      CloseFile ();
      return (0);
   }
   NbrAction = memloc[0];

   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   Action = (ACTION *)memloc;
   memloc = (char *)((u_long)memloc + sizeof (ACTION) * NbrAction);
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);

   for (i = 0; i < NbrAction; i++)      // for every action...
   {
      // Reads the nbr of frames in that action, its type and the links
      if (ReadFile (memloc, 5) != 5)
      {
         CloseFile ();
         return (0);
      }
      numframe = memloc[0];

      Action[i].NumFrame = memloc[0];
      Action[i].ActionType = memloc[1];
      Action[i].DieLink = memloc[2];
      Action[i].ShotLink = memloc[3];
      Action[i].EndLink = memloc[4];
      Action[i].OffsetX = (signed char *)memloc;
      memloc = (char *)(((int)memloc + 3 + numframe + 1) & 0xFFFFFFFC);
      Action[i].OffsetY = (signed char *)memloc;
      memloc = (char *)(((int)memloc + 3 + numframe + 1) & 0xFFFFFFFC);
      Action[i].Animation = (u_char *)memloc;
      memloc = (char *)(((int)memloc + 3 + numframe + 1) & 0xFFFFFFFC);
      Action[i].Wait = (u_char *)memloc;
      memloc = (char *)(((int)memloc + 3 + numframe + 1) & 0xFFFFFFFC);

      // Reads the x offset of every frame
      if (ReadFile ((char *)Action[i].OffsetX, numframe) != numframe)
      {
         CloseFile ();
         return (0);
      }

      // Reads the y offset of every frame
      if (ReadFile ((char *)Action[i].OffsetY, numframe) != numframe)
      {
         CloseFile ();
         return (0);
      }

      // Reads the sprite # every frame refers to
      if (ReadFile ((char *)Action[i].Animation, numframe) != numframe)
      {
         CloseFile ();
         return (0);
      }

      // Reads the duration of every frame
      if (ReadFile ((char *)Action[i].Wait, numframe) != numframe)
      {
         CloseFile ();
         return (0);
      }

      // Reads the gun type of this animation
      if (ReadFile (memloc, 1) != 1)
      {
         CloseFile ();
         return (0);
      }
      Action[i].GunType = memloc[0];

      // If the gun type is different than NONE, reads gun shoot coordinate.
      if ((Action[i].GunType & 127) != 0)
      {
         if (ReadFile (memloc, 2) != 2)
         {
            CloseFile ();
            return (0);
         }
         Action[i].GunOfsX = memloc[0];
         Action[i].GunOfsY = memloc[1];
      }

      Action[i].Animation[numframe] = 255;
      Action[i].OffsetX[numframe] = 255;
      Action[i].OffsetY[numframe] = 255;
      Action[i].Wait[numframe] = 255;
   }

   // Check if the file is terminated by 'end'
   if (ReadFile (memloc, 7) != 7)
   {
      CloseFile ();
      return (0);
   }

   #ifdef MESSAGEDUMPER
      DumpMessage ("Closing sprites file...\n");
   #endif
   if (CloseFile () == -1)
      return (0);

   BossAngle = 0;
   BossAngle2 = 1024;
   x = 160;
   MoveDestX = x;
   MoveXSource = x;
   y = 120;
   MoveDestY = y;
   MoveYSource = y;
   MoveTotalStep = 0;
   MoveActualStep = 0;
   scale = ONE;
   ScaleActualStep = 0;
   ScaleTotalStep = 0;
   CurrentFrame = 0;
   CurrentAction = 0;
   depth = 14;
   Death = 1;
   Blink = 0;
   Blinkable = 0;
   BWChangeComplete = 1;
   Chrctristic = 517;
   GunMove = 0;
   GunColorChange = 1;

   //----------------------------------
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   SprCX = memloc;
   memloc += NumSprite;
   memloc = (char *)(((int)memloc + 3) & 0xFFFFFFFC);
   SprCY = memloc;
   memloc += NumSprite;
   for (i = 0; i < NumSprite; i++)
   {
      SprCX[i] = SprW[i] >> 1;
      SprCY[i] = SprH[i] >> 1;
   }

   OriginalClut = (u_short *)0x12345678;
   if ((LE1orLE2 == 0) && (LevelSelected == 5))
   {
      if ((u_long)memloc & 3)
         memloc = (char *)(((u_long)memloc + 4) & 0xFFFFFFFC);
      OriginalClut = (u_short *)memloc;
      memloc += 512;
      for (i = 0; i < 256; i++)
         OriginalClut[i] = ActualClut[i];
   }

   *memptr = (u_long)memloc;

   WaitCnt = 0;
   Lifepnt = 0;
   NewAction = 0;

   Flic = 1;
   PrevSprite = -1;
   VramMemHandle = -1;

   #ifdef MESSAGEDUMPER
      DumpMessage ("Resetting SFX & VAB start & end...\n");
   #endif

   for (i = 0; i < MAXACTION; i++)
   {
      SfxStart[i] = 255;
      SfxEnd[i] = 255;
      VabStart[i] = 255;
      VabEnd[i] = 255;
   }

   #ifdef MESSAGEDUMPER
      DumpMessage ("Try to load the SFX file...\n");
   #endif

   // Try to load the file that contains SFX links
   for (i = 0; (filename[i] != '.') && (filename[i] != 0); i++)
      filnam[i] = filename[i];
   filnam[i] = '.';
   filnam[i + 1] = 'S';
   filnam[i + 2] = 'L';
   filnam[i + 3] = 'K';
   filnam[i + 4] = 0;
   LoadSLK (filnam, (u_long *)*memptr);

   if ((ClutY = AllocClut () + 480) == 479) // Allocate a clut for all the
      return (0);                           // duration of the game

   RECT rect = {0,ClutY, 256,1};            // Put clut in vram
   LoadImage (&rect, (u_long *)ActualClut);

   return (-1);
}

//-------------------------------------
// Reset all animation data for in-
// between stages
//-------------------------------------
void Character::Clear (void)
{
   Death = 1;
   ShotToDeath = 1;

   if (VramMemHandle != -1)
   {
      FreeVram (VramMemHandle);
      VramMemHandle = -1;
   }

   if (Origina)                     // Free the clut space, only if it's an
      FreeClut (ClutY - 480);       // original character
   
   x = 160;
   y = 120;
   scale = ONE;
   ScaleActualStep = 0;
   ScaleTotalStep = 0;
}

//-------------------------------------
// Stop a character's animation
//-------------------------------------
void Character::StopAction (void)
{
   Death = 1;
   if (!((LE1orLE2 == 0) && (LevelSelected == 0)))
      ShotToDeath = 1;

   if (VramMemHandle != -1)
   {
      FreeVram (VramMemHandle);
      VramMemHandle = -1;
   }
}

//-------------------------------------
// Set the current frame number in the
// animation
//-------------------------------------
void Character::Set_Frame (int frame)
{
   CurrentFrame = frame % NumSprite;
}

//-------------------------------------
// Set the coordinate of the animation
// in the buffer
//-------------------------------------
void Character::Set_Location (register int coorx, register int coory)
{
   int i;

   x = coorx;
   MoveDestX = x;
   MoveXSource = x;
   y = coory;
   MoveDestY = y;
   MoveYSource = y;
   MoveTotalStep = 0;
   MoveActualStep = 0;

   for (i = 0; i < NbrAttaches; i++)
   {
      if (AttachTo[i] == this)
         Attach[i]->Set_Location (x + AttachDX[i] * scale / 4096, y + AttachDY[i] * scale / 4096);
   }
}

//-------------------------------------
// Set the scale factor for the anima-
// tion
//-------------------------------------
void Character::Scale (register int scaling)
{
   scale = scaling;
   ScaleTotalStep = 0;
   ScaleActualStep = 0;
}

//-------------------------------------
// Set the depth of the character in
// the backlground
//-------------------------------------
void Character::SetDepth (register int dpth)
{
   depth = dpth;
}

//-------------------------------------
// Make the character dying.
//-------------------------------------
void Character::Die (void)
{
   if (SpecificDeath == -1)
      StartAction (Action[CurrentAction].DieLink, Inverse);
   else
      StartAction (SpecificDeath, Inverse);

   if ((LevelSelected == 5) && (LE1orLE2 == 0) && (Chrctristic & 2048))
      Dettach (this);

   if (Chrctristic & 8)
      Dettach (this);
}

//-------------------------------------
// Set the center coordinate within a
// sprite
//-------------------------------------
void Character::SetCenter (int coorx, int coory, int sprnum)
{
   SprCX[sprnum] = coorx;
   SprCY[sprnum] = coory;
}

//-------------------------------------
// Draw a frame of the animation
//-------------------------------------
void Character::DrawSpr (int sprnum, point p, DoubleBuffer *db)
{
   int x = p.x;
   int y = p.y;
   POLY_FT4 *polygon;
   SPRT *sprite;
   DR_MODE *dr_mode;
   RECT tww = {SprTPX,SprTPY,256,256};

   if ((x + (SprW[sprnum] * scale / 8192) < 0) || (x - (SprW[sprnum] * scale / 8192) > 319) ||
       (y + (SprH[sprnum] * scale / 8192) < 0) || (y - (SprH[sprnum] * scale / 8192) > 239))
      return;

   if (Chrctristic & 32768)
      return;

   if (scale != ONE)
   {
      polygon = db->Get_Poly ();
      polygon->u0 = polygon->u2 = SprU;
      polygon->v0 = polygon->v1 = SprV;
      polygon->v2 = polygon->v3 = SprV + SprH[sprnum] - 1;
      polygon->u1 = polygon->u3 = SprU + SprW[sprnum] - 1;
      polygon->tpage = GetTPage (1, 0, SprTPX,SprTPY);
      if ((Blinkable) && (Blink))
      {
         Blink--;
         polygon->clut = GetClut (0, 511);
      }
      else
         polygon->clut = GetClut (0, ClutY);
      if (Chrctristic & 32)
         SetSemiTrans (polygon, 1);
      else
         SetSemiTrans (polygon, 0);

      int x1,y1, x2,y2;
      x1 = (x - (SprCX[sprnum] * scale) / 4096);
      y1 = (y - (SprCY[sprnum] * scale) / 4096);
      x2 = (x1 + ((SprW[sprnum] - 1) * scale) / 4096);
      y2 = (y1 + ((SprH[sprnum] - 1) * scale) / 4096);
      if (Inverse)
      {
         int temp = x2;
         x2 = x1;
         x1 = temp;
      }
      if ((CharNbr == 4) && (ActualScript == 7) && (LE1orLE2 == 1))
      {        // Special case for the wagon shadow in stage coach boss level
         if (y2 < 154)
            return;
         else if (y1 < 154)
            y1 = 154;
      }
      setXY4 (polygon, x1,y1, x2,y1, x1,y2, x2,y2);
      if (Chrctristic & 320)
         db->Add_Poly (polygon, 14);
      else
         db->Add_Poly (polygon, depth);
   }
   else
   {
      if (!Inverse)
      {
         sprite = db->Get_Spr ();
         setXY0 (sprite, x - SprCX[sprnum],y - SprCY[sprnum]);
         sprite->w = SprW[sprnum];
         sprite->h = SprH[sprnum];
         sprite->u0 = SprU;
         sprite->v0 = SprV;

         if ((Blinkable) && (Blink))
         {
            Blink--;
            sprite->clut = GetClut (0, 511);
         }
         else
            sprite->clut = GetClut (0, ClutY);
         if (Chrctristic & 32)
            SetSemiTrans (sprite, 1);
         else
            SetSemiTrans (sprite, 0);
         if (Chrctristic & 320)
            db->Add_Spr (sprite, 14);
         else
            db->Add_Spr (sprite, depth);

         dr_mode = db->Get_DrMode ();
         SetDrawMode (dr_mode, 0, 0, GetTPage (1, 0, SprTPX,SprTPY), &tww);
         db->Add_DrMode (dr_mode, depth);
      }
      else
      {
         polygon = db->Get_Poly ();
         polygon->u0 = polygon->u2 = SprU;
         polygon->v0 = polygon->v1 = SprV;
         polygon->v2 = polygon->v3 = SprV + SprH[sprnum] - 1;
         polygon->u1 = polygon->u3 = SprU + SprW[sprnum] - 1;
         polygon->tpage = GetTPage (1, 0, SprTPX,SprTPY);
         if ((Blinkable) && (Blink))
         {
            Blink--;
            polygon->clut = GetClut (0, 511);
         }
         else
            polygon->clut = GetClut (0, ClutY);
         if (Chrctristic & 32)
            SetSemiTrans (polygon, 1);
         else
            SetSemiTrans (polygon, 0);

         int x1,y1, x2,y2;
         x2 = (x - (SprCX[sprnum] * scale) / 4096);
         x1 = (x2 + ((SprW[sprnum] - 1) * scale) / 4096);
         y1 = (y - (SprCY[sprnum] * scale) / 4096);
         y2 = (y1 + ((SprH[sprnum] - 1) * scale) / 4096);
         setXY4 (polygon, x1,y1, x2,y1, x1,y2, x2,y2);

         if (Chrctristic & 320)
            db->Add_Poly (polygon, 14);
         else
            db->Add_Poly (polygon, depth);
      }
   }
}

//-------------------------------------
// Start an animation
//-------------------------------------
// actionnum = Number of the animation
//             to be started
// Inverse specifies if the animation
//         is horizontally reversed
// Return 0 if the animation started,
//        -1 otherwise
//-------------------------------------
int Character::StartAction (int actionnum, int inverse)
{
   int i;
   int bigx = 0, bigy = 0;

   // Start an animation over another one only if permitted
   if ((Death) || (Chrctristic & 128) ||
       (Action[actionnum].ActionType == ACTIONDIE1) ||
       (Action[actionnum].ActionType == ACTIONFALL) ||
       (Action[actionnum].ActionType == ACTIONIMHIT))
   {
       if (ShotToDeath)
          return (-1);                      // Animation not started

      if (Chrctristic & 320)                // Calculate the destination
      {                                     // of the knife (or tomahawk)
         if (Death == 0)
            return (0);
         int coorx;
         if (rand () % 2)
            coorx = x + 60;
         else
            coorx = x - 60;
         if ((coorx > 319) || (coorx < 0))
            coorx = x - (coorx - x);

         if ((LevelSelected == 5) && (LE1orLE2 == 0))
         {
            if (inverse)
               GunDeltaX = 1;
            else
               GunDeltaX = -1;
         }
         else
            GunDeltaX = (coorx - x) / 60;

         if (~Chrctristic & 256)
            GunDeltaY = (200 - y) / 60;
         else
         {
            if (((LevelSelected == 5) || (LevelSelected == 1)) && (LE1orLE2 == 0))
               GunDeltaY = 1;
            else
               GunDeltaY = -1;
         }

         if ((LevelSelected == 5) && (LE1orLE2 == 0))
            Inverse = inverse;
         else
         {
            if (GunDeltaX > 0)
               Inverse = 0;
            else
               Inverse = 1;
         }
         scale = 128;
         ScaleActualStep = 0;
         ScaleTotalStep = 0;
         CurrentFrame = 0;
         CurrentAction = 0;
         Flic = 0;
         Death = 0;
         NewAction = 2;
         ActionBefore = CurrentAction;
         Lifepnt = 1;
         WaitCnt = 0;
      }
      else
      {
         if ((CharNbr == 1) && (ActualScript == 10) && (LE1orLE2 == 0))
         {
            if (((actionnum == 1) || (actionnum == 2)) && (CurrentAction != 0))
               return (-1);     // Do not move the box if it's already moving
            if ((actionnum == 1) || (actionnum == 2))
               MetalBoxUp = 2;          // Metal box moving.
            else if ((actionnum == 0) && (CurrentAction == 1))
               MetalBoxUp = 1;          // Metal box down.
            else if ((actionnum == 0) && (CurrentAction == 2))
               MetalBoxUp = 0;          // Metal box up.
         }
         if ((ActualScript == 10) && (LE1orLE2 == 0))
         {
            if ((CharNbr == 19) && (actionnum == 12))
            {
               if (!(((MetalBoxUp & 0x0F) == 0) /*&& ((MetalBoxUp >> 4) < 2)*/))
                  return (-1);          // Guerilla1 needs the box to be up.
               else
                  MetalBoxUp += 16;     
            }
            else if ((CharNbr == 20) && (actionnum == 2))
            {
               if (!(((MetalBoxUp & 0x0F) == 0) /*&& ((MetalBoxUp >> 4) < 2)*/))
                  return (-1);          // Guerilla2 needs the box to be up.
               else
                  MetalBoxUp += 16;     
            }
            else if ((CharNbr == 42) && (actionnum == 9))
            {
               if (!(((MetalBoxUp & 0x0F) == 1) /*&& ((MetalBoxUp >> 4) < 1)*/))
                  return (-1);
               else
                  MetalBoxUp += 16;     // Worker1 needs the box to be down.
            }
         }

         if (actionnum < NbrAction)
         {
            ActionBefore = CurrentAction;
            if ((Chrctristic & 2048) && (Lifepnt <= 0))
            {
               if (Action[actionnum].ActionType == ACTIONDIE1)
                  Lifepnt = 1;
               else
               {
                  if (LevelSelected == 5)
                     Lifepnt = 3 * Difficulty + 10;
                  else
                     Lifepnt = 8 * Difficulty + 10;
               }
            }
            if (Lifepnt <= 0)
               Lifepnt = 1;
            NewAction = 2;
            CurrentAction = actionnum;
            CurrentFrame = 0;
            Inverse = inverse;
            Death = 0;
            WaitCnt = 0;
            if ((Action[actionnum].ActionType == ACTIONDIE1) ||
                (Action[actionnum].ActionType == ACTIONDIE2) ||
                (Action[actionnum].ActionType == ACTIONFALL))
            {
               if (Chrctristic & 512)       // Enemies flickers
                  Flic = 40;
               else if (Chrctristic & 8)    // Guns last longer (but no flicker)
                  Flic = 60;
               else if (Chrctristic & 2)    // Victims last much longer (no flicker)
                  Flic = 120;
               else
                  Flic = 1;                 // Else fuck it.
            }
            else if (Action[CurrentAction].ActionType == ACTIONSHOOT1)
            {
               if (~Chrctristic & 2048)
               {
                  if ((Action[CurrentAction].GunType == 8) && (LE1orLE2 == 0) &&
                      (LevelSelected == 3))
                     Action[CurrentAction].Wait[Action[CurrentAction].NumFrame - 2] = 7;
                  else
                  {
                     if ((LE1orLE2 == 1) && (ActualScript == 14))
                        Action[CurrentAction].Wait[Action[CurrentAction].NumFrame - 2] = 20 - Difficulty * 2;
                     if ((LE1orLE2 == 0) && (ActualScript == 5) && (CharNbr == 52))
                        Action[CurrentAction].Wait[Action[CurrentAction].NumFrame - 2] = 3;
                     else if ((LE1orLE2 == 0) && (ActualScript == 17) && (CharNbr > 90) && (CharNbr < 95))
                        Action[CurrentAction].Wait[Action[CurrentAction].NumFrame - 2] = 3;
                     else
                        Action[CurrentAction].Wait[Action[CurrentAction].NumFrame - 2] = 60 - DynamicDifficulty * 5;
                  }
               }
               else
                  Action[CurrentAction].Wait[Action[CurrentAction].NumFrame - 2] = 10 - Difficulty;
            }
         }

         // Special case for the wagon in Stage coach boss level (LE2)
         if ((CharNbr == 0) && (ActualScript == 7) && (LE1orLE2 == 1) && (FrameCounter == 0))
         {
            scale = 2600;
            ScaleTotalStep = 0;
            ScaleActualStep = 0;
         }
      }

      if (VramMemHandle != -1)   // Allocate VRAM (free previous one if needed)
      {
         FreeVram (VramMemHandle);
         VramMemHandle = -1;
         PrevSprite = -1;
      }

      u_char *spp = Action[actionnum].Animation;
      // Allocate VRAM space for character's texture, and for the clut
      for (i = 0; spp[i] != 255; i++)
      {                                // Find the size of the Vram space
         if (SprW[spp[i]] > bigx)      // we're gonna need, and allocate it.
            bigx = SprW[spp[i]];
         if (SprH[spp[i]] > bigy)
            bigy = SprH[spp[i]];
      }

      if (bigx & 1)
         bigx = (bigx >> 1) + 1;
      else
         bigx >>= 1;

      VramMemSpace = AllocVram (bigx, bigy, &VramMemHandle, 0);

      SprU = (VramMemSpace.x % 64) << 1;    // Reinitialize some variables...
      SprV = (VramMemSpace.y % 256);
      SprTPX = VramMemSpace.x & ~63;
      SprTPY = VramMemSpace.y & ~255;

      if ((LE1orLE2 == 0) && (CharNbr == 61) && (ActualScript == 20))
         WhichClut = ClutY;

      if ((Chrctristic & 2048) && (LE1orLE2 == 0) && (LevelSelected == 2))
      {                                         // Boss Chinatown
         int bossx[9] = {222,215,227,224,215,215,225,215,222};
         int bossy[9] = {115,108,109,113,108,108,105,108,117};
         x = bossx[actionnum];
         MoveDestX = x;
         MoveXSource = x;
         y = bossy[actionnum];
         MoveDestY = y;
         MoveYSource = y;
      }

      if ((Chrctristic & 2048) && (LE1orLE2 == 1) && (LevelSelected == 0))
      {                                         // Boss cannon
         y = 107 - (SprH[Action[CurrentAction].Animation[CurrentFrame]] >> 1);
      }

      if ((Chrctristic & 2048) && (LE1orLE2 == 0) && (LevelSelected == 3))
      {
         if (actionnum == 6)
            BossAirportDown = 1;
         else
            BossAirportDown = 0;
      }

      if ((Chrctristic & 2048) && (LevelSelected == 5) && (LE1orLE2 == 0) &&
          (actionnum == 0))
         InitEnemyLife (this);

      return (0);       // Animation started
   }

   return (-1);         // Animation not started
}

//-------------------------------------
// Reinitialize some values to their
// default values
//-------------------------------------
void Character::ReInit (void)
{
   int i;

   SpecificDeath = -1;
   ShotToDeath = 0;
   Projectile = 0;
   ActualProjectile = 0;
   x = 160;
   MoveDestX = x;
   MoveXSource = x;
   y = 120;
   MoveDestY = y;
   MoveYSource = y;
   MoveActualStep = 0;
   MoveTotalStep = 0;
   scale = ONE;
   ScaleTotalStep = 0;
   ScaleActualStep = 0;
   Lifepnt = 0;
   Death = 1;
   WaitCnt = 0;
   Flic = 40;
   CurrentFrame = 0;
   CurrentAction = 0;
   NewAction = 0;
   PrevSprite = -1;
   VramMemHandle = -1;
   Blink = 0;
   Blinkable = 0;
   BWChangeComplete = 1;
   GunMove = 0;
   GunColorChange = 2;
   BossAngle = 0;
   BossAngle2 = 0;
   CarState = 0;
   CarLoopX = 0;
   CarLoopY = 0;
   CarShake = 0;
}

//-------------------------------------
// Initialize the sprites arrays.
// Should not be called by anyone else
// than the Clone_Character procedure.
//-------------------------------------
void Character::Init_Spr_Array (int numsprite, char compress, u_short characte,
                      u_short *sprw, u_short *sprh, char *sprcx, char *sprcy,
                      int *spraddress, short *sprhandle, u_short *sprmapsize,
                      char *sprhitw, char *sprhith, int *sprhitmapadr)
{
   int i;
   int bigx = 0, bigy = 0;

   NumSprite = numsprite;
   compressed = compress;
   Chrctristic = characte;

   SprW = sprw;
   SprH = sprh;
   SprCX = sprcx;
   SprCY = sprcy;
   SprAddress = spraddress;
   SprHandle  = sprhandle;
   SprMapSize = sprmapsize;
   SprHitW = sprhitw;
   SprHitH = sprhith;
   SprHitMapAdr = sprhitmapadr;
}

//-------------------------------------
// Initialize action & animation arrays
// Should not be called by anyone else
// than the Clone_Character procedure.
//-------------------------------------
void Character::Init_Anim_Array (int nbraction, int nbranimation,
                                 ACTION *action, int cluty, u_short *actualclut,
                                 u_short *destclut, char sfxstart[],
                                 char sfxend[], char vabstart[], char vabend[],
                                 int origina)
{
   int i;

   NbrAction = nbraction;
   NbrAnimation = nbranimation;

   Action = action;

   ActualClut = actualclut;
   DestClut = destclut;
   ClutY = cluty;

   for (i = 0; i < NbrAction; i++)
   {
      SfxStart[i] = sfxstart[i];
      SfxEnd[i] = sfxend[i];
      VabStart[i] = vabstart[i];
      VabEnd[i] = vabend[i];
   }

   if ((Origina = origina) == 1)
   {
      ClutY = AllocClut () + 480;   // Allocate its own clut space
      RECT rect = {0,ClutY, 256,1};            // Put clut in vram
      LoadImage (&rect, (u_long *)ActualClut);
   }
}

//-------------------------------------
// Make a clone of the actual character
//-------------------------------------
// In: pal = 1 if the character needs
//             it's own clut.
// Out: A ptr to the new character
//-------------------------------------
Character *Character::Clone_Character (int pal, u_long *memptr)
{
   u_short *newclut;
   u_short *newdestclut;
   int i;

   Character *chara;                        // Allocate a new character
   *memptr = (u_long)((*memptr + 3) & 0xFFFFFFFC);
   chara = (Character *)*memptr;
   *memptr += sizeof (Character);

   if (pal)     // We have to duplicate the clut, on demand (if pal = 1)
   {
      *memptr = (u_long)((*memptr + 3) & 0xFFFFFFFC);
      newclut = (u_short *)*memptr;
      *memptr += 512;
      *memptr = (u_long)((*memptr + 3) & 0xFFFFFFFC);
      newdestclut = (u_short *)*memptr;
      *memptr += 512;

      for (i = 0; i < 512; i++)
      {
         newclut[i] = ActualClut[i];
         newdestclut[i] = DestClut[i];
      }

   }

   chara->ReInit ();
   if (pal)
      chara->Init_Anim_Array (NbrAction, NbrAnimation, Action, ClutY,
                              newclut, newdestclut, SfxStart, SfxEnd, VabStart,
                              VabEnd, pal);
   else
      chara->Init_Anim_Array (NbrAction, NbrAnimation, Action, ClutY,
                              ActualClut, DestClut, SfxStart, SfxEnd, VabStart,
                              VabEnd, pal);
   chara->Init_Spr_Array (NumSprite, compressed, Chrctristic, SprW, SprH,
                          SprCX, SprCY, SprAddress, SprHandle, SprMapSize,
                          SprHitW, SprHitH, SprHitMapAdr);
   chara->SetDepth (depth);

   return (chara);
}

//-------------------------------------
// Draw the actual frame of the anima-
// tion.
//-------------------------------------
void Character::DrawFrame (int bkdx, int bkdy, DoubleBuffer *db)
{
   int   i;
   point po;

   if (!Death)              // Don't draw anything if the character is dead
   {
      if (Chrctristic & 320)                        // Throwed knife
      {                                             // (or tomahawk)...
         i = CurrentFrame % Action[0].NumFrame;

         po.x = x + bkdx;
         po.y = y + bkdy;
         DrawSpr (i, po, db);

         return;
      }

      if (Action[CurrentAction].Animation[CurrentFrame] != 255)
      {
         i = Action[CurrentAction].Animation[CurrentFrame];

         if (i != PrevSprite)
         {
            //DecompressTexture (SprW[i], SprH[i],
            //                   (u_char *)SprAddress[i], (u_char *)decom);
            RECT rect = {SprTPX + (SprU >> 1),SprTPY + SprV, SprW[i] >> 1,SprH[i]};
            //LoadImage (&rect, (u_long *)SprAddress[i]);
            LoadCompressedImage(&rect, i);
            PrevSprite = i;
         }

         po.x = x + bkdx;
         po.y = y + bkdy;
         DrawSpr (i, po, db);
      }
      else if ((Action[CurrentAction].ActionType == ACTIONDIE1) ||
               (Action[CurrentAction].ActionType == ACTIONFALL))
      {
         if ((Flic--) == 0)
         {
            Death = 1;
            if (VramMemHandle != -1)
            {
               FreeVram (VramMemHandle);
               VramMemHandle = -1;
            }
            PrevSprite = -1;
         }
         else if (Flic % 3)
         {
            i = Action[CurrentAction].Animation[CurrentFrame - 1];
            if (i != PrevSprite)
            {
               //DecompressTexture (SprW[i], SprH[i],
               //                   (u_char *)SprAddress[i], (u_char *)decom);
               RECT rect = {SprTPX + (SprU >> 1),SprTPY, SprW[i] >> 1,SprH[i]};
               //LoadImage (&rect, (u_long *)SprAddress[i]);
               LoadCompressedImage(&rect, i);
               PrevSprite = i;
            }
            po.x = x + bkdx;
            po.y = y + bkdy;
            DrawSpr (i, po, db);
         }
      }
   }
}

//-------------------------------------
// Specify the # of life points the
// character must have
//-------------------------------------
void Character::LifePoint (int life)
{
   if (life)
      Lifepnt = life;
}

//-------------------------------------
// Return the character's life points
//-------------------------------------
int Character::GetLifePoint (void)
{
   if (ShotToDeath)
      return (0);
   else
      return (Lifepnt);
}

//-------------------------------------
// Return the depth of the character
//-------------------------------------
int Character::GetDepth (void)
{
   return (depth);
}

//-------------------------------------
// Reads a SFX link file (.SLK)
//-------------------------------------
int Character::LoadSLK (char filename[], u_long *memptr)
{
   int i;
   char *memloc = (char *)memptr;

   memloc = (char *)ALIGN ((int)memloc);

   OpenFile (filename);

   // Read the file header and do a validation of it
   if (ReadFile (memloc, 5) != 5)
   {
      CloseFile ();
      return (-1);
   }

   if ((memloc[0] != 'L') || (memloc[1] != 'S') || (memloc[2] != 'F') ||
       (memloc[3] != 'X'))
   {
      CloseFile ();
      return (-1);
   }
   i = memloc[4];

   // Read VAB nbrs containing the SFX nbrs played at start of the animations
   if (ReadFile (VabStart, i) != i)
   {
      CloseFile ();
      return (-1);
   }

   // Read VAB nbrs containing the SFX nbrs played at end of the animations
   if (ReadFile (VabEnd, i) != i)
   {
      CloseFile ();
      return (-1);
   }

   // Read SFX numbers played at start of the animations
   if (ReadFile (SfxStart, i) != i)
   {
      CloseFile ();
      return (-1);
   }

   // Read SFX numbers played at end of the animations
   if (ReadFile (SfxEnd, i) != i)
   {
      CloseFile ();
      return (-1);
   }

   // Check if there is a 'end' at the end of the file
   if (ReadFile (memloc, 3) != 3)
   {
      CloseFile ();
      return (-1);
   }
   if ((memloc[0] != 'e') || (memloc[1] != 'n') || (memloc[2] != 'd'))
   {
      CloseFile ();
      return (-1);
   }

   return (CloseFile ());
}

//-------------------------------------
// Move a sprite with a specified delta
//-------------------------------------
void Character::Move (int dx, int dy)
{
   int j = 0;

   x += dx;
   MoveDestX = x;
   MoveXSource = x;
   y += dy;
   MoveDestY = y;
   MoveYSource = y;

   while (j < NbrAttaches)
   {
      if (AttachTo[j] == this)
         Attach[j]->Move (dx, dy);
      j++;
   }
}

//-------------------------------------
// Return sprite's coordinate
//-------------------------------------
point Character::Coordinate (void)
{
   point po;

   po.x = x;
   po.y = y;

   return (po);
}

//-------------------------------------
// Specifies that an action is popping.
//-------------------------------------
void Character::Pop (int actio)
{
   Action[actio].GunType |= 128;
}

//-------------------------------------
// Enables the character to blink
//-------------------------------------
void Character::MakeBlinkable (void)
{
   Blinkable = 1;
}

//-------------------------------------
// Makes the character translucent
//-------------------------------------
void Character::MakeTranslucent (void)
{
   int i;

   if (!Origina)                // Only perform Clut work if the character's
      return;                   // an original (has a clut)

   for (i = 0; i < 256; i++)
   {
      if (ActualClut[i] & ~32768)
         ActualClut[i] |= 32768;
   }

   RECT rect = {0,ClutY, 256,1};            // Put modified clut in vram
   LoadImage (&rect, (u_long *)ActualClut);
}

//-------------------------------------
// Makes the character turns white
//-------------------------------------
void Character::TurnWhite (void)
{
   Blink = 5;
}


//-------------------------------------
// Flush a sprite's data to a PC file
//-------------------------------------
void Character::FlushSprFile (int sprnbr)
{
   char *marde;
   int sprsize;
   int filehandle;

   //marde = (char *)SprAddress[sprnbr];
   //sprsize = SprAddress[sprnbr + 1] - SprAddress[sprnbr];
   marde = (char *)SprCacheDecompress(SprHandle[sprnbr], (u_char *)SprAddress[sprnbr],
                                      SprW[sprnbr], SprH[sprnbr]);
   sprsize = SprW[sprnbr] * SprH[sprnbr];

   #ifdef TARGETCD
      filehandle = PCcreat ("\\SPRFLUSH.CD", 0);
      PCwrite (filehandle, marde, sprsize);
      PCclose (filehandle);
   #endif
   #ifdef TARGETPC
      filehandle = PCcreat ("\\SPRFLUSH.PC", 0);
      PCwrite (filehandle, marde, sprsize);
      PCclose (filehandle);
   #endif
}

//-------------------------------------
//
//-------------------------------------
void Character::DumpData (void)
{
//   FntPrint ("CarLoopY: %d\n", CarLoopY);
//   FntPrint ("Char[%d]  Action: %2d\n", CharNbr, CurrentAction);
//   FntPrint ("Char[%d]  Death:%d  ShotToDeath:%d\n", CharNbr, Death, ShotToDeath);
//   FntPrint ("Char[%d]  Depth: %d  Scale:%d\n", CharNbr, depth, scale);
//   FntPrint ("Char[%d]  X:%d Y:%d SpecificDeath:%d\n", CharNbr, x, y, SpecificDeath);
//   FntPrint ("Scale Actl:%d  Dest:%d  Step:%d/%d\n", scale, ScaleDest,
//             ScaleActualStep, ScaleTotalStep);
   FntPrint ("Characteristics: %d\n", Chrctristic);
//   FntPrint ("Flic: %d\n", Flic);
   FntPrint ("Lifepoints: %d\n", Lifepnt);
}

//-------------------------------------
// Build a destination clut
//-------------------------------------
// 0 = Black & white with fading
// 1 = White with fading
// 2 = Black with fading
// 3 = Green without fading
// 4 = Back to color without fading
//-------------------------------------
void Character::BuildDestClut (int typeclut)
{
   int i, temp;

   if (!Origina)
      return;

   if (typeclut == 0)                   // Black & white with fading
   {
      for (i = 0; i < 256; i++)
      {
         temp = ((ActualClut[i] & 31) + ((ActualClut[i] & 992) >> 5) +
                 ((ActualClut[i] & 31744) >> 10)) / 3;
         if ((temp == 0) && ((ActualClut[i] & ~32768) != 0))
            temp = 1;
         DestClut[i] = temp + (temp << 5) + (temp << 10) +
                          (ActualClut[i] & 32768);
      }
      BWChangeComplete = 0;
   }
   else if (typeclut == 1)              // White with fading
   {
      for (i = 0; i < 256; i++)
      {
         if ((ActualClut[i] & ~32768) == 0)
            DestClut[i] = ActualClut[i];
         else
            DestClut[i] = 0x7FFF + (ActualClut[i] & 0x8000);
      }
      BWChangeComplete = 0;
   }
   else if (typeclut == 2)              // Black with fading
   {
      for (i = 0; i < 256; i++)
      {
         if ((ActualClut[i] & ~32768) == 0)
         {
            DestClut[i] = ActualClut[i];
         }
         else
         {
            DestClut[i] = (ActualClut[i] & 0x8000) + 0x421;
         }
      }
      BWChangeComplete = 0;
   }
   else if (typeclut == 3)              // Green without fading
   {
      for (i = 0; i < 256; i++)
      {
         temp = ((OriginalClut[i] & 31) + ((OriginalClut[i] & 992) >> 5) +
                 ((OriginalClut[i] & 31744) >> 10)) / 3;
         if ((temp == 0) && ((OriginalClut[i] & ~32768) != 0))
            temp = 1;
         if ((~ActualClut[i] & 0x8000) && (temp == 0))
            ActualClut[i] = 0x0;
         else
         {
            if (temp == 0x1F)
               temp = 0x1E;
            ActualClut[i] = (~(temp << 5)) & 0x3E0 + (ActualClut[i] & 0x8000);
         }
      }

      RECT rect = {0,ClutY, 256,1};          // Put clut in vram
      LoadImage (&rect, (u_long *)ActualClut);
      BWChangeComplete = 1;
   }
   else if (typeclut == 4)              // Color without fading
   {
      for (i = 0; i < 256; i++)
         ActualClut[i] = OriginalClut[i];

      RECT rect = {0,ClutY, 256,1};          // Put clut in vram
      LoadImage (&rect, (u_long *)ActualClut);
      BWChangeComplete = 1;
   }
}

//-------------------------------------
// Change a clut so that it becomes
// closer to a specified target clut.
// Returns 0 if both cluts are the
// same.
//-------------------------------------
int Character::ChangeClut (int black)
{
   int i, j, k;
   u_short temp;

   if (!Origina)
      return (0);

   if (BWChangeComplete == 1)
      return (0);

   if (Chrctristic & 8)
   {
      j = 254;
      k = 2;
   }
   else
   {
      j = 256;
      k = 0;
   }

   for (i = 0; i < 256; i++)
   {
      if (ActualClut[i] == DestClut[i])
         j--;
      else
      {
         temp = ActualClut[i] & 31;
         if (temp != (DestClut[i] & 31))
         {
            ActualClut[i] &= ~31;
            if (temp > (DestClut[i] & 31))
               ActualClut[i] |= temp - 1;
            else
               ActualClut[i] |= temp + 1;
         }

         temp = ActualClut[i] & 992;
         if (temp != (DestClut[i] & 992))
         {
            ActualClut[i] &= ~992;
            if (temp > (DestClut[i] & 992))
               ActualClut[i] |= temp - 32;
            else
               ActualClut[i] |= temp + 32;
         }

         temp = ActualClut[i] & 31744;
         if (temp != (DestClut[i] & 31744))
         {
            ActualClut[i] &= ~31744;
            if (temp > (DestClut[i] & 31744))
               ActualClut[i] |= temp - 1024;
            else
               ActualClut[i] |= temp + 1024;
         }
      }
   }

   RECT rect = {0,ClutY, 256,1};        // Put new clut in vram
   LoadImage (&rect, (u_long *)ActualClut);

   if (j == 0)
      BWChangeComplete = 1;
   return (j);
}

//-------------------------------------
// Set a character  with a specific
// attribute, like victim, object, etc.
//-------------------------------------
void Character::SetCharacteristics (u_short thing)
{
   Chrctristic = thing;
   if (Chrctristic & 32)
      MakeTranslucent ();
   if (Chrctristic & 8)                 // Initialise the red contour of the
      ActualClut[1] == 0x1;             // gun
   if (Chrctristic & 2048)
      Reappear = -1;
}

//-------------------------------------
// Detect if there is a collision
// between a rectangle and the object's
// bounding box
//-------------------------------------
int Character::Collision (RECT *hitbox, int dpth)
{
   int x1,y1, x2,y2;
   int hx1,hy1, hx2,hy2;

   dpth = (dpth / 5) * 5 + 1;           // Needs to be on the same depth area
   if (((depth / 5) * 5 + 1) != dpth)
      return (0);
   
   if ((!Death) && (Lifepnt) && (Chrctristic & 3))
   {
      int spp = Action[CurrentAction].Animation[CurrentFrame];
      x1 = x - SprW[spp] / 2;                       // Object's bounding box
      x2 = x + SprW[spp] / 2;
      y1 = y - SprH[spp] / 2;
      y2 = y + SprH[spp] / 2;

      hx1 = hitbox->x - hitbox->w / 2;              // Hit box bounding box
      hx2 = hitbox->x + hitbox->w / 2;
      hy1 = hitbox->y - hitbox->h / 2;
      hy2 = hitbox->y + hitbox->h / 2;

      // Check the bounding boxes...
      if ((x1 < hx2) && (x2 > hx1) && (y1 < hy2) && (y2 > hy1) && (hy1 >= y1))
      {
         Die ();                                    //
         ShotToDeath = 1;
         return (-1);                               // Hit!
      }
   }

   return (0);                                      // No hit...
}

//-------------------------------------
// Return the hit box of the object
//-------------------------------------
RECT *Character::GetHitBox (void)
{
   int i = Action[CurrentAction].Animation[CurrentFrame];

   setRECT (&Collisio, x,y, SprW[i], SprH[i]);

   return (&Collisio);
}

//-------------------------------------
// Draw the next frame of the anima-
// tion.
//-------------------------------------
shootpnt *Character::DrawNextFrame (int bkdx, int bkdy, DoubleBuffer *db,
                                    int *sfx, int *vab, int *posi)
{
   int    i, j;
   point  po;
   int    check;
   int    ofsx, ofsy;

   (*sfx) = -1;

   if ((CharNbr == 80) && (LE1orLE2 == 0) && (ActualScript == 20))
   {
      bkdx = 0;
      bkdy = 0;
   }

   if (!Death)          // Don't draw anything if the character is dead
   {
      // MoveTo crap.
      if ((MoveActualStep != MoveTotalStep))
      {
         x = ((MoveDestX - MoveXSource) * (MoveActualStep) /
              MoveTotalStep) + MoveXSource;
         y = ((MoveDestY - MoveYSource) * (MoveActualStep++) /
              MoveTotalStep) + MoveYSource;
         for (i = 0; i < NbrAttaches; i++)
         {
            if (AttachTo[i] == this)
               Attach[i]->Set_Location (x + AttachDX[i] * scale / 4096, y + AttachDY[i] * scale / 4096);
         }
      }

      // ScaleTo stuff
      if (ScaleTotalStep != ScaleActualStep)
      {
         scale = ((ScaleDest - ScaleSource) * (++ScaleActualStep) /
                  ScaleTotalStep) + ScaleSource;

         if ((CharNbr == 0) && (ActualScript == 7) && (LE1orLE2 == 1))
         {     // Place the wagon on the y coor. in accordance to its scale
            if (scale < 2800)
            {
               y = 120 - (scale - 2600) * 34 / 200;
               depth = 4;
            }
            else
            {
               y = 86;
               depth = 9;
            }
            for (i = 0; i < NbrAttaches; i++)
            {
               if (AttachTo[i] == this)
                  Attach[i]->SetDepth (depth - 2);
            }
         }

         for (i = 0; i < NbrAttaches; i++)
         {
            if (AttachTo[i] == this)
            {
               Attach[i]->Set_Location (x + AttachDX[i] * scale / 4096, y + AttachDY[i] * scale / 4096);
               Attach[i]->Scale (scale);
            }
         }

         if (ScaleActualStep == ScaleTotalStep)
         {
            if ((ActualScript == 7) && (CharNbr == 0) && (LE1orLE2 == 1))
            {
               if (scale == 2000)
                  BossStageCoachDead = 3;
               int jobus = rand () % 1000 + 2800;
               int jobstepus = ABS (jobus - scale) * 120 / 1000;
               ScaleTo (jobus, jobstepus);
            }
         }
      }

      // Special case for Stage Coach boss level (LE2)
      if ((ActualScript == 7) && (LE1orLE2 == 1))
      {
         if (CharNbr == 0)      // Move the wagon left and right
         {
            if ((FrameCounter > 250) && (!BossStageCoachDead))
            {
               if ((CarLoopX += 8) > 4096)
                  CarLoopX -= 4096;
               x = 160 - csin (CarLoopX) * 110 / 4096;
               for (i = 0; i < NbrAttaches; i++)
                  if (AttachTo[i] == this)
                     Attach[i]->Set_Location (x + AttachDX[i] * scale / 4096, y + AttachDY[i] * scale / 4096);
            }
         }
         else if ((CharNbr < 12) && (CharNbr > 6))      // Barrels...
         {
            CarLoopY += 40;
            if (Inverse)
               x += 1;
            else
               x -= 1;
            y = (ABS (csin (CarLoopY)) * 70 / 4096) * 4000 / scale;
            y = 230 - (6200 - scale) * 80 / 5000 - y;
            depth = 14 - (6200 - scale) / 1200;

            if (scale == 6200)
            {
               Death = 1;
               ShootPoint.x = x;
               ShootPoint.y = y;
               ShootPoint.GunType = 7;
               NewExplosion (x, y, depth, scale);
               return (&ShootPoint);
            }
         }
         else if ((CharNbr < 17) && (CharNbr > 11)) // Barrel shadows
         {
            if (Inverse)
               x += 1;
            else
               x -= 1;
            y = 248 - (6200 - scale) * 80 / 5000;
            if (scale == 6200)
            {
               Death = 1;
               return (NULL);
            }
         }
      }

      // Special case for the drug dealer's level 3
      if ((ActualScript == 16) && (LE1orLE2 == 0) && (FrameCounter > 2178))
      {
         if ((CharNbr > 63) && (CharNbr < 69) && (scale < 5500))
            scale += 10;
         if ((CharNbr == 64) && (scale < 5500))
         {
            depth = 11;
            if ((FrameCounter % 40) == 0)
            {
               y++;
               MoveDestY = y;
               MoveYSource = y;
            }
         }
         else if ((CharNbr == 65) && (scale < 5500))
         {
            if ((FrameCounter % 10) == 0)
            {
               y--;
               MoveDestY = y;
               MoveYSource = y;
            }
         }
         else if ((CharNbr == 66) && (scale < 5500))
         {
            depth = 11;
            if ((FrameCounter % 7) == 0)
            {
               x--;
               MoveDestX = x;
               MoveXSource = x;
            }
            if (FrameCounter == 2240)
            {
               Death = 1;
               StartAction (3, 0);
            }

         }
         else if ((CharNbr == 67) && (scale < 5500))
         {
            depth = 11;
            if ((FrameCounter % 7) == 0)
            {
               x++;
               MoveDestX = x;
               MoveXSource = x;
            }
            if (FrameCounter == 2290)
            {
               Death = 1;
               StartAction (3, 1);
            }
         }
         else if ((CharNbr == 68) && (scale < 5500))
         {
            if ((FrameCounter % 10) == 0)
            {
               y--;
               MoveDestY = y;
               MoveYSource = y;
            }
         }
      }

      // Special case for the drug dealer's car chase
      if ((ActualScript == 17) && (LE1orLE2 == 0))
      {
         if ((CharNbr == 67) && (CarChaseTimerNear == 8)) // Helico's door
         {
            for (i = 0; i < NbrAttaches; i++)   // Open the door!!!
               if (Attach[i] == this)
               {
                  if (--AttachDX[i] == 33)
                  {
                     CarChaseTimerNear = 9;
                     Chrctristic = 0;
                  }
               }
         }
         else if ((CharNbr > 90) && (CharNbr < 95))  // Motorcycles...
         {
            if (CarState == 0)          // Car coming in
            {
               x += 2;
               MoveDestX = x;
               MoveXSource = x;
               if (x > 420)
               {
                  x = 420;
                  MoveDestX = x;
                  MoveXSource = x;
                  Death = 1;
               }
               else if ((x == 30) || (x == 191))
                  StartAction (2, 0);
            }
            else                        // Car going out
            {
               x -= 7;
               if (x < -100)
               {
                  StopAction ();
                  CarChaseTimerFar++;
               }
            }
         }
         else if (CharNbr == 61)                // Helicopter shit.
         {
            if (CarState == 0)                  // Go up
            {
               if (--y < 100)
               {
                  CarState = 1;
                  CarChaseTimerNear = 8;
               }
            }
            else if (CarState == 1)             // Move around
            {
               CarLoopY += 20;
               if (CarLoopY > 4095)
                  CarLoopY -= 4096;
               y = 100 - csin (CarLoopY) * 20 / 4096;
               MoveDestY = y;
               MoveYSource = y;
               x = 100 - csin (CarLoopX) * 75 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               CarLoopX += 15;
               if (CarLoopX > 4095)
                  CarLoopX -= 4096;
               if (CarKilled[5] > 4)
                  CarState = 2;
            }
            else                                // Go down & die...
            {
               if (++y > 230)
               {
                  StopAction ();
                  for (i = 0; i < NbrAttaches; i++)
                     if (AttachTo[i] == this)
                        Attach[i]->StopAction ();
               }

               if ((y % 25) == 0)
                  NewExplosion (x + 80, y - 10, 6, 8192);

               x = 100 - csin (CarLoopX) * 75 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               CarLoopX += 15;
               if (CarLoopX > 4095)
                  CarLoopX -= 4096;
               if (CarKilled[5] > 4)
                  CarState = 2;
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
                  Attach[j]->Set_Location (x + AttachDX[j], y + AttachDY[j]);
               j++;
            }
         }
         else if (CharNbr == 11)                // Cadillac 1
         {
            CarLoopY += 20;
            if (CarLoopY > 4095)
               CarLoopY -= 4096;
            y = 180 - csin (CarLoopY) * 10 / 4096;
            MoveDestY = y;
            MoveYSource = y;
            if ((CarShake == 1) && (VSync (-1) & 4))
            {
               y += 3;
               MoveDestY = y;
               MoveYSource = y;
            }

            if (CarState == 0)          // Car coming in
            {
               x -= 2;
               if (x <= 160)
                  CarState = 1;
            }
            else if (CarState == 1)     // Car moving around
            {
               x = 160 - csin (CarLoopX) * 75 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               CarLoopX += 15;
               if (CarLoopX > 4095)
                  CarLoopX -= 4096;

               if (CarKilled[0] == 5)   // Wait until everybody in the car
                  CarState = 2;         // is dead to go away
               if (++CarTimer[0] == 660)
                  CarTimer[0] = 0;
            }
            else                        // Car going out
            {
               x -= 2;
               if (x <= -200)
               {
                  StopAction ();
                  CarChaseTimerNear++;
                  for (i = 0; i < NbrAttaches; i++)
                     if (AttachTo[i] == this)
                        Attach[i]->StopAction ();
               }
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
               {
                  Attach[j]->Set_Location (x + AttachDX[j] * scale / 4096, y + AttachDY[j] * scale / 4096);
                  scale = 6400 + (y - 180) * 30;
                  ScaleTotalStep = 0;
                  ScaleActualStep = 0;
               }
               j++;
            }
         }
         else if (CharNbr == 29)        // TransAM
         {
            CarLoopY += 20;
            if (CarLoopY > 4095)
               CarLoopY -= 4096;
            y = 95 - csin (CarLoopY) / 4096;
            MoveDestY = y;
            MoveYSource = y;

            if (CarState == 0)          // Car coming in
            {
               x += 2;
               MoveDestX = x;
               MoveXSource = x;
               if (x >= 130)
               {
                  CarState = 1;
               }
            }
            else if (CarState == 1)     // Car moving around
            {
               x = 130 - csin (CarLoopX) * 50 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               CarLoopX += 15;
               if (CarLoopX > 4095)
                  CarLoopX -= 4096;

               if (CarKilled[1] == 5)   // Wait until everybody in the car
                  CarState = 2;         // is dead to go away
               if (++CarTimer[1] == 660)
                  CarTimer[1] = 0;
            }
            else                        // Car going out
            {
               x -= 2;
               if (x <= -200)
               {
                  StopAction ();
                  CarChaseTimerFar++;
                  CarChaseTimerNear++;
                  for (i = 0; i < NbrAttaches; i++)
                     if (AttachTo[i] == this)
                        Attach[i]->StopAction ();
               }
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
               {
                  Attach[j]->Set_Location (x + AttachDX[j] * scale / 4096, y + AttachDY[j] * scale / 4096);
                  scale = 5300 + (y - 120) * 30;
                  ScaleTotalStep = 0;
                  ScaleActualStep = 0;
               }
               j++;
            }
         }
         else if (CharNbr == 0)         // White car
         {
            CarLoopY += 20;
            if (CarLoopY > 4095)
               CarLoopY -= 4096;
            y = 180 - csin (CarLoopY) * 10 / 4096;
            MoveDestY = y;
            MoveYSource = y;
            if ((CarShake == 1) && (VSync (-1) & 4))
            {
               y += 3;
               MoveDestY = y;
               MoveYSource = y;
            }
      
            if (CarState == 0)          // Car coming in
            {
               x -= 2;
               if (x <= 160)
                  CarState = 1;
            }
            else if (CarState == 1)     // Car moving around
            {
               x = 160 - csin (CarLoopX) * 75 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               CarLoopX += 15;
               if (CarLoopX > 4095)
                  CarLoopX -= 4096;

               if (CarKilled[2] == 5)   // Wait until everybody in the car
                  CarState = 2;         // is dead to go away
               if (++CarTimer[2] == 660)
                  CarTimer[2] = 0;
            }
            else                        // Car going out
            {
               x -= 2;
               if (x <= -200)
               {
                  StopAction ();
                  CarChaseTimerNear++;
                  for (i = 0; i < NbrAttaches; i++)
                     if (AttachTo[i] == this)
                        Attach[i]->StopAction ();
               }
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
               {
                  Attach[j]->Set_Location (x + AttachDX[j] * scale / 4096, y + AttachDY[j] * scale / 4096);
                  scale = 6400 + (y - 180) * 30;
                  ScaleActualStep = 0;
                  ScaleTotalStep = 0;
               }
               j++;
            }
         }
         if (CharNbr == 20)             // Cadillac 2
         {
            CarLoopY += 20;
            if (CarLoopY > 4095)
               CarLoopY -= 4096;
            y = 180 - csin (CarLoopY) * 10 / 4096;
            MoveDestY = y;
            MoveYSource = y;
            if ((CarShake == 1) && (VSync (-1) & 4))
            {
               y += 3;
               MoveDestY = y;
               MoveYSource = y;
            }

            if (CarState == 0)          // Car coming in
            {
               x -= 2;
               if (x <= 160)
                  CarState = 1;
            }
            else if (CarState == 1)     // Car moving around
            {
               x = 160 - csin (CarLoopX) * 75 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               CarLoopX += 15;
               if (CarLoopX > 4095)
                  CarLoopX -= 4096;

               if (CarKilled[3] == 5)   // Wait until everybody in the car
                  CarState = 2;         // is dead to go away
               if (++CarTimer[3] == 660)
                  CarTimer[3] = 0;
            }
            else                        // Car going out
            {
               x -= 2;
               if (x <= -200)
               {
                  StopAction ();
                  CarChaseTimerNear++;
                  for (i = 0; i < NbrAttaches; i++)
                     if (AttachTo[i] == this)
                        Attach[i]->StopAction ();
               }
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
               {
                  Attach[j]->Set_Location (x + AttachDX[j] * scale / 4096, y + AttachDY[j] * scale / 4096);
                  scale = 6400 + (y - 180) * 30;
                  ScaleTotalStep = 0;
                  ScaleActualStep = 0;
               }
               j++;
            }
         }
      }

      // Special case for the bank's car chase
      if ((ActualScript == 5) && (LE1orLE2 == 0))
      {
         if ((CharNbr == 7) || (CharNbr == 33)) 
         {                                   // The guys that open car's door
            if (Action[CurrentAction].ActionType == ACTIONDIE1)
            {
               for (i = 0; i < NbrAttaches; i++)
               {
                  if (Attach[i] == this)
                  {
                     AttachDX[i] -= 7;
                     i = NbrAttaches;
                  }
               }
            }
         }

         if ((CharNbr == 71) && (Action[CurrentAction].ActionType == ACTIONDIE1))
         {
            for (i = 0; i < NbrAttaches; i++)
            {
               if (Attach[i] == this)
               {
                  if (CurrentFrame > 7)
                     AttachDX[i] -= 4;
                  else if (((CurrentFrame == 6) || (CurrentFrame == 7)) && (WaitCnt == 1))
                     AttachDY[i] += 30;
                  i = NbrAttaches;
               }
            }
         }

         if (CharNbr == 0)              // White car
         {
            CarLoopY += 20;
            if (CarLoopY > 4095)
               CarLoopY -= 4096;
            y = 180 - csin (CarLoopY) * 10 / 4096;
            MoveDestY = y;
            MoveYSource = y;
            if ((CarShake == 1) && (VSync (-1) & 4))
            {
               y += 3;
               MoveDestY = y;
               MoveYSource = y;
            }
      
            if (CarState == 0)          // Car coming in
            {
               x -= 2;
               if (x <= 160)
               {
                  CarState = 1;
                  CarChaseTimerNear = 1;
               }
            }
            else if (CarState == 1)     // Car moving around
            {
               x = 160 - csin (CarLoopX) * 75 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               CarLoopX += 15;
               if (CarLoopX > 4095)
                  CarLoopX -= 4096;

               if (CarKilled[0] == 5)   // Wait until everybody in the car
                  CarState = 2;         // is dead to go away
               if (++CarTimer[0] == 660)
                  CarTimer[0] = 0;
            }
            else                        // Car going out
            {
               x -= 2;
               if (x <= -200)
               {
                  StopAction ();
                  CarChaseTimerNear++;
                  for (i = 0; i < NbrAttaches; i++)
                     if (AttachTo[i] == this)
                        Attach[i]->StopAction ();
               }
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
               {
                  Attach[j]->Set_Location (x + AttachDX[j] * scale / 4096, y + AttachDY[j] * scale / 4096);
                  scale = 6400 + (y - 180) * 30;
                  ScaleTotalStep = 0;
                  ScaleActualStep = 0;
               }
               j++;
            }
         }
         else if (CharNbr == 36)        // TransAM
         {
            CarLoopY += 20;
            if (CarLoopY > 4095)
               CarLoopY -= 4096;
            y = 120 - csin (CarLoopY) * 10 / 4096;
            MoveDestY = y;
            MoveYSource = y;
            if ((CarShake == 1) && (VSync (-1) & 4))
            {
               y += 3;
               MoveDestY = y;
               MoveYSource = y;
            }

            if (CarState == 0)          // Car coming in
            {
               x += 2;
               MoveDestX = x;
               MoveXSource = x;
               if (x >= 130)
               {
                  CarState = 1;
                  CarChaseTimerFar = 2;
               }
            }
            else if (CarState == 1)     // Car moving around
            {
               x = 130 - csin (CarLoopX) * 50 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               CarLoopX += 15;
               if (CarLoopX > 4095)
                  CarLoopX -= 4096;

               if (CarKilled[3] == 2)   // Wait until everybody in the car
                  CarState = 2;         // is dead to go away
               if (++CarTimer[1] == 400)
                  CarTimer[1] = 0;
            }
            else                        // Car going out
            {
               x -= 2;
               if (x <= -200)
               {
                  StopAction ();
                  CarChaseTimerFar++;
                  for (i = 0; i < NbrAttaches; i++)
                     if (AttachTo[i] == this)
                        Attach[i]->StopAction ();
               }
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
               {
                  Attach[j]->Set_Location (x + AttachDX[j] * scale / 4096, y + AttachDY[j] * scale / 4096);
                  scale = 5300 + (y - 120) * 30;
                  ScaleActualStep = 0;
                  ScaleTotalStep = 0;
               }
               j++;
            }
         }
         else if (CharNbr == 11)        // Caddy (Grey-black car)
         {
            CarLoopY += 20;
            if (CarLoopY > 4095)
               CarLoopY -= 4096;
            y = 180 - csin (CarLoopY) * 10 / 4096;
            MoveDestY = y;
            MoveYSource = y;
            if ((CarShake == 1) && (VSync (-1) & 4))
            {
               y += 3;
               MoveDestY = y;
               MoveYSource = y;
            }

            if (CarState == 0)          // Car coming in
            {
               x -= 2;
               if (x <= 160)
               {
                  CarChaseTimerNear++;
                  CarState = 1;
               }
            }
            else if (CarState == 1)     // Car moving around
            {
               x = 160 - csin (CarLoopX) * 75 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               CarLoopX += 15;
               if (CarLoopX > 4095)
                  CarLoopX -= 4096;

               if (CarKilled[1] == 5)   // Wait until everybody in the car
                  CarState = 2;         // is dead to go away
               if (++CarTimer[2] == 660)
                  CarTimer[2] = 0;
            }
            else                        // Car going out
            {
               x -= 2;
               if (x <= -200)
               {
                  StopAction ();
                  CarChaseTimerNear = 6;
                  for (i = 0; i < NbrAttaches; i++)
                     if (AttachTo[i] == this)
                        Attach[i]->StopAction ();
               }
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
               {
                  Attach[j]->Set_Location (x + AttachDX[j] * scale / 4096, y + AttachDY[j] * scale / 4096);
                  scale = 6400 + (y - 180) * 30;
                  ScaleActualStep = 0;
                  ScaleTotalStep = 0;
               }
               j++;
            }
         }
         else if (CharNbr == 52)        // MotorCycle
         {
            if (CarState == 0)          // Car coming in
            {
               x += 2;
               MoveDestX = x;
               MoveXSource = x;
               if (x > 420)
               {
                  x = 420;
                  MoveDestX = x;
                  MoveXSource = x;
                  CarState = 1;
               }
               else if ((x == 30) || (x == 191))
                  StartAction (2, 0);
            }
            else if (CarState == 1)     // Car moving around
            {
               x -= 2;
               if (x < -100)
                  CarState = 0;
               else if ((x == 130) || (x == 290))
                  StartAction (2, 0);
            }
            else                        // Car going out
            {
               x -= 7;
               if (x < -100)
               {
                  StopAction ();
                  CarChaseTimerFar++;
               }
            }
         }
         else if (CharNbr == 20)        // Miata
         {
            y = 120;
            MoveDestY = y;
            MoveYSource = y;
            if ((CarShake == 1) && (VSync (-1) & 4))
            {
               y += 2;
               MoveDestY = y;
               MoveYSource = y;
            }

            if (CarState == 0)          // Car coming in
            {
               x += 3;
               MoveDestX = x;
               MoveXSource = x;
               if (x >= 450)
               {
                  Death = 1;
                  CarChaseTimerFar = 7;
                  for (i = 0; i < NbrAttaches; i++)
                     if (AttachTo[i] == this)
                        Attach[i]->StopAction ();
               }
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
                  Attach[j]->Set_Location (x + AttachDX[j] * scale / 4096, y + AttachDY[j] * scale / 4096);
               j++;
            }
         }
         else if (CharNbr == 25)        // Sedan
         {
            CarLoopY += 20;
            if (CarLoopY > 4095)
               CarLoopY -= 4096;
            y = 180 - csin (CarLoopY) * 10 / 4096;
            MoveDestY = y;
            MoveYSource = y;
            if ((CarShake == 1) && (VSync (-1) & 4))
            {
               y += 3;
               MoveDestY = y;
               MoveYSource = y;
            }

            if (CarState == 0)          // Car coming in
            {
               x -= 2;
               if (x <= 160)
               {
                  CarState = 1;
               }
            }
            else if (CarState == 1)     // Car moving around
            {
               x = 160 - csin (CarLoopX) * 75 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               CarLoopX += 15;
               if (CarLoopX > 4095)
                  CarLoopX -= 4096;

               if (CarKilled[2] == 5)   // Wait until everybody in the car
                  CarState = 2;         // is dead to go away
               if (++CarTimer[3] == 660)
                  CarTimer[3] = 0;
            }
            else                        // Car going out
            {
               x -= 2;
               if (x <= -200)
               {
                  StopAction ();
                  CarChaseTimerNear++;
                  for (i = 0; i < NbrAttaches; i++)
                     if (AttachTo[i] == this)
                        Attach[i]->StopAction ();
               }
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
               {
                  Attach[j]->Set_Location (x + AttachDX[j] * scale / 4096, y + AttachDY[j] * scale / 4096);
                  scale = 6400 + (y - 180) * 30;
                  ScaleTotalStep = 0;
                  ScaleActualStep = 0;
               }
               j++;
            }
         }
         else if (CharNbr == 42)        // Biohazard truck
         {
            y = 80;
            MoveDestY = y;
            MoveYSource = y;
            if ((CarShake == 1) && (VSync (-1) & 4))
            {
               y += 3;
               MoveDestY = y;
               MoveYSource = y;
            }

            if (CarState == 0)          // Car coming in
            {
               x += 2;
               MoveDestX = x;
               MoveXSource = x;
               if (x >= 110)
               {
                  CarChaseTimerNear++;
                  CarState = 1;
               }
            }
            else if (CarState == 1)     // Car moving around
            {
               x = 110 + csin (CarLoopX) * 75 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               CarLoopX += 15;
               if (CarLoopX > 4095)
                  CarLoopX -= 4096;

               if (CarKilled[4] == 255) // Wait until everybody in the car
                  CarState = 2;         // is dead to go away
               if (++CarTimer[4] == 400)
                  CarTimer[4] = 0;
            }
            else if (CarState == 4)     // Car going out
            {
               x -= 2;
               if (x < 230)
               {
                  StopAction ();
                  for (i = 0; i < NbrAttaches; i++)
                     if (AttachTo[i] == this)
                        Attach[i]->StopAction ();
               }
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
               {
                  Attach[j]->Set_Location (x + AttachDX[j] * scale / 4096, y + AttachDY[j] * scale / 4096);
                  if (Attach[j]->GetCharNbr () < 48)
                     Attach[j]->Scale (scale);
               }
               j++;
            }
         }
      }

      // Special case for the chemical's helicopter
      if ((ActualScript == 20) && (LE1orLE2 == 0))
      {
         if ((CharNbr == 91) && (x == 415))     // Tank 1's top
         {
            Death = 1;
            StartAction (1, 1);
         }

         if ((x == 563) && (CharNbr == 89))     // Tank 2's base
         {
            x = 562;
            MoveDestX = x;
            MoveXSource = x;
            Death = 1;
            StartAction (6, 1);
         }
         if ((y == 444) && (CharNbr == 89))
         {
            y = 445;
            MoveDestY = y;
            MoveYSource = y;
            Death = 1;
            StartAction (5, 1);
         }
         if ((CurrentAction == 4) && (CharNbr == 89))
         {
            scale += 7;
            depth = ((y - 370) / 10) + 6;
         }

         if ((x == 570) && (CharNbr == 93))     // Tank 2's top
         {
            x = 569;
            MoveDestX = x;
            MoveXSource = x;
            Death = 1;
            StartAction (1, 1);
         }

         if ((CharNbr == 92) && (x == 290))     // Tank 3's top
         {
            Death = 1;
            StartAction (1, 0);
         }

         if ((x == 293) && (CharNbr == 90))     // Tank 4's base
         {
            x = 294;
            MoveDestX = x;
            MoveXSource = x;
            Death = 1;
            StartAction (6, 0);
         }
         if ((y == 424) && (CharNbr == 90))
         {
            y = 425;
            MoveDestY = y;
            MoveYSource = y;
            Death = 1;
            StartAction (5, 0);
         }
         if ((CurrentAction == 4) && (CharNbr == 90))
         {
            scale += 7;
            depth = ((y - 370) / 8) + 6;
         }

         if ((x == 286) && (CharNbr == 94))     // Tank 4's top
         {
            x = 287;
            MoveDestX = x;
            MoveXSource = x;
            Death = 1;
            StartAction (1, 0);
         }

         if ((CharNbr > 86) && (CharNbr < 91))  // Make sure the top portion
         {                                      //  of the tank follows the
            for (i = 0; i < NbrAttaches; i++)   //  base
            {
               if (AttachTo[i] == this)
               {
                  if (!Attach[i]->Dead ())
                  {
                     Attach[i]->Scale (scale);
                     //Attach[i]->SetDepth (depth);
                  }
                  i = NbrAttaches;
               }
            }
         }
      
         //-------------
         if ((CharNbr == 78) && (HelicoScale == 7))
         {
            StartAction (19, 0);
            HelicoScale = 8;
         }

         if ((CharNbr == 61) && (scale < 4096) && (HelicoScale == 6))
         {
            if ((scale += 16) > 3784)
               depth = 6;
            if (scale > 4095)
               HelicoScale = 7;
         }

         if ((CharNbr > 61) && (CharNbr < 79) && (scale > 3784))
            depth = 7;

         if ((CharNbr == 79) && (scale > 3784))
            depth = 7;

         if ((CharNbr == 61) && (VSync (-1) & 2))
         {
            if (y < 50)
            {
               y++;
               MoveDestY = y;
               MoveYSource = y;
            }
            else
            {
               x = 428 + csin (BossAngle) * 50 / 4096;
               MoveDestX = x;
               MoveXSource = x;
               if (HelicoScale == 10)
               {
                  if ((y += 3) > 320)
                     Death = 1;
                  MoveDestY = y;
                  MoveYSource = y;
               }
               else
               {
                  y = 80 - csin (BossAngle2) * 30 / 4096;
                  MoveDestY = y;
                  MoveYSource = y;
               }
               BossAngle += 24;
               BossAngle2 += 12;
            }

            j = 0;
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
               {
                  Attach[j]->Set_Location (x + AttachDX[j] * scale / 4096, y + AttachDY[j] * scale / 4096);
                  Attach[j]->Scale (scale);
               }
               j++;
            }
         }
      }

      if (GunMove)
      {
         x += GunDeltaX;
         MoveDestX = x;
         MoveXSource = x;
         y += GunDeltaY;
         MoveDestY = y;
         MoveYSource = y;
      }

      if (Chrctristic & 320)            // Thrown object ?
      {
         x += GunDeltaX;
         MoveDestX = x;
         MoveXSource = x;
         y += GunDeltaY;
         MoveDestY = y;
         MoveYSource = y;
         if (Lifepnt != 0)
            scale += 100;
         else if (NewAction)            // Play the "knife deflection" sfx
         {                              //
            *sfx = SfxStart[1];         //
            *vab = VabStart[1];         //
            NewAction = 0;              //
         }

         if (scale > 6000)
         {
            if (Action[0].GunType == 7)
               NewExplosion (x, y, depth, scale);

            ShootPoint.x = x;
            ShootPoint.y = y;
            ShootPoint.GunType = Action[0].GunType;
            Lifepnt = 0;
            Death = 1;
            if (VramMemHandle != -1)
            {
               FreeVram (VramMemHandle);
               VramMemHandle = -1;
            }
            PrevSprite = -1;
            return (&ShootPoint);
         }
         else
         {
            if ((Lifepnt) && (Chrctristic & 256))
            {
               if ((LevelSelected == 5) && (LE1orLE2 == 0))
               {                                       // Helicopter missiles
                  if (Inverse)
                  {
                     if ((GunDeltaX = -(CurrentFrame - 3)) < -2)
                        GunDeltaX = -2;
                  }
                  else
                  {
                     if ((GunDeltaX = CurrentFrame - 3) > 2)
                        GunDeltaX = 2;
                  }
               }
               else if ((LevelSelected == 1) && (LE1orLE2 == 0))
                  GunDeltaY = ((FrameCounter & 2) >> 1); // CarChase missiles
               else
               {
                  if ((GunDeltaY = CurrentFrame - 3) > 3) // Airport missiles
                     GunDeltaY = 3;
               }
            }

            i = CurrentFrame % Action[0].NumFrame;

            if (((x + bkdx - SprW[i] / 2) > 319) ||
                ((x + bkdx + SprW[i] / 2) < 0) ||
                ((y + bkdy - SprH[i] / 2) > 239) ||
                ((y + bkdy + SprH[i] / 2) < 0))
            {
               if (VramMemHandle != -1)
               {
                  FreeVram (VramMemHandle);
                  VramMemHandle = -1;
               }
               PrevSprite = -1;
               Death = 1;
               return (NULL);
            }

            if (i != PrevSprite)
            {
               RECT recti = {VramMemSpace.x, VramMemSpace.y, SprW[i] >> 1, SprH[i]};
               //LoadImage (&recti, (u_long *)SprAddress[i]);
               LoadCompressedImage(&recti, i);
               PrevSprite = i;
            }

            if (WaitCnt++ >= 4)
            {
               WaitCnt = 0;
               CurrentFrame++;
            }

            po.x = x + bkdx;
            po.y = y + bkdy;
            *posi = x + bkdx;
            DrawSpr (i, po, db);
         }

         return (NULL);
      }

      if ((Chrctristic & 2048) && (LE1orLE2 == 1) && (LevelSelected == 0))
      {
         short SmlFireX[33] = { 21, 21, 17,  1,-15,-20,-15, -8, -4, -8,-15,
                               -11, -2,  2,  3,-15,-18, -2,-15,-16, -5, -3,
                               -10,-10,-21,-32,-35,  9, 34, 38, -9,-20,-23};
         short SmlFireY[33] = {  0, -7,-20,-36,-27,-39,-37,-32,-20,-12,-13,
                               -17,-18,-19,-17,-21,-19,-35,-10,-21,-34,-18,
                               -24,-30,-25, -7, 10,-22, -7, 12,-25,-11, 12};
         int zubo = Action[CurrentAction].Animation[CurrentFrame];
         SmallFireX = x + SmlFireX[zubo];
         SmallFireY = y + SmlFireY[zubo];
      }

      if ((Chrctristic & 2048) && (LevelSelected == 3) && (LE1orLE2 == 0) &&
          (CurrentAction == 12) && (CurrentFrame > 7))
      {
         scale += 100;
         depth = 13;
      }
      
      if ((Chrctristic & 8) && (Origina))   // Make the red color around the
      {                                     //  gun to flicker
         ActualClut[1] += GunColorChange;
         if ((ActualClut[1] == 0x1F) || (ActualClut[1] == 0x1))
            GunColorChange = -GunColorChange;
         ActualClut[1] &= 0x1F;
         RECT rect = {0,ClutY, 3,1};        // Put the new red color in vram
         LoadImage (&rect, (u_long *)ActualClut);
      }

      if (NewAction)        
      {                     
         if (Action[CurrentAction].GunType & 128)
            depth += 5;

         check = 0;               // Check if there's a link at start of this
         while (check < NbrLinks) // Animation
         {
            if ((LinkList[check].Caller == this) &&
                (CurrentAction == LinkList[check].CallerAction) &&
                (LinkList[check].When == 0))
            {
               Character *cha = LinkList[check].Receiver;
               if (cha->StartAction (LinkList[check].ReceiverAction, LinkList[check].Inverse) == 0)
                  cha->Set_Location (LinkList[check].CoorX, LinkList[check].CoorY);
               
            }
            check++;
         }

         if ((SfxStart[CurrentAction] != 255) && (NewAction == 2))
         {                                   // Play a sound fx at start of
            *sfx = SfxStart[CurrentAction];  // the animation if we need to
            *vab = VabStart[CurrentAction];
         }

         NewAction = 0;           
      }

      if (Action[CurrentAction].Animation[CurrentFrame] != 255)
      {
         i = Action[CurrentAction].Animation[CurrentFrame];

         if (i != PrevSprite)
         {
            //DecompressTexture (SprW[i], SprH[i],
            //                   (u_char *)SprAddress[i], (u_char *)decom);
            RECT rect = {SprTPX + (SprU >> 1),SprTPY + SprV,
                         SprW[i] >> 1,SprH[i]};
            //LoadImage (&rect, (u_long *)SprAddress[i]);
            LoadCompressedImage(&rect, i);
            PrevSprite = i;
         }

         if ((WaitCnt == 0) && (MoveTotalStep == MoveActualStep))
         {                           // Is it time to move the character ?
            if (!Inverse)            // Move in the inverse direction if
            {                        // the inverse flag is set
               ofsx = Action[CurrentAction].OffsetX[CurrentFrame] * scale / 4096;
               x += ofsx;
               MoveDestX = x;
               MoveXSource = x;
            }
            else
            {
               ofsx = -Action[CurrentAction].OffsetX[CurrentFrame] * scale / 4096;
               x += ofsx;
               MoveDestX = x;
               MoveXSource = x;
            }

            ofsy = Action[CurrentAction].OffsetY[CurrentFrame] * scale / 4096;
            y += ofsy;
            MoveDestY = y;
            MoveYSource = y;

            j = 0;              // Also move all the attached characters
            while (j < NbrAttaches)
            {
               if (AttachTo[j] == this)
               {
                  Attach[j]->Move (ofsx, ofsy);
                  //Attach[j]->Scale (scale);
               }
               j++;
            }

            // Stop the fork lift in chemical 1
            if (CharNbr == 80)
            {
               if ((LE1orLE2 == 0) && (ActualScript == 18) && (x == 190))
               {
                  x = 191;
                  MoveDestX = x;
                  MoveXSource = x;
                  Death = 1;
                  StartAction (1, 0);
               }
            }

            // Guys in yellow suit will fall & die if the pass is fallen
            if ((ActualScript == 18) && (LE1orLE2 == 0) && (PassFallen) &&
                (y < 100) && (x > 205) && (x < 285) && (!ShotToDeath))
            {
               StartAction (14, Inverse);
               ShotToDeath = 1;
            }

            // Special case for boxes in airport 2
            if ((ActualScript == 11) && (LE1orLE2 == 0))
            {
               if (CharNbr < 15)
               {
                  i = Action[CurrentAction].Animation[CurrentFrame];
                  if ((y + SprW[i] / 2) > 220)
                  {
                     y = 220 - SprW[i] / 2;
                     MoveDestY = y;
                     MoveYSource = y;
                  }
               }
            }
         }

         po.x = x + bkdx;
         po.y = y + bkdy;
         *posi = x + bkdx;
         DrawSpr (i, po, db);

         if (++WaitCnt >= Action[CurrentAction].Wait[CurrentFrame])
         {
            WaitCnt = 0;
            CurrentFrame++;
         }
      }
      else if ((Action[CurrentAction].ActionType == ACTIONDIE1) ||
               (Action[CurrentAction].ActionType == ACTIONFALL))
      {
         if ((SfxEnd[CurrentAction] != 255) && (Flic == 40))
         {                                 // Play a sound fx at the end of
            *sfx = SfxEnd[CurrentAction];  // the animation if we need to
            *vab = VabEnd[CurrentAction];
         }

         if ((CharNbr == 6) && (ActualScript == 20) && (LE1orLE2 == 0))
            NightVision = 1;

         if (!((LevelSelected == 0) && (LE1orLE2 == 0)))
            ShotToDeath = 1;

         if (~Chrctristic & 1)
            Death = 0;

         if ((Flic--) == 0)
         {
            Death = 1;
            if (VramMemHandle != -1)
            {
               FreeVram (VramMemHandle);
               VramMemHandle = -1;
            }
            PrevSprite = -1;
         }
         else if ((Flic % 3) || (~Chrctristic & 1))
         {
            i = Action[CurrentAction].Animation[CurrentFrame - 1];
            if (i != PrevSprite)
            {
               //DecompressTexture (SprW[i], SprH[i],
               //                   (u_char *)SprAddress[i], (u_char *)decom);
               RECT rect = {SprTPX + (SprU >> 1),SprTPY, SprW[i] >> 1,
                            SprH[i]};
               //LoadImage (&rect, (u_long *)SprAddress[i]);
               LoadCompressedImage(&rect, i);
               PrevSprite = i;
            }
            po.x = x + bkdx;
            po.y = y + bkdy;
            *posi = x + bkdx;
            DrawSpr (i, po, db);
         }
      }

      // If the character goes out of the screen, stop the animation
      if ((Action[CurrentAction].Animation[CurrentFrame + 1] == 255) &&
          (Chrctristic & 4))
      {
         int spp = Action[CurrentAction].Animation[CurrentFrame];
         if (((x + bkdx - SprW[spp] * scale / 8192) > 319) ||
            ((x + bkdx + SprW[spp] * scale / 8192) < 0) ||
            ((y + bkdy - SprH[spp] * scale / 8192) > 239) ||
            ((y + bkdy + SprH[spp] * scale / 8192) < 0))
         {
            if ((LE1orLE2 == 0) && (ActualScript == 20))
               if ((CharNbr == 89) || (CharNbr == 90) || (CharNbr == 93) || (CharNbr == 94))
               {
                  scale = 4096;
                  ScaleTotalStep = 0;
                  ScaleActualStep = 0;
               }

            Death = 1;
            if (VramMemHandle != -1)
            {
               FreeVram (VramMemHandle);
               VramMemHandle = -1;
            }
            if (Chrctristic & 2)        // Victims die for good if they go
               ShotToDeath = 1;         // out of the screen
            if ((LE1orLE2 == 0) && (CharNbr == 66) && (ActualScript == 6))
               ShotToDeath = 1;         // Kill the chicken too!!!
            else if ((ActualScript == 11) && (LE1orLE2 == 0) && (CharNbr < 15))
               ShotToDeath = 1;         // Kill the boxes in airport 2
            PrevSprite = -1;

            return (NULL);
         }
      }

      if (Action[CurrentAction].Animation[CurrentFrame] == 255)
      {
         check = 0;               // Check if there's a link at end of this
         while (check < NbrLinks) // Animation
         {
            if ((LinkList[check].Caller == this) &&
                (CurrentAction == LinkList[check].CallerAction) &&
                (LinkList[check].When == 1))
            {
               Character *cha = LinkList[check].Receiver;
               if (cha->StartAction (LinkList[check].ReceiverAction, LinkList[check].Inverse) == 0)
                  cha->Set_Location (LinkList[check].CoorX, LinkList[check].CoorY);
            }
            check++;
         }
      }

      if ((Action[CurrentAction].ActionType == ACTIONIMHIT) &&
          (Action[CurrentAction].Animation[CurrentFrame] == 255))
      {
         // If we shoot a tire, it doesn't die...
         if ((LE1orLE2 == 0) && (ActualScript == 5))
         {
            char meutire[12] = {4,5,13,14,22,23,29,30,38,39,46,47};
            for (i = 0; i < 12; i++)       // Tires don't die
            {
               if (CharNbr == meutire[i])
               {
                  StartAction (CurrentAction, 0);
                  return (NULL);
               }
            }
         }
         // If we shoot a tire, it doesn't die...
         if ((LE1orLE2 == 0) && (ActualScript == 17))
         {
            char meutire[8] = {4,5,13,14,22,23,31,32};
            for (i = 0; i < 8; i++)             // Tires don't die
            {
               if (CharNbr == meutire[i])
               {
                  StartAction (CurrentAction, 0);
                  return (NULL);
               }
            }
         }
         if ((Chrctristic & 2048) && (LE1orLE2 == 0) && (LevelSelected == 3))
         {
            if (++Bossu > 1)                    // Boss airport
            {
               Bossu = 0;
               StartAction (6, 0);
               BossAirportDown = 1;
            }
            else
               StartAction (ActionBefore, Inverse);
         }
         else if ((Chrctristic & 2048) && (LE1orLE2 == 0) && (LevelSelected == 1))
         {                                      // Boss bank truck
            if (ActionBefore != 0)
               StartAction (ActionBefore, Inverse);
            else
               StartAction (1, Inverse);
         }
         else if ((Chrctristic & 2048) && (LE1orLE2 == 1) && (LevelSelected == 0))
         {                              // Boss cannon
            u_short xposi[2] = {418,443};

            x = xposi[CarState];
            if (CarState)
               StartAction (10, Inverse);
            else
               StartAction (5 + 2 * (rand () & 1), Inverse);
         }
         else if ((Chrctristic & 2048) && (LE1orLE2 == 1)) // Boss stage coach
            StartAction (4, Inverse);
         else
            StartAction (ActionBefore, Inverse);
      }

      if ((Action[CurrentAction].ActionType == ACTIONSHOOT1) &&
          (Action[CurrentAction].Animation[CurrentFrame] != 255) &&
          (Action[CurrentAction].Animation[CurrentFrame + 1] == 255) &&
          (WaitCnt == 0))
      {
         if (Action[CurrentAction].GunOfsX < 128)
         {
            if (!Inverse)
               ShootPoint.x = x + (Action[CurrentAction].GunOfsX * scale / 4096);
            else
               ShootPoint.x = x - (Action[CurrentAction].GunOfsX * scale / 4096);
         }
         else
         {
            if (!Inverse)
               ShootPoint.x = x + ((Action[CurrentAction].GunOfsX - 256) * scale / 4096);
            else
               ShootPoint.x = x - ((Action[CurrentAction].GunOfsX - 256) * scale / 4096);
         }

         if (Action[CurrentAction].GunOfsY < 128)
            ShootPoint.y = y + (Action[CurrentAction].GunOfsY * scale / 4096);
         else
            ShootPoint.y = y + ((Action[CurrentAction].GunOfsY - 256) * scale / 4096);
         ShootPoint.GunType = Action[CurrentAction].GunType & 127;
         // Handle the case where the gun's fire is bigger...
         if ((depth == 14) && (ShootPoint.GunType == 1))
            ShootPoint.GunType = 21;
         if ((ShootPoint.GunType == 8) && Projectile)
         {            // The guy is throwing a knife, but HE's not a knife
            ProjChar[ActualProjectile]->Set_Location (ShootPoint.x, ShootPoint.y);

            if (ShootPoint.x >= 160)
               ProjChar[ActualProjectile]->StartAction (0, 1);
            else
               ProjChar[ActualProjectile]->StartAction (0, 0);
            if ((++ActualProjectile) >= Projectile)
               ActualProjectile = 0;
            return (NULL);
         }
         else if ((ActualScript == 20) && (LE1orLE2 == 0) && (CharNbr > 90) && (CharNbr < 95))
         {
            ShootPoint.GunType = 22;
            ShootPoint.x -= 2;
            return (&ShootPoint);
         }
         else
         {
            if (Chrctristic & 2)
            {
               AddFlash (&ShootPoint);
               return (NULL);
            }
            else
            {
               if ((LE1orLE2 == 1) && (ActualScript == 14) && (CharNbr < 6))
                  YouLose = 1;
               return (&ShootPoint);      
            }
         }
      }

      // Check if there's a link at the end of this animation
      if ((Action[CurrentAction].Animation[CurrentFrame] == 255) &&
          (Action[CurrentAction].ActionType != ACTIONDIE1) &&
          (Action[CurrentAction].ActionType != ACTIONFALL))
      {
         Death = 1;

         if (SfxEnd[CurrentAction] != 255) // Play a sound fx at the end of
         {                                 // the animation if we need to
            *sfx = SfxEnd[CurrentAction];
            *vab = VabEnd[CurrentAction];
         }

         if ((Chrctristic & 2048) && (LevelSelected == 5) && (LE1orLE2 == 0))
         {
            u_char NextAction[5] = {1,1,1,1,2};         // Boss chemical
            u_char NbrNextAction[5] = {1,5,1,4,0};
            u_char FirstAction[5] = {0,0,0,1,0};

            StartAction (NextAction[(rand () % NbrNextAction[CurrentAction]) +
                         FirstAction[CurrentAction]], 0);
         }
         else if ((Chrctristic & 2048) && (LevelSelected == 3) && (LE1orLE2 == 0))  
         {                                                  // Boss airport
            u_char NextActionZ[8] = {1,2,3,7, 1,2,3,7};
            u_char NbrNextActionZ[13] = {1,4,4,4,0,4,0,3,0,0,0,0,0};
            u_char FirstActionZ[13] = {0,0,0,0,0,4,0,0,0,0,0,0,0};

            if (NbrNextActionZ[CurrentAction] != 0)
               StartAction (NextActionZ[(rand () % NbrNextActionZ[CurrentAction]) +
                            FirstActionZ[CurrentAction]], 0);
         }
         else if ((Chrctristic & 2048) && (LevelSelected == 2) && (LE1orLE2 == 0))
         {                                              // Boss chinatown
            u_char NextAction[13] = {1,2,6, 2,3,6, 1,3,6, 1,2,3,6};
            u_char NbrNextAction[8] = {3,3,3,3,4,4,4,4};
            u_char FirstAction[8] = {0,3,6,0,9,9,9,9};
            
            StartAction (NextAction[(rand () % NbrNextAction[CurrentAction]) +
                         FirstAction[CurrentAction]], 0);
         }
         else if ((Chrctristic & 2048) && (LevelSelected == 0) && (LE1orLE2 == 1))
         {                                              // Boss cannon
            u_char   NextAction[11] = {3,4,5,7,10,5,7,10,5,7,10};
            u_char   NextActionLeft[5] = {3,5,7,5,7};
            u_char   NextActionRight[2] = {4,10};
            u_short  xposi[2] = {418,443};
            int      suif;

            switch (CurrentAction)
            {
               case 0:  // Use CarState to know where the boss is:
               case 3:  //          0 = Left
               case 4:  //          1 = Right
               case 6:
               case 8:
               case 11: if (CarState)   // On the right?
                        {
                           if (CurrentAction == 3)
                              suif = 10;
                           else
                              suif = NextActionRight[rand () % 2];
                        }
                        else
                        {
                           if (CurrentAction == 4)
                              suif = NextActionLeft[(rand () % 4) + 1];
                           else
                              suif = NextActionLeft[rand () % 5];
                        }
                        x = xposi[CarState];
                        if (suif == 3)
                           CarState = 1;
                        else if (suif == 4)
                           CarState = 0;
                        StartAction (suif, Inverse);
                        NewAction = 1;
                        break;
               default: if ((Action[CurrentAction].EndLink != 255) &&
                            (Action[CurrentAction].EndLink != 20))
                        {
                           StartAction (Action[CurrentAction].EndLink, Inverse);
                           NewAction = 1;
                        }
                        break;
            }
         }
         else if ((LevelSelected == 1) && (Chrctristic & 2048) && (LE1orLE2 == 1))
         {                                      // Boss stage coach (LE2)
            u_char NextAction[10] = {4, 10, 12, 11, 5, 4,4,4,8,9};
            u_char NbrNextAction[13] = {1,1,1,1,1,1,1,1,1,1,5,5,5};
            u_char FirstAction[13] =   {0,0,0,0,4,1,2,3,0,0,5,5,5};

            StartAction (NextAction[(rand () % NbrNextAction[CurrentAction]) +
                         FirstAction[CurrentAction]], 0);
         }
         else if ((Action[CurrentAction].EndLink != 255) &&
                  (Action[CurrentAction].EndLink != 20))
         {
            StartAction (Action[CurrentAction].EndLink, Inverse);
            NewAction = 1;
         }
         else if (Chrctristic & 2)  // If it's a victim and doesn't go to
            ShotToDeath = 1;        // another action, then die for good
         
         if (Death == 1)
         {
            PrevSprite = -1;
            if (VramMemHandle != -1)
            {
               FreeVram (VramMemHandle);
               VramMemHandle = -1;
            }
         }
      }

      if ((Action[CurrentAction].ActionType == ACTIONFALL) &&
          (Chrctristic & 16))
      {
         ShootPoint.GunType = 250;
         return (&ShootPoint);
      }

      if ((Action[CurrentAction].ActionType == ACTIONDIE1) &&
          (Chrctristic & 2) && (WaitCnt == 0) &&
          (Action[CurrentAction].Animation[CurrentFrame + 1] == 255))
         NewInnocent (-20 - (SprH[Action[CurrentAction].Animation[CurrentFrame]] >> 1), depth, this);

      if ((Chrctristic & 2048) && (Death) && (!ShotToDeath))
         Reappear = (rand () % 90) + 60; // Reappear after 1 to 2.5 seconds
   }
   else if ((Chrctristic & 2048) && (LevelSelected == 3) && (!ShotToDeath) && (LE1orLE2 == 0))
   {
      if (Reappear != -1)
      {
         if ((--Reappear) == 0)
         {
            x = ((rand () % 6) * 44) + 50;
            MoveDestX = x;
            MoveXSource = x;
            y = 139;
            MoveDestY = y;
            MoveYSource = y;
            StartAction (5, 0);
         }
      }
   }

   if ((Chrctristic & 2048) && (ActualScript == 7) && (LE1orLE2 == 1))
   {
      if (Action[CurrentAction].Animation[CurrentFrame] == 22)
         BarrelUp++;
      else
         BarrelUp = 0;
   }

   return (NULL);
}

//-------------------------------------
// Specify if a character is reversed
// or not
//-------------------------------------
void Character::Reverse (int inv)
{
   Inverse = inv;
}

//-------------------------------------
// Detect if the guy has received a
// shot.
// Return : 0: No hit
//          1: got hit
//          2: Got hit but was a victim
//          -1: Got hit and was a boss
//-------------------------------------
int Character::GetHit (register int coorx, register int coory,
                       register PLAYER *plr, register int shotforce,
                       Background *bkd, DoubleBuffer *db)
{
   char *HitMap;
   int cx, cy;
   int current;
   int i;
   int reload[7] = {23,36,6,8,12,6,12};  // Nbr of bullets we have at reload
   short sfxnbr[5][6] = {{-1,-1,-1,-1,3,5}, {-1,4,-1,17,6,-1},
                         {-1,-1,9,5,1,-1}, {-1,-1,-1,-1,-1,-1},
                         {-1,6,-1,7,-1,4}};

   // Don't take any hit if already dead or shot
   if ((Death) || (ShotToDeath))
      return (0);

   if ((Action[CurrentAction].ActionType == ACTIONDIE1) ||
       (Action[CurrentAction].ActionType == ACTIONIMHIT) ||
       (Action[CurrentAction].ActionType == ACTIONFALL))
      return (0);

   if (Chrctristic & 320)
      current = CurrentFrame % Action[0].NumFrame;
   else
      current = Action[CurrentAction].Animation[CurrentFrame];

   // First of all check if the shot is out of the sprite boundaries
   if ((coorx < x - SprW[current] * scale / 8192) ||
       (coorx >= x + SprW[current] * scale / 8192) ||
       (coory < y - SprH[current] * scale / 8192) ||
       (coory >= y + SprH[current] * scale / 8192))
      return (0);                   // No hit

   if ((ActualScript == 14) && (CharNbr < 6) && ((CurrentAction == 10) || (CurrentAction == 11)))
   {
      StartAction (9, Inverse);
      YouLose = 1;
      return (0);
   }

   cx = ((coorx - (x - SprW[current] * scale / 8192)) * 4096 / scale) >> 3;
   if (Inverse)
      cx = SprHitW[current] - 1 - cx;
   cy = ((coory - (y - SprH[current] * scale / 8192)) * 4096 / scale) >> 3;

   HitMap = (char *)SprHitMapAdr[current];

   if (!(HitMap[cy * SprHitW[current] + cx]))
      return (0);                   // No hit...

   if (Chrctristic & 16384)         // If this character cannot die,
      return (1);                   //   just return

   if ((Chrctristic & 2048) && (LevelSelected == 2) && (LE1orLE2 == 0)) 
   {                                                    // Boss Chinatown
      if (rand () % 11 > Difficulty)
      {
         StartAction ((rand () % 2) + 4, Inverse);
         return (1);
      }
   }

   if (Blinkable)
      TurnWhite ();

   if ((Chrctristic & 1) && (DynamicDifficulty < 11)) // If the character is an enemy, the dynamic
      DynamicDifficulty++;      // level of difficulty becomes harder

   // Put a blood burst if the character is an enemy or a victim, and if
   // the bloody mode is on
   if ((Chrctristic & 3) && (BloodyMode))   // If a victim or enemy splashes
   {                                        //  blood
      int whichblood;
      if ((HitMap[cy * SprHitW[current] + cx] * shotforce) > 1)
         whichblood = 1;
      else
         whichblood = 0;

      AddBlood (coorx, coory, whichblood);
   }

   // If we shoot a tire, it doesn't die...
   if ((LE1orLE2 == 0) && (ActualScript == 5))
   {
      char meutire[12] = {4,5,13,14,22,23,29,30,38,39,46,47};
      for (i = 0; i < 12; i++)       // Tires don't die
      {
         if (CharNbr == meutire[i])
         {
            StartAction (CurrentAction + 1, 0);
            for (i = 0; i < NbrAttaches; i++)   // Make the car to which the
            {                                   // tire is attached to shake
               if (Attach[i] == this)
               {
                  AttachTo[i]->ShakeCar ();
                  return (0);
               }
            }
         }
      }
   }
   // If we shoot a tire, it doesn't die...
   if ((LE1orLE2 == 0) && (ActualScript == 17))
   {
      char meutire[8] = {4,5,13,14,22,23,31,32};
      for (i = 0; i < 8; i++)       // Tires don't die
      {
         if (CharNbr == meutire[i])
         {
            StartAction (CurrentAction + 1, 0);
            for (i = 0; i < NbrAttaches; i++)   // Make the car to which the
            {                                   // tire is attached to shake
               if (Attach[i] == this)
               {
                  AttachTo[i]->ShakeCar ();
                  return (0);
               }
            }
         }
      }
   }

   if ((Lifepnt -= (HitMap[cy * SprHitW[current] + cx] * shotforce)) <= 0)
   {
      Lifepnt = 0;
      if ((Chrctristic & 2) && (plr != NULL))   // Shot a victim ?
         plr->VictimsMiss++;

      if ((Chrctristic & 8) && (plr != NULL))   // Shot a gun ?
      {
         plr->BulletType = CurrentAction + 1;
         plr->Bullets = reload[CurrentAction + 1];
         GunDeltaX = ((25 + plr->PlayerNbr * 270) - bkd->Bkd_X () - x) / 10;
         GunDeltaY = (230 - y - bkd->Bkd_Y ()) / 10;
         PlaySFX (bkdvab, sfxnbr[LevelSelected - 1][plr->BulletType - 1],
                  127,127);
         GunMove = 1;
      }

      if (Chrctristic & 320)                    // Shot a thrown object ?
      {
         if (x > 159)
            GunDeltaX = (350 - x) / 10;
         else
            GunDeltaX = -((x + 30) / 10);
         GunDeltaY = -4;
         if (Action[0].GunType == 7)
         {
            Death = 1;
            NewExplosion (x, y, depth, scale);
            plr->Score += 2;
            return (1);
         }
      }

      ScaleActualStep = ScaleTotalStep;
      MoveActualStep = MoveTotalStep;
      Die ();

      if ((ActualScript == 7) && (LE1orLE2 == 1))
      {
         if ((CharNbr > 6) && (CharNbr < 12))   // Barrel ?  Then explode.
            NewExplosion (x, y, depth, scale);
         else if (Chrctristic & 2048)           // Boss dying ?
            BossStageCoachDead = 1;
      }

      if ((ActualScript == 17) && (LE1orLE2 == 0))
      {
         if ((CharNbr > 14) && (CharNbr < 20))       // Guys in cadillac 1
            CarKilled[0]++;
         else if ((CharNbr > 32) && (CharNbr < 38))  // Guys in transam
            CarKilled[1]++;
         else if (CharNbr == 40)           // TransAM patch for the explosion
            CarKilled[1] = 5;
         else if ((CharNbr > 5) && (CharNbr < 11))   // Guys in white car
            CarKilled[2]++;
         else if ((CharNbr > 23) && (CharNbr < 29))  // Guys in cadillac 2
            CarKilled[3]++;
         else if ((CharNbr > 70) && (CharNbr < 76))  // Helicopter's patches
            CarKilled[5]++;
         if ((CharNbr > 90) && (CharNbr < 95))       // Motorcycle
            CarState = 2;
      }

      if ((ActualScript == 5) && (LE1orLE2 == 0))
      {
         if (CharNbr == 52)             // Motorcycle
            CarState = 4;
         else if ((CharNbr > 5) && (CharNbr < 11))
            CarKilled[0]++;
         else if ((CharNbr > 14) && (CharNbr < 20))
            CarKilled[1]++;
         else if ((CharNbr > 30) && (CharNbr < 36))
            CarKilled[2]++;
         else if ((CharNbr > 39) && (CharNbr < 42))
            CarKilled[3]++;
         else if ((CharNbr > 47) && (CharNbr < 52))
            CarKilled[4]++;
         else if (CharNbr == 55)        // TransAM patch for the explosion
            CarKilled[3] = 2;
      }

      if ((Chrctristic & 2048) && (LE1orLE2 == 0) && (LevelSelected == 5))
      {
         BossChemicalDead = 1;
         AirportEndCnt = 0;
      }

      if ((ActualScript == 20) && (LE1orLE2 == 0) && (CharNbr > 90) && (CharNbr < 95))
         NewExplosion (x, y, depth, 4096);

      if ((ActualScript == 18) && (LE1orLE2 == 0) && (CharNbr == 0))
         PassFallen = 1; // Hard code shit for the falling pass in chemical 1
      if ((ActualScript == 20) && (LE1orLE2 == 0))
      {
         if ((CharNbr > 71) && (CharNbr < 78))
            HelicoScale++;
         if ((CharNbr > 61) && (CharNbr < 79))
            NewExplosion (x, y, depth + 1, scale);
      }

      if ((Chrctristic & 2048) && (LE1orLE2 == 0))
      {
         if (LevelSelected == 2)        // Boss Chinatown
         {
            x = 210;
            MoveDestX = x;
            MoveXSource = x;
            y = 130;
            MoveDestY = y;
            MoveYSource = y;
            depth = 13;
         }

         if (LevelSelected == 3)        // Boss airport
         {
            BossAirportDead = 1;
            AirportEndCnt = 0;
         }

         if (LevelSelected == 5)        // Boss chemical
            HelicoScale = 10;

         if (LevelSelected == 1)        // Boss bank robbery
            CarKilled[4] = 255;
      }

      if (!(Chrctristic & 320))
      {
         if (!((LE1orLE2 == 0) && (LevelSelected == 0)))
            ShotToDeath = 1;
      }

      // Add to the player's score if necessary
      if (plr != NULL)
      {
         if (Chrctristic & 1)
            plr->Score += 8;
         else if (Chrctristic & 2)
         {
            if ((plr->Score -= 10) < 0)
               plr->Score = 0;
         }
         else
            plr->Score += 2;
      }
   }
   else
   {
      if ((Chrctristic & 2048) && (LevelSelected == 1) && (LE1orLE2 == 1))
         if (Action[CurrentAction].Animation[CurrentFrame] == 22)
            BarrelUp = 6;

      if (!((Chrctristic & 2048) && (LevelSelected == 3) && (LE1orLE2 == 0) &&
          ((CurrentAction == 5) || (CurrentAction == 6) || (CurrentAction == 0))))
         StartAction (Action[CurrentAction].ShotLink, Inverse);
   }

   if ((Chrctristic & 2048) && (Action[CurrentAction].ActionType == ACTIONDIE1))
   {                                               // Has been hit.  If it's a
      if ((LevelSelected == 1) && (LE1orLE2 == 1)) // boss, the level is done.
         return (1);
      else
         return (-1);
   }
   else                                 
   {
      if (Chrctristic & 2)
         return (2);                    // Has been hit but was a victim!
      else
         return (1);                    // Has been hit
   }
}

//-------------------------------------
// Return a value that says if this
// character has been shot to death
//-------------------------------------
int Character::HasBeenShot (void)
{
   return (ShotToDeath);
}

//-------------------------------------
// Return if the character is dead or
// not
//-------------------------------------
int Character::Dead (void)
{
   return (Death || ShotToDeath);
}

//-------------------------------------
// Return if the character is actually
// displayed on the screen
//-------------------------------------
int Character::Displayed (void)
{
   return (!Death && (~Chrctristic & 32768));
}

//-------------------------------------
// Return the character's type
//-------------------------------------
int Character::Characteristic (void)
{
   return (Chrctristic);
}

//-------------------------------------
// Assign another character to this one
// that will be considered a projectile
//-------------------------------------
void Character::AssignProj (Character *proj)
{
   ProjChar[Projectile] = proj;
   Projectile++;
}

//-------------------------------------
//
//-------------------------------------
void Character::SetCharNbr (char num)
{
   CharNbr = num;

   // Placve the rope at the good coordinates in Chemical 1
   if ((LE1orLE2 == 0) && (ActualScript == 17))
   {
      if (CharNbr == 82)
      {
         x = 75;
         MoveDestX = x;
         MoveXSource = x;
         y = -35;
         MoveDestY = y;
         MoveYSource = y;
      }
      else if (CharNbr == 83)
      {
         x = 405;
         MoveDestX = x;
         MoveXSource = x;
         y = -35;
         MoveDestY = y;
         MoveYSource = y;
      }
      else if (CharNbr == 84)
      {
         x = 390;
         MoveDestX = x;
         MoveXSource = x;
         y = -35;
         MoveDestY = y;
         MoveYSource = y;
      }
   }
}

//-------------------------------------
// Return character's number
//-------------------------------------
int Character::GetCharNbr (void)
{
   return (CharNbr);
}

//-------------------------------------
// Start to shake a car
//-------------------------------------
void Character::ShakeCar (void)
{
   CarShake = 1;
}


//-------------------------------------
// Decompress a sprite and load it to
// VRAM
//-------------------------------------
void Character::LoadCompressedImage (RECT *rect, int i)
{
   LoadImage(rect, SprCacheDecompress(SprHandle[i],
             (u_char *)SprAddress[i], SprW[i], SprH[i]));
}

//-------------------------------------
// Sprite Cache Functions
//-------------------------------------
#define  SPRITE_INDEX_MAX  512
#define  SPRITE_CACHE_SIZE (256 * 1024)   // 1/4 Meg for sprite cache

u_long   *SpriteIndex[SPRITE_INDEX_MAX];
u_long   SpriteStart, SpriteEnd, SpriteNext;
short    SpriteHandle, SpriteHandleFirst;

u_long SprCacheInit (u_long heap)
{
   //printf("SprCacheInit()\n");

   SpriteStart = ALIGN (heap);
   SpriteEnd = SpriteStart + SPRITE_CACHE_SIZE;
   SpriteHandleFirst = 0;

   SprCacheReset ();
   return (SpriteEnd);
}

//-------------------------------------
//
//-------------------------------------
void SprCacheSetFirst (void)
{
   //printf("SprCacheSetFirst(): %d\n", SpriteHandle);
   SpriteHandleFirst = SpriteHandle;
}

//-------------------------------------
//
//-------------------------------------
void SprCacheReset (void)
{
   //printf("SprCacheReset()\n");

   SpriteHandle = SpriteHandleFirst;
   SprCacheFlush ();
}

//-------------------------------------
//
//-------------------------------------
void SprCacheFlush (void)
{
   int i;

   //printf("SprCacheFlush()\n");
   for (i = 0; i < SPRITE_INDEX_MAX; i++)
   {
      SpriteIndex[i] = NULL;
   }

   SpriteNext = SpriteStart;
}

//-------------------------------------
//
//-------------------------------------
short SprCacheHandle (void)
{
   //printf("SprCacheHandle(): %d\n", SpriteHandle);
   return (SpriteHandle++);
}

//-------------------------------------
//
//-------------------------------------
u_long *SprCacheDecompress (short handle, u_char *compressed, int width,
                            int height)
{
   int   total = width * height;
   int   count = 0;
   int   repeat;
   u_char   *memory;

   if (SpriteIndex[handle] == NULL)
   {
      if ((SpriteNext + total) >= SpriteEnd)
      {
         printf("Sprite Cache Full!\n");
         SprCacheFlush ();
      }

      memory = (u_char *)SpriteNext;
      SpriteIndex[handle] = (u_long *)SpriteNext;
      SpriteNext = ALIGN (SpriteNext + total);

      while (total > 0)
      {
         repeat = compressed[count++];    // Non-displayed pixels first
         psx_memset (memory, 0, repeat);
         memory += repeat;
         total -= repeat;

         if (total <= 0)                  // Check for the end of the sprite...
            break;

         repeat = compressed[count++];    // Displayed pixels
         psx_memcpy (memory, &compressed[count], repeat);
         count += repeat;
         memory += repeat;
         total -= repeat;
      }

      //printf("SprCacheDecompress(): Free = 0x%x\n", SpriteEnd - SpriteNext);
   }

   return (SpriteIndex[handle]);
}

//-------------------------------------
// Fast inline versions of memset ()
// and mempy ()
//-------------------------------------
inline void psx_memset (u_char *dst, u_char c, int n)
{
   u_long l;
   u_long *dl;

   if (n > 4)
   {
      while ((u_long)dst & 0x00000003)
      {
         *dst++ = c;
         n--;
      }

      l = ((u_long)c << 24) | ((u_long)c << 16) | ((u_long)c << 8) | ((u_long)c << 0);
      dl = (u_long *)dst;
      for (; n >= 4; n -= 4)
      {
         *dl++ = l;
      }
      dst = (u_char *)dl;
   }

   for (; n > 0; n--)
   {
      *dst++ = c;
   }
}

//-------------------------------------
//
//-------------------------------------
inline void psx_memcpy (u_char *dst, u_char *src, int n)
{
   for (; n > 0; n--)
   {
      *dst++ = *src++;
   }
}

//-------------------------------------
// Instruct the character to scale up
// or down to a specific scale in a
// specific number of steps.
//-------------------------------------
void Character::ScaleTo (int scaledest, int scalestep)
{
   if (scalestep == 0)
   {
      scale = scaledest;
      ScaleTotalStep = 0;
      ScaleActualStep = 0;
   }
   else
   {
      ScaleTotalStep = scalestep;
      ScaleActualStep = 0;
      ScaleDest = scaledest;
      ScaleSource = scale;
   }
}

//-------------------------------------
// Instruct the character to move to
// a specified coordinate
//-------------------------------------
void Character::MoveTo (int destx, int desty, int movestep)
{
   if (movestep == 0)
      Set_Location (destx, desty);
   else
   {
      MoveDestX = destx;
      MoveDestY = desty;
      MoveXSource = x;
      MoveYSource = y;
      MoveActualStep = 0;
      MoveTotalStep = movestep;
   }
}

//-------------------------------------
// Make a character that's been killed
// to come back to life
//-------------------------------------
void Character::Relive (void)
{
   ShotToDeath = 0;
   Lifepnt = 1;
   Death = 1;

   if ((ActualScript == 7) && (LE1orLE2 == 1) && (CharNbr > 6) &&
       (CharNbr < 12) && (Death))  // Barrels in Stage coach level 2
      CarLoopY = 512;
}

//-------------------------------------
// Return the actual scale of the
// character
//-------------------------------------
int Character::GetScale (void)
{
   return (scale);
}

//-------------------------------------
// Specify an animation as being the
// one to call when the character die.
// Note that this number is gonna over-
// pass any other deathlink.
//-------------------------------------
void Character::SetDeathAnim (int anim)
{
   SpecificDeath = anim;
}
