// Copyright 1983 by A. Clapman - Design and original ZX BASIC programming
// Copyright 1996,2006 by H. Bezemer - Structured design and Arena programming

// Permission is granted by the author to use and distribute this software
// provided these copyright notices are preserved.

// This file is part of the Arena interpreter package

// Extract from a time-travellers diary discovered in the pyramid of
// Ikhotep, pharoah of the ninth dynasty, on the planet Sirius B,
// in the dog star system.

// "I have been attempting to discover the secret of the pyramid for some
// months now. It is the only way I will be able to escape this barren
// planet. After my time-machine was destroyed by the warrior tribe I
// found my way to this dusty monument after consulting a man they regard
// as a wizard. He is in fact a fellow traveller in time and space exiled
// by the Time Lords to this lost planet. He has decided to stay and
// persue his black arts amoung the warrior folk. But he has told me of
// a time gate which will lead me back to the main time lanes and freedom.
// He said the gate was hidden within the pyramid. I have uncovered some
// clues but not enough to lead me to the final solution. I can only keep
// trying. But I feel that, for me at least, time is running out."

// The diary was found next to a small pile of oddly shaped bones deep
// within the heart of the pyramid.

// Can you find your way out of the pyramid and off the barren planet?
// You will find several rooms within the pyramid and several objects
// within those rooms which must be collected to solve the riddle of the
// ancient monument. The program uses the standard two word entry system
// and adjectives should not be entered. To move simply type in the
// direction you want to go, for example 'N' or 'north'. Other useful
// words are TAKE, GET, THROW, DROP, INVENTORY.

cmd = "";                              // command line

todo = 0;                              // is still alive
done = 1;                              // is already dead
seen = 2;                              // dragon has seen you

is_dragon = todo;                      // is dragon alive
is_tree   = todo;                      // is tree uncut
is_door   = todo;                      // is the door intact
is_wall   = todo;                      // is the wall closed
is_helmet = todo;                      // is the helmet off
is_power  = todo;                      // is the power off

watch      = 0;                        // objects in grid
dragon     = 1;
generator  = 2;
tree       = 3;
slab       = 4;
door       = 5;
saw        = 6;
mirror     = 7;
reflector  = 8;
coin       = 9;
knife      = 10;
dagger     = 11;
helmet     = 12;
axe        = 13;

Nobjects   = 14;                       // number of objects in grid

objects = mkstruct (
  "watch",     watch ,
  "dragon",    dragon ,
  "generator", generator ,
  "tree",      tree ,
  "slab",      slab ,
  "door",      door ,
  "saw",       saw ,
  "mirror",    mirror ,
  "reflector", reflector ,
  "coin",      coin ,
  "knife",     knife ,
  "dagger",    dagger ,
  "helmet",    helmet ,
  "axe",       axe
);

stays = 0;                             // cannot be moved
moves = 1;                             // can be moved

attributes = mkarray (                 // map attributes to objects
  "\twrist watch\n",                   moves ,
  "\tmagenta, firebreathing dragon\n", stays ,
  "\tmobile electricity generator\n",  moves ,
  "\tCanadian Redwood tree\n",         stays ,
  "\tgranite slab\n",                  moves ,
  "\tthick wooden door\n",             stays ,
  "\telectric saw\n",                  moves ,
  "\tpurple mirror\n",                 moves ,
  "\tgreen reflector\n",               moves ,
  "\t10 pence coin\n",                 moves ,
  "\tbutter knife\n",                  moves ,
  "\trazor sharp dagger\n",            moves ,
  "\tsafety helmet\n",                 moves ,
  "\tsharp axe\n",                     moves
);

_Exit_       = _Person_ = -1;          // locations
_NoWhere_    = 0;
_RoadWE_     = 1;
_RoadBend_   = 2;
_ShackW_     = 3;
_ShackE_     = 4;
_RoadNS_     = 5;
_RoadN_      = 6;
_Pyramid_    = 7;
_Embalm_     = 8;
_Recreation_ = 9;
_ANKH_       = 10;
_Triangular_ = 11;
_Oblong_     = 12;
_Funeral_    = 13;
_Treasure_   = 14;
_Cave_       = 15;
_Garden_     = 16;

