//---------------------------------------------------------------------------
// Script language things!
//---------------------------------------------------------------------------

#include "links.h"

#define MAXSCRIPTINST 400

char *ScriptInst[MAXSCRIPTINST];
int  ScriptSize;
int  ScriptNbrInst;
int  CurrentInst;
int  LoopInst;
int  NbrDead;
int  instparam[36] = {0,-1,-1,-1,-1,4,2,1,8,5,1,2,9,2,8,2,2,2,2,2,2,1,1,1,1,2,0,0,0,0,0,0,4,5,3,3};
int  ScriptNbrChars;

//-------------------------------------
// Read a script file in memory
//-------------------------------------
int LoadScript (char filename[], u_long *memptr)
{
   int fhandle;
   char *memloc;
   int i, j, k;
   int filesize;
   char *fnam;

   //----------------------------------
   if (DemoMode)                        // Load the demo file
   {                                    //
      fnam = (char *)*memptr;
      for (i = 0; (filename[i] != 0) && (filename[i] != '.'); i++)
         fnam[i] = filename[i];
      fnam[i + 0] = '.';
      fnam[i + 1] = 'D';
      fnam[i + 2] = 'M';
      fnam[i + 3] = 'O';
      fnam[i + 4] = 0;

      LoadDemoFile (fnam);
   }

   memloc = (char *)*memptr;
   if ((u_long)memloc & 3)
      memloc = (char *)(((u_long)memloc + 4) & 0xFFFFFFFC);
   ScriptInst[0] = memloc;

   if (OpenFile (filename) == -1)
   {
      return (-1);
   }

   // Reads the header of the animation file
   if (ReadFile (memloc, 5) != 5)
   {
      CloseFile ();
      return (-2);
   }

   // Check to be sure it is a script file
   if ((memloc[0] != 'S') || (memloc[1] != 'C') || (memloc[2] != 'P'))
   {
      CloseFile ();
      return (-3);
   }
   ScriptNbrInst = memloc[3] + memloc[4] * 256;

   // Check the filesize
   filesize = FileSize ();

   // Reads the rest of the file in memory
   if (ReadFile (memloc, filesize - 5) != filesize - 5)
   {
      CloseFile ();
      return (-4);
   }

   // Close the file
   if (CloseFile () == -1)
      return (-5);

   //----------------------------------
   i = 0;
   for (j = 0; j < ScriptNbrInst; j++)
   {
      ScriptInst[j] = &memloc[i];
      if (instparam[memloc[i]] == -1)  // Is the parameter a string?
      {
         i++;
         while (memloc[i] != 0)        //Search for the end of the string...
            i++;
         i++;
      }
      else
         i += 2 * instparam[memloc[i]] + 1;
   }

   //----------------------------------
   memloc = (char *)((int)memloc + filesize - 5);

   *memptr = (u_long)memloc;

   return (0);
}

