/*
 *      ADV.C - Collosal Cave Adventure
 *      (C) 1986 Ravi Bhavnani
 *      All rights reserved
 *
 *      main1()         mainline code module 1
 *      main2()         mainline code module 2
 *      usage()         prints usage msg
 *      welcome()       startup welcome message
 *      phog()          controls the nature of the fog
 *      getscore()      computes score
 *      coroner()       tells him he died and offers reincarnation
 *      finis()         displays score and ends game
 *      retort()        prints a retort to obscene input
 *      hint_logic()    decides whether to offer a hint
 *      magician()      handles magic words
 *      magician2()     handles magic exit from cylindrical room
 *      clock4()        administrative clock
 *      tick()          once per input blob routine
 *      close_the_cave()        does just that
 *      do_cameo()      cameo appearance
 *      lamprey()       lamp is dying
 */

#include "advdef.h"
#include "advglob.h"

main (argc, argv)
int     argc;
char    *argv[];
{
        if (argc==2)
           if (!strcmp(argv[1],"/d"))
              debug = 1;
           else
              usage();
        else
           debug = 0;

	strcpy (str, argv[0]);
        init();
        welcome();

        do
        {
          if (debug)
             printf ("----\nmain() - pirate flags [%d,%d], clock=%d, closure=%d\n", special1[pirate], special2[pirate],  clock, closure);

	  main0();
          if (moved)
             main1();
             
          if (!justdied)
             main2();
        }
        forever;
}


main0()
/*
 *	MODULE 0 OF 0,1,2 MAINLINE MODULES.  THIS MODULE CONTAINS CODE THAT
 *	MUST BE EXECUTED BEFORE GETTING USER INPUT AND IS INDEPENDENT OF
 *	WHETHER WE MOVED OR NOT.
 */
{
	  dbg ("main0()\n");

          if (ticker)
             tick();

	/*
	 * MANIPULATE SCHIZOID OBJECTS, VIZ:
	 *
	 * grate	incave, depression
	 * tree		forest, forest2
	 * quicksand	arch_cor_1, arch_cor_2
	 * safe		vault, peelgrunt
	 * wheatstone	breathtaker, faces
	 * fissure	eastoffissur, westoffissur
	 * steps	pit, mists
	 * message	deadend1, mazed_140
	 * trapdoor	nearstove, belowtrap
	 * troll2	neofchasm, swofchasm
	 */

	if (at(incave) || at(depression))
	   apport (grate, here);
	if (at(forest) || at(forest2))
	   apport (tree, here);
	if (at(arch_cor_1) || at(arch_cor_2))
	   apport (quicksand, here);
	if (at(vault) || at(peelgrunt))		/* ADDED, SINCE SAFE IS */
	   apport (safe, here);			/* ACCESSIBLE FROM BOTH */
	if (at(breathtaker) || at(faces))	/* SEE BUG REPORT 19-AUG-1988 BY JANUS::SPALDING */
	   apport (wheatstone, here);
	if (at(eastoffissur) || at(westoffissur))
	   apport (fissure, here);
	if (at(pit) || at(mists))
	   {
	     apport (steps, here);
	     val[steps] = (here == mists);
	     invisible[steps] = carrying(gold);
	   }
	if (at(deadend1) || (at(mazed_140) && where_is[chest]==mazea_26))
	   {
	     apport (message, here);
	     nodesc [message] = (here == deadend1);
	   }
	if (at(nearstove) || at(belowtrap))
	   {
	     apport (trapdoor, here);
	     nodesc[trapdoor] = (here == belowtrap);
	   }

	if (at(neofchasm) || at(swofchasm))
	   if (near(troll))
	      if (near(troll2))
	         apport (troll2, limbo);    	/* Real troll takes precedence over fake troll */
	      else
	         ;
	   else
	      if (!val[chasm])
	         apport (troll2, here);
}