locations = mkarray (                 // map locations to directions
  "a road leading west and east. Two things are pointing to the west.\n",
  _NoWhere_ , _NoWhere_ , _Exit_ , _RoadBend_ ,
  "a bend in the road.\n",
  _NoWhere_ , _RoadNS_ , _RoadWE_ , _NoWhere_ ,
  "a small dark shack.\n",
  _NoWhere_ , _NoWhere_ , _NoWhere_ , _RoadNS_ ,
  "a small dark shack.\n",
  _NoWhere_ , _NoWhere_ , _RoadNS_ , _Cave_ ,
  "a road leading north and south. There are shacks either side.\n",
  _RoadBend_ , _RoadN_ , _ShackW_ , _ShackE_ ,
  "a road leading north. There is a pyramid south.\n",
  _RoadNS_ , _Pyramid_ , _NoWhere_ , _NoWhere_ ,
  "the entrance hall of the pyramid. There is a road north.\n",
  _RoadN_ , _Oblong_ , _Embalm_ , _Recreation_ ,
  "the embalming room.\n",
  _NoWhere_ , _NoWhere_ , _ANKH_ , _Pyramid_ ,
  "the recreation room. An exit to the garden is east.\n",
  _NoWhere_ , _Funeral_ , _Pyramid_ , _Garden_ ,
  "the room of ANKH.\n",
  _NoWhere_ , _NoWhere_ , _NoWhere_ , _Embalm_ ,
  "a small triangular room.\n",
  _NoWhere_ , _NoWhere_ , _NoWhere_ , _Oblong_ ,
  "a long oblong room.\n",
  _Pyramid_ , _Treasure_ , _Triangular_ , _NoWhere_ ,
  "the funeral parlour.\n",
  _Recreation_ , _NoWhere_ , _NoWhere_ , _NoWhere_ ,
  "the treasure room. It has been looted. There is a smashed door north.\n",
  _Oblong_ , _NoWhere_ , _NoWhere_ , _NoWhere_ ,
  "a small circular cave.\n",
  _NoWhere_ , _NoWhere_ , _ShackE_ , _NoWhere_ ,
  "a small garden.\n",
  _NoWhere_ , _NoWhere_ , _Recreation_ , _NoWhere_
);

map = mkarray(
   _ShackW_ ,                          // watch in west shack
   _ShackE_ ,                          // dragon in east shack
   _RoadBend_ ,                        // generator in bend of the road
   _Garden_ ,                          // tree in the garden
   _Funeral_ ,                         // slab in funeral parlor
   _Oblong_ ,                          // door in oblong room
   _Triangular_ ,                      // saw in triangular room
   _ANKH_ ,                            // mirror in room of ANKH
   _Cave_ ,                            // reflector in cave
   _Treasure_ ,                        // coin in treasure room
   _RoadNS_ ,                          // knife on north/south road
   _NoWhere_ ,                         // dagger is nowhere
   _Pyramid_ ,                         // helmet in pyramid
   _NoWhere_                           // axe is nowhere
);

void CANNOT () {                       // default warning
  print ("YOU CAN'T, IDIOT!!\n\n");
}

void DEAD () {                         // exit game
  print ("YOU'RE DEAD!!\n\n");
  exit (0);
}

void IDUNNO (string a) {               // I don't know!
  printf ("I don't know what a %s is.\n\n", a);
}

void OUTSIDE () {                      // not here!
  print ("It isn't here!!\n\n");
}

void MISSING () {
  print ("You aren't carrying it, stupid!!\n\n");
}

north = _NoWhere_;
south = _NoWhere_;
west  = _NoWhere_;
east  = _NoWhere_;

where = _RoadBend_;                    // room where you are

string verb () {
  x = strchr (cmd, " ");
  return (x == () ? cmd : left (cmd, x));
}

string noun () {
  x = strrchr (cmd, " ");
  return (x == () ? cmd : trim (right (cmd, strlen (cmd) - x)));
}

void execute (string command) {
  action = struct_get (actions, command);

  if (action == ()) printf ("I don't know how to %s.\n\n", command);
  else action ();
}

int room () {
  return ((where - 1) * 5);
}
                                       // keep locked until task is done
