(* ########################## *)
(* PYRO 2.2 MAIN PROGRAM LOOP *)
(* ########################## *)

BEGIN
  (********** PC-SPECIFIC INITIALIZATION -- DON'T WORRY ABOUT THIS **********)
  FindParams;
  Copyright;
  BetaTest(false);
  WorkDisk;
  InitScreen(OrigMode);
  InitPalette;
  BlackOut;
  FindFadeInc;
  vidspeed := videospeed;


  (********** THE FOLLOWING IS NOT REALLY NECESSARY, BUT NICE **********)

  (* First we make the variable "Zero", which we will use in place of the
     constant "0".  This is only so that people will have a harder time
     hacking up the program with their favorite debugger so that they start
     with 1,000,000 points or something stupid like that. *)
  zero := -1;
  inc(zero);

  (* Next we make an array of various square root values, so we don't have to
     compute them during game play.  Square roots are slow to compute! *)
  MakeSqrtArray(SqrtArr);


  (********** INITIALIZE A FEW VARIABLES **********)
  demo := false;      (* We're not in the middle of playing a demo. *)
  demow := false;     (* We're not in the middle of "recording" a demo. *)
  building := 0;      (* We're not playing. *)
  playing := false;   (* " *)
  PrevBld   := zero;  (* This will contain the last building we died in, for
                         use in warping back there using the secret warp. *)
  PrevSkill := zero;  (* Same as above, but contains last skill level. *)
  Calibrated := False;(* Joystick has not been calibrated -- it will be the
                         first time a joystick button is pressed. *)
  Elapsed    := 0;    (* Elapsed time, in clock ticks (1/18ths of a sec) *)


  (********** SHOW THE INTERACTIVE INSTRUCTIONS IF NECESSARY **********)
  if FirstRun or (cmdline[cl_Instructions]<maxint) then
    Instructions;


  (******* MAKE THE "PYRO ][" LOGO ON THE TOP TEN LINES OF THE SCREEN *******)
  LoadTopTitle(Logo);
  MakeTitle;


  (********** PICK A SCENARIO AND SHOW THE LOGO **********)

  Scen := PickScenario; (* Pick from menu of choices.  Choices are read from
                           the file PYRO22.OPT *)

  LoadScenarioTitle(Logo,Scen); (* Load the logo.  World Terrorism's logo is
                                   in PYRO22.LWT; Secret Agent's is in
                                   PYRO22.LAT. *)

  level := zero; (* Just to make damn sure that the screen redraw routines
                    know we're not playing. *)

  if directvideo and (vidspeed<10) then (* If screen I/O is fast enough, then
                                           do a fancy logo... *)
    CASE dl_type OF
      1    : MakeTitle;
      2    : FireTitle;
      3    : ZoomTitle(dl_o, dl_d, dl_zoom, (dl_zoom>1), false);
      4..5 : ZoomTitle(dl_o, dl_d, dl_zoom, (dl_type=5), true);
    END (* case *)
  else MakeTitle; (* Otherwise, just draw it on the screen plain. *)

  LoadTopTitle(Logo); (* Reload the array "logo" with the "Pyro ][" logo that
                         normally takes the top ten lines of the screen. *)


  (********** START THE MAIN LOOP **********)
  (* This loop waits for you to press a key, then plays the game, then does
     the whole process over.  While it is waiting for you to press a key, it
     can play demos. *)

  REPEAT

    if demo then       (* If there was a demo playing, end it. *)
      demo := false
    else               (* Otherwise, wait until all keys are released. *)
      PauseUntilClear;

    Randomize;         (* Set the random seed to the system clock. *)

    Building := zero;  (* We're not in the middle of gameplay *)

    if graphics then   (* Ignore this, it is PC-specific. *)
      chargen(1);

    DrawTitle(Elapsed); (* Show either the copyright screen, top ten screen,
                           or credits, depending on the value of "Elapsed". *)

    PressToStart;       (* Write "Press any key to start" on the bottom of the
                           screen *)


    (********** WAIT LOOP **********)
    (* Wait for them to press a key to start the game *)

    InitTime(LastTurn);
    REPEAT
      InitTime(NewTime);         (* Measure how much time has passed. *)
      if (NewTime>LastTurn) then
        Elapsed := Elapsed + (NewTime - LastTurn);
      LastTurn := NewTime;
      if (abs((elapsed mod 4800)-4400) < 40) then
        Demo := True             (* If enough time has passed, play a demo. *)
      else                       (* Otherwise, just keep scrolling through the
                                    title screens (top ten, credits) *)
        TitleScreens(Elapsed,TopTen);
                                 (* Keep doing this until they press a key or
                                    a joystick button, or it is time to play
                                    a demo. *)
    UNTIL (KeyPressed or Demo or ( (cmdline[cl_NoJoystick]>0) and
          (JoyStkBtn(BtnA1) or JoyStkBtn(BtnA2)) ) );

    if ((not calibrated) and (not keypressed) and (not demo)) then
    (* If they pressed a joystick button, and the joystick has not been
       calibrated yet, then calibrate it. *)
      calibrate(JoyLeft,JoyRight,JoyUp,JoyDown,Calibrated)
    else
    (* If they pressed a key, make sure it wasn't ALT-Q (Quit).  If it was,
       quit.  If not, absorb it. *)
      if keypressed then
        keystroke(c);

    InitVariables;
    CheckDemoMake; (* If we're recording a demo (done with command-line
                      options), open up all the necessary output files. *)


    (********** NEW BUILDING **********)

    REPEAT
      if demo then (* If we're playing a demo, load the necessary level info
                      from the demo file. *)
        StartDemoPlay
      else         (* If we're playing a real game, determine the necessary
                      info for the next building to be played. *)
        BEGIN

          (* If the won the game, start over at the next skill level. *)
          if ((level=endlev) and (building=endbld)) then
            BEGIN
              building := zero;
              inc(skill)
            END; (* if *)

          (* Load the building info, like what color the walls are. *)
          NewBuilding(Building,Level,Wall,Cans);

          (* Determine how many floors are in the building. *)
          MaxLevel := Floors[Building];

          (* Write the title screen for the building. *)
          DescribeBuilding(Building,Titles,MaxLevel,Calibrated);

          (* These are for cheating and making demos, all done by secret
             command line options.  You can safely ignore them or put in
             your own backdoors. *)
          CheckLevelCheat;
          CheckDemoMake;

        END; (* else *)


      (********** NEW LEVEL **********)

      REPEAT

        (* Figure out how much time until the secret warp door opens. *)
        ComputeJumpTime(JumpTime,Jump,Building,Level,MaxLevel);

        (* Initialize all the variables that need to be initialized for this
           new level. *)
        NewLevel(Level,FuseLength,Man,Enemy,Done,MaxFire,MaxMaxFire,
                 MaxLeak,MinTurn,Turn,Jump,Cans,Tripped,Populace,Building);

        (* Create the floorplan for this level.  This includes placing gas cans
           and potions and things. *)
        DrawFloor(Building,Level,MaxLevel,Wall,Screen,
                  Dir,Man,Covering,Enemy,EnCover,Jump,Sensor,Bomb,Potion);

        (* Resets the toggle keys -- Scroll Lock, Alt, and Caps Lock *)
        ResetToggle(Pickup,HighSpeed,NoSpread,Throwing);

        (* Loads the toggle keys with current values *)
        if not demo then CheckToggle(Pickup,HighSpeed,NoSpread,Throwing);

        (* If this floor has a nuclear reactor, show it. *)
        if ((def[building,20]<>'0') and (level=floors[building])) then
          BEGIN
            ReactorOne(Screen);
            Refresh(Screen);
            ReactorTwo(Screen,Wall);
            sensor[1].o := 0;
            bomb.o := 0
          END (* if *)
        (* Otherwise, show the screen that DrawFloor() created. *)
        else
          Refresh(Screen);

        (* Write the name of the building on the side of the screen. *)
        WriteName(Buildings,Building,Level,MaxLevel,Populace);

        (* Show how many viles you are holding, on the top-left of the screen. *)
        ShowViles(Viles);

        (* Fade the screen in if you have VGA. *)
        FadeIn;

        (* Move your man one square.  This is done to plot him. *)
        Move(Man,ManLast,Screen,Wall,Dir,Covering,Cans,MakeLeak,Pickup,
             Dropped,Done,True,Jump,Building,Level,MaxLevel,Bomb,Invis,Viles);

        (* If there is an enemy on this floor, move him one square.
           This is done to plot him. *)
        if (enemy.o>0) and (enemystart=0) then
          MoveEnemy(Enemy,Screen,Wall,Dir,EnCover,Building,Level,MaxLevel,Dead);

        (* Reset the toggle keys again. *)
        ResetToggle(Pickup,HighSpeed,NoSpread,Throwing);

        (* Some explanation required here:
           "HighSpeed" is set to true if the person is holding Scroll Lock
           down.  The routine "Wait()" does the following:
             (1) pause for a few milliseconds
             (2) move any projectiles (gas cans that have been thrown)
             (3) pause for a few more milliseconds
           So, if "HighSpeed" is false, we can just call "Wait()".  If it
           is true, however, we don't want to pause, but we still need to
           move the projectiles like "Wait()" would.  So we call
           "MoveProjectiles()". *)
        if not HighSpeed then
          Wait(LastTurn,Pickup,HighSpeed,NoSpread,Throwing,Building,Skill)
        else
          MoveProjectiles;

        playing := true;


        (********** GAME LOOP **********)
        (* Loop until the player completes the level or dies.  Note that the
           same code is used whether the player is "live" or just a demo. *)

        REPEAT
          inc(turn);

          (* Get a keystroke or joystick command *)
          Input(Man.NDi,MakeLeak,Calibrated,Cans,Pickup,Dropped,JoyLeft,
                JoyRight,JoyUp,JoyDown,NoSpread,LastKey,Invis,Viles);

          (* If the command was to spill a gas can, do so *)
          if makeleak and (not dead) then
            StartLeak(MakeLeak,Covering,Leak,MaxLeak,Man,NoSpread);

          (* If the command was to throw a gas can, do so *)
          if makemissile and (not dead) then
            StartMissile(man);

          (* Move any gas cans that are in mid-flight *)
          MoveProjectiles;

          (* If it is time, open up the secret warp door *)
          if (((turn=JumpTime) or (turn=JumpTime+20)) and
             (building<maxbld) and
             ((level=1) or (level=ord(def[building,1])-ord('0')))) then
            Passage(Turn,JumpTime,Screen,Wall,Done,Dead);

          (* If the player is done with the level or dead, count up how many
             turns it takes the fire to reach the end of his fuse.  This will
             determine how long his fuse is on the next level. *)
          if done or dead then
            CountFuse(FuseLength,Screen)

          (* Otherwise, move the player one square in the direction he is
             travelling.  Also, if he has requested a new direction and it is
             possible to make a turn here, then change the direction he is
             travelling. *)
          else
            Move(Man,ManLast,Screen,Wall,Dir,Covering,Cans,MakeLeak,
                 Pickup,Dropped,Done,False,Jump,Building,Level,MaxLevel,
                 Bomb,Invis,Viles);

          (* If there is a live enemy, move him one square. *)
          if (enemy.o>0) and (turn>=enemystart) and (not enemydone) then
            MoveEnemy(Enemy,Screen,Wall,Dir,EnCover,
                      Building,Level,MaxLevel,Dead);

          (* If either the player, the enemy, or spreading gas has tripped
             off the sensor on a security door, start the door closing. *)
          Sense(Tripped,Sensor,Man,Screen,Wall,SenseTime);

          (* If there is an unstable gas can on this level, see if it is
             getting ready to explode from the heat. *)
          if (bomb.i>0) then
            CheckBomb(Screen,Bomb,MaxFire,Fire,Turn);

          (* If there is gas speading because the player dumped out a gas can,
             let it spead some. *)
          Spread(Leak,MaxLeak,Screen,Building,NoSpread);

          (* Move any gas cans that are in mid-flight, again. *)
          MoveProjectiles;

          (* For each piece of fire on the screen, make it either:
               (1) keep burning,
               (2) spread, or
               (3) go out. *)
          Burn(Fire,MaxFire,Screen,LastFire,Code,
               MinTurn,Turn,Building,Score,Bomb,Enemy,Skill);

          (* Check the toggle keys: Scroll Lock, Alt, Caps Lock *)
          ResetToggle(Pickup,HighSpeed,NoSpread,Throwing);
          if not demo then
            CheckToggle(Pickup,HighSpeed,NoSpread,Throwing);

          (* Some explanation required here (like above):
             "HighSpeed" is set to true if the person is holding Scroll Lock
             down.  The routine "Wait()" does the following:
               (1) pause for a few milliseconds
               (2) move any projectiles (gas cans that have been thrown)
               (3) pause for a few more milliseconds
             So, if "HighSpeed" is false, we can just call "Wait()".  If it
             is true, however, we don't want to pause, but we still need to
             move the projectiles like "Wait()" would.  So we call
             "MoveProjectiles()". *)
          if not HighSpeed then
            Wait(LastTurn,Pickup,HighSpeed,NoSpread,Throwing,Building,Skill)
          else
            MoveProjectiles

          (* keep on doing the loop until:
               (1) the player pressed ALT-Q (quit)
               (2) it was a demo, not a "live" player, and it finished
               (3) it was a demo, not a "live" player, and a key was pressed
               (4) the person walked down the stairs and then all the fire
                   burned itself out
               (5) the person died and then all the fire burned itself out *)
        UNTIL ( Quit or (Demo and (DemoDone or Keypressed)) or
                ( (Done or Dead) and (MaxFire=0)
                  and (Turn>MinTurn+10) ) );

        (* We're done playing this level, or we're done showing the demo. *)
        playing := false;
        CloseDemoPlay;
        CloseDemoMake;

        (* Show the score so far, if it wasn't a demo. *)
        if not (quit or demo) then ShowScore(Score,Total,Screen,Wall,Calibrated,
                                             Quit,Building,Level);

      (* Go on to the next level, unless:
           (1) that was the last level in this building,
           (2) that was a demo,
           (3) the player died,
           (4) the player pressed ALT-Q (quit), or
           (5) the player won the game. *)
      UNTIL ((Level=MaxLevel) or Dead or Quit or Demo or
            ((building=endbld) and (level=endlev)));

      (* If that was a "live" player, not a demo, then save what building he
         was just playing, so that he can "warp" back there with the secret
         warp on building 1 level 1. *)
      if not demo then
        BEGIN
          if (building<>prevbld) then
            if (building=0) then
              prevbld := 1
            else
              prevbld := building-1;
          prevskill := skill;
        END; (* if *)

    (* Go on to the next building, unless:
         (1) the player died,
         (2) the player pressed ALT-Q (quit), or
         (3) that was a demo, not a "live" player. *)
    UNTIL (Dead or Quit or Demo);

    (********** END OF GAME **********)

    (* Write "QUIT" on the screen if ALT-Q was pressed. *)
    if quit then
      DrawQuitLogo;

    (* If there was a "live" player, not a demo, then if the player got a top
       score, then get his name and save it to the top-ten file. *)
    if not demo then
      BEGIN
        TopScore(TopTen,Total,Building,Level,Skill);
        if (total >= topten[10].score) and (total > 0) then
          WriteScenario(Buildings,Floors,Titles,MaxBld,TopTen,Populace,Scen);
      END (* if *)

  (* Go back to the wait loop, where the game waits for you to press a key
     to start, and plays a demo for you if you don't press a key in a certain
     amount of time. *)
  UNTIL False;
END.