main1()
/*
 *	MODULE 1 OF 0,1,2 MAINLINE MODULES.  THIS MODULE CONTAINS CODE THAT
 *	MUST BE EXECUTED BEFORE GETTING USER INPUT, ONLY IF WE MOVED.
 */
{
int     knives,         /* # knives thrown by dwarves */
        derf;

	dbg ("main1()\n");

        /* IS A DWARF GIVING HIM A HARD TIME? */

	justdied = 0;
        if (onexit[prevloc] && where_is[dwarf]==prevloc)
           {
             here = prevloc;
             moved = 0;
             msay (dwarfblock);

             /* ANY KNIVES THROWN? */

             knives = random(dwarrows+4) - 3;
             if (knives > 0)
                {
                  if (knives == 1)
                     msay (knifethrown);
                  else
                     mval (knivesthrown, knives);

                  /* COMPUTE ACCURACY OF THROW */

                  derf = (-5)*invct + 95;  /* hard to hit a moving target */
                  if (special2[dwarf])     /* is he angry? */
                     derf -= 20;
                  if (val[mushroom] == 2)
                     derf += 25;
                  derf = derf / knives;    /* slim chance if more than 1 knife! */

                  if (chance(derf) || special1[dwarf])
                     {
                       if (knives == 1)
                          msay (misses);
                       else
                          msay (knivesmiss);
                       special1[dwarf] = 0;
                     }
                  else
                     {
                       if (knives == 1)
                          msay (getsyou);
                       else
                          msay (knifegotyou);
                       coroner();
                       RETURN;
                     }
                }

	     RETURN;
           }

	/*
	 * phog() MUST BE DONE *BEFORE* CLEARING moved BECAUSE IT CHECKS TO
	 * SEE IF WE'RE STUCK AT plain_2.
	 */

        if (near (fog))
           phog();
        moved = 0;

        /* HAS HE BEEN PLAYING TOO LONG? */

        nmoves++;
        if (nmoves == (demo ? MAXDEMO : MAXGAME))
           {
             msay (wizard_ends);
             finis();
           }

        /* CHECK HIS LAMP */

        if (near(lamp) && val[lamp]==1)
	   {
	     lamplife--;
	     if ((lamplife==40) || (lamplife==10) || (lamplife==0))
	        lamprey();
	     if (justdied)
	        RETURN;
	    }

        /* GOBLIN STUFF */

        if (where_is[goblins] != limbo)
           {
             apport (goblins, here);
             if (val[goblins] > -1)	/* DON'T SAY "YOU ARE BEING PURSUED" THE FIRST TIME.	*/
                msay (goblin_chase);	/* SO THEY ARE DESCRIBED AS AN OBJECT			*/
             val[goblins]++;
	     invisible[goblins] = 0;
           }

        /* IF THERE'S LIGHT AND WE HAVEN'T SAID ANYTHING YET... */

        if (ok2desc)
           if ((lit[here] || (near(lamp) && val[lamp]==1)))
              {

                /* DESCRIBE LOCATION */

                if (fastmode || (visited[here] && (briefmode || (visited[here] % DETAIL) )))
                   ssay (here);
                else
                   lsay (here);
                visited [here]++;

                /* FIRST DESCRIBE PRIMARY OBJECTS */

                derf = 0;
                for (thing=MINOBJECTS; (thing <= MAXOBJECTS); thing++)
                    if (!invisible[thing] && firstdesc[thing] && where_is[thing]==here && !nodesc[thing])
                       {
                         if (!derf)
                            {
                              printf ("\n");
                              derf = 1;
                            }

                         seen[thing] = 1;
                         osay (thing, val[thing]);
                       }

                /* THEN DESCRIBE VISIBLE OBJECTS */

                derf = 0;
                for (thing=MINOBJECTS; (thing <= MAXOBJECTS); thing++)
                    if (near(thing) && !carrying(thing) && !invisible[thing] && !firstdesc[thing] && !nodesc[thing])
                       {
                         if (!derf)
                            printf ("\n");
                         osay (thing, val[thing]);
                         seen[thing] = 1;
                         derf = 1;
                       }
              }

           /* ...ELSE CHECK FOR SUDDEN DEATH! */

           else
              if (lit[prevloc] || chance(75) || ranout)
                 msay (itisnowdark);
              else
                 {
                   msay (crunch);
                   coroner();
                   RETURN;
                 }

        ranout = 0;
	if (seen[ogre])
	   if (++val[ogre] == 7)
	      val[ogre] = 1;

        if (at(y2) && chance(15) && near(lamp) && val[lamp]==1)
           msay (saysplugh);

        /* MORE GOBLIN AND DWARF STUFF */

        if (near(goblins) && (++val[goblins] > 6))
           {
             coroner();
             RETURN;
           }
        if (where_is[dwarf]!=limbo && !notincave[here] && !nodwarf[here] && !val[blob])
           apport (dwarf, here);
        if (!notincave[here])
           {
             clock -= 2;
             if (visited[here] == 1)
                clock--;
             if (clock < 1)
                clock4();
           }

        if (near(dwarf))
           {
             special1[pirate] = 0;
             if (dwarfcount)
                {

                  /* HOW MANY DWARVES HERE? */

                  msay (blank);
                  if (dwarrows == 1)
                     msay (dwarfhere);
                  else
                     mval (dwarveshere, dwarrows);

                  /* ANY KNIVES THROWN? */

                  knives = random(dwarrows+4) - 3;
                  if (knives > 0)
                     {
                       if (knives == 1)
                          msay (knifethrown);
                       else
                          mval (knivesthrown, knives);

                       /* COMPUTE ACCURACY OF THROW */

                       derf = (-5)*invct + 95;  /* hard to hit a moving target */
                       if (special2[dwarf])     /* is he angry? */
                          derf -= 20;
                       if (val[mushroom] == 2)
                          derf += 25;
                       derf = derf / knives;    /* slim chance if more than 1 knife! */

                       if (chance(derf) || special1[dwarf])
                          {
                            if (knives == 1)
                               msay (misses);
                            else
                               msay (knivesmiss);
                            special1[dwarf] = 0;
                          }
                       else
                          {
                            if (knives == 1)
                               msay (getsyou);
                            else
                               msay (knifegotyou);
                            coroner();
                            RETURN;
                          }
                     }
                }
           }

        /* CHECK FOR END GAME */

        if (where_is[lamp]==ylem && at(road) && closure<4)
           {
             msay (lamp_dead);
             quitting = 1;
             finis();
           }
	RETURN;
}