int noentry (int done, int here, int dir) {
  if (where == here) return (done ? dir : _NoWhere_);
  else return (dir);  
}

void directions (int here) {
  north = locations [here + 1];
  south = locations [here + 2];
  west  = locations [here + 3];
  east  = locations [here + 4];
  global ("north", "south", "west", "east");
}

void is_north () {                     // going north ?
  if (north) print ("North\t");
}

void is_south () {                     // going south ?
  south = noentry (is_door, _Oblong_, south);
  if (south) print ("South\t");
  global ("south");
}

void is_west () {                      // going west ?
  west = noentry (is_wall, _Embalm_, west);
  if (west) print ("West\t");
  global ("west");
}

void is_east () {                      // going east ?
  east = noentry (is_dragon, _ShackE_, east);
  if (east) print ("East\t");
  global ("east");
}

void is_lastroom () {
  if (where == _Exit_) {               // make sure we're there
    print ("LASER BOLTS FLASH OUT FROM THE KILLO-ZAP GUNS FIXED TO THE ROAD!\n");
                                       // can't we reflect anything?
    if ((map [mirror] != _Person_) && (map [reflector] != _Person_)) {
      print ("FRIZZLE!!\n"); DEAD ();
    }
                                       // are we missing a reflector
    if ((map [reflector] != _Person_)) {
      print ("THE LEFT RAY IS REFLECTED BY THE MIRROR. THE RIGHT ONE ISN'T!!\n"); DEAD ();
    }
                                       // are we missing a mirror
    if ((map [mirror] != _Person_)) {
      print ("THE RIGHT RAY IS REFLECTED BY THE REFLECTOR. THE LEFT ONE ISN'T!!\n"); DEAD ();
    }
                                       // we got them both!
    print ("BOTH THE RAYS ARE REFLECTED BY THE MIRROR AND THE REFLECTOR!!\n");
    print ("YOU HAVE MANAGED TO ESCAPE ALIVE!!\n\n");
    exit (0);
  }
}

void contents () {
  t = 0;                               // show the contents of a room
  for (x = 0; x < Nobjects; x++)       // loop through the map for a match
      if (map [x] == where) {
         t++; print (attributes [x * 2]);
      }
      
  if (t == 0) print ("\tNone\n");      // nothing in this room..
                                       // all exceptions
  if ((where != _ShackE_) && (is_dragon == seen))
     is_dragon = todo;                 // if we aren't in the shack but the 
                                       // dragon has seen us we make him forget
  if ((where == _ShackE_) && (is_dragon == seen)) {
     print ("\nThe dragon doesn't like you so he kills you.\n"); DEAD ();
  }

  if ((where == _ShackE_) && (is_dragon == todo)) {
     print ("\nThe dragon blocks a hole in the EAST wall.\n");
     is_dragon = seen;
  }

  if ((where == _ShackE_) && (is_dragon == done))
     print ("\nThe dragon is dead.\n");

  if ((where == _Garden_) && (is_tree == done))
     print ("\nThe tree is lying on the ground\n");

  if ((where == _Garden_) && (is_tree == todo))    
     print ("\nThere is something glistening at the top of the tree.\n");

  if ((where == _Embalm_) && (is_wall == todo))
     print ("\nThere is a small slot on the west wall.\n");

  if ((where == _Oblong_) && (is_door == done))
     print ("\nThe door is smashed down\n");
    
  global ("is_dragon");
}

void showroom () {                     // describe a room
  is_lastroom ();                      // are we in the last room?
  print ("You are at ");
  print (locations [room ()]);
  directions (room ());
  print ("Directions you may proceed in:\n\t");
  is_north (); is_south ();
  is_west (); is_east ();              // show exits and items
  print ("\n\nThings of interest here:\n"); 
  contents ();
}

void do_move () {
  execute (noun ());
}

void do_go (int x) {
  if (x) where = x; else CANNOT ();
  global ("where");
}

void go_north () {                     // go north
  do_go (north);
}

void go_south () {                     // go south
  do_go (south);
}

void go_west () {                      // go west
  do_go (west);
}

void go_east () {                      // go east
  do_go (east);
}