//-------------------------------------
// Execute the script's instructions
// that has to be executed at start.
//-------------------------------------
int ExecScriptStart (Background *bkgnd, Character *chars[], u_long *memptr,
                     int *vabnbr, DoubleBuffer *db)
{
   int i;
   ScriptNbrChars = 0;
   short param0, param1, param2, param3, param4, param5, param6, param7;

   int DefScale = 4096;
   CurrentInst = 0;
   LoopInst = 0;

   //----------------------------------
   for (i = 0; i < ScriptNbrInst; i++)
   {
      switch (*ScriptInst[i])
      {
         case 1: bkgnd->Load (ScriptInst[i] + 1, memptr);          // LoadBKD
                 break;
         case 2: if ((*memptr) & 3)                                // LoadKRT
                    *memptr = (((*memptr) + 4) & 0xFFFFFFFC);
                 chars[ScriptNbrChars] = (Character *)*memptr;
                 *memptr += sizeof (Character);
                 chars[ScriptNbrChars]->Load (ScriptInst[i] + 1, memptr);
                 chars[ScriptNbrChars]->Scale (DefScale);
                 chars[ScriptNbrChars]->SetCharNbr (ScriptNbrChars);
                 ScriptNbrChars++;
                 break;
         case 3: *vabnbr = LoadSFX (ScriptInst[i] + 1, memptr);    // LoadVAB
                 break;
         case 5: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                 param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                 param2 = *(ScriptInst[i] + 5) + (*(ScriptInst[i] + 6) * 256);
                 bkgnd->SetLocation (param1, param2);       // SetBKDLoc
                 break;
         case 6: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                 param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                 chars[param0]->SetDepth (param1);     //SetDepth
                 break;
         case 7: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                 chars[ScriptNbrChars] = chars[param0]->Clone_Character (0, memptr);
                 chars[ScriptNbrChars]->Scale (DefScale);
                 chars[ScriptNbrChars]->SetCharNbr (ScriptNbrChars);
                 ScriptNbrChars++;
                 break;
         case 8: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                 param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                 param2 = *(ScriptInst[i] + 5) + (*(ScriptInst[i] + 6) * 256);
                 param3 = *(ScriptInst[i] + 7) + (*(ScriptInst[i] + 8) * 256);
                 param4 = *(ScriptInst[i] + 9) + (*(ScriptInst[i] + 10) * 256);
                 param5 = *(ScriptInst[i] + 11) + (*(ScriptInst[i] + 12) * 256);
                 param6 = *(ScriptInst[i] + 13) + (*(ScriptInst[i] + 14) * 256);
                 param7 = *(ScriptInst[i] + 15) + (*(ScriptInst[i] + 16) * 256);
                 AddLink (chars[param1], chars[param0], param3, param2,
                          param4, param5, param6, param7);
                 break;
         case 11: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                  param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                  chars[param0]->LifePoint (param1);// SetTotalHitPoints
                  break;
         case 13: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                  param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                  chars[param0]->SetCharacteristics (param1);
                  break;
         case 16: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                  param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                  chars[param0]->Scale (param1);     //SetScale
                  break;
         case 19: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                  param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                  #ifdef TARGETCD
                     #ifdef PLAY_CDXAS
                        if (!DemoMode)                    // PlayTrack
                        {
                           CDXAId = AudioPlayCue (param0);
                           AudioChangeVolume (CDXAId, MusicVol);
                        }
                     #endif
                  #endif
                  break;
         case 20: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                  param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                  chars[param0]->AssignProj (chars[param1]);
                  break;
         case 24: DefScale = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                  break;
         case 35: param2 = *(ScriptInst[i] + 5) + (*(ScriptInst[i] + 6) * 256);
                  if (param2 == -1)             // SetDeath (frame# = -1)
                  {
                     param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                     param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                     chars[param0]->SetDeathAnim (param1);
                  }
                  else if (CurrentInst == 0)// Point to the first instruction
                  {                         // that depends on the framenumber
                     CurrentInst = i;
                     LoopInst = i;
                  }

                  break;
         default: if (CurrentInst == 0)     // Point to the first instruction
                  {                         // that depends on the framenumber
                     CurrentInst = i;
                     LoopInst = i;
                  }
      }
   }

   return (ScriptNbrChars);
}