main2()
/*
 *      MODULE 2 OF 2 MAINLINE MODULES.
 */
{
	dbg ("main2() - ");

        /* DOES HE NEED ANY HELP? */

        if (hintable[here])
           {
             hintime++;
             if (hintime>20 && !inmaze[here] || hintime>30)
                hint_logic();
           }
        else
           hintime = 0;

        /* TALLY INVENTORY */

        invct = 0;
	for (thing=MINOBJECTS; (thing <= MAXOBJECTS); thing++)
            if (carrying(thing) && !weightless[thing])
               invct++;

        /* GET USER INPUT */

	margin = 0;
        if (debug)
           printf ("loc = %d, visited = %d\n", here, visited[here]);
        get_command();
	
        ok2desc = 1;
        nturns++;

        /* IF NULL INPUT, REPEAT BRIEF DESCRIPTION... SPECIAL CASE FOR FOG/GLOW */

        if (!inword)
	   {
	     if (where_is[glow]==here)
	        osay (glow, val[glow]);
	     else
	        if (where_is[fog]==here && val[fog]<8)
	           osay (fog, val[fog]);
	        else
                   if (lit[here] || (near(lamp) && val[lamp]==1))
                      ssay (here);
                   else
                      msay (itisnowdark);
             RETURN;
	   }

	/* ...ELSE DO APPROPRIATE ACTION */

	/* FIRST HANDLE "mist". */

	if (arg1==mist || arg2==mist)
	   {
	     mist_proc();
	     RETURN;
	   }

        /* ...ELSE DO APPROPRIATE ACTION */

	if (ismagic(arg1))
	   if (closure < 3)
	      magician();
	   else
	      magician2();
	else
	   {
	     escap = 0;
	     if ((arg1==errword || arg2==errword) && arg1!=say)
                {
                  ok2desc = 0;
                  if (chance(50))
                     msay (huh);
                  else
                     mtext (nocomprende, (arg1==errword ? word1 : word2));
                }
             else
                if (arg1==obscenity || arg2==obscenity)
                   retort();
                else
                   if (isdir(arg1) || (arg1==move) || isplace(arg1))
                      travel();
                   else
                      if (isverb(arg1))
	                 if (arg2 != treasure)
	                    do_verb();
	                 else
	                    if (arg1==obtain || arg1==drop)
                               do_verb();
	                    else
	                       msay (what);
                      else
                         if (isutil(arg1))
                            utilitarian();
                         else
                            if (isobj(arg1))
	                       if (arg1 == all)
	                          printf ("Please be more specific.\n");
	                       else
	                          if (arg2==null)
	                             if (arg1 == treasure)
	                                msay (what);
	                             else
                                        if (!invisible[arg1] && where_is[arg1]==here)
                                           {
                                             mword (whatdo, arg1);
                                             context = arg1;
                                           }
                                        else
                                           mword (idontsee, arg1);
	                          else
	                             msay (huh);
                            else
	                       {
	                         printf ("<GLITCH!> No action performed for arg1=%d, arg2=%d, context=%d\n",
	                                 arg1, arg2, context);
	                         printf ("Please report this bug.  Thanks.\n");
	                       }
	   }

	RETURN;
}

welcome()
/*
 *      WELCOME THE ADVENTURER, (CHECK IF PRIMETIME) & OFFER INSTRUCTIONS.
 */
{
	dbg ("welcome()\n");

	printf ("Welcome to *New* Adventure FT%d.%01d-%d. Say \"NEWS\" to get up-to-date game details.\n",
	        MAJOR, MINOR, RELEASE);

/*	msay (logon);	*/

        if (query (hithere))
           msay (intro);
        else
           msay (ok);

        msay (blank);
        here = road;
        visited[road] = 1;
        lsay (road);
        time (&startplay);
	RETURN;
}


phog()
/*
 *      CONTROL THE FOG AND THE GLOW IN THE FOG AND DESCRIBE THEM.
 */
{
	dbg ("phog()\n");

        /* STATE 8 = INVISIBLE FOG */

        if (val[fog] < 8)
	   {
             val[fog] = random(8);
	     invisible[fog] = 0;
	   }
	else
	   invisible[fog] = 1;

	/* GLOW APPEARS IN A RANDOM SPOT (1 OF 8 POSSIBLE DIRECTIONS) */

	val[glow] = random(8);

        /* LAMP MASKS OUT FAINT GLOW */

        if (near(lamp) && val[lamp]==1)
	   {
             apport (glow, limbo);
	     if (!invisible[fog])
	        {
	          osay (fog, val[fog]);
	          ok2desc = 0;
	        }
	   }
        else
           {
             apport (glow, plain_2);
/*	     if (at(plain_2)&& !moved)	*/
	     if (at(plain_2))		/* NEED TO DESCRIBE WHEN WE TRAVEL FROM plain_3 TO plain_2 */
	        {
                  osay (glow, val[glow]);
	          ok2desc = 0;		/* SO FOG IS NOT DESCRIBED */
	        }
           }
	RETURN;
}


splatter()
/*
 *	splatter() IS CALLED EVERYTIME IT'S DESIRABLE TO HAVE THE
 *	ADVENTURER FALL TO A PAINFUL DEATH.  ONE OF A SERIES OF
 *	MESSAGES IS PRINTED, DEPENDING ON HOW MANY TIMES HE HAS
 *	BEEN KILLED SO FAR.  THE CALLING ROUTINE SHOULD SET here
 *	TO THE LOC THAT'S AT THE BOTTOM OF WHATEVER THE ADVENTURER
 *	HAS JUMPED INTO - IF THAT PLACE ISN'T WELL DEFINED (BOTTOM
 *	OF CHASM, VOLCANIC GORGE, ETC.), SEND HIM TO YLEM.
 */
{
	dbg ("splatter()\n");

	msay (plummet + deaths);
	RETURN;
}