void do_take () {                      // take an object
  thing = noun ();
  object = struct_get (objects, thing);

  if (object == ()) IDUNNO (thing);
  else {
     if (map [object] == _Person_) {   // do we already have it?
        print ("YOU ARE ALREADY CARRYING IT!!\n\n"); return;
     }
                                       // do we have a watch?     
     if ((object != watch) && (map [watch] != _Person_)) {
        print ("YOU HAVEN'T GOT ANYTHING TO CARRY IT IN!!\n\n"); return;
     }
                                       // is it here?
     if (map [object] != where) {
        OUTSIDE (); return;
     }
                                       // can we move it?
     if (attributes [(object * 2) + 1] == stays) {
        CANNOT (); return;
     }
                                       // zoom it in your watch
     print (object == watch ?
           "YOU STRAP IT ON YOUR WRIST.\n\n" :
           "IT ZOOMS SAFELY INTO YOUR WATCH!\n\n");
                                       // now you carry it
     map [object] = _Person_; global ("map");
  }
}

void do_drop () {                      // drop object
  thing = noun ();
  object = struct_get (objects, thing);

  if (object == ()) IDUNNO (thing);
  else {
    if (map [object] != _Person_) MISSING ();
    else {
      map [object] = where;
      if (object == helmet) is_helmet = todo;
      global ("map", "is_helmet");
    }
  }
}

void do_saw () {                       // saw tree
  thing = noun ();
  object = struct_get (objects, thing);

  if (object == ()) IDUNNO (thing);
  else {
    if ((object != tree) || (is_tree == done) || 
       (map [generator] != _Person_) || (map [saw] != _Person_)) {
       CANNOT (); return;              // saw a tree or is it already cut?
    }                                  // do we have a generator and a saw?

    if (where != _Garden_) {           // are we in the garden?
       OUTSIDE (); return;
    }
    
    if (is_power == todo) {            // do we have power?
       print (" The saw won't work without electricity!!\n\n"); return;
    }

    if (is_helmet == todo) {           // are we wearing a helmet?
      print ("The tree falls on your unprotected head. Crunch.\n\n"); 
      DEAD ();
    }

    is_tree = done;                    // now cut the tree
    print ("The tree falls down on your safety helmet.\n");
    print ("An axe falls out of the top of the tree.\n\n");
    map [axe] = where;                 // and show the axe
    global ("map", "is_tree");
  }
}

void do_smash () {                     // smash door
  thing = noun ();
  object = struct_get (objects, thing);

  if (object == ()) IDUNNO (thing);
  else {
    if ((object != door) || (map [axe] != _Person_) || (is_door == done)) {
       CANNOT (); return;              // do we smash a door?  
    }                                  // do we have an axe?
                                       // is the door smashed?
    if (where != _Oblong_) {           // are we in the oblong room?
       OUTSIDE (); return;
    }

    print ("Chop chop smash smash.. The door has been smashed down.\n\n");
    is_door = done; global ("is_door");
  }                                    // now smash the door
}

void do_wear () {                      // wear helmet
  thing = noun ();
  object = struct_get (objects, thing);

  if (object == ()) IDUNNO (thing);
  else {
    if (object != helmet) {
       CANNOT (); return;              // want to wear a helmet? 
    }

    if (map [helmet] != _Person_) {    // do we have a helmet?
       MISSING (); return;
    }
    
    is_helmet = done; global ("is_helmet");
  }                                    // now put on the helmet
}

void do_connect () {                   // connect generator
  thing = noun ();
  object = struct_get (objects, thing);

  if (object == ()) IDUNNO (thing);
  else {
    if ((object != generator) && (object != saw)) {
       CANNOT (); return;              // do we connect a generator 
    }                                  // or a saw?

    if (map [object] != _Person_) {    // do we have it?
       MISSING (); return;
    }

    if ((map [saw] != _Person_) || (map [generator] != _Person_)) {
       CANNOT (); return;              // do we miss anything?
    }
                                       // ok, power is on! 
    is_power = done; global ("is_power");
  }
}

