// Main1.cpp : Second of 3 mainline functions
//
// Public functions:
//    main1
//
// Private functions:
//    clock4
//    close_the_cave
//    do_cameo
//    lamprey

#include "stdafx.h"

#include "AdvIO.h"
#include "AdvMain.h"
#include "AdvUtil.h"

/////////////////////
// Forward references
void close_the_cave (AdvGlobalContext& gc);
void clock4 (AdvGlobalContext& gc);
void do_cameo (AdvGlobalContext& gc);
void lamprey (AdvGlobalContext& gc);

///////////////////
// Public functions

void main1
  (AdvGlobalContext& gc)   // global context
//
//  Second of 3 mainline functions.
//
//  This function must be executed before getting player input, only
//  if the player moved.
//
{
long  nKnives;    // # knives thrown by dwarves

  gc.m_bJustDied = false;

  // If a dwarf giving him a hard time ...
  if ((gc.m_oneExit [gc.m_nPrevLoc]) && (gc.m_nWhereIs [dwarf] == gc.m_nPrevLoc))
     {
       // Inform him and compute # knives thrown
       gc.m_nHere = gc.m_nPrevLoc;
       gc.m_bMoved = false;
       sayMessage (gc, dwarfblock);
       nKnives = random (gc.m_nDwarvesInRoom + 5) - 3;

       // Knives were rarely being thrown - this makes it likely that at least
       // one knife is thrown.  (RAB, 12-Dec-2000)
       if (nKnives <= 0)
          if (chance (75))
             nKnives = 1;

       // If one or more knives thrown ...
       if (nKnives > 0)
          {
            // Inform him
            if (nKnives == 1)
               sayMessage (gc, knifethrown);
            else
               sayMessageValue (gc, knivesthrown, nKnives);

             // Compute accuracy of throw
             long nSurvival = (-5)*gc.m_nInventory + 75;  // hard to hit a moving target
             if (gc.m_special2 [dwarf])                   // is he angry?
                nSurvival -= 20;
             if (gc.m_nState [mushroom] == 2)             // eating mushroom gives strength
                nSurvival += 25;
             if (nSurvival < 0)
                nSurvival = 0;
             nSurvival = nSurvival / nKnives;             // slim chance if more than one knife!

             if (chance (nSurvival) || gc.m_special1 [dwarf])
                {
                  // He survived the throw!
                  if (nKnives == 1)
                     sayMessage (gc, misses);
                  else
                     sayMessage (gc, knivesmiss);
                  gc.m_special1 [dwarf] = false;
                }
             else
                {
                  // He was struck by the knives!
                  if (nKnives == 1)
                     sayMessage (gc, getsyou);
                  else
                     sayMessage (gc, knifegotyou);
                  coroner (gc);
                  return;
                }
          }
       return;
     }

  // phog() must be done *before* clearing gc.m_bMoved because it checks to
  // see if we're stuck at plain_2
  if (near (fog))
     phog (gc);
  gc.m_bMoved = false;

  // Check his lamp - warn him when lamp is running out of power
  if (near (lamp) && (gc.m_nState [lamp] == 1))
     {
       gc.m_nLampLife--;
       if ((gc.m_nLampLife == 40) ||
           (gc.m_nLampLife == 10) ||
           (gc.m_nLampLife == 0))
          lamprey (gc);
       if (gc.m_bJustDied)
          return;
     }

  // Do goblin stuff
  if (gc.m_nWhereIs [goblins] != limbo)
     {
       apport (goblins, gc.m_nHere);
       if (gc.m_nState [goblins] > (-1))    // Don't say "You are being pursued..." the first time
          sayMessage (gc, goblin_chase);    // So they are described as an object
       gc.m_nState [goblins]++;
       gc.m_invisible [goblins] = false;
     }

  // If there's light here and we haven't said anything ...
  if (gc.m_bOk2Describe)
     if (gc.m_lit [gc.m_nHere] ||
         (near (lamp) && (gc.m_nState [lamp] == 1)))
        {
          // Describe location
          bool  bDetail = true;
          if (gc.m_bFastMode ||
              (gc.m_visited [gc.m_nHere] && (gc.m_bBriefMode || (gc.m_visited [gc.m_nHere] % DETAIL))))
             bDetail = false;
          describePlace (gc, gc.m_nHere, bDetail);
          gc.m_visited [gc.m_nHere]++;

          // First describe primary objects
          long  nObject;
          bool  bObjectDescribed = false;
          for (nObject=MINOBJECTS; (nObject <= MAXOBJECTS); nObject++)
              if (!gc.m_invisible [nObject] &&
                  gc.m_firstDesc  [nObject] &&
                  (gc.m_nWhereIs [nObject] == gc.m_nHere) &&
                  !gc.m_noDesc [nObject])
                 {
                   if (!bObjectDescribed)
                      {
                        printf ("\n");
                        bObjectDescribed = true;
                      }
                    gc.m_seen [nObject] = true;
                    describeObject (gc, nObject, gc.m_nState [nObject]);
                 }

          // Then describe other visible objects
          bObjectDescribed = false;
          for (nObject=MINOBJECTS; (nObject <= MAXOBJECTS); nObject++)
              if (near (nObject) &&
                  !carrying (nObject) &&
                  !gc.m_invisible [nObject] &&
                  !gc.m_firstDesc [nObject] &&
                  !gc.m_noDesc [nObject])
                 {
                   if (!bObjectDescribed)
                      {
                        printf ("\n");
                        bObjectDescribed = true;
                      }
                    gc.m_seen [nObject] = true;
                    describeObject (gc, nObject, gc.m_nState [nObject]);
                 }
        }
     else
        // Otherwise if there's no light here, check for sudden death!
        if (gc.m_lit [gc.m_nPrevLoc] || chance (75) || gc.m_bRanOut)
           sayMessage (gc, itisnowdark);
        else
           {
             sayMessage (gc, crunch);
             coroner (gc);
             return;
           }

  // Do ogre stuff
  gc.m_bRanOut = false;
	if (gc.m_seen [ogre])
	   if (++gc.m_nState [ogre] == 7)
	      gc.m_nState [ogre] = 1;

  // A hollow voice says "Plugh"
  if (at (y2))
     if (chance (15))
        if (near (lamp))
           if (gc.m_nState[lamp] == 1)
              sayMessage (gc, saysplugh);

  // More goblin stuff
  if (near (goblins) && (++gc.m_nState[goblins] > 6))
     {
       coroner (gc);
       return;
     }

  // More dwarf stuff
  if ((gc.m_nWhereIs [dwarf] != limbo) &&
      !gc.m_notInCave [gc.m_nHere] &&
      !gc.m_noDwarf [gc.m_nHere] &&
      (gc.m_nState [blob] == 0))
     apport (dwarf, gc.m_nHere);

  if (!gc.m_notInCave [gc.m_nHere])
     {
       gc.m_nClockTick -= 2;
       if (gc.m_visited [gc.m_nHere] == 1)
          gc.m_nClockTick--;
       if (gc.m_nClockTick < 1)
          clock4 (gc);
     }

  if (near (dwarf))
     {
       gc.m_special1 [pirate] = false;
       if (gc.m_nDwarfCount > 0)
          {
            // How many dwarves here?
            sayMessage (gc, blank);
            if (gc.m_nDwarvesInRoom == 1)
               sayMessage (gc, dwarfhere);
            else
               sayMessageValue (gc, dwarveshere, gc.m_nDwarvesInRoom);

            // Any knives thrown?
            long nKnives = random (gc.m_nDwarvesInRoom + 4) - 3;
            if (nKnives <= 0)
               if (chance (80))
                  nKnives = 1;

            // XXX
            // printf ("Knives thrown = %d\n", nKnives);

            if (nKnives > 0)
               {
                 if (nKnives == 1)
                    sayMessage (gc, knifethrown);
                 else
                    sayMessageValue (gc, knivesthrown, nKnives);

                 // Compute accuracy of throw
                 long nSurvival;
                 nSurvival = (-5)*gc.m_nInventory + 90;     // hard to hit a moving target
                 if (gc.m_special2 [dwarf])                 // is he angry?
                    nSurvival -= 20;
                 if (gc.m_nState [mushroom] == 2)           // mushroom makes you strong!
                    nSurvival += 25;
                 nSurvival = nSurvival / nKnives;           // slim chance if more than one knife!

                 // XXX
                 // printf ("Survival probability = %d\n", nSurvival);

                 if (chance (nSurvival) || gc.m_special1 [dwarf])
                    {
                      // He survived the throw!
                      if (nKnives == 1)
                         sayMessage (gc, misses);
                      else
                         sayMessage (gc, knivesmiss);
                      gc.m_special1 [dwarf] = false;
                    }
                 else
                    {
                      // He was struck by the knives!
                      if (nKnives == 1)
                         sayMessage (gc, getsyou);
                      else
                         sayMessage (gc, knifegotyou);
                      coroner (gc);
                      return;
                    }
               }
          }
     }

  // Check for end game
  if ((gc.m_nWhereIs [lamp] == ylem) && at(road) && (gc.m_nClosure < 4))
     {
       sayMessage (gc, lamp_dead);
       gc.m_bQuitting = true;
       finis (gc);
     }
}