coroner()
/*
 *      THE POOR GUY KILLED HIMSELF.
 */
{
int     derf;

	dbg ("coroner()\n");

        justdied = 1;
        quitting = 0;
        nomagic = 0;                    /* reset magic-inhibit mode */
        ticker = 0;                     /* clear once-per-move */
        val [blob] = 0;                 /* reset the blob */
        apport (blob, limbo);           /* and get rid off him */
        apport (goblins, limbo);        /* the same with the goblins */

        if (val[basilisk]==1)           /* adjust the basilisk so that */
           val[basilisk] = 0;           /* he gets viewed from the south */
        else                            /* whether he's petrified or not */
           if (val[basilisk]==3);
              val[basilisk] = 2;

        apport (fog, plain_1);          /* move the fog back to its initial */
        val[fog] = 8;                   /* position and make it semi-visible */

        deaths++;
        if (closure > 1)
           {
             if (closure==2)
                msay (dead_and_closed);
             else
                deaths--;               /* don't charge for dying in repository */
             finis();
           }

        /* TELL HIM ABOUT IT */

        derf = youaredead_1 + 2*(deaths - 1);
        if (query (derf))
           {
             msay (derf + 1);
             if (deaths > MAXDIE)
                finis();

             /* SHATTER THE VASE AND DROP ALL OBJECTS */

             if (carrying(vase))
                {
                  dropit (vase);
                  apport (vase, limbo);
                  getit (shards);
                }
             for (thing=MINOBJECTS; (thing <= MAXOBJECTS); thing++)
                 if (carrying(thing))
                    dropit (thing);
             invct = 0;

             phog();
             apport (oil, limbo);
             apport (water, limbo);
	     val[lamp] = 0;
             apport (lamp, road);

             here = building;
             prevloc = building;	/* was "null" - see Note 15.0 */
	     ok2desc = 1;
	     moved = 1;

             apport (dwarf, limbo);
             dwarrows = 0;
             special1[pirate] = 0;      /* clear chasing mode */

             if (!lamplife || special1[lamp] || !visited[lair])
                {
                  loc = where_is [batteries];
                  if (loc==building || notincave[loc])
                     apport (lamp, ylem);
                }

             if (debug)
                printf ("Returning from coroner()\n");

             RETURN;
           }
        else
           finis();
	RETURN;
}


finis()
/*
 *      TELL HIM HOW HE DID AND END THE SHOW.  ADVENTURER RATINGS ARE
 *      AS FOLLOWS:
 *
 *        score         rating
 *        -----         ------
 *        0 - 28        rank amateur   (hasn't entered cave yet)
 *       29 - 104       novice         (0-5   treasures kept in building)
 *      105 - 214       experienced    (6-13  treasures kept in building)
 *      215 - 344       seasoned       (14-21 treasures kept in building)
 *      345 - 419       junior master  (21-26 treasures kept in building)
 *      420 - 463       Master Adventurer Class C
 *      464 - 507       Master Adventurer Class B
 *      508 - 549       Master Adventurer Class A
 *         550          grandmaster
 */
{
int     derf;

	dbg ("finis()\n");

        getscore();
        mval (you_scored, scorex);
        mval (topscore, maxscore);
        mval (inmoves, nturns);         /* was nmoves */

        if (scorex < 29)
           {
             derf = fish;
             scorex -= 29;
           }
        else
           if (scorex < 105)
              {
                derf = novice;
                scorex -= 105;
              }
           else
              if (scorex < 215)
                 {
                   derf = experienced;
                   scorex -= 215;
                 }
              else
                 if (scorex < 345)
                    {
                      derf = seasoned;
                      scorex -= 345;
                    }
                 else
                    if (scorex < 420)
                       {
                         derf = juniormaster;
                         scorex -= 420;
                       }
                    else
                       if (scorex < 464)
                          {
                            derf = master_c;
                            scorex -= 464;
                          }
                       else
                          if (scorex < 508)
                             {
                               derf = master_b;
                               scorex -= 508;
                             }
                          else
                             if (scorex < 550)
                                {
                                  derf = master_a;
                                  scorex -= 550;
                                }
                             else
                                {
                                  derf = grand;
                                  scorex = 0;
                                }

        msay (derf);
        msay (blank);
        scorex = -scorex;
        if (scorex > 0)
           if (scorex == 1)
              msay (need1);
           else
              mval (need, scorex);

	/* RESET CTRL/Y MASK */

	lib$enable_ctrl (&oldcmask);
        exit ();
}


getscore()
/*
 *      COMPUTES scorex AND maxscore ACCORDING TO THE FOLLOWING ALGORITHM:
 *
 *      condition                               max score possible
 *      ---------                               ------------------
 *      visited building                                9
 *      able to enter cave (thru debris room or Y2)     20
 *      delivered magazines to Witt's End               1
 *      2 points for locating each of 27 treasures      54  : total
 *      13 points for keeping each in building          351 :  405
 *      visited lair                                    10
 *      visited beach                                   10
 *      visited faces                                   10
 *      20 points for each phase of closure             100
 *      -10 points for each of 3 allowed deaths         0
 *      -5  points for each clue/penalty                0
 *
 *                                              TOTAL   565
 */
{
	dbg ("getscore()\n");

        scorex = (visited[building] ? 9 : 0);
        maxscore = 9;
	if (debug)
	   printf ("Visited building: %d / %d\n", scorex, maxscore);

	if (saidxyzzyplugh)
	   scorex += 20;
	maxscore += 20;
	if (debug)
	   printf ("Entered cave via XYZZY/PLUGH: %d / %d\n", scorex, maxscore);

        if (where_is[magazines] == wittsend)
           scorex++;
        maxscore++;
	if (debug)
	   printf ("Magazines at Witt's End: %d / %d\n", scorex, maxscore);

	for (thing=MINTREASURES; (thing <= MAXTREASURES); thing++)
            if (where_is[thing]==building || closure>2)
               scorex += 15;
            else
               if (seen[thing])
                  scorex += 2;
	maxscore += 15 * (MAXTREASURES - MINTREASURES + 1);
	if (debug)
	   printf ("Treasures transported: %d / %d\n", scorex, maxscore);

	if (visited[lair])
	   scorex += 10;
	maxscore += 10;
	if (debug)
	   printf ("Visited lair: %d / %d\n", scorex, maxscore);

	if (visited[beach])
	   scorex += 10;
	maxscore += 10;
	if (debug)
	   printf ("Visited beach: %d / %d\n", scorex, maxscore);
	if (visited[faces])
	   scorex += 10;
	maxscore += 10;
	if (debug)
	   printf ("Visited faces: %d / %d\n", scorex, maxscore);

        scorex += closure*20;
        maxscore += 100;
	if (debug)
	   printf ("Closure credit (%d x 20): %d / %d\n", closure, scorex, maxscore);

	scorex = scorex - deaths*10 - penalties;
	if (debug)
	   printf ("Penalties and deaths: %d / %d\n", scorex, maxscore);

        if (scorex < 0)
           scorex = 0;
	RETURN;
}