void do_push () {                      // push wall
  thing = noun ();
  object = struct_get (objects, thing);

  if (object == ()) IDUNNO (thing);
  else {
    if (object != coin) {
       CANNOT (); return;              // do we insert a coin?
    }

    if (map [coin] != _Person_) {      // do we have it?
       MISSING (); return;
    }
    
    if (where != _Embalm_)             // are we in the embalming room?
       print ("I can't see anywhere to insert it!!");
    else {
       map [coin] = _NoWhere_;         // coin disappears in slot
       print ("The wall suddenly shakes and glides one side leaving a doorway west!!");
       is_wall = done; global ("map", "is_wall");
    }                                  // the wall is open!
    print ("\n\n");
  }
}

void do_file () {                      // file knife
  thing = noun ();
  object = struct_get (objects, thing);

  if (object == ()) IDUNNO (thing);
  else {
    if (object != knife) {
       CANNOT (); return;              // do we want to file a knife?
    }    

    if (map [knife] != _Person_) {
       MISSING (); return;
    }

    if (map [slab] != _Person_) 
       print ("You haven't got anything to sharpen it on!!");
    else {
       print ("The knife turns extra sharp!!");
       map [knife]  = _NoWhere_;       // the knife turns into a dagger
       map [dagger] = _Person_; global ("map");
    }
    print ("\n\n");
  }
}

void do_kill () {                      // kill dragon
  thing = noun ();
  object = struct_get (objects, thing);

  if (object == ()) IDUNNO (thing);
  else {                              // do we want to kill a dragon?
    if ((object != dragon) || (map [dagger] != _Person_)) {
       CANNOT (); return;             // and do we have a dagger?
    }

    if (where != _ShackE_) {          // are we in the east shack?
       OUTSIDE (); return;
    }

    if (is_dragon == done)             // is the dragon alive?
       print ("The poor thing is already dead.");
    else {
      is_dragon = done; global ("is_dragon");               
      print ("Squelch. The dagger sinks to the hilt in the dragon.\n");
      print ("It's dead. Poor thing."); 
    }                                  // now he is!
    print ("\n\n");
  }
}

void do_list () {                      // shows the inventory
  print ("You are carrying:\n");
  
  t = 0;                               // show the contents of a room
  for (x = 0; x < Nobjects; x++)       // loop through the map for a match
      if (map [x] == _Person_) {
         t++; print (attributes [x * 2]);
      }
      
  if (t == 0) print ("\tNothing\n");   // nothing in this room..
  print ("\n");
}

void do_die () {                       // end game quickly
  print ("There is no easy way out of here!!\n");
  print ("So you decide to commit suicide.\n");
  DEAD ();
}

/*
void do_flags () {                     // debugging code 
  print ("Dragon: ",   is_dragon);
  print ("\nTree:   ", is_tree);
  print ("\nHelmet: ", is_helmet);
  print ("\nPower:  ", is_power);
  print ("\nDoor:   ", is_door);
  print ("\nWall:   ", is_wall);
  print ("\n\n");
}
*/

actions = mkstruct (                   // map vocabulary to actions
//  "flags",     do_flags ,
  "go",        do_move ,
  "move",      do_move ,
  "run",       do_move ,
  "walk",      do_move ,
  "north",     go_north ,
  "n",         go_north ,
  "south",     go_south ,
  "s",         go_south ,
  "west",      go_west ,
  "w",         go_west ,
  "east",      go_east ,
  "e",         go_east ,
  "get",       do_take ,
  "take",      do_take ,
  "steal",     do_take ,
  "drop",      do_drop ,
  "throw",     do_drop ,
  "leave",     do_drop ,
  "saw",       do_saw ,
  "cut",       do_saw ,
  "fell",      do_saw ,
  "chop",      do_smash ,
  "smash",     do_smash ,
  "axe",       do_smash ,
  "wear",      do_wear ,
  "connect",   do_connect ,
  "insert",    do_push ,
  "push",      do_push ,
  "sharpen",   do_file ,
  "file",      do_file ,
  "kill",      do_kill ,
  "stab",      do_kill ,
  "knife",     do_kill ,
  "invent",    do_list ,
  "objects",   do_list ,
  "inventory", do_list ,
  "list",      do_list ,
  "die",       do_die ,
  "exit",      do_die ,
  "quit",      do_die
);

void venture () {
  for (;;) {
    showroom ();
    print ("\nCommand: ");
    cmd = tolower (trim (fgets (stdin))); global ("cmd");
    print ("\n"); execute (verb ()); 
  }
}

venture ();