////////////////////
// Private functions

void close_the_cave
  (AdvGlobalContext& gc)
//
//  The cave has just closed on the adventurer!
//
{
  // The sepulchral voice speaks...
  sayMessage (gc, go_reposit);
  gc.m_bFastMode = false;

  // Get rid off of all objects
  for (long nObject=MINOBJECTS; (nObject < MAXOBJECTS); nObject++)
      if (gc.m_notInCave [gc.m_nWhereIs [nObject]] ||
          (carrying (nObject) && gc.m_portable [nObject]))
         apport (nObject, ylem);

  // Pretend we haven't visited any location outside the cave, except the building
  for (long nLocation=road; (nLocation < limbo); nLocation++)
      if (gc.m_notInCave [nLocation])
         gc.m_visited [nLocation] = 0;
  gc.m_visited [building] = 1;

  // Transport the adventurer to the cylinderical room
  gc.m_nClosure = 3;
  gc.m_nClockTick = 999;
  gc.m_nPrevLoc = cylinderical;
  gc.m_nHere = cylinderical;
  describePlace (gc, cylinderical, true);
  gc.m_nEscape = 0;
}

void clock4
  (AdvGlobalContext& gc)
//
//  The administrative clock has ticked.  This function is executed whenever
//  gc.m_nClockTick is <= zero, and sets the value of gc.m_nClosure.
//
{
  if (gc.m_nClosure == 0)   // normal play
     {
       gc.m_nClosure = 1;
       for (long nObject=MINTREASURES; (nObject <= MAXTREASURES); nObject++)
           if (gc.m_nWhereIs [nObject] != building)
              {
                gc.m_nClosure = 0;
                break;
              }

        // A dwarf shows up when gc.m_nCockTick becomes < 1.  Dwarves show up at smaller
        // time intervals as they're killed.
        if (!gc.m_seen [axe])
           gc.m_nClockTick = 15 + (gc.m_nClosure == 1 ? 5 : random (10));
        else
           gc.m_nClockTick = gc.m_nDwarfCount + (gc.m_nClosure == 1 ? 5 : random (8));

        // Skip the sculpture's "on shelf" state
        if (gc.m_nState [sculpture] != 0)
            gc.m_nState [sculpture] = 1 + random (gc.m_nState [sculpture] - 1);

        // Skip the sword's "in stone" state
        if (gc.m_nState [sword])
           gc.m_nState [sword] = 1 + random (gc.m_nState [sword] - 1);

        // Dragon stuff
        if (gc.m_nState [dragon] == 2)
           {
             gc.m_nDragonTime -= gc.m_nLastClock;
             if (gc.m_nDragonTime < 0)
                gc.m_nState [dragon] = 3;
           }

        // Djinn stuff
        if (gc.m_special1 [djinn] &&
            !gc.m_special2 [djinn] &&
            !near (dwarf))
           {
             gc.m_special2 [djinn] = true;
             sayMessage (gc, phugg_data);
             gc.m_nClockTick = 5;
             return;
           }

        // Mushroom stuff
        if (gc.m_nState [mushroom] > 1)
           {
             gc.m_nMushTime -= gc.m_nLastClock;
             if (gc.m_nMushTime < 0)
                if (gc.m_nState [mushroom] == 2)
                   {
                     gc.m_nState [mushroom] = 3;
                     gc.m_nMushTime = 40;
                     describeObject (gc, mushroom, gc.m_nState [mushroom]);
                     gc.m_nStrength = 7;
                     gc.m_nClockTick = 8;
                     return;
                   }
                else
                   {
                     gc.m_nState [mushroom] = 0;
                     apport (mushroom, cubicle);
                   }
           }

        // Cameo appearance
        if ((gc.m_nCameoTime > 0) && (gc.m_nCameoTime < gc.m_nMoves))
           {
             gc.m_nClockTick = 10 + random (10);
             do_cameo (gc);
             return;
           }

        // Pirate stuff
        if (gc.m_visited [mists] || gc.m_visited [y2])
           {
             if ((gc.m_nMoves > 150) && !gc.m_seen [chest])
                gc.m_special1 [pirate] = true;          // set chasing mode
             if (gc.m_noDwarf [gc.m_nHere] || gc.m_notInCave [gc.m_nHere])
                {
                  gc.m_special1 [pirate] = false;       // clear chasing mode
                  gc.m_nClockTick = 5 + random (10);    // set short clock interval
                }
             else
                if (gc.m_special1 [pirate] || chance (10 * (gc.m_nDwarfCount + 5)))
                   if (gc.m_special1 [pirate] || (gc.m_nDwarvesInRoom + random (10) == 0))
                      if (gc.m_seen [chest]           ||    // found treasure chest yet?
                          gc.m_seen [pirate]          ||    // seen pirate?
                          gc.m_lit [gc.m_nHere]       ||    // don't pounce in lit rooms
                          !near (lamp)                ||    // or if lamp elsewhere
                          (gc.m_nState [lamp] == 0))        // or lamp is dead
                         ;                                  // do nothing
                      else
                         {
                           // About to see a pirate ...
                           gc.m_special1 [pirate] = false;    // clear chasing mode
                           gc.m_valued [ring] = false;        // so that it's not stolen by pirate

                           // Pirate steals his treasure
                           bool bPirateStoleTreasures = false;
                           for (long nObject=MINOBJECTS; (nObject <= MAXOBJECTS); nObject++)
                               if (gc.m_valued [nObject] && near (nObject))
                                  {
                                    apport (nObject, mazea_26);
                                    bPirateStoleTreasures = true;
                                  }
                           gc.m_valued [ring] = true;

                           // Inform the poor sap
                           if (bPirateStoleTreasures)
                              {
                                sayMessage (gc, pirate_zotz);
                                gc.m_seen [pirate] = true;
                              }
                           else
                              if (gc.m_nWhereIs [chest] == limbo)   // 1st time thru?
                                 {
                                   sayMessage (gc, pirate_runs);
                                   gc.m_seen [pirate] = true;
                                 }
                              else
                                 {
                                   sayMessage (gc, rustling);       // pirate is
                                   gc.m_special1 [pirate] = true;   // following
                                   gc.m_nClockTick = 4 + random (10);
                                 }

                           // First time thru?
                           if (gc.m_nWhereIs [chest] == limbo)
                              {
                                apport (chest, mazea_26);
                                apport (message, mazed_140);
                              }
                         }
                   else
                      {
                        // About to see a dwarf ...
                        if (gc.m_nDwarfCount > 0)
                           if (gc.m_seen [axe])     // have we seen a dwarf yet?
                              {
                                apport (dwarf, gc.m_nHere);
                                if (++gc.m_nDwarvesInRoom == 1)
                                   {
                                     gc.m_special1 [dwarf] = true;    // knife not yet throen
                                     gc.m_special2 [dwarf] = false;   // dwarf is not angry
                                   }
                              }
                           else
                              {
                                apport (axe, gc.m_nHere);       // fetch axe
                                gc.m_seen [axe] = true;
                                sayMessage (gc, firstdwarf);    // invoke scared dwarf
                              }
                      }
           }
     }
  else
     if (gc.m_nClosure == 1)    // closing time
        {
          gc.m_nClosure  = 2;                 // set "closing soon"
          gc.m_nState [grate] = 0;            // lock the grate
          sayMessage (gc, closing_now);       // sepulchral voice

          // Remove any dwarves
          if (near(dwarf))
             sayMessage (gc, dwarf_quits);    // fades into the gloom
          apport (dwarf, limbo);              // get rid of him/them
          gc.m_nDwarvesInRoom = 0;            // zilch all present dwarves
          gc.m_nDwarfCount = 0;               // don't let them reappear

          gc.m_nState [fissure] = 0;          // destroy the bridge
          gc.m_invisible [fissure] = true;
          gc.m_nState [wheatstone] = 0;	      // destroy Wheatstone bridge
          gc.m_invisible [wheatstone] = true;

          apport (troll, limbo);              // destroy troll
          apport (dragon, limbo);             // destroy dragon
          gc.m_nState [troll] = 5;            // scared - inhibit return
          apport (troll2, swofchasm);         // fake troll
          gc.m_nClockTick = 25;               // time to try to leave
        }
     else
        // Must be closing time!
        if (gc.m_bPanicked)                   // did he try to get out?
           {
             gc.m_bPanicked = false;          // reset paniced flag
             gc.m_nClockTick = 15;            // let him get frantic
           }
        else
           close_the_cave (gc);               // if he was calm

  // Save clock tick
  gc.m_nLastClock = gc.m_nClockTick;
}