retort()
/*
 *      PRINTS A RETORT TO OBSCENE INPUT.
 */
{
	dbg ("retort()\n");

        nretort++;
        if (nretort == MAXRETORT)
           nretort = 1;

        if (nretort == 1)
           msay (watchit);
        else
           msay (retort1 + nretort - 1);
	RETURN;
}


hint_logic()
/*
 *      DECIDES WHETHER TO OFFER A HINT.
 */
{
int     derf;

	dbg ("hint_logic()\n");

        hintime = 0;
        derf = 0;

        if (at(depression) && !val[grate] && !carrying(keys))
           derf = lookingcave;
        if (at(birdchamber) && near(bird) && !val[bird] && carrying(rod))
           derf = birdhint;
        if (at(mtking) && near(snake) && !near(bird))
           derf = getpastsnake;
        if (at(wittsend))
           derf = hint_witts;
        if ((at(plover) || at(alcove) || at(dark)) && !visited[dark])
           derf = hint_plover;
        if (at(plain_2))
           derf = plain_hint;
        if (inmaze[here])
           derf = hint_maze;
        if (here>=slide && here<=icecave_30)
           derf = ice_hint;
	if (at(nearstove))
	   derf = stove_hint;
	if (at(mirrorroom))
	   derf = mirror_hint;

        if (derf)
           {
	     if (arg1 != hint)
	        msay (blank);
             if (query(derf))
                {
                  mval (ill_give_hint, HINTCOST);
                  if (query(want_hint))
                     {
                       msay (derf+1);
                       penalties += HINTCOST;
                       hintable[here] = 0;
                       if (at(plover) || at(alcove) || at(dark))
                          {
                            hintable[plover];
                            hintable[alcove];
                            hintable[dark];
                          }
                       if (inmaze[here])
                          for (derf=ylem; (derf<limbo); derf++)
                              if (inmaze[derf])
                                 hintable[derf] = 0;
                     }
                  else
	             msay (ok);
                }
             else
	        msay (ok);
           }
	RETURN;
}