//-------------------------------------
// Execute the script's instructions
// that refers to a specified frame #.
// If -1 is specified, all the ins-
// tructions without a frame# are
// executed (like loadBKD for example)
//-------------------------------------
int ExecScript (Background *bkgnd, Character *chars[], u_long *framenbr)
{
   int    i;
   point  Popo;
   short  param0, param1, param2, param3, param4;
   short  param5, param6, param7, param8;
   int    valreturn = 0;
   int    instruction = 1;
   int    framenbrofs[36] = {0,0,0,0,0,7,0,0,0,9,0,0,9,0,11,3,0,3,3,3,0,0,0,0,0,3,0,0,0,0,0,0,7,9,5,5};
   Character *che;

   while (instruction)
   {
      if (CurrentInst == LoopInst)
         NbrDead = 0;
      i = CurrentInst;
      instruction = 0;
      switch (*ScriptInst[i])
      {
         case 9: param4 = *(ScriptInst[i] + 9) + (*(ScriptInst[i] + 10) * 256);
                 if (param4 == *framenbr)                   // ScrollBKDTo
                 {
                    if (NbrDead == (CurrentInst - LoopInst))
                    {
                       param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                       param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                       param2 = *(ScriptInst[i] + 5) + (*(ScriptInst[i] + 6) * 256);
                       param3 = *(ScriptInst[i] + 7) + (*(ScriptInst[i] + 8) * 256);
                       bkgnd->ScrollTo (param1, param2, param3);
                       CurrentInst++;
                       LoopInst = CurrentInst;
                       NbrDead = 0;
                    }
                    else
                    {
                       CurrentInst = LoopInst;
                       int j = framenbrofs[*ScriptInst[LoopInst]];
                       i = LoopInst;
                       *framenbr = *(ScriptInst[i] + j) +
                                   (*(ScriptInst[i] + j + 1) * 256) - 1;
                    }
                 }
                 break;
         case 12: param4 = *(ScriptInst[i] + 9) + (*(ScriptInst[i] + 10) * 256);
                  if (param4 == *framenbr)                      // Attach
                  {
                     param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                     param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                     param8 = *(ScriptInst[i] + 17) + (*(ScriptInst[i] + 18) * 256);
                     che = chars[param0];
                     if ((param8 <= Difficulty) && (!che->HasBeenShot ()))
                     {
                        param2 = *(ScriptInst[i] + 5) + (*(ScriptInst[i] + 6) * 256);
                        param3 = *(ScriptInst[i] + 7) + (*(ScriptInst[i] + 8) * 256);
                        param5 = *(ScriptInst[i] + 11) + (*(ScriptInst[i] + 12) * 256);
                        param6 = *(ScriptInst[i] + 13) + (*(ScriptInst[i] + 14) * 256);
                        param7 = *(ScriptInst[i] + 15) + (*(ScriptInst[i] + 16) * 256);
                        Popo = chars[param1]->Coordinate ();
                        chars[param0]->Set_Location (Popo.x + param5,
                                                     Popo.y + param6);
                        AddAttach (chars[param0], chars[param1], param5, param6);
                        chars[param0]->SetDepth (param7);
                        chars[param0]->StartAction (param2, param3);
                        if (che->Characteristic () & 1024)
                           NbrDead++;
                     }
                     else
                     {
                        if ((che->HasBeenShot ()) || (param7 > Difficulty) ||
                            (che->Characteristic () & 1024))
                           NbrDead++;
                        int j = framenbrofs[*ScriptInst[i + 1]];
                        *framenbr = *(ScriptInst[i + 1] + j) +
                                    (*(ScriptInst[i + 1] + j + 1) * 256) - 1;
                     }
                     instruction = 1;
                     CurrentInst++;
                  }
                  break;
         case 14: param5 = *(ScriptInst[i] + 11) + (*(ScriptInst[i] + 12) * 256);
                  if (param5 == *framenbr)      ////////// StartActionXY
                  {
                     param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                     param7 = *(ScriptInst[i] + 15) + (*(ScriptInst[i] + 16) * 256);
                     che = chars[param0];
                     if ((param7 <= Difficulty) && (!che->HasBeenShot ()))
                     {
                        param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                        param2 = *(ScriptInst[i] + 5) + (*(ScriptInst[i] + 6) * 256);
                        param3 = *(ScriptInst[i] + 7) + (*(ScriptInst[i] + 8) * 256);
                        param4 = *(ScriptInst[i] + 9) + (*(ScriptInst[i] + 10) * 256);
                        param6 = *(ScriptInst[i] + 13) + (*(ScriptInst[i] + 14) * 256);

                        // Here's a special case for the FUCKIN' metal box in
                        // the FUCKIN' first level of the HiJacking
                        if ((*framenbr == 785) && (ActualScript == 10) && (LE1orLE2 == 0))
                        {
                           if (!(((MetalBoxUp & 0x0F) != 0) ||
                               ((MetalBoxUp >> 4) == 0) || (chars[42]->HasBeenShot ()) ||
                               (chars[19]->Displayed ()) || (chars[20]->Displayed ()) ))
                           {
                              if ((che->StartAction (param1, param6)) == 0)
                              {
                                 che->Set_Location (param2, param3);
                                 che->SetDepth (param4);
                              }
                           }
                           if (che->Characteristic () & 1024)
                              NbrDead++;
                        }
                        else if ((*framenbr == 1130) && (ActualScript == 10) && (LE1orLE2 == 0))
                        {
                           if (!(((MetalBoxUp & 0x0F) != 1) ||
                                ((MetalBoxUp >> 4) == 0) ||
                                ((chars[19]->HasBeenShot ()) && (chars[20]->HasBeenShot ())) ||
                                (chars[42]->Displayed ()) ))
                           {
                              if (!(che->StartAction (param1, param6)))
                              {
                                 che->Set_Location (param2, param3);
                                 che->SetDepth (param4);
                              }
                           }
                           if (che->Characteristic () & 1024)
                              NbrDead++;
                        }
                        else
                        {
                           if ((che->StartAction (param1, param6)) == 0)
                           {
                              che->Set_Location (param2, param3);
                              che->SetDepth (param4);
                           }

                           if (che->Characteristic () & 1024)
                           NbrDead++;
                        }
                     }
                     else
                     {
                        if ((che->HasBeenShot ()) || (param7 > Difficulty) ||
                            (che->Characteristic () & 1024))
                           NbrDead++;
                        int j = framenbrofs[*ScriptInst[i + 1]];
                        *framenbr = *(ScriptInst[i + 1] + j) +
                                    (*(ScriptInst[i + 1] + j + 1) * 256);
                     }
                     instruction = 1;
                     CurrentInst++;
                  }
                  break;
         case 15: param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                  if (param1 == *framenbr)                  // ClearBKD
                  {
                     param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                     bkgnd->Clear ();
                     instruction = 1;
                     CurrentInst++;
                  }
                  break;
         case 17: param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                  if (param1 == *framenbr)                  // StopAction
                  {
                     param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                     chars[param0]->Clear ();
                     instruction = 1;
                     CurrentInst++;
                     NbrDead++;
                  }
                  break;
         case 18: param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                  if (param1 == *framenbr)                  // PlaySound
                  {
                     param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                     PlaySFX (bkdvab, param0, 127,127);
                     instruction = 1;
                     CurrentInst++;
                     NbrDead++;
                  }
                  break;
         case 21: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                  if (param0 == *framenbr)                  // EndLevel
                  {
                     valreturn = 1;
                     instruction = 1;
                     CurrentInst++;
                     bkgnd->BuildDestClut (0);
                  }
                  break;
         case 22: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                  if (param0 == *framenbr)                  // EndRound
                  {
                     valreturn = 2;
                     instruction = 1;
                     CurrentInst++;
                     bkgnd->BuildDestClut (0);
                     for (i = 0; i < ScriptNbrChars; i++)
                        chars[i]->BuildDestClut (0);
                  }
                  break;
         case 23: param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                  if (param0 == *framenbr)              // ClearFrameCounter
                  {
                     *framenbr = 0;
                     *(ScriptInst[i]) = 25; // Don't clear the timer twice...
                     instruction = 1;
                     CurrentInst++;
                  }
                  break;
         case 25: param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                  if (param1 == *framenbr)                  // AddEnemyBar
                  {
                     param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                     instruction = 1;
                     CurrentInst++;
                     InitEnemyLife (chars[param0]);
                  }
                  break;
         case 32: param3 = *(ScriptInst[i] + 7) + (*(ScriptInst[i] + 8) * 256);
                  if (param3 == *framenbr)                  // ScaleTo
                  {
                     param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                     param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                     param2 = *(ScriptInst[i] + 5) + (*(ScriptInst[i] + 6) * 256);

                     chars[param0]->ScaleTo (param1, param2);
                     instruction = 1;
                     CurrentInst++;
                     NbrDead++;
                  }
                  break;
         case 33: param4 = *(ScriptInst[i] + 9) + (*(ScriptInst[i] + 10) * 256);
                  if (param4 == *framenbr)                  // MoveTo
                  {
                     param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                     param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);
                     param2 = *(ScriptInst[i] + 5) + (*(ScriptInst[i] + 6) * 256);
                     param3 = *(ScriptInst[i] + 7) + (*(ScriptInst[i] + 8) * 256);

                     chars[param0]->MoveTo (param1, param2, param3);
                     instruction = 1;
                     CurrentInst++;
                     NbrDead++;
                  }
                  break;
         case 34: param2 = *(ScriptInst[i] + 5) + (*(ScriptInst[i] + 6) * 256);
                  if (param2 == *framenbr)          // ResetCharacteristics
                  {
                     param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                     param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);

                     chars[param0]->SetCharacteristics (param1);
                     instruction = 1;
                     CurrentInst++;
                     NbrDead++;
                  }
                  break;
         case 35: param2 = *(ScriptInst[i] + 5) + (*(ScriptInst[i] + 6) * 256);
                  if (param2 == *framenbr)          // SetDeath
                  {
                     param0 = *(ScriptInst[i] + 1) + (*(ScriptInst[i] + 2) * 256);
                     param1 = *(ScriptInst[i] + 3) + (*(ScriptInst[i] + 4) * 256);

                     chars[param0]->SetDeathAnim (param1);
                     instruction = 1;
                     CurrentInst++;
                     NbrDead++;
                  }
                  break;
      }
   }

   return (valreturn);
}

//-------------------------------------
// Dump script file information on the
// screen
//-------------------------------------
void ScriptDump ()
{
   int i, j;
           
   FntPrint ("Current Inst: %d\n", CurrentInst);
   FntPrint ("Loop Inst: %d\n", LoopInst);
   FntPrint ("Inst %d:", CurrentInst);
   i = CurrentInst;
   if (instparam[*(ScriptInst[i])] != -1)
      for (j = 0; j < instparam[*(ScriptInst[i])]; j++)
         FntPrint (" %d", *(ScriptInst[i] + j * 2 + 1) +
                           (*(ScriptInst[i] + j * 2 + 2) * 256));
   FntPrint ("\n");
}