void do_cameo
  (AdvGlobalContext& gc)
//
// Generate a strange cameo appearances if possible.
//
{
  gc.m_nCameoTime = 0;    // only one cameo per game

  if (gc.m_notInCave [gc.m_nHere] ||    // cameos work only in cave
      gc.m_noDwarf[gc.m_nHere]    ||    // and only in dwarf-accessible locations
      gc.m_lit [gc.m_nHere]       ||    // and only in unlit rooms
      gc.m_oneExit [gc.m_nHere]   ||    // that have many exits
      !near (lamp)                ||    // and only if you have your lamp
      (gc.m_nState [lamp] == 0)   ||    // which must be turned on
      gc.m_special1 [pirate]      ||    // and not if pirate is chasing you
      near (dwarf)                ||    // or being plagued by dwarves
      near (dragon)               ||    // or near the live or dead dragon
      near (troll)                ||    // or arguing with the troll
      near (snake)                ||    // or trying to get past the snake
      near (quicksand))                 // or trying to cross the quicksand
     return;

  sayMessage (gc, cameo_1 + random (cameo_6 - cameo_1 + 1));
}

void lamprey
  (AdvGlobalContext& gc)
//
//  Lamp is getting dim or has gone out.
//
{
  if (gc.m_nLampLife > 0)
     if (gc.m_nState [batteries] == 1)
        sayMessage (gc, lamp_nofuel);
     else
        if (near (batteries))
           {
             sayMessage (gc, lamp_refuel);
             gc.m_nState [batteries] = 1;
             gc.m_nLampLife += 300;
             gc.m_special1 [lamp] = false;    // clear recharged flag
           }
        else
           if (gc.m_seen [batteries])
              sayMessage (gc, lamp_batteries);
           else
              sayMessage (gc, lamp_is_dim);
  else
     if (gc.m_nClosure == 2)
        close_the_cave (gc);
     else
        if (near (batteries) && (gc.m_nState [batteries] == 0))
           {
             sayMessage (gc, lamp_refuel);
             gc.m_nState [batteries] = 1;
             gc.m_nLampLife += 300;
           }
        else
           {
             sayMessage (gc, lamp_is_dead);
             gc.m_nState [lamp] = 0;
             gc.m_bRanOut = true;   // don't fall into a pit this move
             phog (gc);             // chase glow into place
           }
}