magician()
/*
 *      HANDLES THE FOLLOWING MAGIC WORDS:
 *		xyzzy
 *		melenkurion
 *		noside
 *		samoht
 *		thurb
 *		knerl
 *		zorton
 *		klaetu
 *		snoeze
 *		blerbi
 *		phuggg
 *		fee fie foe foo (fum) sequence
 *		sesame
 *		vamotu
 *		utomav
 *
 *	AND VARIOUS "OLD" MAGIC WORDS.
 */
{
	dbg ("magician() ");

        if (debug)
           printf (": arg1 = %d, arg2 = %d, closure = %d, nomagic = %d\n",
	              arg1, arg2, closure, nomagic);

        switch (arg1)
        {
	  case utomav:
	    msay (nothing);
	    break;

	  case vamotu:
	    if (at(belowtrap))
	       if (!invisible[trapdoor] && !val[trapdoor])
	          {
	            msay (trapopens);
	            val[trapdoor] = 1;
	          }
	       else
	          msay (nothing);
	    else
	       msay (nothing);
	    break;

	  case oldmagic:
	    msay (magickword);
	    break;

	  case fee:
	  case fie:
	  case foe:
	  case foo:
	    if (foobar==(fee-arg1))
	       {
	         foobar++;
	         if (foobar < 4)
	            msay (ok);
	         else
	            {
	              foobar = 0;

	              /* NO EFFECT IF EGGS ARE ALREADY IN GIANT ROOM OR HAVE BEEN THROWN IN THE CHASM */

	              if (where_is[eggs]==giant || where_is[eggs]==ylem)
	                 msay (nothing);
	              else
	                 {
	                   /* TELL HIM ABOUT IT */

	                   if (near(eggs))
	                      val[eggs] = 1;		/* "The nest of golden eggs has vanished!" */
	                   else
	                      if (at(giant))
	                         val[eggs] = 0;		/* "There is a nest of golden eggs here!" */
	                      else
	                         val[eggs] = 2;		/* "Done!" */
	                   osay (eggs, val[eggs]);

	                   /* TROLL DOESN'T LIKE IT IF YOU DO THIS MORE THAN ONCE! */

	                   if (where_is[eggs]==limbo)	/* if eggs were given as toll */
	                      {
	                        special1[troll] = 1;	/* can't be fooled twice 	*/
	                        if (val[troll]==1 ||	/* bought off, not crossed 	*/
	                            val[troll]==2)	/* bought off, crossed 		*/
	                           {

				     /* AND IF YOU DO IT AT THE CHASM, THE TROLL COMES BACK */

	                             if (near(troll2))	/* at the chasm with a good bridge 	*/
	                                {
	                                  msay (blank);
	                                  val[troll] = 5;		/* The burly troll, muttering under his breath, steps out */
	                                  osay(troll,val[troll]);	/* from beneath the bridge.  He looks positively furious! */
	                                  val[troll] = 0;		/* normal troll mode	*/
	                                  apport (troll2, limbo);	/* discard fake troll	*/
	                                }

				     /*
				      * BRING THE TROLL BACK TO THE CORRECT LOCATION.  IF special2[troll] IS SET, THE
				      * ADVENTURER IS CURRENTLY IN THE neofchasm SIDE OF THE WORLD; OTHERWISE HE'S IN
				      * THE swofchasm SIDE.
				      */

				     if (at(swofchasm) || !special2[troll])
				        apport (troll, swofchasm);
				     else
				        apport (troll, neofchasm);
	                           }
	                      }

	                   apport (eggs, giant);
	                   val [eggs] = 0;
	                   val [troll] = 0;
	                 }
	            }
	       }
	    else
	       {
	         msay (start_over);
	         foobar = 0;
	       }
	    break;
	  case fum:
	    msay (start_over);
	    foobar = 0;
	    break;

          case xyzzy:
            if (inword==1 && (here==building || here==debris))
               if (closure<2 && !nomagic)
                  {
                    saidxyzzyplugh = 1;
                    prevloc = here;
                    here = (here == building ? debris : building);
                    moved = 1;
                    break;
                  }
               else
                  paniced = 1;
            ok2desc = 0;
            msay (nothing);
            break;

          case plugh:
            if (inword==1 && (at(building) || at(y2) || at(fake_y2) || at(platform)))
               if (closure<2 && !nomagic)
                  {
                    saidxyzzyplugh = 1;
                    prevloc = here;
	            switch (here)
	            {
	              case building: here = y2; break;
	              case y2:       here = building; break;
	              case fake_y2:  here = platform; break;
	              case platform: here = fake_y2; break;
	            }
                    moved = 1;
                    break;
                  }
               else
                  paniced = 1;
            ok2desc = 0;
            msay (nothing);
            break;

	  case melenkurion:
            ok2desc = 0;
	    if (near(statue) && !val[statue])
	       {
	         val[statue] = 1;
	         nodesc[statue] = 0;
	         msay (crumble);
	       }
	    else
	       msay (nothing);
	    break;

	  case noside:
	    ok2desc = 0;
	    if (arg2 != samoht)
	       msay (nothing);
	    else
	       if (special1[lamp] || lit[here] || !visited[lair] || !near(lamp))
	          msay (nothing);
	       else
	          if (carrying(lamp))
	             {
	               ok2desc = 1;
	               msay (fzap);
	               coroner();
	             }
	          else
	             if (lamplife > 40)
	                {
	                  apport (lamp, ylem);
	                  val[batteries] = 1;	/* so lamp doesn't come back */
	                  val[lamp] = 0;
	                  lamplife = 0;
	                  if (chance(50))
	                     {
	                       msay (lamp_goes_poof);
	                       msay (itisnowdark);	/* do we need this? */
	                     }
	                  else
	                     {
	                       msay (lamp_explodes);
	                       coroner();
	                     }
	                }
	             else
	                {
	                  msay (lamp_recharged);
	                  lamplife += 150;
	                  val[lamp] = 1;
	                  special1[lamp] = 1;
	                  moved = 1;
	                  ok2desc = 1;
	                }
	    break;

	  case thurb:
	    if (at(icecave_36))
	       {
	         here = ice;
	         prevloc = here;	/* BACK shouldn't work */
	         moved = 1;
	       }
	    else
	       {
	         msay (nothing);
	         ok2desc = 0;
               }
	    break;

	  case samoht:
	    msay (nothing);
	    ok2desc = 0;
	    break;

	  case knerl:
	  case zorton:
	  case klaetu:
	  case snoeze:
	  case blerbi:
	    ok2desc = 0;
	    if (near(safe))
	       if (!val[safe])
	          if (inword==1 && arg1==password)
	             {
	               msay (safe_opens);
	               val [safe] = 1;
	               special1 [safe] = 1;
	             }
	          else
	             if (!special1 [safe])
	                {
	                  msay (safe_fuses);
	                  msay (blank);
	                  val [safe] = 2;	/* melt the safe's door shut */
	                  val [blob] = 1;	/* wake up the blob */
	                  ticker = 1;		/* blob is chasing us - quickly! */
	                  nomagic = 1;		/* inhibit PLUGH, etc. */
	                  val [grate] = 0;	/* lock him in cave */
	                }
	             else
	                msay (nothing);
	       else
	          msay (nothing);
	    else
	       msay (nothing);
	    break;

	  case phuggg:				/* useful when being bothered by dwarves */
	    if (near(water))
	       {
	         waterphuggg++;
	         if (waterphuggg == 1)		/* but don't use it near water! */
	            {
	              msay (jellyfish);
	              coroner();
	              RETURN;
	            }
	         else
	            {
	              msay (cave_destroyed);	/* and certainly not twice near water! */
	              finis();
	            }
	       }
	    else
	       if (near(dwarf))
	          if (near(diamonds) || near(sword) || near(shards) || near(axe) || chance(20))
	             {
	               if (dwarrows == 1)
	                  msay (it_didnt_work_1 + 2*random(3));		/* and not near sharp objects either! */
	               else
	                  msay (it_didnt_work_2 + 2*random(3));
	               coroner();
	               RETURN;
	             }
	          else
	             {
	               dwarrows = 0;					/* otherwise works 80% of the time */
	               apport (dwarf, limbo);
	               dwarfcount--;
	               if (dwarrows == 1)
	                  msay (it_worked_1 + 2*random(3));
	               else
	                  msay (it_worked_2 + 2*random(3));
	               ok2desc = 0;
	             }
	       else
	          msay (nothing);
	    break;

	  case sesame:
	    msay (magickword);
	    break;

	  default:
	    printf ("GLITCH!  magician() called with (%d, %d)\n", arg1, arg2);
	    break;
        }
	RETURN;
}


magician2()
/*
 *	HANDLES EXITING FROM SMALL CYLINDERICAL CHAMBER (ENDGAME).
 */
{
#define respond( progress)	if (escap==progress)			\
				   { escap++; msay(ok); break; }	\
				else					\
				   { escap=0; msay(nothing); break; }

	dbg ("magician2() ");

        if (debug)
           printf (": escap = %d\n", escap);

	switch (arg1)
	{
	  case blerbi:
	    respond (15);
	  case fee:
	    respond (14);
	  case fie:
	    respond (13);
	  case foe:
	    respond (12);
	  case foo:
	    respond (11);
	  case klaetu:
	    respond (10);
	  case knerl:
	    respond (9);
	  case melenkurion:
	    respond (8);
	  case noside:
	    respond (7);
	  case phuggg:
	    respond (6);
	  case plugh:
	    respond (5);
	  case samoht:
	    respond (4);

/*	  case sesame:
	    respond (4);	Not in CP/M version */

	  case snoeze:
	    respond (3);
	  case thurb:
	    respond (2);
	  case xyzzy:
	    respond (1);
	  case zorton:
	    respond (0);
	  default:
	    msay (nothing);
	    escap = 0;
	    break;
	}

	if (escap==16)
	   {
	     closure = 4;
	     moved = 1;
	     here = road;
	     prevloc = road;
	   }

	RETURN;
}


lamprey()
/*
 *      LAMP IS GETTING DIM OR HAS GONE OUT.
 */
{
	dbg ("lamprey()\n");

        if (lamplife > 0)
           if (val[batteries] == 1)
              msay (lamp_nofuel);
           else
              if (near(batteries))
                 {
                   msay (lamp_refuel);
                   val[batteries] = 1;
                   lamplife += 300;
                   special1[lamp] = 0;  /* clear recharged flag */
                 }
              else
                 if (seen[batteries])
                    msay (lamp_batteries);
                 else
                    msay (lamp_is_dim);

        else
           if (closure == 2)
              close_the_cave();
           else
              if (near(batteries) && !val[batteries])
                 {
                   msay (lamp_refuel);
                   val[batteries] = 1;
                   lamplife += 300;
                 }
              else
                 {
                   msay (lamp_is_dead);
                   val[lamp] = 0;
                   ranout = 1;          /* don't fall into a pit this move */
                   phog();              /* chase glow into place */
                 }
	RETURN;
}


tick()
/*
 *      ONCE PER INPUT ROUTINE.  IF THE BLOB HAS BEEN AWAKENED (SEE magician()),
 *	CHANGE ITS DESCRIPTION AS IT CHASES THE POOR GUY.  WHEN THE BLOB REACHES
 *	STATE 10, IT KILLS THE POOR GUY.
 */
{
	dbg ("tick()\n");

        if (val[blob])
           {
/*	     msay (blank);  */
             osay (blob, val[blob]);

	     if (val[blob] == 9)	/* blob is in same room as us	*/
	        apport (blob, here);	/* and can be referenced	*/

             if (val[blob] == 10)       /* you're dead, Jim!		*/
                {
	          apport (blob, limbo);
                  coroner();
                  RETURN;
                }
	     else
	        msay (blank);
             val[blob]++;
           }
	RETURN;
}


clock4()
/*
 *      ADMINISTRATIVE CLOCK HAS TICKED.  THIS ROUTINE IS EXECUTED WHENEVER
 *	clock IS <= ZERO AND SETS THE VALUE OF closure.
 */
{
int     derf;

	dbg ("clock4()\n");

        if (!closure)                   /* normal play */
           {
             closure = 1;
	     for (thing=MINTREASURES; (thing <= MAXTREASURES); thing++)
                 if (where_is[thing]!=building)
                    {
                      closure = 0;
	              if (debug)
	                 printf ("clock4(): Treasure %d not in building\n", thing);
                      break;
                    }
	     if (debug)
	        printf ("clock4() has set CLOSURE to %d\n", closure);

/* CLOCK WAS TOO HIGH - WEREN'T SEEING A DWARF FOR A LONG WHILE.  A DWARF SHOWS
 * UP WHEN CLOCK BECOMES < 1.  SET CLOCK SO THAT THE DWARVES SHOW UP AT SMALLER
 * TIME INTERVALS AS THEY'RE KILLED.
 *
 *	     clock = 30 + (closure==1 ? 5 : random(10));
 */

	     if (!seen[axe])
	        clock = 15 + (closure==1 ? 5 : random(10));
	     else
	        clock = dwarfcount + (closure==1 ? 5 : random(8));

             /* skip the "on shelf" state */
             if (val[sculpture])
                val[sculpture] = 1 + random(val[sculpture] - 1);

             /* skip the "in stone" state */
             if (val[sword])
                val[sword] = 1 + random(val[sword] - 1);

             if (val[dragon] == 2)
                {
                  dragtime -= lastclock;
                  if (dragtime < 0)
                     val[dragon] = 3;
                }

             if (special1[djinn] && !special2[djinn] && !near(dwarf))
                {
                  special2[djinn] = 1;
                  msay (phugg_data);
                  clock = 5;
                  RETURN;
                }

             if (val[mushroom] > 1)
                {
                  mushtime -= lastclock;
                  if (mushtime < 0)
                     if (val[mushroom] == 2)
                        {
                          val[mushroom] = 3;
                          mushtime = 40;
                          osay (mushroom, val[mushroom]);
                          strength = 7;
                          clock = 8;
                          RETURN;
                        }
                     else
                        {
                          val[mushroom] = 0;
                          apport (mushroom, cubicle);
                        }
                }

             if (cameotime>0 && cameotime<nmoves)
                {
                  clock = 10 + random(10);
                  do_cameo();
                  RETURN;
                }

             if (visited[mists] || visited[y2])
                {
                  if (nmoves>150 && !seen[chest])
                     special1[pirate] = 1;      /* set chasing mode */
                  if (nodwarf[here] || notincave[here])
                     {
                       special1[pirate] = 0;    /* clear chasing mode */

	               /* CLOCK WAS TOO HIGH.
	                *
	                * clock = 8 + random(10);  
	                *
	                */

                       clock = 5 + random(10);  /* set short clock interval */
                     }
                  else
                     if (special1[pirate] || chance(10*(dwarfcount+4)))
                        if (special1[pirate] || !(dwarrows+random(10)))
                           if (seen[chest] ||   /* found treasure chest yet? */
	                       seen[pirate] ||	/* seen pirate?              */
                               lit[here] ||     /* don't pounce in lit rooms */
                               !near(lamp) ||   /* or if lamp elsewhere      */
                               !val[lamp])      /* or lamp dead              */
                               ;                /* do nothing                */
                           else
                              {
                                if (debug)
                                   printf ("about to see a pirate...\n");

                                special1[pirate] = 0;   /* clear chasing mode */
                                valued[ring] = 0;       /* so it's not stolen */
                                derf = 0;
	                        for (thing=MINOBJECTS; (thing <= MAXOBJECTS); thing++)
                                    if (valued[thing] && near(thing))
                                       {
                                         apport (thing, mazea_26);
                                         derf = 1;
                                       }
                                valued[ring] = 1;

                                if (derf)
	                           {
                                     msay (pirate_zotz);
	                             seen[pirate] = 1;
	                           }
                                else
                                   if (where_is[chest] == limbo) /* 1st time */
	                              {
                                        msay (pirate_runs);        /* thru?    */
	                                seen[pirate] = 1;
	                              }
                                   else
                                      {
                                        msay (rustling);
                                        special1[pirate] = 1;   /* following */
                                        clock = 4 + random(10);
                                      }

                                if (where_is[chest] == limbo)   /* 1st time  */
                                   {                            /* thru?     */
                                     apport (chest, mazea_26);
                                     apport (message, mazed_140);
                                   }
                              }
                        else
                           {
                             if (debug)
                                printf ("about to see a dwarf...\n");

                             if (dwarfcount > 0)
                                if (seen[axe])    /* have we seen a dwarf yet? */
                                   {
                                     apport (dwarf, here);
                                     if (++dwarrows == 1)
                                        {                         /* knife not  */
                                          special1[dwarf] = 1;    /* thrown yet */
                                          special2[dwarf] = 0;    /* not angry  */
                                        }
                                   }
                                else              /* nope - fetch axe,   */
                                   {              /* invoke scared dwarf */
                                     apport (axe, here);
                                     seen[axe] = 1;
                                     msay (firstdwarf);
                                   }
                           }
                }
           }

        else
           if (closure == 1)            /* is it closing time? */
              {
                closure = 2;            /* set "closing soon" */
                val[grate] = 0;         /* lock the grate */
                msay (closing_now);     /* sepulchral voice */
                if (near(dwarf))
                   msay (dwarf_quits);  /* fades into the gloom */
                apport (dwarf, limbo);  /* get rid of him/them */
                dwarrows = 0;           /* zilch all present dwarves */
                dwarfcount = 0;         /* don't let them reappear */
                val[fissure] = 0;       /* destroy the bridge */
                val[wheatstone] = 0;	/* destroy wheatstone bridge */
                apport (troll, limbo);  /* destroy troll */
                apport (dragon, limbo); /* destroy dragon */
                val[troll] = 5;         /* scared - inhibit return */
                apport (troll2, swofchasm);     /* fake troll */
                invisible[fissure] = 1;
                invisible[wheatstone] = 1;
                clock = 25;             /* time to try to leave */
              }

           else                         /* MUST be closing time! */
              if (paniced)              /* did he try to get out? */
                 {
                   paniced = 0;         /* reset paniced flag */
                   clock = 15;          /* let him get frantic */
                 }

              else
                 close_the_cave();      /* if he was calm */

        lastclock = clock;
	RETURN;
}


close_the_cave()
{
	dbg ("close_the_cave()\n");

        msay (go_reposit);
        fastmode = 0;
	for (thing=MINOBJECTS; (thing < MAXOBJECTS); thing++)
            if (notincave[where_is[thing]] || carrying(thing)&&portable[thing])
               apport (thing, ylem);
        for (loc=road; (loc < limbo); loc++)
            if (notincave[loc])
               visited[loc] = 0;
	visited[building] = 1;

        closure = 3;
        clock = 999;
	prevloc = cylinderical;
        here = cylinderical;
	lsay (cylinderical);
        escap = 0;
	RETURN;
}


do_cameo()
/*
 *      GENERATE STRANGE CAMEO APPEARANCES IF POSSIBLE.
 */
{
	dbg ("do_cameo()\n");

        cameotime = 0;                  /* only 1 cameo per game */

        if (notincave[here]     ||      /* cameos work only in cave */
            nodwarf[here]       ||      /* and only in dwarf-accessible locs */
            lit[here]           ||      /* and only in unlit rooms */
            onexit[here]        ||      /* that have many exits */
            !near(lamp)         ||      /* and only if you have your lamp */
            !val[lamp]          ||      /* which must be turned on */
            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;

        msay (cameo_1 + random (cameo_6 - cameo_1 + 1));
	RETURN;
}


usage()
/*
 *      ONLY /d ALLOWED IN COMMAND LINE!
 */
{
	dbg ("usage()\n");

        printf ("\n");
        printf ("From behind the screen appears a dejected looking dwarf, his clothes dirty\n");
        printf ("with patches of grey dust, muttering to himself.  As his eyes fall on you,\n");
        printf ("he shakes his head and says,\n\n");

        printf ("\"For years I have been cooped up on this disk, with nary a kind word nor\n");
        printf ("glass of ale.  The one chance I get to stretch my legs and meet other people,\n");
        printf ("you have to go and louse things up.  Remember, only a wizard can specify\n");
        printf ("options when running Adventure - and you sure don't look like a wizard to me!\".\n\n");

        printf ("With that, he sneezes violently and fades away faster than the echoes of\n");
        printf ("his nasal explosion.\n");
        exit (1);
}
