The Catacomb open source release. master
authorRichard Mandel <richard@tech-transfer.com>
Thu, 5 Jun 2014 23:49:32 +0000 (19:49 -0400)
committerRichard Mandel <richard@tech-transfer.com>
Thu, 5 Jun 2014 23:49:32 +0000 (19:49 -0400)
17 files changed:
CATACOMB.C [new file with mode: 0644]
CATACOMB.PRJ [new file with mode: 0644]
CATASM.ASM [new file with mode: 0644]
CATED.C [new file with mode: 0644]
CAT_PLAY.C [new file with mode: 0644]
COPYING [new file with mode: 0644]
CPANEL.C [new file with mode: 0644]
NGRABCA2.H [new file with mode: 0644]
OBJECTS.C [new file with mode: 0644]
OLDCAT.C [new file with mode: 0644]
PCRLIB.H [new file with mode: 0644]
PCRLIB_A.ASM [new file with mode: 0644]
PCRLIB_C.C [new file with mode: 0644]
README.md [new file with mode: 0644]
RLEASM.ASM [new file with mode: 0644]
SOUNDS.H [new file with mode: 0644]
_PIRACY.OBJ [new file with mode: 0644]

diff --git a/CATACOMB.C b/CATACOMB.C
new file mode 100644 (file)
index 0000000..7d70ada
--- /dev/null
@@ -0,0 +1,1288 @@
+/* The Catacomb Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#define CATALOG\r
+\r
+/*\r
+** catacomb II -- the c translation...\r
+*/\r
+\r
+#include "pcrlib.h"\r
+#include "NGRABCA2.H"\r
+#include "SOUNDS.H"\r
+\r
+#define NUMDEMOS 1\r
+\r
+#define maxpics 2047\r
+#define numtiles 24*24   /*number of tiles displayed on screen*/\r
+#define numlevels 30\r
+#define maxobj 200           /*maximum possible active objects*/\r
+#define solidwall 129\r
+#define blankfloor 128\r
+#define leftoff 11\r
+#define topoff 11\r
+#define tile2s 256          /*tile number where the 2*2 pictures start*/\r
+#define tile3s tile2s+67*4\r
+#define tile4s tile3s+35*9\r
+#define tile5s tile4s+19*16\r
+#define lasttile tile5s+19*25\r
+\r
+typedef enum {playercmd,gargcmd,dragoncmd,ramstraight,ramdiag,straight,idle,\r
+    fade,explode,gunthinke,gunthinks} thinktype;\r
+\r
+typedef enum {benign,monster,pshot,mshot,nukeshot} tagtype;\r
+\r
+typedef enum {nothing,player,goblin,skeleton,ogre,gargoyle,dragon,turbogre,\r
+    wallhit,shot,bigshot,rock,dead1,dead2,dead3,dead4,dead5,dead6,teleporter,\r
+    torch,secretgate,gune,guns,lastclass} classtype;\r
+\r
+typedef enum {ingame,intitle,inscores} statetype;\r
+\r
+\r
+typedef struct {\r
+  boolean active;      /*if false, the object has not seen the player yet*/\r
+  classtype  class;\r
+  byte  x,y,           /*location of upper left corner in world*/\r
+    stage,             /*animation frame being drawn*/\r
+    delay;             /*number of frames to pause without doing anything*/\r
+  dirtype  dir;                /*direction facing*/\r
+  char hp;             /*hit points*/\r
+  byte oldx,oldy;      /*position where it was last drawn*/\r
+  int oldtile;         /*origin tile when last drawn*/\r
+  char filler[1];      /*pad to 16 bytes*/\r
+   } activeobj;\r
+\r
+typedef struct {       /*holds a copy of activeobj, and its class info*/\r
+  boolean  active;     /*if false, the object has not seen the player yet*/\r
+  classtype  class;\r
+  byte  x,y,           /*location of upper left corner in world*/\r
+    stage,             /*animation frame being drawn*/\r
+    delay;             /*number of frames to pause without doing anything*/\r
+  dirtype  dir;                /*direction facing*/\r
+  char hp;             /*hit points*/\r
+  byte oldx,oldy;              /*position where it was last drawn*/\r
+  int oldtile;         /*origin tile when last drawn*/\r
+  char filler[1];      /*pad to 16 bytes*/\r
+\r
+  byte think;\r
+  byte contact;\r
+  byte solid;\r
+  word  firstchar;\r
+  byte  size;\r
+  byte  stages;\r
+  byte  dirmask;\r
+  word  speed;\r
+  byte  hitpoints;\r
+  byte  damage;\r
+  word  points;\r
+  char filler2[2];     /*pad to 32 bytes*/\r
+  } objdesc;\r
+\r
+\r
+/*=================*/\r
+/*                */\r
+/* typed constants */\r
+/*                        */\r
+/*=================*/\r
+  char altmeters[14][14] = {\r
+ {127,127,127,127,127,127,127,127,127,127,127,127,127},\r
+ {23,127,127,127,127,127,127,127,127,127,127,127,127},\r
+ {23,25,127,127,127,127,127,127,127,127,127,127,127},\r
+ {23,24,25,127,127,127,127,127,127,127,127,127,127},\r
+ {23,24,24,25,127,127,127,127,127,127,127,127,127},\r
+ {23,24,24,24,25,127,127,127,127,127,127,127,127},\r
+ {23,24,24,24,24,25,127,127,127,127,127,127,127},\r
+ {23,24,24,24,24,24,25,127,127,127,127,127,127},\r
+ {23,24,24,24,24,24,24,25,127,127,127,127,127},\r
+ {23,24,24,24,24,24,24,24,25,127,127,127,127},\r
+ {23,24,24,24,24,24,24,24,24,25,127,127,127},\r
+ {23,24,24,24,24,24,24,24,24,24,25,127,127},\r
+ {23,24,24,24,24,24,24,24,24,24,24,25,127},\r
+ {23,24,24,24,24,24,24,24,24,24,24,24,25} };\r
+\r
+  char meters[14][14] = {\r
+ {127,127,127,127,127,127,127,127,127,127,127,127,127},\r
+ {26,127,127,127,127,127,127,127,127,127,127,127,127},\r
+ {26,28,127,127,127,127,127,127,127,127,127,127,127},\r
+ {26,27,28,127,127,127,127,127,127,127,127,127,127},\r
+ {26,27,27,28,127,127,127,127,127,127,127,127,127},\r
+ {26,27,27,27,28,127,127,127,127,127,127,127,127},\r
+ {26,27,27,27,27,28,127,127,127,127,127,127,127},\r
+ {26,27,27,27,27,27,28,127,127,127,127,127,127},\r
+ {26,27,27,27,27,27,27,28,127,127,127,127,127},\r
+ {26,27,27,27,27,27,27,27,28,127,127,127,127},\r
+ {26,27,27,27,27,27,27,27,27,28,127,127,127},\r
+ {26,27,27,27,27,27,27,27,27,27,28,127,127},\r
+ {26,27,27,27,27,27,27,27,27,27,27,28,127},\r
+ {26,27,27,27,27,27,27,27,27,27,27,27,28} };\r
+\r
+ dirtype opposite[9] =\r
+    {south,west,north,east,southwest,northwest,northeast,southeast,nodir};\r
+\r
+\r
+/*==================*/\r
+/*                 */\r
+/* global variables */\r
+/*                 */\r
+/*==================*/\r
+  enum {quited,killed,reseted,victorious} gamexit; /*determines what to do after playloop*/\r
+\r
+  int oldtiles [numtiles];             /*tile displayed last refresh*/\r
+  int background[87][86];              /*base map*/\r
+  int view[87][86];                    /*base map with objects drawn in*/\r
+  int originx, originy;                        /*current world location of ul corn*/\r
+  byte priority [maxpics+1];           /*tile draw overlap priorities*/\r
+\r
+  int items[6],saveitems[6];\r
+  int shotpower;                       /*0-13 characters in power meter*/\r
+  int side;                            /*which side shots come from*/\r
+  int boltsleft;                       /*number of shots left in a bolt*/\r
+\r
+  activeobj o[maxobj+1],saveo[1];      /*everything that moves is here*/\r
+  objdesc obj , altobj;                        /*total info about objecton and alt*/\r
+  int altnum;                          /*o[#] of altobj*/\r
+  int numobj,objecton;                 /*number of objects in o now*/\r
+\r
+  struct {\r
+    byte think;                        /*some of these sizes are for the*/\r
+    byte contact;                      /*convenience of the assembly routines*/\r
+    byte solid;\r
+    word firstchar;\r
+    byte size;\r
+    byte stages;\r
+    byte dirmask;\r
+    word speed;\r
+    byte hitpoints;\r
+    byte damage;\r
+    word points;\r
+    byte filler[2];\r
+  } objdef [lastclass];\r
+\r
+\r
+  int i,j,k,x,y,z;\r
+  boolean playdone, leveldone;\r
+\r
+  boolean tempb;\r
+  char far *tempp;\r
+\r
+  int chkx,chky,chkspot;               /*spot being checked by walk*/\r
+\r
+  word frameon;\r
+  char far *grmem;\r
+  classtype clvar;\r
+\r
+  int VGAPAL;                          // just to make pcrlib happy\r
+\r
+  boolean exitdemo,resetgame;\r
+  statetype gamestate;\r
+\r
+  ControlStruct ctrl;\r
+\r
+  char far *pics, far *picsexact;\r
+\r
+  unsigned EGADATASTART;\r
+\r
+  long savescore;\r
+\r
+//NOLAN ADDED\r
+       boolean GODMODE = false;\r
+\r
+\r
+/****************************************************************************/\r
+\r
+//////////////////////////////////\r
+//\r
+// function prototypes\r
+//\r
+//////////////////////////////////\r
+\r
+void extern drawobj (void);\r
+void extern eraseobj (void);\r
+void extern doall (void);\r
+void extern egamove (void);\r
+void extern cgarefresh (void);\r
+void extern egarefresh (void);\r
+void dofkeys (void);\r
+\r
+\r
+\r
+/*==============================*/\r
+/*                             */\r
+/* xxxrefresh                   */\r
+/* refresh the changed areas of */\r
+/* the tiles map in the various */\r
+/* graphics modes.              */\r
+/*                             */\r
+/*==============================*/\r
+\r
+char demowin [5][16] = {\r
+  {14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16},\r
+  {17,' ','-','-','-',' ','D','E','M','O',' ','-','-','-',' ',18},\r
+  {17,'S','P','A','C','E',' ','T','O',' ','S','T','A','R','T',18},\r
+  {17,'F','1',' ','T','O',' ','G','E','T',' ','H','E','L','P',18},\r
+  {19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21} };\r
+\r
+void refresh (void)\r
+{\r
+  int x,y,basex,basey;\r
+  word underwin [5][16];\r
+\r
+  basex=originx+4;\r
+  basey=originy+17;\r
+  if (indemo)\r
+  {\r
+    for (y=0; y<=4; y++)\r
+      for (x=0; x<=15; x++)\r
+       {\r
+         underwin[y][x]=view[y+basey][x+basex];\r
+         view[y+basey][x+basex]=demowin[y][x];\r
+       };\r
+  }\r
+\r
+  WaitVBL ();\r
+  if (grmode==CGAgr)\r
+    cgarefresh();\r
+  else\r
+    egarefresh();\r
+\r
+  if (indemo)\r
+  {\r
+    for (y=0; y<=4; y++)\r
+      for (x=0; x<=15; x++)\r
+       view[y+basey][x+basex]=underwin[y][x];\r
+  }\r
+\r
+  WaitVBL ();\r
+}\r
+\r
+\r
+void simplerefresh(void)\r
+{\r
+  WaitVBL ();\r
+  if (grmode==CGAgr)\r
+    cgarefresh();\r
+  else\r
+    egarefresh();\r
+\r
+}\r
+\r
+/*\r
+===================\r
+=\r
+= loadgrfiles\r
+=\r
+= Loads the tiles and sprites, and sets up the pointers and tables\r
+=\r
+===================\r
+*/\r
+\r
+void loadgrfiles ()\r
+{\r
+  int i;\r
+\r
+  if (grmode==CGAgr)\r
+  {\r
+    if (picsexact != NULL)\r
+      farfree (picsexact);\r
+    pics= (char far *)bloadin("CGACHARS.CA2");\r
+    picsexact = lastparalloc;\r
+    installgrfile ("CGAPICS.CA2",0,0);\r
+    setscreenmode (grmode);\r
+  }\r
+  else\r
+  {\r
+    EGADATASTART = 0xA800;\r
+    installgrfile ("EGAPICS.CA2",0,0);\r
+    setscreenmode (grmode);\r
+    moveega ();\r
+    pics= (char far *)bloadin("EGACHARS.CA2");\r
+    EGAmove ();\r
+    farfree (lastparalloc);            // chars are allready in EGA mem\r
+  }\r
+\r
+}\r
+\r
+\r
+/*======================================*/\r
+/*                                     */\r
+/* restore                              */\r
+/* redraws every tile on the tiled area */\r
+/* by setting oldtiles to -1.  used to  */\r
+/* erase any temporary windows.         */\r
+/*                                     */\r
+/*======================================*/\r
+\r
+void clearold (void)\r
+{\r
+  memset (&oldtiles,0xff,sizeof(oldtiles)); /*clear all oldtiles*/\r
+};\r
+\r
+\r
+void restore (void)\r
+{\r
+  clearold ();\r
+  simplerefresh ();\r
+};\r
+\r
+\r
+\r
+/*      */\r
+/* help */\r
+/*      */\r
+boolean wantmore (void)\r
+{\r
+  sx=2;\r
+  sy=20;\r
+  print ("(space for more/esc)");\r
+  sx=12;\r
+  sy=21;\r
+  ch = get ();\r
+  if (ch==27)\r
+    return false;\r
+\r
+  return true;\r
+};\r
+\r
+\r
+/*        */\r
+/* charpic */\r
+/*        */\r
+void charpic(int x,int y, classtype c, dirtype dir, int stage)\r
+{\r
+  int xx,yy,size,tilenum;\r
+\r
+  size=objdef[c].size;\r
+  tilenum=objdef[c].firstchar+size*size\r
+    * ((dir & objdef[c].dirmask)*objdef[c].stages+stage);\r
+\r
+  for (yy=y;yy<=y+size-1;yy++)\r
+    for (xx=x;xx<=x+size-1;xx++)\r
+       drawchar (xx,yy,tilenum++);\r
+};\r
+\r
+void help (void)\r
+{\r
+  int x,y;\r
+  char far *oldcharptr;\r
+#define OLDSET oldcharptr = charptr;charptr = MK_FP(0xa400,0);\r
+#define NEWSET charptr = oldcharptr;\r
+\r
+  centerwindow (20,20);\r
+  print ("  C A T A C O M B   \n");\r
+  print ("   - - - - - - -    \n");\r
+  print (" by John Carmack    \n");\r
+  print ("                    \n");\r
+  print ("\n");\r
+  print ("f1 = help           \n");\r
+  print ("f2 = control panel  \n");\r
+  print ("f3 = game reset     \n");\r
+  print ("f4 = save game      \n");\r
+  print ("f5 = load saved game\n");\r
+  print ("f9 = pause          \n");\r
+  print ("f10 / ESC = quit    \n");\r
+  print ("\n");\r
+  print ("hit fire at the demo\n");\r
+  print ("to begin playing.   \n");\r
+  if (!wantmore())\r
+    return;\r
+\r
+  centerwindow (20,20);\r
+  print ("\nKeyboard controls:  \n\n");\r
+  print ("move    : arrows    \n");\r
+  print ("button1 : ctrl      \n");\r
+  print ("button2 : alt       \n");\r
+  print ("\nTo switch to mouse \n");\r
+  print ("or joystick control,\n");\r
+  print ("hit f2             \n");\r
+\r
+  if (!wantmore())\r
+    return;\r
+\r
+  centerwindow (20,20);\r
+  print ("Button 1 / ctrl key:\n");\r
+  print ("Builds shot power.  \n");\r
+  print ("If the shot power   \n");\r
+  print ("meter is full when  \n");\r
+  print ("the button is       \n");\r
+  print ("released, a super   \n");\r
+  print ("shot will be        \n");\r
+  print ("launched.           \n");\r
+  print ("\n");\r
+\r
+  OLDSET;\r
+\r
+  for (y=11; y<=18; y++)\r
+    for (x=3; x<=20; x++)\r
+      drawchar (x,y,128);\r
+\r
+  charpic (4,14,player,east,2);\r
+  charpic (19,15,shot,east,1);\r
+  charpic (17,14,shot,east,0);\r
+  charpic (15,15,shot,east,1);\r
+  charpic (8,14,bigshot,east,0);\r
+\r
+  NEWSET;\r
+\r
+  if (!wantmore())\r
+    return;\r
+\r
+  centerwindow (20,20);\r
+  print ("Button 2 / alt key:\n");\r
+  print ("Allows you to move  \n");\r
+  print ("without changing the\n");\r
+  print ("direction you are   \n");\r
+  print ("facing.  Good for   \n");\r
+  print ("searching walls and \n");\r
+  print ("fighting retreats.  \n");\r
+\r
+  OLDSET;\r
+\r
+  for (y=11; y<=18; y++)\r
+    for (x=3; x<=20; x++)\r
+      if (y==15)\r
+       drawchar (x,y,129);\r
+      else if (y==16)\r
+       drawchar (x,y,131);\r
+      else\r
+       drawchar (x,y,128);\r
+  charpic (6,13,player,south,2);\r
+  sx=6;\r
+  sy=15;\r
+  print ("\35\35\36\36\37\37");\r
+\r
+  NEWSET;\r
+\r
+  if (!wantmore())\r
+    return;\r
+\r
+  centerwindow (20,20);\r
+  print ("\"P\" or \"space\" will \n");\r
+  print ("take a healing      \n");\r
+  print ("potion if you have  \n");\r
+  print ("one.  This restores \n");\r
+  print ("the body meter to   \n");\r
+  print ("full strength.  Keep\n");\r
+  print ("a sharp eye on the  \n");\r
+  print ("meter, because when \n");\r
+  print ("it runs out, you are\n");\r
+  print ("dead!               \n\n");\r
+  print ("\"B\" will cast a bolt\n");\r
+  print ("spell if you have   \n");\r
+  print ("any.  You can mow   \n");\r
+  print ("down a lot of       \n");\r
+  print ("monsters with a bit \n");\r
+  print ("of skill.           \n");\r
+\r
+  if (!wantmore())\r
+    return;\r
+\r
+  centerwindow (20,20);\r
+  print ("\"N\" or \"enter\" will \n");\r
+  print ("cast a nuke spell.  \n");\r
+  print ("This usually wipes  \n");\r
+  print ("out all the monsters\n");\r
+  print ("near you.  Consider \n");\r
+  print ("it a panic button   \n");\r
+  print ("when you are being  \n");\r
+  print ("mobbed by monsters! \n\n");\r
+\r
+  OLDSET;\r
+\r
+  print ("               \200\200\200\n");\r
+  print ("POTIONS:       \200\242\200\n");\r
+  print ("               \200\200\200\n");\r
+  print ("SCROLLS:       \200\243\200\n");\r
+  print (" (BOLTS/NUKES) \200\200\200\n");\r
+  print ("TREASURE:      \200\247\200\n");\r
+  print (" (POINTS)      \200\200\200\n");\r
+  print ("               \200\200\200\n");\r
+\r
+  NEWSET;\r
+\r
+  wantmore();\r
+\r
+};\r
+\r
+/*       */\r
+/* reset */\r
+/*       */\r
+void reset(void)\r
+{\r
+  centerwindow (18,1);\r
+  print ("reset game (y/n)?");\r
+  ch= get ();\r
+  if (ch=='y')\r
+    {\r
+      gamexit=killed;\r
+      playdone=true;\r
+    };\r
+};\r
+\r
+\r
+\r
+/*=========================================================================*/\r
+\r
+\r
+/*==============================*/\r
+/*                             */\r
+/* loadlevel / savelevel        */\r
+/* loads map level into memory, */\r
+/* and sets everything up.      */\r
+/*                             */\r
+/*==============================*/\r
+\r
+void loadlevel(void)\r
+{\r
+  int i;\r
+\r
+  classtype tokens[256-230]  =\r
+    {player,teleporter,goblin,skeleton,ogre,gargoyle,dragon,turbogre,\r
+     guns,gune,secretgate,nothing,nothing,nothing,nothing,nothing,\r
+     nothing,nothing,nothing,nothing,nothing,nothing,nothing,nothing,\r
+     nothing,nothing};\r
+\r
+  char filename[64],st[64];\r
+  int x,y,xx,yy,recs, btile;\r
+  char sm[4096],rle[4096];\r
+\r
+  strcpy (filename,"level");\r
+  itoa (level,st,10);\r
+  strcat (filename,st);\r
+  strcat (filename,".CA2");\r
+\r
+  LoadFile (filename,rle);\r
+  RLEExpand(&rle[4],&sm,4096);\r
+\r
+  numobj=0;\r
+  o[0].x=13;          /*just defaults if no player token is found*/\r
+  o[0].y=13;\r
+  o[0].stage=0;\r
+  o[0].delay=0;\r
+  o[0].dir=east;\r
+  o[0].oldx=0;\r
+  o[0].oldy=0;\r
+  o[0].oldtile=-1;\r
+\r
+\r
+  for (yy=0; yy<64; yy++)\r
+    for (xx=0; xx<64; xx++)\r
+      {\r
+       btile=sm[yy*64+xx];\r
+       if (btile<230)\r
+         background[yy+topoff][xx+leftoff]=btile;\r
+       else\r
+         {\r
+\r
+/*hit a monster token*/\r
+           background[yy+topoff][xx+leftoff]=blankfloor;\r
+           if (tokens[btile-230]==player)\r
+\r
+/*the player token determines where you start in level*/\r
+\r
+             {\r
+               o[0].x=xx+topoff;\r
+               o[0].y=yy+leftoff;\r
+             }\r
+            else\r
+\r
+/*monster tokens add to the object list*/\r
+\r
+             {\r
+               numobj++;\r
+               o[numobj].active=false;\r
+               o[numobj].class=tokens[btile-230];\r
+               o[numobj].x=xx+leftoff;\r
+               o[numobj].y=yy+topoff;\r
+               o[numobj].stage=0;\r
+               o[numobj].delay=0;\r
+               o[numobj].dir=(dirtype)(rndt()/64);  /*random 0-3*/\r
+               o[numobj].hp=objdef[o[numobj].class].hitpoints;\r
+               o[numobj].oldx=x;\r
+               o[numobj].oldy=y;\r
+               o[numobj].oldtile=-1;\r
+             };\r
+\r
+           };\r
+\r
+         };\r
+\r
+\r
+\r
+  originx = o[0].x-11;\r
+  originy = o[0].y-11;\r
+\r
+  shotpower=0;\r
+  for (y=topoff-1; y<65+topoff; y++)\r
+    for (x=leftoff-1; x<64+leftoff; x++)\r
+      view[y][x]=background[y][x];\r
+\r
+  sx=33;                  /*print the new level number on the right window*/\r
+  sy=1;\r
+  printint (level);\r
+  print (" ");          /*in case it went from double to single digit*/\r
+  restore();\r
+\r
+\r
+\r
+  for (i=0;i<6;i++)\r
+    saveitems[i] = items[i];\r
+  savescore = score;\r
+  saveo[0] = o[0];\r
+};\r
+\r
+\r
+/*==========================================================================*/\r
+\r
+\r
+#include "cat_play.c"\r
+\r
+#include "objects.c"\r
+\r
+\r
+/*\r
+==============\r
+=\r
+= drawside\r
+=\r
+==============\r
+*/\r
+void drawside ()\r
+{\r
+  int i;\r
+\r
+  for (sx=0;sx<40;sx++)\r
+    drawchar (sx,24,0);\r
+\r
+  for (sy=0;sy<24;sy++)\r
+    drawchar (39,sy,0);\r
+\r
+  drawwindow (24,0,38,23);  /*draw the right side window*/\r
+  print ("  level\n\nscore:\n\ntop  :\n\nk:\np:\nb:\nn:\n\n");\r
+  print (" shot power\n\n\n    body\n\n\n");\r
+  printhighscore();\r
+  printbody();\r
+  printshotpower();\r
+  printscore();\r
+  sx=33;                  /*print the new level number on the right window*/\r
+  sy=1;\r
+  printint (level);\r
+\r
+  drawpic (25*8,17*8,SIDEPIC);\r
+\r
+  for (i=1; i<=items[1] && i<11; i++)\r
+    drawchar (26+i,7,31);  /*key icon*/\r
+\r
+  for (i=1; i<=items[2] && i<11; i++)\r
+    drawchar (26+i,8,29);  /*potion icon*/\r
+\r
+  for (i=1; i<=items[3] && i<11; i++)\r
+    drawchar (26+i,9,30);  /*scroll icon*/\r
+\r
+  for (i=1; i<=items[5] && i<11; i++)\r
+    drawchar (26+i,10,30);  /*scroll icon*/\r
+}\r
+\r
+\r
+/*================================*/\r
+/*                               */\r
+/* playsetup                      */\r
+/* set up all data for a new game */\r
+/* does not start it playing      */\r
+/*                               */\r
+/*================================*/\r
+\r
+void playsetup()\r
+{\r
+  int i;\r
+\r
+  shotpower=0;\r
+  bar (0,0,23,23,0);\r
+\r
+  if (level==0)                        // not restarting a saved game\r
+  {\r
+    for (i=1; i<6; i++)\r
+      items[i]=0;\r
+    score=0;\r
+    level=1;\r
+    o[0].active = true;\r
+    o[0].class = player;\r
+    o[0].hp = 13;\r
+    o[0].dir=west;\r
+    o[0].stage=0;\r
+    o[0].delay=0;\r
+\r
+    drawside ();\r
+  /*give them a few items to start with*/\r
+\r
+    givenuke();\r
+    givenuke();\r
+    givebolt();\r
+    givebolt();\r
+    givebolt();\r
+    givepotion();\r
+    givepotion();\r
+    givepotion();\r
+  }\r
+  else\r
+    drawside ();\r
+\r
+};\r
+\r
+\r
+\r
+/*\r
+=============\r
+=\r
+= repaintscreen\r
+=\r
+=============\r
+*/\r
+\r
+void repaintscreen ()\r
+{\r
+  switch (gamestate)\r
+  {\r
+    case intitle:\r
+      drawpic (0,0,TITLEPIC);\r
+      break;\r
+    case ingame:\r
+      restore ();\r
+      drawside ();\r
+      printscore ();\r
+      sx=33;                  /*print the new level number on the right window*/\r
+      sy=1;\r
+      printint (level);\r
+      break;\r
+    case inscores:\r
+      restore ();\r
+      drawside ();\r
+      printscore ();\r
+      sx=33;                  /*print the new level number on the right window*/\r
+      sy=1;\r
+      printint (level);\r
+      indemo = demoplay;\r
+      break;\r
+    default:\r
+      sx=sy=10;\r
+      print ("Bad gamestate!");\r
+      clearkeys ();\r
+      get();\r
+  }\r
+\r
+}\r
+\r
+\r
+/*\r
+=============\r
+=\r
+= dofkeys\r
+=\r
+= Checks to see if an F-key is being pressed and handles it\r
+=\r
+=============\r
+*/\r
+\r
+void dofkeys (void)\r
+{\r
+  int i,handle;\r
+  char st2[10];\r
+  int key=bioskey(1)/256;\r
+  if (key==1)                  // make ESC into F10\r
+    key=0x44;\r
+  if (key<0x3b || key>0x44)\r
+    return;\r
+\r
+  switch (key)\r
+  {\r
+    case 0x3b:                 // F1\r
+      clearkeys ();\r
+      help ();\r
+      break;\r
+    case 0x3c:                 // F2\r
+      clearkeys ();\r
+      controlpanel ();\r
+      break;\r
+    case 0x3d:                 // F3\r
+      clearkeys ();\r
+      expwin (18,1);\r
+      print ("RESET GAME (Y/N)?");\r
+      ch=toupper(get());\r
+      if (ch=='Y')\r
+       resetgame = true;\r
+      break;\r
+\r
+    case 0x3e:                 // F4\r
+      clearkeys ();\r
+      expwin (22,4);\r
+      if (indemo != notdemo)\r
+      {\r
+       print ("Can't save game here!");\r
+       get ();\r
+       break;\r
+      }\r
+      print ("Save as game #(1-9):");\r
+      ch=toupper(get());\r
+      drawchar (sx,sy,ch);\r
+      if (ch<'1' || ch>'9')\r
+       break;\r
+      //\r
+      // save game\r
+      //\r
+      strcpy (str,"GAME0.CA2");\r
+      str[4]=ch;\r
+      if (_Verify(str))\r
+      {\r
+       print ("\nGame exists,\noverwrite (Y/N)?");\r
+       ch=get();\r
+       if (ch!='Y' && ch!='y')\r
+         break;\r
+       sx=leftedge;\r
+       print ("                    ");\r
+       sy--;\r
+       sx=leftedge;\r
+       print ("                    ");\r
+       sx=leftedge;\r
+       sy--;\r
+      }\r
+      if ((handle = open(str, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE)) == -1)\r
+       return;\r
+      write(handle, &saveitems, sizeof(items));\r
+      write(handle, &savescore, sizeof(score));\r
+      write(handle, &level, sizeof(level));\r
+      write(handle, &saveo[0],sizeof(o[0]));\r
+\r
+      close(handle);\r
+      print ("\nGame saved.  Hit F5\n");\r
+      print ("when you wish to\n");\r
+      print ("restart the game.");\r
+      get();\r
+      break;\r
+\r
+    case 0x3f:                 // F5\r
+      clearkeys ();\r
+      expwin (22,4);\r
+      print ("Load game #(1-9):");\r
+      ch=toupper(get());\r
+      drawchar (sx,sy,ch);\r
+      if (ch<'1' || ch>'9')\r
+       break;\r
+      //\r
+      // load game\r
+      //\r
+      strcpy (str,"GAME0.CA2");\r
+      str[4]=ch;\r
+      if ((handle = open(str, O_RDONLY | O_BINARY, S_IWRITE | S_IREAD)) == -1)\r
+      {\r
+       print ("\nGame not found.");\r
+       get ();\r
+       break;\r
+      }\r
+      read(handle, &items, sizeof(items));\r
+      read(handle, &score, sizeof(score));\r
+      read(handle, &level, sizeof(level));\r
+      read(handle, &o[0],sizeof(o[0]));\r
+      close(handle);\r
+      exitdemo = true;\r
+      if (indemo != notdemo)\r
+       playdone = true;\r
+      drawside ();             // draw score, icons, etc\r
+      leveldone = true;\r
+      break;\r
+    case 0x43:                 // F9\r
+      clearkeys ();\r
+      expwin (7,1);\r
+      print ("PAUSED");\r
+      get ();\r
+      break;\r
+    case 0x44:                 // F10\r
+      clearkeys ();\r
+      expwin (12,1);\r
+      print ("QUIT (Y/N)?");\r
+      ch=toupper(get());\r
+      if (ch=='Y')\r
+\r
+       _quit ("");\r
+      break;\r
+\r
+    default:\r
+      return;\r
+  }\r
+\r
+  clearold ();\r
+  clearkeys ();\r
+  repaintscreen ();\r
+}\r
+\r
+\r
+\r
+/*=========================================================================*/\r
+\r
+/*\r
+=============\r
+=\r
+= dotitlepage\r
+=\r
+=============\r
+*/\r
+\r
+void dotitlepage (void)\r
+{\r
+  int i;\r
+  drawpic (0,0,TITLEPIC);\r
+\r
+  gamestate=intitle;\r
+  for (i=0;i<300;i++)\r
+  {\r
+    WaitVBL ();\r
+    indemo = notdemo;\r
+    ctrl = ControlPlayer (1);\r
+    if (ctrl.button1 || ctrl.button2 || keydown[0x39])\r
+    {\r
+      level = 0;\r
+      exitdemo = true;\r
+      break;\r
+    }\r
+    indemo = demoplay;\r
+    if (bioskey (1))\r
+      dofkeys ();\r
+    if (exitdemo)\r
+      break;\r
+  }\r
+  gamestate=ingame;\r
+}\r
+\r
+\r
+/*=========================================================================*/\r
+\r
+/*\r
+=============\r
+=\r
+= doendpage\r
+=\r
+=============\r
+*/\r
+\r
+void doendpage (void)\r
+{\r
+  WaitEndSound();\r
+  drawpic (0,0,ENDPIC);\r
+  PlaySound (TREASURESND);\r
+  WaitEndSound();\r
+  PlaySound (TREASURESND);\r
+  WaitEndSound();\r
+  PlaySound (TREASURESND);\r
+  WaitEndSound();\r
+  PlaySound (TREASURESND);\r
+  WaitEndSound();\r
+\r
+  drawwindow (0,0,17,9);\r
+  print ("Congratulation! \n");\r
+  print ("One as skilled  \n");\r
+  print ("as yourself     \n");\r
+  print ("deserves the    \n");\r
+  print ("10,000,000 gold \n");\r
+  print ("you pulled out  \n");\r
+  print ("of the palace! ");\r
+  clearkeys();\r
+  get ();\r
+  drawwindow (0,0,17,9);\r
+  print ("Let us know what\n");\r
+  print ("you enjoyed     \n");\r
+  print ("about this game,\n");\r
+  print ("so we can give  \n");\r
+  print ("you more of it. \n");\r
+  print ("Thank you for   \n");\r
+  print ("playing!");\r
+  get ();\r
+\r
+}\r
+\r
+\r
+/*=========================================================================*/\r
+\r
+\r
+/*\r
+=============\r
+=\r
+= dodemo\r
+=\r
+= Shows a random demo\r
+=\r
+=============\r
+*/\r
+\r
+void dodemo (void)\r
+{\r
+  int i;\r
+\r
+  while (!exitdemo)\r
+  {\r
+    dotitlepage ();\r
+\r
+    if (exitdemo)\r
+      break;\r
+\r
+    i=random(NUMDEMOS)+1;\r
+    LoadDemo (i);\r
+    level=0;\r
+    playsetup ();\r
+    playloop ();\r
+    if (exitdemo)\r
+      break;\r
+\r
+    level = 0;\r
+    gamestate=inscores;\r
+    indemo = demoplay;\r
+    _showhighscores ();\r
+    for (i=0;i<500;i++)\r
+    {\r
+      WaitVBL ();\r
+      indemo = notdemo;\r
+      ctrl = ControlPlayer (1);\r
+      if (ctrl.button1 || ctrl.button2 || keydown[0x39])\r
+      {\r
+       exitdemo = true;\r
+       break;\r
+      }\r
+      if (bioskey (1))\r
+       dofkeys ();\r
+      if (exitdemo)\r
+       break;\r
+    }\r
+\r
+  }\r
+}\r
+\r
+/*=========================================================================*/\r
+\r
+/*\r
+============\r
+=\r
+= gameover\r
+=\r
+= End game, check for high score\r
+=\r
+============\r
+*/\r
+\r
+void gameover (void)\r
+{\r
+  int i;\r
+\r
+  expwin (11,4);\r
+  print ("\n GAME OVER\n     ");\r
+  WaitEndSound ();\r
+  for (i=0;i<120;i++)\r
+    WaitVBL ();\r
+  gamestate=inscores;\r
+  _checkhighscore ();\r
+  level = 0;\r
+  for (i=0;i<500;i++)\r
+  {\r
+        WaitVBL ();\r
+        ctrl = ControlPlayer (1);\r
+        if (ctrl.button1 || ctrl.button2 || keydown[0x39])\r
+               break;\r
+        if (bioskey (1))\r
+               dofkeys ();\r
+        if (exitdemo)\r
+               break;\r
+  }\r
+}\r
+\r
+\r
+/***************************************************************************/\r
+/***************************************************************************/\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     US_CheckParm() - checks to see if a string matches one of a set of\r
+//             strings. The check is case insensitive. The routine returns the\r
+//             index of the string that matched, or -1 if no matches were found\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+int\r
+US_CheckParm(char *parm,char **strings)\r
+{\r
+       char    cp,cs,\r
+                       *p,*s;\r
+       int             i;\r
+\r
+       while (!isalpha(*parm)) // Skip non-alphas\r
+               parm++;\r
+\r
+       for (i = 0;*strings && **strings;i++)\r
+       {\r
+               for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)\r
+               {\r
+                       cs = *s++;\r
+                       if (!cs)\r
+                               return(i);\r
+                       cp = *p++;\r
+\r
+                       if (isupper(cs))\r
+                               cs = tolower(cs);\r
+                       if (isupper(cp))\r
+                               cp = tolower(cp);\r
+               }\r
+       }\r
+       return(-1);\r
+}\r
+\r
+/***************************************************************************/\r
+/***************************************************************************/\r
+\r
+static char                    *EntryParmStrings[] = {"detour",0};\r
+\r
+/*=========================*/\r
+/*                        */\r
+/* m a i n   p r o g r a m */\r
+/*                        */\r
+/*=========================*/\r
+\r
+void main (void)\r
+{\r
+       boolean LaunchedFromShell = false;\r
+\r
+       if (stricmp(_argv[1], "/VER") == 0)\r
+       {\r
+               printf("The Catacomb\n");\r
+               printf("Copyright 1990-93 Softdisk Publishing\n");\r
+               printf("Version 1.02\n");\r
+               exit(0);\r
+       }\r
+\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               switch (US_CheckParm(_argv[i],EntryParmStrings))\r
+               {\r
+               case 0:\r
+                       LaunchedFromShell = true;\r
+                       break;\r
+               }\r
+       }\r
+#ifndef CATALOG\r
+       if (!LaunchedFromShell)\r
+       {\r
+               clrscr();\r
+               puts("You must type START at the DOS prompt to run THE CATACOMB.");\r
+               exit(0);\r
+       }\r
+#endif\r
+\r
+\r
+  initobjects();\r
+\r
+  memset (&priority,99,sizeof(priority));\r
+\r
+  priority[blankfloor]=0;\r
+  for (i=objdef[teleporter].firstchar; i<=objdef[teleporter].firstchar+20;i++)\r
+        priority[i]=0;\r
+  for (clvar=dead2; clvar<=dead5; clvar++)\r
+    for (i=objdef[clvar].firstchar; i<=objdef[clvar].firstchar+\r
+    objdef[clvar].size*objdef[clvar].size; i++)\r
+      priority[i]=0;           /*deadthing*/\r
+  for (i=152; i<=161; i++)\r
+    priority[i]=2;             /*shots*/\r
+  for (i=objdef[bigshot].firstchar; i<= objdef[bigshot].firstchar + 31; i++)\r
+    priority[i]=2;             /*bigshot*/\r
+  for (i=0; i<=tile2s-1; i++)\r
+    if (priority [i]==99)\r
+      priority[i]=3;           /*most 1*1 tiles are walls, etc*/\r
+  priority[167]=1;             // chest\r
+  for (i=tile2s; i<=maxpics; i++)\r
+    if (priority[i]==99)\r
+      priority[i]=4;           /*most bigger tiles are monsters*/\r
+  for (i=objdef[player].firstchar; i<= objdef[player].firstchar + 63; i++)\r
+    priority[i]=5;             /*player*/\r
+\r
+\r
+  side=0;\r
+\r
+  for (x=0; x<=85; x++)\r
+    {\r
+      for (y=0; y<=topoff-1; y++)\r
+       {\r
+         view[x][y]=solidwall;\r
+         view[x][85-y]=solidwall;\r
+         background[x][y]=solidwall;\r
+         background[x][85-y]=solidwall;\r
+       };\r
+               view[86][x]=solidwall;\r
+        };\r
+  for (y=11; y<=74; y++)\r
+        for (x=0; x<=leftoff-1; x++)\r
+               {\r
+       view[x][y]=solidwall;\r
+       view[85-x][y]=solidwall;\r
+       background[x][y]=solidwall;\r
+       background[85-x][y]=solidwall;\r
+               };\r
+\r
+\r
+//   puts ("CATACOMB II is executing");\r
+\r
+//  _dontplay = 1;     // no sounds for debugging and profiling\r
+\r
+  _numlevels = 30;\r
+  _maxplayers = 1;\r
+\r
+  _cgaok = true;\r
+  _egaok = true;\r
+  _vgaok = false;\r
+\r
+  _extension = "CA2";\r
+\r
+  _setupgame ();\r
+\r
+  expwin (33,13);\r
+  print ("  Softdisk Publishing presents\n\n");\r
+  print ("          The Catacomb\n\n");\r
+  print ("        By John Carmack\n\n");\r
+  print ("       Copyright 1990-93\n");\r
+  print ("      Softdisk Publishing");\r
+  print ("\n\n");\r
+  print ("\n\n");\r
+  print ("         Press a key:");\r
+  get();\r
+\r
+  clearkeys ();\r
+\r
+  screencentery = 11;\r
+  screencenterx = 11;\r
+\r
+  exitdemo = false;\r
+  level = 0;\r
+\r
+  while (1)                    // go until quit () is called\r
+  {\r
+        dodemo ();\r
+        playsetup ();\r
+        indemo = notdemo;\r
+        gamestate = ingame;\r
+        playloop ();\r
+        if (!indemo)\r
+        {\r
+               exitdemo = false;\r
+               if (level > numlevels)\r
+       doendpage ();           // finished all levels\r
+               gameover ();\r
+        }\r
+  }\r
+}\r
+\r
diff --git a/CATACOMB.PRJ b/CATACOMB.PRJ
new file mode 100644 (file)
index 0000000..fbb4b24
Binary files /dev/null and b/CATACOMB.PRJ differ
diff --git a/CATASM.ASM b/CATASM.ASM
new file mode 100644 (file)
index 0000000..af0a4d9
--- /dev/null
@@ -0,0 +1,828 @@
+; The Catacomb Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+       MASM\r
+\r
+       locals\r
+       .MODEL  SMALL,C\r
+\r
+       .DATA\r
+\r
+       EXTRN   xormask:WORD\r
+       EXTRN   oldtiles, view, background, originx, originy: WORD\r
+       EXTRN   priority: BYTE\r
+       EXTRN   obj, altobj: objtype\r
+       EXTRN   pics:WORD\r
+\r
+\r
+objtype        STRUC\r
+\r
+active dw      ?\r
+class  dw      ?\r
+x      db      ?\r
+y      db      ?\r
+stage  db      ?\r
+delay  db      ?\r
+dir    dw      ?\r
+hp     db      ?\r
+oldx   db      ?\r
+oldy   db      ?\r
+oldtile dw     ?\r
+filler1        db      ?\r
+\r
+think  db      ?\r
+contact db     ?\r
+solid  db      ?\r
+firstchar dw   ?\r
+psize  db      ?\r
+stages db      ?\r
+dirmask db     ?\r
+speed  dw      ?\r
+hitpoints db   ?\r
+damage         db      ?\r
+points         dw      ?\r
+filler2        db      ?,?\r
+\r
+objtype        ENDS\r
+\r
+obp    dw      ?               ;temporary for turbo's BP\r
+\r
+\r
+       .CODE\r
+\r
+;========================================================================\r
+\r
+;{=========================================}\r
+;{                                         }\r
+;{ DRAWOBJ                                 }\r
+;{ Draws the object to TILES in the proper }\r
+;{ direction and state.                    }\r
+;{                                         }\r
+;{=========================================}\r
+;\r
+;Procedure DrawObj;\r
+;var\r
+;  objpri,bsize,tilenum:integer;\r
+;Begin\r
+;  tilenum:=obj.firstchar + obj.size * obj.size\r
+;  *((integer(obj.dir) and obj.dirmask) * obj.stages + obj.stage);\r
+;  obj.oldtile:= tilenum;\r
+;  obj.oldx:=obj.x;\r
+;  obj.oldy:=obj.y;\r
+;  objpri:=priority[tilenum];\r
+;  For y:=obj.y to obj.y+obj.size-1 do\r
+;    for x:=obj.x to obj.x+obj.size-1 do\r
+;      Begin\r
+;      if priority[view[y,x]]<=objpri then\r
+;        view[y,x]:=tilenum;\r
+;      inc(tilenum);\r
+;      end;\r
+;End;\r
+;\r
+;\r
+squares        db      0,1,4,9,16,25,36,49,64\r
+\r
+table86 dw    0,  86, 172, 258, 344, 430, 516, 602, 688, 774, 860, 946,1032,1118\r
+    dw 1204,1290,1376,1462,1548,1634,1720,1806,1892,1978,2064,2150,2236,2322\r
+    dw 2408,2494,2580,2666,2752,2838,2924,3010,3096,3182,3268,3354,3440,3526\r
+    dw 3612,3698,3784,3870,3956,4042,4128,4214,4300,4386,4472,4558,4644,4730\r
+    dw 4816,4902,4988,5074,5160,5246,5332,5418,5504,5590,5676,5762,5848,5934\r
+    dw 6020,6106,6192,6278,6364,6450,6536,6622,6708,6794,6880,6966,7052,7138\r
+    dw 7224,7310,7396\r
+\r
+\r
+drawobj        PROC   NEAR\r
+       USES    SI,DI\r
+       PUBLIC drawobj\r
+\r
+       mov     al,BYTE PTR obj.dir\r
+       and     al,obj.dirmask\r
+       mul     obj.stages\r
+       add     al,obj.stage\r
+       mov     cl,al\r
+\r
+       mov     bl,obj.psize\r
+       xor     bh,bh\r
+       mov     al,cs:[squares+bx]\r
+\r
+       mul     cl\r
+\r
+       add     ax,obj.firstchar\r
+\r
+       mov     SI,ax\r
+       mov     obj.oldtile,SI          ;SI holds the first tile to put in\r
+\r
+       mov     dl,[priority+SI]        ;entire object has same priority\r
+                                       ;priority is saved in DL\r
+\r
+       mov     bl,obj.y\r
+       mov     obj.oldy,bl\r
+       xor     bh,bh\r
+       shl     bx,1\r
+       mov     ax,cs:[table86+bx] ;View is 86*86\r
+       mov     bl,obj.x\r
+       mov     obj.oldx,bl\r
+       xor     bh,bh\r
+       add     ax,bx           ;calculate origin's offset in VIEW\r
+       shl     ax,1            ;becuase view is WORD width\r
+       mov     di,ax           ;DI will point into VIEW\r
+\r
+       mov     al,obj.psize    ;throughout loop\r
+       xor     ah,ah\r
+\r
+       mov     dh,al           ;do this many lines\r
+@@yloop:\r
+       mov     cx,ax           ;do this many characters / line\r
+\r
+@@xloop:\r
+       mov     bx,[view+DI]\r
+       cmp     dl,[priority+bx] ;check tiles priority level\r
+       jb      @@next          ;don't draw if lower than what's there\r
+       mov     [view+di],si\r
+\r
+@@next:\r
+       inc     si\r
+       inc     di\r
+       inc     di\r
+       loop    @@xloop\r
+\r
+       sub     di,ax\r
+       sub     di,ax\r
+       add     di,86*2         ;position destination at start of next line\r
+\r
+       dec     dh              ;any more lines to do?\r
+       jnz     @@yloop\r
+\r
+       ret\r
+\r
+DrawObj        ENDP\r
+\r
+;========================================================================\r
+\r
+;{=======================================}\r
+;{                                       }\r
+;{ ERASEOBJ                              }\r
+;{ Erases the current object by copying  }\r
+;{ the background onto the view where the}\r
+;{ object is standing                    }\r
+;{                                       }\r
+;{=======================================}\r
+;\r
+;Procedure EraseObj;\r
+;var\r
+;  objpri,bsize,tilenum:integer;\r
+;Begin\r
+;  tilenum:=obj.oldtile;\r
+;  For y:=obj.oldy to obj.oldy+obj.size-1 do\r
+;    for x:=obj.oldx to obj.oldx+obj.size-1 do\r
+;      Begin\r
+;      if view[y,x]=tilenum then\r
+;        view[y,x]:=background[y,x];\r
+;      inc(tilenum);\r
+;      end;\r
+;End;\r
+;\r
+eraseobj PROC   NEAR\r
+       USES    SI,DI\r
+       PUBLIC eraseobj\r
+\r
+       mov     SI,obj.oldtile ;only erase chars that match what was\r
+                               ;drawn by the last drawobj\r
+       mov     bl,obj.oldy\r
+       xor     bh,bh\r
+       shl     bx,1\r
+       mov     ax,cs:[table86+bx] ;View is 86*86\r
+       mov     bl,obj.oldx\r
+       xor     bh,bh\r
+       add     ax,bx           ;calculate origin's offset in VIEW\r
+       shl     ax,1            ;becuase view is WORD width\r
+       mov     di,ax           ;DI will point into VIEW\r
+\r
+       mov     al,obj.psize    ;throughout loop\r
+       xor     ah,ah\r
+\r
+       mov     dh,al           ;do this many lines\r
+@@yloop:\r
+       mov     cx,ax           ;do this many characters / line\r
+\r
+@@xloop:\r
+       cmp     si,[view+DI]\r
+       jne     @@next          ;don't erase if its not part of the shape\r
+       mov     bx,[background+di]\r
+       mov     [view+di],bx    ;erase it\r
+\r
+@@next:\r
+       inc     si\r
+       inc     di\r
+       inc     di\r
+       loop    @@xloop\r
+\r
+       sub     di,ax\r
+       sub     di,ax\r
+       add     di,86*2         ;position destination at start of next line\r
+\r
+       dec     dh              ;any more lines to do?\r
+       jnz     @@yloop\r
+\r
+       ret\r
+\r
+EraseObj       ENDP\r
+\r
+;========================================================================\r
+\r
+;{====================}\r
+;{                    }\r
+;{ DoAll              }\r
+;{ The main play loop }\r
+;{                    }\r
+;{====================}\r
+;\r
+;Procedure Doall;\r
+;begin\r
+;  Repeat  {until leveldone or playdone}\r
+;    For objecton:=numobj downto 0 do\r
+;      Begin\r
+;      move (o[objecton],obj.active,sizeof(o[objecton]) );\r
+;      if obj.class<>nothing then {class=nothing means it was killed}\r
+;        Begin\r
+;          move (ObjDef[obj.class],obj.think,sizeof(objdef[obj.class]) );\r
+;          if obj.active then\r
+;            DoActive\r
+;          else\r
+;            DoInactive;\r
+;        end;\r
+;      end;\r
+;   refresh;\r
+;   inc (frameon);\r
+;  until leveldone or playdone;\r
+;end;\r
+\r
+       .DATA\r
+\r
+       EXTRN   o:objtype\r
+       EXTRN   objdef:WORD     ;actually the second half of objtype record\r
+       EXTRN   frameon:WORD\r
+       EXTRN   numobj:WORD\r
+       EXTRN   objecton:WORD\r
+       EXTRN   leveldone:BYTE\r
+       EXTRN   playdone:BYTE\r
+\r
+       .CODE\r
+\r
+       EXTRN   refresh:NEAR\r
+       EXTRN   doactive:NEAR\r
+       EXTRN   doinactive:NEAR\r
+\r
+doall  PROC    NEAR\r
+       USES    SI,DI\r
+       PUBLIC  doall\r
+\r
+@@repeat:\r
+       mov     ax,[numobj]\r
+       mov     [objecton],ax\r
+@@forobjecton:\r
+       mov     si,[objecton]\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1            ;o[] is 16 bytes wide\r
+       add     si,OFFSET o\r
+       mov     di,OFFSET obj\r
+       push    ds\r
+       pop     es\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw                   ;copy 16 bytes\r
+\r
+       mov     al,[BYTE PTR obj.class]\r
+       or      al,al\r
+       jz      @@next\r
+       xor     ah,ah\r
+       mov     si,ax\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1            ;objdef is 16 bytes wide\r
+       add     si,OFFSET objdef\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw                   ;copy second 16 bytes into obj\r
+\r
+       mov     al,[BYTE PTR obj.active]\r
+       or      al,al\r
+       jnz     @@isactive\r
+       call    DoInactive\r
+       jmp     @@next\r
+@@isactive:\r
+       call    DoActive\r
+\r
+@@next:\r
+       mov     al,[leveldone]  ; check end\r
+       or      al,al\r
+       jnz     @@done\r
+       mov     al,[playdone]\r
+       or      al,al\r
+       jnz     @@done\r
+\r
+       dec     [objecton]\r
+       jns     @@forobjecton   ; END for\r
+\r
+       call    refresh\r
+       inc     [frameon]\r
+\r
+       mov     al,[leveldone]  ; UNTIL\r
+       or      al,al\r
+       jnz     @@done\r
+       mov     al,[playdone]\r
+       or      al,al\r
+       jz      @@repeat\r
+\r
+@@done:\r
+       ret\r
+\r
+DoAll  ENDP\r
+\r
+;========================================================================\r
+\r
+\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+;\r
+; Move EGA tiles into EGA memory at "EGATileLoc"!\r
+;\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+EGAmove        PROC    NEAR\r
+       USES    SI,DI\r
+       PUBLIC  EGAmove\r
+\r
+       mov     dx,3c4h         ;turn MapMask register on!\r
+       mov     al,2\r
+       out     dx,al\r
+       inc     dx\r
+\r
+       mov     ax,0A400h\r
+       mov     es,ax   ;ES:DI = just after screen in latch memory\r
+       xor     di,di\r
+\r
+       mov     ax,WORD PTR [pics+2]\r
+       mov     ds,ax\r
+       xor     si,si   ;DS:SI = start of tiles!\r
+\r
+       cld\r
+       mov     cx,2048\r
+\r
+@@0:\r
+       mov     ah,1b   ;start at bitplane 0\r
+       mov     bx,di\r
+\r
+@@2:\r
+       mov     al,ah\r
+       out     dx,al   ;select new bitplane!\r
+\r
+       movsb\r
+       movsb\r
+       movsb\r
+       movsb\r
+       movsb\r
+       movsb\r
+       movsb\r
+       movsb\r
+\r
+       mov     di,bx\r
+\r
+       shl     ah,1\r
+       test    ah,1111b\r
+       jnz     @@2     ;do all bitplanes in shape\r
+\r
+       add     di,8\r
+\r
+       loop    @@0     ;do all tiles\r
+\r
+       mov     al,1111b\r
+       out     dx,al   ;select all bitplanes\r
+\r
+       mov     ax,ss   ;reset DATA segment\r
+       mov     ds,ax\r
+\r
+       ret\r
+\r
+EGAMove        ENDP\r
+\r
+;=======================================================================\r
+\r
+;=========\r
+;\r
+; CGAREFRESH redraws the tiles that have changed in the tiled screen area\r
+;\r
+;=========\r
+\r
+cgarefresh PROC        NEAR\r
+       USES    SI,DI\r
+       PUBLIC  cgarefresh\r
+\r
+       mov     obp,bp          ;save off turbo's BP\r
+\r
+       mov     ax,0B800h       ;start of CGA memory\r
+       mov     es,ax\r
+       cld                     ;just in case\r
+       mov     cx,OFFSET @@next        ;so it can be JMPd to\r
+\r
+       mov     ax,originy\r
+       mov     bl,86           ;View is 86*86 so no clipping\r
+       mul     bl\r
+       add     ax,originx      ;calculate origin's offset in VIEW\r
+       shl     ax,1            ;becuase view is WORD width\r
+       mov     bp,ax           ;BP will point into VIEW\r
+       mov     dx,ax\r
+       add     dx,48           ;when BP=DX, one row has been filled\r
+\r
+       xor     bx,bx           ;fast mov bx,0\r
+\r
+@@check:\r
+       mov     ax,view[bp]     ;load the current tile\r
+       cmp     ax,oldtiles[bx] ;compare it with the old tile\r
+       jne     @@drawone               ;if different, redraw\r
+@@next:\r
+       add     bx,2            ;next oldtile\r
+       add     bp,2            ;next view tile\r
+       cmp     bp,dx           ;has an entire row from VIEW been drawn?\r
+       jne     @@check\r
+\r
+       cmp     bx,24*24*2      ;have all tiles been drawn?\r
+       je      @@done\r
+       add     bp,124          ;point it to the start of the next row\r
+       add     dx,172          ;point to end of next row\r
+       jmp     @@check\r
+\r
+@@done:\r
+       mov     bp,obp          ;restore turbo's BP\r
+       ret\r
+\r
+@@drawone:\r
+       mov     oldtiles[bx],ax ;store the tile back to the oldtiles\r
+       mov     di,word ptr cs:CGAtileloc[bx]   ;set di to screen address\r
+       shl     ax,1            ;character number * 16 = start of data\r
+       shl     ax,1\r
+       shl     ax,1\r
+       shl     ax,1\r
+       mov     si,ax\r
+       mov     ds,[pics+2]     ;segment of pics (para aligned)\r
+       movsw                   ;load in a row of the tile's picture\r
+       add     di,1FFEh\r
+       movsw\r
+       sub     di,1FB2h\r
+       movsw\r
+       add     di,1FFEh\r
+       movsw\r
+       sub     di,1FB2h\r
+       movsw\r
+       add     di,1FFEh\r
+       movsw\r
+       sub     di,1FB2h\r
+       movsw\r
+       add     di,1FFEh\r
+       movsw\r
+       mov     ax,ss\r
+       mov     ds,ax           ;restore turbo's data segment\r
+       jmp     CX              ;CX holds OFFSET NEXT\r
+\r
+\r
+CgaRefresh ENDP\r
+\r
+\r
+;=======================================================================\r
+\r
+;=========\r
+;\r
+; EGAREFRESH redraws the tiles that have changed in the tiled screen area\r
+;\r
+;=========\r
+\r
+egarefresh PROC        NEAR\r
+       USES    SI,DI\r
+       PUBLIC  egarefresh\r
+\r
+       mov     obp,bp          ;save off turbo's BP\r
+\r
+       mov     ax,105h\r
+       mov     dx,3ceh         ;set write mode 1\r
+       out     dx,ax\r
+\r
+       mov     ax,0A000h       ;start of EGA memory\r
+       mov     es,ax\r
+       cld                     ;just in case\r
+       mov     cx,OFFSET @@next        ;so it can be JMPd to\r
+\r
+       mov     ax,originy\r
+       mov     bl,86           ;View is 86*86 so no clipping\r
+       mul     bl\r
+       add     ax,originx      ;calculate origin's offset in VIEW\r
+       shl     ax,1            ;becuase view is WORD width\r
+       mov     bp,ax           ;BP will point into VIEW\r
+       mov     dx,ax\r
+       add     dx,48           ;when BP=DX, one row has been filled\r
+\r
+       xor     bx,bx           ;fast mov bx,0\r
+\r
+@@check:\r
+       mov     ax,view[bp]     ;load the current tile\r
+       cmp     ax,oldtiles[bx] ;compare it with the old tile\r
+       jne     @@drawone               ;if different, redraw\r
+@@next:\r
+       add     bx,2            ;next oldtile\r
+       add     bp,2            ;next view tile\r
+       cmp     bp,dx           ;has an entire row from VIEW been drawn?\r
+       jne     @@check\r
+\r
+       cmp     bx,24*24*2      ;have all tiles been drawn?\r
+       je      @@done\r
+       add     bp,124          ;point it to the start of the next row\r
+       add     dx,172          ;point to end of next row\r
+       jmp     @@check\r
+\r
+@@done:\r
+       mov     bp,obp          ;restore turbo's BP\r
+       ret\r
+\r
+@@drawone:\r
+       mov     oldtiles[bx],ax ;store the tile back to the oldtiles\r
+       mov     di,word ptr cs:EGAtileloc[bx]   ;set di to screen address\r
+       shl     ax,1            ;character number * 8 = start of data\r
+       shl     ax,1\r
+       shl     ax,1\r
+       add     ax,4000h        ;because the ega pics are in same bank as screen\r
+       mov     si,ax\r
+\r
+       mov     ax,es\r
+       mov     ds,ax           ;pics are just later in screen memory\r
+\r
+       movsb                   ;load in a row of the tile's picture\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax           ;restore turbo's data segment\r
+       jmp     CX              ;CX holds OFFSET NEXT\r
+\r
+\r
+EgaRefresh ENDP\r
+\r
+\r
+dataseg\r
+\r
+linetables     dw      0,0\r
+\r
+blockingtiles  db      128 dup (0)\r
+               db      1,1,1,1,1,1,1\r
+               db      2048 dup (0)\r
+codeseg\r
+\r
+;===========================================================================\r
+\r
+if 0\r
+;=========\r
+;\r
+; EGAREFRESH with hidden area removal\r
+;\r
+;=========\r
+\r
+egarefresh PROC        NEAR\r
+       USES    SI,DI\r
+       PUBLIC  egarefresh\r
+\r
+       mov     obp,bp          ;save off turbo's BP\r
+\r
+       mov     ax,105h\r
+       mov     dx,3ceh         ;set write mode 1\r
+       out     dx,ax\r
+\r
+       mov     ax,0A000h       ;start of EGA memory\r
+       mov     es,ax\r
+       cld                     ;just in case\r
+       mov     cx,OFFSET @@next        ;so it can be JMPd to\r
+\r
+       mov     ax,originy\r
+       mov     bl,86           ;View is 86*86 so no clipping\r
+       mul     bl\r
+       add     ax,originx      ;calculate origin's offset in VIEW\r
+       shl     ax,1            ;becuase view is WORD width\r
+       mov     bp,ax           ;BP will point into VIEW\r
+       mov     dx,ax\r
+       add     dx,48           ;when BP=DX, one row has been filled\r
+\r
+       xor     bx,bx           ;fast mov bx,0\r
+\r
+@@check:\r
+       mov     cx,view[bp]     ;load the current tile\r
+;\r
+; check for blocking tiles\r
+;\r
+       mov     ds,[linetables+2]\r
+       mov     si,[bx] ;start of blocking table\r
+@@checkblock:\r
+       lodsw\r
+       test    ax,ax\r
+       jz      @@cansee        ;0 is end of offset list\r
+       add     si,bx           ;si holds tile offset from map start\r
+       mov     si,[ss:view+si]\r
+       cmp     [BYTE PTR ss:blockingtiles+si],0\r
+       jz      @@checkblock\r
+\r
+       xor     cx,cx           ;can't see it, draw black\r
+\r
+@@cansee:\r
+       mov     ax,ss\r
+       mov     ds,ax           ;restore data segment\r
+       mov     ax,cx\r
+\r
+       cmp     ax,oldtiles[bx] ;compare it with the old tile\r
+       jne     @@drawone               ;if different, redraw\r
+@@next:\r
+       add     bx,2            ;next oldtile\r
+       add     bp,2            ;next view tile\r
+       cmp     bp,dx           ;has an entire row from VIEW been drawn?\r
+       jne     @@check\r
+\r
+       cmp     bx,24*24*2      ;have all tiles been drawn?\r
+       je      @@done\r
+       add     bp,124          ;point it to the start of the next row\r
+       add     dx,172          ;point to end of next row\r
+       jmp     @@check\r
+\r
+@@done:\r
+       mov     bp,obp          ;restore turbo's BP\r
+       ret\r
+\r
+@@drawone:\r
+       mov     oldtiles[bx],ax ;store the tile back to the oldtiles\r
+       mov     di,word ptr cs:EGAtileloc[bx]   ;set di to screen address\r
+       shl     ax,1            ;character number * 8 = start of data\r
+       shl     ax,1\r
+       shl     ax,1\r
+       add     ax,4000h        ;because the ega pics are in same bank as screen\r
+       mov     si,ax\r
+\r
+       mov     ax,es\r
+       mov     ds,ax           ;pics are just later in screen memory\r
+\r
+       movsb                   ;load in a row of the tile's picture\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+       add     di,39\r
+       movsb\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax           ;restore turbo's data segment\r
+       jmp     CX              ;CX holds OFFSET NEXT\r
+\r
+\r
+EgaRefresh ENDP\r
+\r
+endif\r
+\r
+;=======================================================================\r
+\r
+\r
+;=========\r
+;\r
+; TILELOC has the offsets from $B800 of all 24*24 tiles\r
+;\r
+;=========\r
+\r
+CGAtileloc     label   word\r
+    dw    0,   2,   4,   6,   8,  10,  12,  14,  16,  18,  20,  22,  24,  26\r
+    dw   28,  30,  32,  34,  36,  38,  40,  42,  44,  46, 320, 322, 324, 326\r
+    dw  328, 330, 332, 334, 336, 338, 340, 342, 344, 346, 348, 350, 352, 354\r
+    dw  356, 358, 360, 362, 364, 366, 640, 642, 644, 646, 648, 650, 652, 654\r
+    dw  656, 658, 660, 662, 664, 666, 668, 670, 672, 674, 676, 678, 680, 682\r
+    dw  684, 686, 960, 962, 964, 966, 968, 970, 972, 974, 976, 978, 980, 982\r
+    dw  984, 986, 988, 990, 992, 994, 996, 998,1000,1002,1004,1006,1280,1282\r
+    dw 1284,1286,1288,1290,1292,1294,1296,1298,1300,1302,1304,1306,1308,1310\r
+    dw 1312,1314,1316,1318,1320,1322,1324,1326,1600,1602,1604,1606,1608,1610\r
+    dw 1612,1614,1616,1618,1620,1622,1624,1626,1628,1630,1632,1634,1636,1638\r
+    dw 1640,1642,1644,1646,1920,1922,1924,1926,1928,1930,1932,1934,1936,1938\r
+    dw 1940,1942,1944,1946,1948,1950,1952,1954,1956,1958,1960,1962,1964,1966\r
+    dw 2240,2242,2244,2246,2248,2250,2252,2254,2256,2258,2260,2262,2264,2266\r
+    dw 2268,2270,2272,2274,2276,2278,2280,2282,2284,2286,2560,2562,2564,2566\r
+    dw 2568,2570,2572,2574,2576,2578,2580,2582,2584,2586,2588,2590,2592,2594\r
+    dw 2596,2598,2600,2602,2604,2606,2880,2882,2884,2886,2888,2890,2892,2894\r
+    dw 2896,2898,2900,2902,2904,2906,2908,2910,2912,2914,2916,2918,2920,2922\r
+    dw 2924,2926,3200,3202,3204,3206,3208,3210,3212,3214,3216,3218,3220,3222\r
+    dw 3224,3226,3228,3230,3232,3234,3236,3238,3240,3242,3244,3246,3520,3522\r
+    dw 3524,3526,3528,3530,3532,3534,3536,3538,3540,3542,3544,3546,3548,3550\r
+    dw 3552,3554,3556,3558,3560,3562,3564,3566,3840,3842,3844,3846,3848,3850\r
+    dw 3852,3854,3856,3858,3860,3862,3864,3866,3868,3870,3872,3874,3876,3878\r
+    dw 3880,3882,3884,3886,4160,4162,4164,4166,4168,4170,4172,4174,4176,4178\r
+    dw 4180,4182,4184,4186,4188,4190,4192,4194,4196,4198,4200,4202,4204,4206\r
+    dw 4480,4482,4484,4486,4488,4490,4492,4494,4496,4498,4500,4502,4504,4506\r
+    dw 4508,4510,4512,4514,4516,4518,4520,4522,4524,4526,4800,4802,4804,4806\r
+    dw 4808,4810,4812,4814,4816,4818,4820,4822,4824,4826,4828,4830,4832,4834\r
+    dw 4836,4838,4840,4842,4844,4846,5120,5122,5124,5126,5128,5130,5132,5134\r
+    dw 5136,5138,5140,5142,5144,5146,5148,5150,5152,5154,5156,5158,5160,5162\r
+    dw 5164,5166,5440,5442,5444,5446,5448,5450,5452,5454,5456,5458,5460,5462\r
+    dw 5464,5466,5468,5470,5472,5474,5476,5478,5480,5482,5484,5486,5760,5762\r
+    dw 5764,5766,5768,5770,5772,5774,5776,5778,5780,5782,5784,5786,5788,5790\r
+    dw 5792,5794,5796,5798,5800,5802,5804,5806,6080,6082,6084,6086,6088,6090\r
+    dw 6092,6094,6096,6098,6100,6102,6104,6106,6108,6110,6112,6114,6116,6118\r
+    dw 6120,6122,6124,6126,6400,6402,6404,6406,6408,6410,6412,6414,6416,6418\r
+    dw 6420,6422,6424,6426,6428,6430,6432,6434,6436,6438,6440,6442,6444,6446\r
+    dw 6720,6722,6724,6726,6728,6730,6732,6734,6736,6738,6740,6742,6744,6746\r
+    dw 6748,6750,6752,6754,6756,6758,6760,6762,6764,6766,7040,7042,7044,7046\r
+    dw 7048,7050,7052,7054,7056,7058,7060,7062,7064,7066,7068,7070,7072,7074\r
+    dw 7076,7078,7080,7082,7084,7086,7360,7362,7364,7366,7368,7370,7372,7374\r
+    dw 7376,7378,7380,7382,7384,7386,7388,7390,7392,7394,7396,7398,7400,7402\r
+    dw 7404,7406\r
+\r
+EGAtileloc     label   word\r
+    dw    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13\r
+    dw   14,  15,  16,  17,  18,  19,  20,  21,  22,  23, 320, 321, 322, 323\r
+    dw  324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337\r
+    dw  338, 339, 340, 341, 342, 343, 640, 641, 642, 643, 644, 645, 646, 647\r
+    dw  648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661\r
+    dw  662, 663, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971\r
+    dw  972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983,1280,1281\r
+    dw 1282,1283,1284,1285,1286,1287,1288,1289,1290,1291,1292,1293,1294,1295\r
+    dw 1296,1297,1298,1299,1300,1301,1302,1303,1600,1601,1602,1603,1604,1605\r
+    dw 1606,1607,1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,1618,1619\r
+    dw 1620,1621,1622,1623,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929\r
+    dw 1930,1931,1932,1933,1934,1935,1936,1937,1938,1939,1940,1941,1942,1943\r
+    dw 2240,2241,2242,2243,2244,2245,2246,2247,2248,2249,2250,2251,2252,2253\r
+    dw 2254,2255,2256,2257,2258,2259,2260,2261,2262,2263,2560,2561,2562,2563\r
+    dw 2564,2565,2566,2567,2568,2569,2570,2571,2572,2573,2574,2575,2576,2577\r
+    dw 2578,2579,2580,2581,2582,2583,2880,2881,2882,2883,2884,2885,2886,2887\r
+    dw 2888,2889,2890,2891,2892,2893,2894,2895,2896,2897,2898,2899,2900,2901\r
+    dw 2902,2903,3200,3201,3202,3203,3204,3205,3206,3207,3208,3209,3210,3211\r
+    dw 3212,3213,3214,3215,3216,3217,3218,3219,3220,3221,3222,3223,3520,3521\r
+    dw 3522,3523,3524,3525,3526,3527,3528,3529,3530,3531,3532,3533,3534,3535\r
+    dw 3536,3537,3538,3539,3540,3541,3542,3543,3840,3841,3842,3843,3844,3845\r
+    dw 3846,3847,3848,3849,3850,3851,3852,3853,3854,3855,3856,3857,3858,3859\r
+    dw 3860,3861,3862,3863,4160,4161,4162,4163,4164,4165,4166,4167,4168,4169\r
+    dw 4170,4171,4172,4173,4174,4175,4176,4177,4178,4179,4180,4181,4182,4183\r
+    dw 4480,4481,4482,4483,4484,4485,4486,4487,4488,4489,4490,4491,4492,4493\r
+    dw 4494,4495,4496,4497,4498,4499,4500,4501,4502,4503,4800,4801,4802,4803\r
+    dw 4804,4805,4806,4807,4808,4809,4810,4811,4812,4813,4814,4815,4816,4817\r
+    dw 4818,4819,4820,4821,4822,4823,5120,5121,5122,5123,5124,5125,5126,5127\r
+    dw 5128,5129,5130,5131,5132,5133,5134,5135,5136,5137,5138,5139,5140,5141\r
+    dw 5142,5143,5440,5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451\r
+    dw 5452,5453,5454,5455,5456,5457,5458,5459,5460,5461,5462,5463,5760,5761\r
+    dw 5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775\r
+    dw 5776,5777,5778,5779,5780,5781,5782,5783,6080,6081,6082,6083,6084,6085\r
+    dw 6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096,6097,6098,6099\r
+    dw 6100,6101,6102,6103,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409\r
+    dw 6410,6411,6412,6413,6414,6415,6416,6417,6418,6419,6420,6421,6422,6423\r
+    dw 6720,6721,6722,6723,6724,6725,6726,6727,6728,6729,6730,6731,6732,6733\r
+    dw 6734,6735,6736,6737,6738,6739,6740,6741,6742,6743,7040,7041,7042,7043\r
+    dw 7044,7045,7046,7047,7048,7049,7050,7051,7052,7053,7054,7055,7056,7057\r
+    dw 7058,7059,7060,7061,7062,7063,7360,7361,7362,7363,7364,7365,7366,7367\r
+    dw 7368,7369,7370,7371,7372,7373,7374,7375,7376,7377,7378,7379,7380,7381\r
+    dw 7382,7383\r
+\r
+\r
+       END\r
+\r
+\r
diff --git a/CATED.C b/CATED.C
new file mode 100644 (file)
index 0000000..8ab26bf
--- /dev/null
+++ b/CATED.C
@@ -0,0 +1,430 @@
+/* The Catacomb Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#if 0\r
+\r
+/*====================*/\r
+/*                   */\r
+/* editorloop         */\r
+/* the editor mode... */\r
+/*                   */\r
+/*====================*/\r
+\r
+void editorloop()\r
+\r
+label\r
+  cmdover;\r
+\r
+const\r
+  samplepics : array[1..12] of string[13] =\r
+    ("\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80",\r
+     "\x80\x80\x81\x81\x87\x80\x80\x80\x87\x87\xb1\x80\x80",\r
+     "\x80\x81\x81\x81\x81\x87\x80\x87\x87\x87\x87\xb1\x80",\r
+     "\x80\x81\x81\x81\x81\x82\x80\x87\xb2\xb3\xb4\xac\x80",\r
+     "\x80\x86\x81\x81\x85\x84\x80\xb0\x87\x87\xaf\xae\x80",\r
+     "\x80\x80\x86\x83\x84\x80\x80\x80\xb0\xad\xae\x80\x80",\r
+     "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80",\r
+     "\x80\xa2\x80\xa3\x80\xa4\x80\xa7\x80\xa5\x80\xa6\x80",\r
+     "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80",\r
+     "\x80\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\x80",\r
+     "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80",\r
+     "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80");\r
+\r
+var\r
+  drawtile:integer;\r
+  ltx,lty,ltt,x,y,i:integer;\r
+  dor: dirtype;\r
+  b1,b2: boolean;\r
+/*$i-*/\r
+\r
+/*                           */\r
+/*                           */\r
+/* loadlevel                    */\r
+/* loads map level into memory, */\r
+/* nothing more                 */\r
+/*                           */\r
+/*                           */\r
+\r
+procedure loadlevel;\r
+label\r
+  tryopen,fileread;\r
+var\r
+  filename : string;\r
+  st: string[3];\r
+  x,y,xx,yy,recs, btile : integer;\r
+  iofile: file;\r
+  tile: byte;\r
+  sm : array [0..4095] of byte;\r
+\r
+{\r
+  str(level:1,st);\r
+  filename=concat ('level',st,'.cat');\r
+\r
+tryopen:\r
+\r
+  assign (iofile,filename);\r
+  reset (iofile,1);\r
+  if ioresult<>0\r
+/*create a blank level for the editor*/\r
+      {\r
+       for x=0 to 63 do\r
+         for y=0 to 63 do\r
+           background[y+topoff,x+leftoff]=blankfloor;\r
+       for x=0 to 63 do\r
+         {\r
+           background[topoff,x]=131;     /*perspective north wall*/\r
+           background[topoff+63,x]=129;  /*solid south wall*/\r
+           background[x,leftoff]=130;    /*perspective west wall*/\r
+           background[x,leftoff+63]=129; /*solid east wall*/\r
+         };\r
+       background [topoff,leftoff]=133;  /*perspective nw corner*/\r
+        goto fileread;\r
+      }\r
+\r
+    else\r
+\r
+  blockread (iofile,sm,4096,recs);\r
+  close (iofile);\r
+\r
+  numobj=0;\r
+\r
+  for yy=0 to 63 do\r
+    for xx=0 to 63 do\r
+      {\r
+       tile=sm[yy*64+xx];\r
+\r
+/*if tile is an exploding block, change it to a special icon for editor*/\r
+\r
+       if (tile>=136) and (tile<=145)\r
+         tile=tile+35;\r
+       background[yy+topoff,xx+leftoff]=tile;\r
+      };\r
+\r
+fileread:\r
+\r
+  for y=topoff to 63+topoff do\r
+    for x=leftoff to 63+leftoff do\r
+      view[y,x]=background[y,x];\r
+  sx=33;                  /*print the new level number on the right window*/\r
+  sy=1;\r
+  printint (level);\r
+  print (' ');          /*in case it went from double to single digit*/\r
+  restore;\r
+};\r
+\r
+\r
+\r
+/*         */\r
+/* save level */\r
+/*         */\r
+procedure saveit;\r
+var\r
+  iofile : file;\r
+  filename : string;\r
+  x,y,recs : integer;\r
+  tile: byte;\r
+  st: string[3];\r
+  sm : array [0..4095] of byte;\r
+{\r
+  centerwindow (9,1);\r
+  print ('saving...');\r
+  for y=0 to 63 do\r
+    for x=0 to 63 do\r
+      {\r
+       tile=background[y+topoff,x+leftoff] and $00ff;\r
+\r
+/*if the tile was an exploding block, change back to undetectable*/\r
+\r
+       if (tile>=171) and (tile<=180)\r
+         tile=tile-35;\r
+       sm[y*64+x]=tile;\r
+      };\r
+  str(level:1,st);\r
+  filename=concat ('level',st,'.cat');\r
+  assign (iofile,filename);\r
+  rewrite (iofile,1);\r
+  blockwrite (iofile,sm,4096,recs);\r
+  close (iofile);\r
+  restore;\r
+};\r
+\r
+\r
+\r
+/*           */\r
+/* select level */\r
+/*           */\r
+function selectlevel:boolean;\r
+var\r
+  err:integer;\r
+  lv:string;\r
+{\r
+  selectlevel=false;              /*editor won't reload a level if false*/\r
+  centerwindow (16,2);\r
+  print ('edit which level](1-99):');\r
+  input (lv,2);\r
+  if lv[1]=27         /*allow esc to quit editor mode*/\r
+    {\r
+      leveldone=true;\r
+      playdone=true;\r
+    };\r
+  val (lv,level,err);\r
+  if level>=1\r
+    selectlevel=true;\r
+  restore;\r
+};\r
+\r
+\r
+/*           */\r
+/* toggle block */\r
+/*           */\r
+procedure toggleblock;\r
+var\r
+  x,y,block:integer;\r
+{\r
+  x=originx+topoff;\r
+  y=originy+leftoff;\r
+  block=background [y,x];\r
+\r
+  if block=blankfloor\r
+    block=solidwall\r
+  else\r
+    block=blankfloor;\r
+\r
+  background [y,x]=block;\r
+  view [y,x]=block;\r
+};\r
+\r
+/*        */\r
+/* print map */\r
+/*        */\r
+procedure printmap;\r
+var\r
+  x,y,block:integer;\r
+  ch: char;\r
+{\r
+  writeln (lst);\r
+  writeln (lst,'catacomb level ',level);\r
+  for y=0 to 65 do\r
+    {\r
+      for x=0 to 65 do\r
+       {\r
+         block=background[topoff-1+y,leftoff-1+x];\r
+         case block of\r
+           0..127: ch=block;   /*ascii*/\r
+           128: ch=' ';                /*floor*/\r
+           129..135: ch='#';           /*walls*/\r
+           171..177: ch='*';           /*exploding*/\r
+           178..180: ch='!';           /*hidden stuff*/\r
+           162: ch='p';                /*potion*/\r
+           163: ch='s';                /*scroll*/\r
+           164: ch='k';                /*key*/\r
+           165: ch='|';                /*door*/\r
+           166: ch='-';                /*door*/\r
+           167: ch='$';                /*treasure*/\r
+           230..238: ch='0'+block-229; /*tokens*/\r
+           else ch='?';\r
+         };\r
+         write (lst,ch);\r
+      };\r
+    writeln (lst);\r
+  };\r
+  writeln (lst,12);\r
+};\r
+\r
+/*==================================*/\r
+\r
+{\r
+\r
+  regs.ax=0;\r
+  intr($33,regs);    /*show the mouse cursor*/\r
+\r
+  drawwindow (24,0,38,23);  /*draw the right side window*/\r
+  print ('  level]] map editor]]f4=exit]f7=load]f8=save]^p=print');\r
+\r
+  sx=25;\r
+  leftedge=sx;\r
+  sy=10;\r
+  for i=1 to 12 do\r
+    print (samplepics[i]+']');\r
+\r
+  drawtile=solidwall;\r
+  ltx=28;\r
+  lty=13;\r
+  ltt=solidwall;\r
+  xormask=$ffff;\r
+  drawchar (ltx,lty,ltt);         /*highlight the new block*/\r
+  xormask=0;\r
+\r
+  level=1;\r
+  playdone=false;\r
+\r
+  repeat\r
+    leveldone=false;\r
+    originx=0;\r
+    originy=0;\r
+\r
+    if selectlevel  /*let them choose which level to edit*/\r
+      loadlevel\r
+    else\r
+      goto cmdover;     /*so if they pressed esc, they can leave*/\r
+\r
+    repeat\r
+      simplerefresh;\r
+\r
+      regs.ax=1;\r
+      intr($33,regs);    /*show the mouse cursor*/\r
+      WaitVBL();           /*make sure it gets seen*/\r
+      WaitVBL();\r
+\r
+      repeat\r
+       regs.ax=3;\r
+       intr($33,regs);  /*mouse status*/\r
+      until keypressed or (regs.bx and 3>0);\r
+\r
+      sx=regs.cx div 16;   /*tile on screen mouse is over*/\r
+      sy=regs.dx div 8;\r
+\r
+      regs.ax=2;\r
+      intr($33,regs);    /*hide the mouse cursor*/\r
+\r
+      checkkeys;       /*handles f keys and returns a keypress*/\r
+\r
+      ch=0;\r
+      altkey=false;\r
+      if keypressed\r
+       {\r
+         ch=upcase(readkey);\r
+         if ch=0\r
+           {\r
+             altkey=true;\r
+             ch=readkey;\r
+           }\r
+       };\r
+\r
+      if (sx<24) and (sy<24)\r
+/*buttons pressed in tile map*/\r
+       {\r
+         x=originx+sx;\r
+         y=originy+sy;\r
+          if (x>=leftoff) and (x<leftoff+64) and\r
+            (y>=topoff) and (y<topoff+64)\r
+           {\r
+             if (regs.bx and 1>0)\r
+\r
+/*left button places/deletes a drawtile*/\r
+\r
+               {\r
+                 background[y,x]=drawtile;\r
+                 view[y,x]=drawtile;\r
+               };\r
+\r
+             if (regs.bx and 2>0)   /*right button places a blankfloor*/\r
+               {\r
+                 background[y,x]=blankfloor;\r
+                 view[y,x]=blankfloor;\r
+               };\r
+\r
+             if (not altkey) and ((ch>='a') and (ch<='z')\r
+             or ((ch>='0') and (ch<='9') ) )\r
+               {\r
+                 if (ch>='0') and (ch<='9')\r
+                   background[y,x]=ord(ch)+161   /*map numbers are later*/\r
+                 else\r
+                   background[y,x]=ord(ch)+32; /*map letters are lowercase*/\r
+                 view[y,x]=background[y,x];\r
+                 regs.ax=4;\r
+                 regs.cx=regs.cx+16;\r
+                 intr ($33,regs);        /*move the mouse over*/\r
+               };\r
+\r
+             if (not altkey) and (ch=' ')   /*space makes a solidwall*/\r
+               {\r
+                 background[y,x]=solidwall;\r
+                 view[y,x]=solidwall;\r
+                 regs.ax=4;\r
+                 regs.cx=regs.cx+16;\r
+                 intr ($33,regs);        /*move the mouse over*/\r
+               };\r
+\r
+           };\r
+       };\r
+\r
+\r
+      x=sx-24;\r
+      y=sy-9;\r
+      if  (regs.bx and 1>0) and (x>0) and (x<14) and (y>0) and (y<13)and\r
+       (samplepics[y][x]<>#128)\r
+/*button pressed in samplepics*/\r
+       {\r
+         drawchar (ltx,lty,ltt);         /*unhighlight the old drawtile*/\r
+         drawtile=ord(samplepics[y][x]);\r
+         ltx=sx;\r
+         lty=sy;\r
+         ltt=drawtile;\r
+         xormask=$ffff;\r
+         drawchar (ltx,lty,ltt);         /*highlight the new block*/\r
+         xormask=0;\r
+       };\r
+\r
+\r
+      rd_keyboard (dir,b1,b2);\r
+      case dir of\r
+       north: if originy>0\r
+               originy=originy-1\r
+              else\r
+               PlaySound (blockedsnd);\r
+       west: if originx>0\r
+\r
+               originx=originx-1\r
+              else\r
+               PlaySound(blockedsnd);\r
+       east: if originx<51+leftoff\r
+               originx=originx+1\r
+              else\r
+               PlaySound(blockedsnd);\r
+       south: if originy<51+topoff\r
+               originy=originy+1\r
+              else\r
+               PlaySound(blockedsnd);\r
+      };\r
+\r
+\r
+      if keydown[$19] and keydown[$1d]  /*control-p*/\r
+       printmap;\r
+\r
+      if keydown[$42]\r
+       {\r
+         keydown[$42]=false;\r
+         saveit;\r
+       };\r
+\r
+      if keydown[$41]\r
+       {\r
+         keydown[$41]=false;\r
+         leveldone=true;        /*so selectlevel will be called*/\r
+       };\r
+\r
+cmdover:\r
+\r
+    until leveldone or playdone;\r
+  until playdone;\r
+\r
+};\r
+\r
+#endif\r
+\r
diff --git a/CAT_PLAY.C b/CAT_PLAY.C
new file mode 100644 (file)
index 0000000..da102c0
--- /dev/null
@@ -0,0 +1,1634 @@
+/* The Catacomb Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*=====================================*/\r
+/*                                    */\r
+/* newobject                           */\r
+/* returns the number of a free object */\r
+/*                                    */\r
+/*=====================================*/\r
+\r
+int newobject()\r
+{\r
+  int i;\r
+\r
+  for (i=1; i<= numobj; i++)\r
+    if (o[i].class==nothing)\r
+       goto gotit;\r
+  if (numobj<maxobj)\r
+    numobj++;\r
+  i=numobj;\r
+\r
+gotit:\r
+\r
+  o[i].oldtile=-1;\r
+  o[i].oldx=0;\r
+  o[i].oldy=0;\r
+\r
+  return i;\r
+}\r
+\r
+\r
+/*=================================*/\r
+/*                                */\r
+/* printscore / printhighscore     */\r
+/* prints the scores to the screen */\r
+/*                                */\r
+/*=================================*/\r
+\r
+void printscore()\r
+{\r
+  sx=31;\r
+  sy=3;\r
+  printlong (score);\r
+}\r
+\r
+void printhighscore ()\r
+{\r
+  sx=31;\r
+  sy=5;\r
+  printlong (highscores[1].score);\r
+}\r
+\r
+\r
+/*======================================*/\r
+/*                                     */\r
+/* printshotpower                       */\r
+/* printbody                            */\r
+/* draws the meter to the current value */\r
+/*                                     */\r
+/*======================================*/\r
+\r
+void printshotpower()\r
+{\r
+  sx=25;\r
+  sy=13;\r
+  if (shotpower == 13)\r
+    print (altmeters[13]);\r
+  else\r
+    print (meters[shotpower]);\r
+}\r
+\r
+\r
+void printbody()\r
+{\r
+  sx=25;\r
+  sy=16;\r
+  if (o[0].hp>6)\r
+    print (meters[o[0].hp]);\r
+  else\r
+    print (altmeters[o[0].hp]);\r
+}\r
+\r
+\r
+/*=============================*/\r
+/*                            */\r
+/* levelcleared                */\r
+/* goes to the next level, or  */\r
+/* }s game if all levels done  */\r
+/* checks for warp teleporters */\r
+/*                            */\r
+/*=============================*/\r
+\r
+void levelcleared()\r
+{\r
+  char warp[2];\r
+  int value;\r
+\r
+  leveldone=true;\r
+\r
+  warp[0]=(char) background[altobj.y+2][altobj.x]-161;\r
+  if ( (warp[0]<'0') || (warp[0]>'9') )\r
+    warp[0]='0';\r
+  warp[1]=(char) background[altobj.y+2][altobj.x+1]-161;\r
+  if ( (warp[1]<'0') || (warp[1]>'9') )\r
+    warp[2]=' ';\r
+ value = atoi (warp);\r
+\r
+  if (value>0)\r
+    level=value;\r
+  else\r
+    level++;\r
+\r
+  if (level>numlevels)\r
+/*all levels have been completed*/\r
+    {\r
+      playdone=true;\r
+      gamexit=victorious;\r
+    }\r
+}\r
+\r
+\r
+/*==================================*/\r
+/*                                 */\r
+/* give / take potion / key / bolt  */\r
+/* / nuke                           */\r
+/* increments the item quantity and */\r
+/* draws an extra icon if it fits   */\r
+/*                                 */\r
+/*==================================*/\r
+\r
+void givekey()\r
+{\r
+  int i;\r
+\r
+  i=items[1]+1;\r
+  items[1]=i;\r
+  if (i<11)\r
+    drawchar (26+i,7,31);  /*key icon*/\r
+}\r
+\r
+\r
+void givepotion()\r
+{\r
+  int i;\r
+\r
+  i=items[2]+1;\r
+  items[2]=i;\r
+  if (i<11)\r
+    drawchar (26+i,8,29);  /*potion icon*/\r
+}\r
+\r
+\r
+void givebolt()\r
+{\r
+  int i;\r
+\r
+  i=items[3]+1;\r
+  items[3]=i;\r
+  if (i<11)\r
+    drawchar (26+i,9,30);  /*scroll icon*/\r
+}\r
+\r
+\r
+\r
+void givenuke()\r
+{\r
+  int i;\r
+\r
+  i=items[5]+1;\r
+  items[5]=i;\r
+  if (i<11)\r
+    drawchar (26+i,10,30);  /*scroll icon*/\r
+}\r
+\r
+\r
+/*        */\r
+/* takekey */\r
+/*        */\r
+boolean takekey()\r
+{\r
+  int i;\r
+\r
+  if (items[1]>0)\r
+    {\r
+      i=items[1]-1;\r
+      items[1]=i;\r
+      if (i<10)\r
+       drawchar (27+i,7,32);\r
+      PlaySound (OPENDOORSND);\r
+      return true;\r
+    }\r
+  else\r
+    {\r
+      PlaySound (NOITEMSND);\r
+      return false;\r
+    }\r
+}\r
+\r
+\r
+/*           */\r
+/* takepotion */\r
+/*           */\r
+void takepotion()\r
+{\r
+  int i;\r
+\r
+  if (items[2]>0)\r
+    {\r
+      i=items[2]-1;\r
+      items[2]=i;\r
+      if (i<11)\r
+       drawchar (27+i,8,32);\r
+      PlaySound(POTIONSND);\r
+      o[0].hp=13;\r
+      obj.hp=13;\r
+      printbody();           /*update the body meter*/\r
+    }\r
+  else\r
+    PlaySound(NOITEMSND);\r
+}\r
+\r
+\r
+/*         */\r
+/* castbolt */\r
+/*         */\r
+void castbolt()\r
+{\r
+  int i;\r
+\r
+  if (items[3]>0)\r
+    {\r
+      i=items[3]-1;\r
+      items[3]=i;\r
+      if (i<11)\r
+       drawchar (27+i,9,32);\r
+      boltsleft=8;\r
+\r
+      PlaySound(SPELLSND);\r
+    }\r
+  else\r
+    PlaySound(NOITEMSND);\r
+}\r
+\r
+\r
+\r
+/*         */\r
+/* castnuke */\r
+/*         */\r
+void castnuke()\r
+{\r
+  int i,x,y,n;\r
+  activeobj base;\r
+\r
+  if (items[5]==0)\r
+  {\r
+    PlaySound(NOITEMSND);\r
+    return;\r
+  }\r
+\r
+  i=items[5]-1;\r
+  items[5]=i;\r
+  if (i<11)\r
+    drawchar (27+i,10,32);\r
+\r
+  base.delay=0;\r
+  base.stage=0;\r
+  base.active=true;\r
+  base.x=obj.x;\r
+  base.y=obj.y;      /*start bigshot at same coordinate at player*/\r
+  base.oldx=x;\r
+  base.oldy=y;\r
+  base.oldtile=-1;\r
+  base.class=bigshot;\r
+\r
+  for (x=-1; x<=1; x++)\r
+    {      /*make a whole buch of bigshots*/\r
+      n=newobject();\r
+      o[n]=base;\r
+      o[n].x=o[n].x+x*2;\r
+      o[n].dir=north;\r
+      n=newobject();\r
+      o[n]=base;\r
+      o[n].x=o[n].x+x*2;\r
+      o[n].dir=south;\r
+      n=newobject();\r
+      o[n]=base;\r
+      o[n].y=o[n].y+x*2;\r
+      o[n].dir=east;\r
+      n=newobject();\r
+      o[n]=base;\r
+      o[n].y=o[n].y+x*2;\r
+      o[n].dir=west;\r
+    }\r
+\r
+  PlaySound(SPELLSND);\r
+  obj.stage=2;\r
+  obj.delay=4;\r
+}\r
+\r
+\r
+\r
+/*=======================================*/\r
+/*                                      */\r
+/* playshoot / playbigshoot              */\r
+/* launches a missile of the proper type */\r
+/* from the current object.  chooses from*/\r
+/* smallshot, bigshot, and monshot.      */\r
+/*                                      */\r
+/*=======================================*/\r
+\r
+void playshoot()\r
+{\r
+  int new;\r
+\r
+  obj.stage=2;\r
+  obj.delay=4;\r
+  PlaySound (SHOTSND);\r
+\r
+  new=newobject();\r
+\r
+  o[new].class=shot;\r
+  side^=1;\r
+  o[new].delay=0;\r
+  o[new].stage=0;\r
+  o[new].active=true;\r
+  o[new].dir=obj.dir;  /*missile is allways facing same way as thrower*/\r
+  switch ( o[new].dir )\r
+  {\r
+    case north:\r
+            o[new].x=obj.x+side;\r
+            o[new].y=obj.y;\r
+            break;\r
+    case east:\r
+            o[new].x=obj.x+1;\r
+            o[new].y=obj.y+side;\r
+            break;\r
+    case south:\r
+            o[new].x=obj.x+side;\r
+            o[new].y=obj.y+1;\r
+            break;\r
+    case west:\r
+            o[new].x=obj.x;\r
+            o[new].y=obj.y+side;\r
+            break;\r
+  }\r
+}\r
+\r
+\r
+\r
+void playbigshoot()\r
+{\r
+  int new;\r
+\r
+  obj.stage=2;\r
+  if (boltsleft==0)\r
+    obj.delay=4;\r
+  PlaySound (BIGSHOTSND);\r
+  new=newobject();\r
+  o[new].delay=0;\r
+  o[new].stage=0;\r
+  o[new].active=true;\r
+  o[new].dir=obj.dir;  /*missile is allways facing same way as thrower*/\r
+  o[new].x=obj.x;\r
+  o[new].y=obj.y;      /*start bigshot at same coordinate at player*/\r
+  o[new].class=bigshot;\r
+}\r
+\r
+\r
+/*============================*/\r
+/*                           */\r
+/* givescroll                 */\r
+/* randomly gives a bolt/nuke */\r
+/*                           */\r
+/*============================*/\r
+\r
+void givescroll()\r
+{\r
+  int r;\r
+\r
+  if (rndt()<128)\r
+    givebolt ();\r
+  else\r
+    givenuke ();\r
+}\r
+\r
+\r
+/*=========================================*/\r
+/*                                        */\r
+/* opendoor                                */\r
+/* open the door with a piece at chkx,chky */\r
+/*                                        */\r
+/*=========================================*/\r
+\r
+void opendoor()\r
+{\r
+  int x,y;\r
+\r
+/*clears door icons both ways from the point contacted*/\r
+ PlaySound (OPENDOORSND);\r
+ x=chkx;\r
+ y=chky;\r
+ if (chkspot==165)\r
+   {                 /*vertical doors*/\r
+     do\r
+     {\r
+       view[y][x]=blankfloor;\r
+       background[y][x]=blankfloor;\r
+       y--;\r
+     } while (view[y][x]==165);\r
+     y=chky+1;\r
+     while (view[y][x]==165)\r
+     {\r
+       view[y][x]=blankfloor;\r
+       background[y][x]=blankfloor;\r
+       y++;\r
+     }\r
+   }\r
+ else\r
+   {                /*horizontal doors*/\r
+     do\r
+     {\r
+       view[y][x]=blankfloor;\r
+       background[y][x]=blankfloor;\r
+       x--;\r
+     } while (view[y][x]==166);\r
+     x=chkx+1;\r
+     while (view[y][x]==166)\r
+     {\r
+       view[y][x]=blankfloor;\r
+       background[y][x]=blankfloor;\r
+       x++;\r
+     };\r
+   }\r
+\r
+}\r
+\r
+\r
+\r
+/****************************************************************************/\r
+\r
+\r
+/*======================================*/\r
+/*                                     */\r
+/* tagobject                            */\r
+/* have the obj do its damage to altobj */\r
+/*                                     */\r
+/*======================================*/\r
+\r
+void tagobject ()\r
+\r
+{\r
+  int i=altobj.hp;\r
+\r
+  if ((GODMODE) && (altobj.class == player))\r
+               return;\r
+\r
+  altobj.hp -= obj.damage;\r
+  if (i<=obj.damage)\r
+\r
+/*it died*/\r
+\r
+        {\r
+/*      erasealtobj;           */      /*because dead has lower priority*/\r
+\r
+               if (altobj.class == player)\r
+       {\r
+         altobj.hp = o[0].hp = 0;\r
+         printbody();\r
+         PlaySound (KILLPLAYERSND);\r
+         playdone=true;               /*gameover by way of death*/\r
+         gamexit=killed;\r
+       }\r
+\r
+      else\r
+\r
+       {\r
+         score=score+altobj.points; /*give player points for a kill*/\r
+         printscore();\r
+         PlaySound (KILLMONSND);\r
+       }\r
+      /*change class to a deadthing of same size*/\r
+      o[altnum].class=(classtype)(dead1-1+altobj.size);\r
+      o[altnum].delay=2;\r
+      o[altnum].stage=0; /*start of fade*/\r
+    }\r
+\r
+  else\r
+\r
+/*wasn't killed*/\r
+\r
+    {\r
+      if ( o[altnum].class == guns || o[altnum].class == gune )\r
+       return;\r
+      o[altnum].hp=altobj.hp;  /*save the new hp status*/\r
+      o[altnum].stage=3;       /*set it to ouch stage*/\r
+      if (altnum==0)\r
+       {\r
+         o[0].delay=2;\r
+         printbody();     /*update body bar on screen*/\r
+         PlaySound (TAGPLAYERSND);\r
+       }\r
+      else\r
+       {\r
+         o[altnum].delay=4;      /*four frames for monsters*/\r
+         PlaySound (TAGMONSND);\r
+       }\r
+    }\r
+}\r
+\r
+\r
+/*==============================*/\r
+/*                             */\r
+/* intomonster                  */\r
+/* obj contacted another object */\r
+/*                             */\r
+/*==============================*/\r
+\r
+boolean intomonster()\r
+{\r
+  int i;\r
+  boolean gotit;\r
+\r
+/*figure out which object got hit*/\r
+\r
+  altnum=0;\r
+  gotit=false;\r
+  do\r
+  {\r
+  /* make a copy of the objects info into a global varriable */\r
+\r
+    memcpy (&altobj.active,&o[altnum],sizeof(o[altnum]) );\r
+    if ( (altobj.class>nothing) && (altnum!=objecton) )\r
+      {\r
+       memcpy (&altobj.think,&objdef[altobj.class],sizeof(objdef[altobj.class]) );\r
+       if ( (chkx>=altobj.x) && (chkx-altobj.x<altobj.size)\r
+       && (chky>=altobj.y) && (chky-altobj.y<altobj.size) )\r
+         if (altobj.solid)\r
+           gotit=true;\r
+         else\r
+           if ( (objecton==0) && (altobj.class==teleporter || altobj.class==secretgate) )\r
+           /*player got to the teleporter*/\r
+             levelcleared();\r
+      }\r
+    if (!gotit)\r
+      altnum++;\r
+  } while (!(gotit || altnum>numobj) );\r
+\r
+\r
+  if (!gotit)\r
+    return true;\r
+\r
+/*resolve contact based on attacker and target*/\r
+\r
+  switch (obj.contact)\r
+  {\r
+\r
+    case benign:\r
+      return false;    /*benign objects just don't move through others*/\r
+\r
+    case monster:\r
+    case mshot:\r
+      if (altnum==0)\r
+       {\r
+         tagobject();\r
+         obj.stage=2;   /*set it to attack stage*/\r
+         obj.delay=20;   /*delay for several frames*/\r
+       }\r
+      else\r
+       if (altobj.class==shot)  /*they can walk into shots*/\r
+         return true;\r
+      return false;\r
+\r
+    case pshot:\r
+      if (altnum>0)\r
+       tagobject();\r
+      return false;\r
+\r
+    case nukeshot:\r
+      tagobject();\r
+      return true;   /*nuke shots keep going*/\r
+  }\r
+  return false;\r
+}\r
+\r
+\r
+/*=======================================*/\r
+/*                                      */\r
+/* walkthrough                           */\r
+/* obj is trying to walk through chkspot */\r
+/* at chkx,chky, is it ok?               */\r
+/*                                      */\r
+/*=======================================*/\r
+\r
+boolean walkthrough()\r
+{\r
+  int new;\r
+\r
+  if (chkspot ==blankfloor)\r
+    return true;\r
+\r
+/* big object */\r
+\r
+  if (chkspot>=tile2s && chkspot<=lasttile)\r
+    return intomonster();\r
+\r
+/* walls */\r
+\r
+  if (chkspot>=129 && chkspot<=135)\r
+  {\r
+    if ( (obj.contact==pshot) || (obj.contact==nukeshot) || (obj.contact==mshot) )\r
+  /*make an explosion over the wall*/\r
+    {\r
+      new = newobject();\r
+      o[new].active=true;\r
+      o[new].x=chkx;\r
+      o[new].y=chky;\r
+      o[new].stage=0;\r
+      o[new].delay=2;\r
+      o[new].class=wallhit;\r
+      PlaySound (TAGWALLSND);\r
+    }\r
+    return false;\r
+  }\r
+\r
+/* exploding walls */\r
+\r
+  if (chkspot>=136 && chkspot<=145)\r
+  {\r
+    if ((obj.contact==pshot) || (obj.contact==nukeshot))\r
+    {\r
+     PlaySound (TAGWALLSND);\r
+     if (chkspot<143)\r
+       background[chky][chkx]=blankfloor;\r
+     else\r
+       background[chky][chkx]=chkspot+19;\r
+       /*hidden potion,scroll,key*/\r
+\r
+     /*make an explosion over the wall*/\r
+     new = newobject();\r
+     o[new].active=true;\r
+     o[new].x=chkx;\r
+     o[new].y=chky;\r
+     o[new].stage=0;\r
+     o[new].delay=2;\r
+     o[new].class=dead1;\r
+     if (obj.contact==pshot)\r
+       return false;\r
+     else\r
+       return true;             /*nuke shots keep going after blowing up one*/\r
+    }\r
+\r
+   else\r
+     return false;     /*nothing else goes through exploding walls*/\r
+  }\r
+\r
+/* potion bottle */\r
+  if (chkspot==162)\r
+  {\r
+    if (obj.class==player)\r
+    {\r
+      givepotion();\r
+      view[chky][chkx]=blankfloor;  /*erase icon*/\r
+      background[chky][chkx]=blankfloor;\r
+      PlaySound(ITEMSND);\r
+    }\r
+    return true;       /*everything but player just walks over it*/\r
+  }\r
+\r
+/*scroll*/\r
+  if (chkspot==163)\r
+  {\r
+    if (obj.class==player)\r
+    {\r
+      givescroll();\r
+      view[chky][chkx]=blankfloor;  /*erase icon*/\r
+      background[chky][chkx]=blankfloor;\r
+      PlaySound(ITEMSND);\r
+    }\r
+    return true;       /*everything but player just walks over it*/\r
+  }\r
+\r
+/* key */\r
+  if (chkspot==164)\r
+  {\r
+    if (obj.class==player)\r
+    {\r
+      givekey();\r
+      view[chky][chkx]=blankfloor;  /*erase icon*/\r
+      background[chky][chkx]=blankfloor;\r
+      PlaySound(ITEMSND);\r
+    }\r
+    return true;       /*everything but player just walks over it*/\r
+  }\r
+\r
+\r
+/* doors */\r
+   if (chkspot==165 || chkspot==166)\r
+   {\r
+    if (obj.class==player)\r
+      {\r
+       if (takekey())\r
+       {\r
+         opendoor();\r
+         return true;\r
+       }\r
+      }\r
+     return false;       /*blocks everything else*/\r
+   }\r
+\r
+/* treasure chest */\r
+\r
+   if (chkspot==167)\r
+   {\r
+     if (obj.class==player)\r
+     {\r
+       score+=500;\r
+       printscore();\r
+       background[chky][chkx]=blankfloor;\r
+       view[chky][chkx]=blankfloor;\r
+       PlaySound(TREASURESND);\r
+     }\r
+     return true;       /*everything but player just walks over it*/\r
+   }\r
+\r
+/* blowing up walls */\r
+\r
+  if (chkspot>=29 && chkspot<=31)\r
+    return true;\r
+\r
+  return false;\r
+}\r
+\r
+\r
+/*==========================================*/\r
+/*                                          */\r
+/* walk                                     */\r
+/* tries to move the object forward.  if it */\r
+/* touches another object, contact will be  */\r
+/* resolved based on contact.  returns a    */\r
+/* true / false whether the move is ok      */\r
+/*                                          */\r
+/*==========================================*/\r
+\r
+boolean walk (void)\r
+\r
+{\r
+  int i,size,newx,newy,deltay,deltax;\r
+  boolean try;\r
+\r
+\r
+  switch (obj.dir)\r
+    {\r
+    case north: {\r
+            newx=obj.x;\r
+            newy=obj.y-1;\r
+            chkx=newx;\r
+            chky=newy;\r
+            deltax=1;\r
+            deltay=0;\r
+       break;\r
+          }\r
+    case east : {\r
+            newx=obj.x+1;\r
+            newy=obj.y;\r
+            chkx=obj.x+obj.size;\r
+            chky=newy;\r
+            deltax=0;\r
+            deltay=1;\r
+       break;\r
+          }\r
+    case south: {\r
+            newx=obj.x;\r
+            newy=obj.y+1;\r
+            chkx=newx;\r
+            chky=obj.y+obj.size;\r
+            deltax=1;\r
+            deltay=0;\r
+       break;\r
+          }\r
+    case west : {\r
+            newx=obj.x-1;\r
+            newy=obj.y;\r
+            chkx=newx;\r
+            chky=newy;\r
+            deltax=0;\r
+            deltay=1;\r
+       break;\r
+          }\r
+    default: return (false);   /*should never happen*/\r
+  }\r
+\r
+  for (i=1;i<=obj.size;i++)\r
+    {\r
+      chkspot=view[chky][chkx];\r
+      if (chkspot!=blankfloor)\r
+      {\r
+       try=walkthrough();\r
+        if (leveldone)      /*player hit the teleporter*/\r
+         return(true);\r
+       if (obj.stage==2)    /*if they attacked something, its good*/\r
+         return(true);\r
+       if (!try)    /*ran into something that's not ok*/\r
+         return(false);\r
+      }\r
+      chkx=chkx+deltax;\r
+      chky=chky+deltay;\r
+    }\r
+\r
+  obj.x=newx;\r
+  obj.y=newy;\r
+  obj.stage ^= 1;       /*toggle walking frame*/\r
+  return (true);\r
+}\r
+\r
+\r
+/****************************************************************************/\r
+\r
+/*================*/\r
+/*                */\r
+/* playercmdthink */\r
+/*                */\r
+/*================*/\r
+\r
+void playercmdthink(void)\r
+\r
+{\r
+  dirtype olddir;\r
+  ControlStruct c;\r
+\r
+  c=ControlPlayer (1); /*see what the input device is doing*/\r
+  obj.stage=obj.stage & 1;   /*cancle attack or damaged stage*/\r
+\r
+\r
+/* cheat key... */\r
+\r
+  if (c.button1 && c.button2 && keydown[0x10])   /*'q' + b1 + b2*/\r
+    {\r
+      givepotion();\r
+      givescroll();\r
+      givekey();\r
+    }\r
+\r
+\r
+/*                      */\r
+/* carry out the action */\r
+/*                      */\r
+\r
+  if (c.dir<nodir && frameon%2)\r
+  {\r
+    if (c.button2)       /*if button 2 is down, the move will not*/\r
+      olddir=obj.dir;   /*change the direction of the figure (strafe)*/\r
+\r
+    if (c.dir>west)\r
+    {\r
+      if ((frameon/2)%2)\r
+       switch (c.dir)\r
+       {\r
+         case northeast:\r
+           obj.dir = east;\r
+           walk();\r
+           c.dir = north;\r
+           break;\r
+         case southeast:\r
+                obj.dir = south;\r
+           walk();\r
+           c.dir = east;\r
+           break;\r
+         case southwest:\r
+                obj.dir = west;\r
+           walk();\r
+           c.dir = south;\r
+           break;\r
+         case northwest:\r
+           obj.dir = north;\r
+           walk();\r
+           c.dir = west;\r
+           break;\r
+       }\r
+      else\r
+       switch (c.dir)\r
+       {\r
+         case northeast:\r
+           obj.dir = north;\r
+           walk();\r
+           c.dir = east;\r
+           break;\r
+         case southeast:\r
+           obj.dir = east;\r
+           walk();\r
+           c.dir = south;\r
+           break;\r
+         case southwest:\r
+           obj.dir = south;\r
+           walk();\r
+           c.dir = west;\r
+           break;\r
+         case northwest:\r
+           obj.dir = west;\r
+                walk();\r
+           c.dir = north;\r
+           break;\r
+       }\r
+    }\r
+\r
+    obj.dir=c.dir;        /*set new direction*/\r
+    if (!walk())\r
+      PlaySound (BLOCKEDSND);\r
+    if (c.button2)\r
+      obj.dir=olddir;   /*restore original direction*/\r
+  }\r
+  else\r
+    if (!c.button2)\r
+      switch (c.dir)\r
+      {\r
+       case northwest:\r
+       case north:\r
+         obj.dir = north;\r
+         break;\r
+       case northeast:\r
+       case east:\r
+         obj.dir = east;\r
+         break;\r
+       case southeast:\r
+       case south:\r
+         obj.dir = south;\r
+         break;\r
+       case southwest:\r
+       case west:\r
+         obj.dir = west;\r
+         break;\r
+      }\r
+\r
+/* move screen origin */\r
+\r
+  originx = obj.x-11;\r
+  originy = obj.y-11;\r
+\r
+/* a bolt spell is still going off */\r
+\r
+  if (boltsleft>0)\r
+  {\r
+    if (frameon % 3==0)\r
+    {\r
+      playbigshoot();   /*let off a bigshot*/\r
+      boltsleft--;\r
+    }\r
+  }\r
+  else\r
+\r
+/* button 1 builds shot power / shoots */\r
+\r
+  {\r
+    if (c.button1)\r
+        {\r
+      if (shotpower==0)\r
+       shotpower=1;     /*give power one immediately*/\r
+      else if (shotpower<13 && frameon%2)  /*give extra's only 2 frames*/\r
+       shotpower++;\r
+      printshotpower();\r
+    }\r
+    else if (shotpower>0)    /*player just released the fire button*/\r
+         {\r
+      if (shotpower==13)\r
+       playbigshoot();\r
+           else\r
+             playshoot();\r
+           shotpower=0;\r
+           printshotpower();\r
+         }\r
+\r
+  }\r
+\r
+/*                                       */\r
+/* keys to cast spells and drink potions */\r
+/*                                       */\r
+\r
+  if (!indemo)\r
+  {\r
+    if (keydown [0x19] || keydown [0x39])     /*'p' or ' ' keys*/\r
+    {\r
+      if (obj.hp<13)  /*don't take a potion if not needed*/\r
+      {\r
+       takepotion();\r
+       keydown [0x19]=false;\r
+       keydown [0x39]=false;\r
+      }\r
+    }\r
+    else if (keydown [0x30])        /*'b' key*/\r
+         {\r
+           castbolt();\r
+           keydown [0x30]=false;\r
+         }\r
+    else if (keydown [0x31] || keydown [0x1c])   /*'n' or ret keys*/\r
+    {\r
+      castnuke();\r
+      keydown [0x31]=false;\r
+      keydown [0x1c]=false;\r
+    }\r
+  }\r
+\r
+  dofkeys ();\r
+  if (resetgame)\r
+  {\r
+        resetgame = false;\r
+    playdone = true;\r
+    return;\r
+  }\r
+\r
+  switch (indemo)\r
+  {\r
+        case notdemo:\r
+               if (keydown[0x2e] && keydown[0x14] && keydown[0x39])    //'C-T-SPC'\r
+               {\r
+       centerwindow (16,2);\r
+       print ("warp to which\nlevel (1-99)?");\r
+       clearkeys ();\r
+       level =_inputint ();\r
+       if (level<1)\r
+         level=1;\r
+       if (level>30)\r
+         level=30;\r
+       restore();\r
+       leveldone = true;\r
+               }\r
+\r
+\r
+//NOLAN ADDED\r
+       if (keydown[0x2e] && keydown[0x14] && keydown[0x22])    // c-t-TAB == GODMODE\r
+       {\r
+               int i;\r
+               if (GODMODE)\r
+               {\r
+                       centerwindow (13,1);\r
+                       print("God Mode Off");\r
+                       GODMODE = false;\r
+               }\r
+               else\r
+               {\r
+                       centerwindow (12,1);\r
+                       print("God Mode On");\r
+                       GODMODE = true;\r
+               }\r
+\r
+               clearkeys();\r
+               i = getch ();\r
+\r
+               restore();\r
+               clearkeys();\r
+       }\r
+//NOLAN END\r
+\r
+               break;\r
+\r
+    case demoplay:\r
+    //\r
+    // check for player really hitting a button or space to start a game\r
+    //\r
+      indemo = notdemo;\r
+      ctrl = ControlPlayer (1);\r
+      if (ctrl.button1 || ctrl.button2 || keydown[0x39])\r
+      {\r
+       indemo = demoplay;\r
+       exitdemo = true;\r
+       leveldone = true;\r
+       level = 0;\r
+       return;\r
+      }\r
+      indemo = demoplay;\r
+      break;\r
+\r
+  }\r
+}\r
+\r
+\r
+/*===============================================*/\r
+/*                                              */\r
+/* chasethink                                    */\r
+/* have the current monster go after the player, */\r
+/* either diagonally or straight on              */\r
+/*                                              */\r
+/*===============================================*/\r
+\r
+void chasethink (boolean diagonal)\r
+\r
+{\r
+  int deltax,deltay,i;\r
+  dirtype d[3];\r
+  dirtype tdir, olddir, turnaround;\r
+\r
+  obj.stage=obj.stage & 1;   /*cancle attack or damaged stage*/\r
+  olddir=obj.dir;\r
+  turnaround=opposite[olddir];\r
+\r
+  deltax=o[0].x-obj.x;\r
+  deltay=o[0].y-obj.y;\r
+\r
+    d[1]=nodir;\r
+    d[2]=nodir;\r
+\r
+  if (deltax>0)\r
+    d[1]= east;\r
+  if (deltax<0)\r
+    d[1]= west;\r
+  if (deltay>0)\r
+    d[2]=south;\r
+  if (deltay<0)\r
+    d[2]=north;\r
+\r
+  if (abs(deltay)>abs(deltax))\r
+    {\r
+      tdir=d[1];\r
+      d[1]=d[2];\r
+      d[2]=tdir;\r
+    }\r
+\r
+  if (d[1]==turnaround)\r
+    d[1]=nodir;\r
+  if (d[2]==turnaround)\r
+    d[2]=nodir;\r
+\r
+\r
+  if (diagonal)\r
+  {                           /*ramdiagonals try the best dir first*/\r
+    if (d[1]!=nodir)\r
+      {\r
+        obj.dir=d[1];\r
+       if (walk() || (obj.stage==3))\r
+          return;     /*either moved forward or attacked*/\r
+      }\r
+\r
+  if (d[2]!=nodir)\r
+    {\r
+      obj.dir=d[2];\r
+      if (walk() || (obj.stage==3))\r
+        return;\r
+    }\r
+  }\r
+  else\r
+  {                  /*ramstraights try the second best dir first*/\r
+\r
+  if (d[2]!=nodir)\r
+    {\r
+      obj.dir=d[2];\r
+      if (walk() || (obj.stage==3))\r
+        return;\r
+    }\r
+\r
+  if (d[1]!=nodir)\r
+    {\r
+      obj.dir=d[1];\r
+      if (walk() || (obj.stage==3))\r
+        return;\r
+    }\r
+  }\r
+\r
+/* there is no direct path to the player, so pick another direction */\r
+\r
+  obj.dir=olddir;\r
+  if (walk() || (obj.stage==3))\r
+    return;\r
+\r
+  if (rndt()>128)      /*randomly determine direction of search*/\r
+    {\r
+      for (tdir=north;tdir<=west;tdir++)\r
+      {\r
+        if (tdir!=turnaround)\r
+        {\r
+          obj.dir=tdir;\r
+         if (walk() || (obj.stage==3))\r
+            return;\r
+        }\r
+      }\r
+    }\r
+    else\r
+    {\r
+      for (tdir=west;tdir>=north;tdir--)\r
+      {\r
+        if (tdir!=turnaround)\r
+        {\r
+          obj.dir=tdir;\r
+         if (walk() || (obj.stage==3))\r
+            return;\r
+        }\r
+      }\r
+    }\r
+\r
+  obj.dir=turnaround;\r
+  walk();              /*last chance, don't worry about returned value*/\r
+}\r
+\r
+\r
+/*===========*/\r
+/*           */\r
+/* gargthink */\r
+/*           */\r
+/*===========*/\r
+\r
+void gargthink(void)\r
+\r
+{\r
+  int n,deltax,deltay;\r
+\r
+  if (rndt ()>220)   /*only shoot once in a while*/\r
+  {\r
+    obj.stage=2;\r
+    obj.delay=6;\r
+\r
+/*soundon=false;*/\r
+\r
+    PlaySound (SHOTSND);\r
+    n=newobject();\r
+\r
+// with o[n] do  /*get a free spot*/\r
+\r
+    o[n].class=rock;\r
+    o[n].delay=0;\r
+    o[n].stage=0;\r
+    o[n].active=true;\r
+    o[n].dir=obj.dir;  /*missile is allways facing same way as thrower*/\r
+\r
+    switch (obj.dir)\r
+    {\r
+      case north: {\r
+         o[n].x=obj.x+1+side;\r
+         o[n].y=obj.y;\r
+         break;\r
+                  }\r
+      case east:  {\r
+         o[n].x=obj.x+3;\r
+         o[n].y=obj.y+1+side;\r
+         break;\r
+                  }\r
+      case south: {\r
+         o[n].x=obj.x+1+side;\r
+         o[n].y=obj.y+3;\r
+         break;\r
+                  }\r
+      case west:  {\r
+         o[n].x=obj.x;\r
+         o[n].y=obj.y+1+side;\r
+         break;\r
+                  }\r
+    }\r
+    return;\r
+  }\r
+  else\r
+    chasethink (false);   /*otherwise chase straight*/\r
+}\r
+\r
+\r
+/*=============*/\r
+/*             */\r
+/* dragonthink */\r
+/*             */\r
+/*=============*/\r
+\r
+void dragonthink(void)\r
+\r
+{\r
+  int n;\r
+\r
+  if (rndt()>220)      /*only shoot once in a while*/\r
+    {\r
+      obj.stage=2;\r
+      obj.delay=6;\r
+\r
+/*soundon=false;*/\r
+\r
+      PlaySound (SHOTSND);\r
+      n=newobject();\r
+\r
+// with o[n]. do  /*get a free spot*/\r
+\r
+    o[n].class=bigshot;\r
+    o[n].delay=0;\r
+    o[n].stage=0;\r
+    o[n].active=true;\r
+    o[n].dir=obj.dir;  /*missile is allways facing same way as thrower*/\r
+\r
+    switch (o[n].dir)\r
+    {\r
+      case north: {\r
+         o[n].x=obj.x+1+side;\r
+         o[n].y=obj.y;\r
+         break;\r
+                  }\r
+      case east:  {\r
+         o[n].x=obj.x+3;\r
+         o[n].y=obj.y+1+side;\r
+         break;\r
+                  }\r
+      case south: {\r
+         o[n].x=obj.x+1+side;\r
+         o[n].y=obj.y+3;\r
+         break;\r
+                  }\r
+      case west:  {\r
+         o[n].x=obj.x;\r
+         o[n].y=obj.y+1+side;\r
+         break;\r
+                  }\r
+         }\r
+    return;\r
+  }\r
+  else\r
+    chasethink (false);   /*otherwise chase straight*/\r
+}\r
+\r
+\r
+/*==========*/\r
+/*          */\r
+/* gunthink */\r
+/*          */\r
+/*==========*/\r
+\r
+void gunthink(int dir)\r
+{\r
+  int n;\r
+\r
+  PlaySound (SHOTSND);\r
+  obj.stage=0;\r
+\r
+  n=newobject();\r
+\r
+// with o[n]. do  /*get a free spot*/\r
+\r
+  o[n].class=bigshot;\r
+  o[n].delay=0;\r
+  o[n].stage=0;\r
+  o[n].active=true;\r
+  o[n].dir=dir;  /*missile is allways facing same way as thrower*/\r
+  o[n].x=obj.x;\r
+  o[n].y=obj.y;\r
+}\r
+\r
+\r
+\r
+/*==============*/\r
+/*              */\r
+/* shooterthink */\r
+/*              */\r
+/*==============*/\r
+\r
+void shooterthink(void)\r
+\r
+{\r
+  if ((obj.x<originx-1) || (obj.y<originy-1)\r
+  || (obj.x>originx+22) || (obj.y>originy+22)\r
+  || !walk() || (obj.stage==2))\r
+    {\r
+      obj.class=nothing;\r
+    }\r
+}\r
+\r
+\r
+/*===========*/\r
+/*           */\r
+/* idlethink */\r
+/*           */\r
+/*===========*/\r
+\r
+void idlethink(void)\r
+\r
+{\r
+  obj.stage++;\r
+  obj.delay=2;\r
+  if (obj.stage==obj.stages)\r
+    obj.stage=0;\r
+}\r
+\r
+\r
+/*===========*/\r
+/*           */\r
+/* fadethink */\r
+/*           */\r
+/*===========*/\r
+\r
+void fadethink(void)\r
+\r
+{\r
+  obj.stage++;\r
+  obj.delay=2;\r
+  if (obj.stage==obj.stages)\r
+    {\r
+      obj.class=nothing;\r
+    }\r
+}\r
+\r
+\r
+/*==============*/\r
+/*              */\r
+/* explodethink */\r
+/*              */\r
+/*==============*/\r
+\r
+void killnear(int chkx,int chky)\r
+\r
+{\r
+  int spot,new;\r
+\r
+  spot=background[chky][chkx];\r
+\r
+  if (spot<136 || spot>145)     /* not an exploding wall */\r
+    return;\r
+\r
+  PlaySound (TAGWALLSND);\r
+  if (spot<143)\r
+    background[chky][chkx]=blankfloor;\r
+  else\r
+    background[chky][chkx]=spot+19;  /*hidden potion,scroll,key*/\r
+\r
+  /*make an explosion over the wall*/\r
+\r
+// with o[newobject]. do\r
+\r
+  new = newobject ();\r
+\r
+  o[new].active=true;\r
+  o[new].x=chkx;\r
+  o[new].y=chky;\r
+  o[new].stage=0;\r
+  o[new].delay=2;\r
+  o[new].class=dead1;\r
+}\r
+\r
+\r
+void explodethink(void)\r
+\r
+{\r
+  obj.stage++;\r
+  if (obj.stage==1)    /*on first stage do a recursive flood explosion*/\r
+  {\r
+    killnear(obj.x-1,obj.y);\r
+    killnear(obj.x,obj.y-1);\r
+    killnear(obj.x+1,obj.y);\r
+    killnear(obj.x,obj.y+1);\r
+  }\r
+  obj.delay=2;\r
+  if (obj.stage==obj.stages)\r
+    {\r
+      obj.class=nothing;\r
+    }\r
+}\r
+\r
+\r
+/*========================================*/\r
+/*                                        */\r
+/* think                                  */\r
+/* decides what the object is going to do */\r
+/* and does it. the object will definately*/\r
+/* be redrawn, if nothing else            */\r
+/*                                        */\r
+/*========================================*/\r
+\r
+void think(void)\r
+\r
+{\r
+  if (obj.delay>0)\r
+    obj.delay--;      /*if the object has a delay, it will do nothing*/\r
+  else\r
+    {\r
+      if (rndt () < obj.speed)\r
+      {\r
+        switch (obj.think)\r
+        {\r
+         case playercmd : playercmdthink(); break;\r
+         case ramstraight : chasethink (false); break;\r
+         case ramdiag : chasethink (true); break;\r
+         case gargcmd: gargthink(); break;\r
+         case dragoncmd: dragonthink(); break;\r
+         case straight : shooterthink(); break;\r
+         case idle : idlethink(); break;\r
+         case fade : fadethink(); break;\r
+         case explode : explodethink(); break;\r
+         case gunthinke : gunthink(west); break;\r
+         case gunthinks : gunthink(north); break;\r
+        }\r
+      }\r
+    }\r
+}\r
+\r
+\r
+/*==========*/\r
+/*          */\r
+/* doactive */\r
+/*          */\r
+/*==========*/\r
+\r
+void doactive(void)\r
+\r
+{\r
+/*see if it is way off screen, so deactivate*/\r
+\r
+  if ((obj.class!=dead1) && ( (obj.x<originx-10) || (obj.x>originx+34)\r
+  || (obj.y<originy-10) || (obj.y>originy+34)))\r
+    {\r
+      o[objecton].active=false;\r
+    }\r
+  else\r
+    {\r
+      think();\r
+      eraseobj();\r
+      if (playdone)\r
+        return;\r
+\r
+   /*redraw it even if it hasn't moved, in case it was hit*/\r
+\r
+      if (obj.class>nothing)\r
+       drawobj();\r
+\r
+      /*write the temporary info back into the array*/\r
+\r
+      memcpy(&o[objecton],&obj,sizeof(o[objecton]) );\r
+    }\r
+}\r
+\r
+/*============*/\r
+/*            */\r
+/* doinactive */\r
+/*            */\r
+/*============*/\r
+\r
+void doinactive()\r
+{\r
+  /*if the object just became visable, make it active*/\r
+\r
+  if ((obj.x+obj.size>=originx) && (obj.x<originx+24)\r
+   && (obj.y+obj.size>=originy) && (obj.y<originy+24))\r
+    {\r
+      obj.active=true;\r
+      obj.dir=north;\r
+\r
+      /*write the temporary info back into the array*/\r
+\r
+      memcpy (&o[objecton],&obj.active,sizeof(o[objecton]) );\r
+    }\r
+}\r
+\r
+\r
+/*======================================*/\r
+/*                                   */\r
+/* playloop                             */\r
+/* all the action is directed from here */\r
+/*                                   */\r
+/*======================================*/\r
+\r
+void playloop(void)\r
+\r
+{\r
+  int i,j;\r
+\r
+  screencenterx = 11;\r
+\r
+  do {   /*until playdone*/\r
+\r
+    if (!indemo)\r
+    {\r
+      centerwindow (11,2);\r
+      print (" Entering\nlevel ");\r
+      printint (level);\r
+      print ("...");\r
+      PlaySound (LEVELDONESND);\r
+      WaitEndSound ();\r
+    }\r
+\r
+    clearold();    /*don't refresh the window yet*/\r
+\r
+    loadlevel(); /*load the level to play*/\r
+    leveldone= false;\r
+\r
+    if (keydown[0x41] && keydown[0x20]) // 'D+F7' to record a demo\r
+    {\r
+      clearold ();\r
+      refresh ();\r
+      refresh ();\r
+      clearkeys ();\r
+      centerwindow (12,1);\r
+      print ("RECORD DEMO");\r
+      do\r
+      {\r
+       ch=get ();\r
+      } while (ch!=13);\r
+      RecordDemo ();\r
+      clearold ();\r
+      clearkeys ();\r
+    }\r
+\r
+    playdone=false;\r
+    frameon=0;\r
+    boltsleft=0;\r
+    shotpower=0;\r
+    initrndt (false);\r
+    printshotpower();\r
+\r
+\r
+    doall();\r
+\r
+    if (indemo == recording)\r
+    {\r
+      clearkeys ();\r
+      centerwindow (15,1);\r
+      print ("SAVE AS DEMO#:");\r
+      do\r
+      {\r
+       ch = get ();\r
+      } while (ch<'0' || ch>'9');\r
+      SaveDemo (ch-'0');\r
+      clearold ();\r
+      refresh ();\r
+      refresh ();\r
+    }\r
+\r
+\r
+    if (indemo)\r
+      playdone=true;\r
+\r
+  } while (!playdone);\r
+\r
+}\r
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/CPANEL.C b/CPANEL.C
new file mode 100644 (file)
index 0000000..8958885
--- /dev/null
+++ b/CPANEL.C
@@ -0,0 +1,734 @@
+/* The Catacomb Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/////////////////////////////////\r
+//\r
+// The PC-Arcade control panel\r
+//\r
+// Takes global variables as input\r
+//\r
+// videocard           CGAcard,EGAcard,VGAcard\r
+//\r
+// Modifies global variables relating to graphics, sound, and controls:\r
+//\r
+// grmode              CGAgr,EGAgr,VGAgr\r
+// soundmode           nosound,spkr,adlib\r
+// player1mode         keyboard,mouse,joystick1,joystick2\r
+// player2mode\r
+//\r
+// JoyXlow[2], JoyXhigh[2], JoyYlow[2], JoyYhigh[2]\r
+// MouseSensitivity, key[8], keyB1, keyB2\r
+//\r
+//\r
+// The calling game must have the following functions:\r
+//\r
+// loadgrfiles         loads the files needed for the new mode\r
+// repaintscreen       Clears the screen and totally redoes everything,\r
+//                     including hardware tricks and EGA latched pic data\r
+//\r
+//\r
+// To call, just have a line which checks for F2 being pressed:\r
+//\r
+// if (keydown[0x3c])\r
+//   controlpanel ();\r
+//\r
+// This should allso be called right after the titlepage!\r
+//\r
+/////////////////////////////////\r
+\r
+#include "PCRLIB.H"\r
+\r
+int rowy[4] = {4,9,14,19};\r
+int collumnx[4] = {14,20,26,32};\r
+int spotok[4][5];\r
+\r
+int row,collumn;\r
+grtype oldgrmode, newgrmode;\r
+soundtype oldsoundmode, newsoundmode;\r
+inputtype oldplayermode[3], newplayermode[3];\r
+\r
+int joy1ok,joy2ok,mouseok;\r
+\r
+////////////////////\r
+//\r
+// prototypes\r
+//\r
+////////////////////\r
+\r
+void calibratejoy (int joynum);\r
+void calibratemouse (void);\r
+void printscan (int sc);\r
+void calibratekeys (void);\r
+void drawpanel (void);\r
+void drawpanel (void);\r
+void getconfig (void);\r
+\r
+void controlpanel (void);\r
+\r
+//=========================================================================\r
+\r
+////////////////\r
+//\r
+// calibratejoy\r
+// Brings up a dialog and has the user calibrate\r
+// either joystick1 or joystick2\r
+//\r
+////////////////\r
+\r
+void calibratejoy (int joynum)\r
+{\r
+  int stage,dx,dy,xl,yl,xh,yh;\r
+  ControlStruct ctr;\r
+\r
+  expwin (24,9);\r
+\r
+  print(" Joystick Configuration\n\r");\r
+  print(" ----------------------\n\r");\r
+  print("Hold the joystick in the\n\r");\r
+  print("upper left\n\r");\r
+  print("corner and hit fire:");\r
+  stage=15;\r
+  do                           // wait for a button press\r
+  {\r
+    drawchar (sx,sy,stage);\r
+    WaitVBL ();\r
+    WaitVBL ();\r
+    WaitVBL ();\r
+    if (++stage==23)\r
+      stage=15;\r
+    ReadJoystick (joynum,&xl,&yl);\r
+    ctr = ControlJoystick(joynum);\r
+    if (keydown[1])\r
+      goto done;\r
+  } while (ctr.button1!= 1);\r
+  drawchar (sx,sy,' ');\r
+  do                           // wait for the button release\r
+  {\r
+    ctr = ControlJoystick(joynum);\r
+  } while (ctr.button1);\r
+  WaitVBL ();\r
+  WaitVBL ();                  // so the button can't bounce\r
+\r
+  print("\n\n\rHold the joystick in the\n\r");\r
+  print("lower right\n\r");\r
+  print("corner and hit fire:");\r
+  do                           // wait for a button press\r
+  {\r
+    drawchar (sx,sy,stage);\r
+    WaitVBL ();\r
+    WaitVBL ();\r
+    WaitVBL ();\r
+    if (++stage==23)\r
+      stage=15;\r
+    ReadJoystick (joynum,&xh,&yh);\r
+    ctr = ControlJoystick(joynum);\r
+    if (keydown[1])\r
+      goto done;\r
+  } while (ctr.button1!= 1);\r
+  drawchar (sx,sy,' ');\r
+  do                           // wait for the button release\r
+  {\r
+    ctr = ControlJoystick(joynum);\r
+  } while (ctr.button1);\r
+\r
+  //\r
+  // figure out good boundaries\r
+  //\r
+\r
+  dx=(xh-xl) / 4;\r
+  dy=(yh-yl) / 4;\r
+  JoyXlow[joynum]=xl+dx;\r
+  JoyXhigh[joynum]=xh-dx;\r
+  JoyYlow[joynum]=yl+dy;\r
+  JoyYhigh[joynum]=yh-dy;\r
+\r
+done:\r
+  clearkeys ();\r
+  erasewindow ();\r
+}\r
+\r
+////////////////////////////\r
+//\r
+// calibratemouse\r
+//\r
+////////////////////////////\r
+void calibratemouse (void)\r
+{\r
+  char ch;\r
+\r
+  expwin (24,5);\r
+  print ("  Mouse Configuration   \n\r");\r
+  print ("  -------------------   \n\r");\r
+  print ("Choose the sensitivity  \n\r");\r
+  print ("of the mouse, 1 being   \n\r");\r
+  print ("slow, 9 being fast:");\r
+  do\r
+  {  ch=get() % 256;\r
+    if (ch==27)\r
+      ch='5';\r
+  } while (ch<'1' || ch>'9');\r
+  MouseSensitivity = 15-(ch-'0');\r
+  erasewindow ();\r
+  _AX=0;\r
+  geninterrupt (0x33);         // initialize the mouse\r
+  _AX = 4;\r
+  _CX=320;\r
+  _DX=100;\r
+  geninterrupt (0x33);         // set mouse status\r
+\r
+\r
+}\r
+\r
+/////////////////////////////\r
+//\r
+// print a representation of the scan code key\r
+//\r
+////////////////////////////\r
+void printscan (int sc)\r
+{\r
+ char static chartable[128] =\r
+ {'?','?','1','2','3','4','5','6','7','8','9','0','-','+','?','?',\r
+  'Q','W','E','R','T','Y','U','I','O','P','[',']','|','?','A','S',\r
+  'D','F','G','H','J','K','L',';','"','?','?','?','Z','X','C','V',\r
+  'B','N','M',',','.','/','?','?','?','?','?','?','?','?','?','?',\r
+  '?','?','?','?','?','?','?','?', 15,'?','-', 21,'5', 17,'+','?',\r
+   19,'?','?','?','?','?','?','?','?','?','?','?','?','?','?','?',\r
+  '?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?',\r
+  '?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?'};\r
+\r
+ sc = sc & 0x7f;\r
+\r
+ if (sc==1)\r
+   print ("ESC");\r
+ else if (sc==0xe)\r
+   print ("BKSP");\r
+ else if (sc==0xf)\r
+   print ("TAB");\r
+ else if (sc==0x1d)\r
+   print ("CTRL");\r
+ else if (sc==0x2A)\r
+   print ("LSHIFT");\r
+ else if (sc==0x39)\r
+   print ("SPACE");\r
+ else if (sc==0x3A)\r
+   print ("CAPSLK");\r
+ else if (sc>=0x3b && sc<=0x44)\r
+ {\r
+   char str[3];\r
+   print ("F");\r
+   itoa (sc-0x3a,str,10);\r
+   print (str);\r
+ }\r
+ else if (sc==0x57)\r
+   print ("F11");\r
+ else if (sc==0x59)\r
+   print ("F12");\r
+ else if (sc==0x46)\r
+   print ("SCRLLK");\r
+ else if (sc==0x1c)\r
+   print ("ENTER");\r
+ else if (sc==0x36)\r
+   print ("RSHIFT");\r
+ else if (sc==0x37)\r
+   print ("PRTSC");\r
+ else if (sc==0x38)\r
+   print ("ALT");\r
+ else if (sc==0x47)\r
+   print ("HOME");\r
+ else if (sc==0x49)\r
+   print ("PGUP");\r
+ else if (sc==0x4f)\r
+   print ("END");\r
+ else if (sc==0x51)\r
+   print ("PGDN");\r
+ else if (sc==0x52)\r
+   print ("INS");\r
+ else if (sc==0x53)\r
+   print ("DEL");\r
+ else if (sc==0x45)\r
+   print ("NUMLK");\r
+ else\r
+   drawchar (sx++,sy,chartable[sc]);\r
+}\r
+\r
+/////////////////////////////\r
+//\r
+// calibratekeys\r
+//\r
+////////////////////////////\r
+void calibratekeys (void)\r
+{\r
+  char ch;\r
+  int hx,hy,i,select,new;\r
+\r
+  expwin (22,15);\r
+  print ("Keyboard Configuration\n\r");\r
+  print ("----------------------");\r
+  print ("\n\r0 north    :");\r
+  print ("\n\r1 east     :");\r
+  print ("\n\r2 south    :");\r
+  print ("\n\r3 west     :");\r
+  print ("\n\r4 northeast:");\r
+  print ("\n\r5 southeast:");\r
+  print ("\n\r6 southwest:");\r
+  print ("\n\r7 northwest:");\r
+  print ("\n\r8 button1  :");\r
+  print ("\n\r9 button2  :");\r
+  print ("\n\n\rModify which action:");\r
+  hx=sx;\r
+  hy=sy;\r
+  for (i=0;i<8;i++)\r
+  {\r
+    sx=22;\r
+    sy=7+i;\r
+    printscan (key[i]);\r
+  }\r
+  sx=22;\r
+  sy=15;\r
+  printscan (keyB1);\r
+  sx=22;\r
+  sy=16;\r
+  printscan (keyB2);\r
+\r
+  do\r
+  {\r
+    sx=hx;\r
+    sy=hy;\r
+    ch=get() % 256;\r
+    if (ch<'0' || ch>'9')\r
+      continue;\r
+    select = ch - '0';\r
+    drawchar (sx,sy,ch);\r
+    select = ch - '0';\r
+    print ("\n\rPress the new key:");\r
+    clearkeys ();\r
+    new=-1;\r
+    while (!keydown[++new])\r
+      if (new==0x79)\r
+       new=-1;\r
+      else if (new==0x29)\r
+       new++;                          // skip STUPID left shifts!\r
+    clearkeys ();\r
+    print ("\r                  ");\r
+    if (select<8)\r
+      key[select]=new;\r
+    if (select==8)\r
+      keyB1=new;\r
+    if (select==9)\r
+      keyB2=new;\r
+    sy=select+7;\r
+    sx=22;\r
+    print ("        ");\r
+    sx=22;\r
+    printscan (new);\r
+    ch='0';                            // so the loop continues\r
+    clearkeys ();\r
+  } while (ch>='0' && ch<='9');\r
+\r
+  erasewindow ();\r
+}\r
+\r
+\r
+//=========================================================================\r
+\r
+////////////////////\r
+//\r
+// getconfig\r
+// Checks video cards, mouse, and joysticks\r
+//\r
+////////////////////\r
+\r
+void getconfig (void)\r
+{\r
+  int x,y;\r
+  int far *vect;\r
+\r
+  spotok [0][0] = 1;\r
+  spotok [0][1] = _egaok;\r
+  spotok [0][2] = _vgaok;\r
+  spotok [0][3] = 0;\r
+  spotok [0][4] = 0;\r
+\r
+  spotok [1][0] = 1;\r
+  spotok [1][1] = 1;\r
+  spotok [1][2] = 0;\r
+  spotok [1][3] = 0;\r
+  spotok [1][4] = 0;\r
+\r
+  joy1ok = 0;\r
+  joy2ok = 0;\r
+\r
+  ReadJoystick (1,&x,&y);\r
+  if (x<500)\r
+    joy1ok = 1;\r
+  ReadJoystick (2,&x,&y);\r
+  if (x<500)\r
+    joy1ok = 2;\r
+\r
+  mouseok = 1;\r
+  vect = (int far *) getvect (0x33);\r
+\r
+  if (vect == NULL)\r
+    mouseok = 0;               // vecter is NULL, calling would be bad...\r
+  else\r
+    if ((*vect & 255) == 0xcf) // points to an IRET\r
+      mouseok = 0;\r
+\r
+  spotok [2][0] = 1;\r
+  spotok [2][1] = mouseok;\r
+  spotok [2][2] = joy1ok;\r
+  spotok [2][3] = joy2ok;\r
+  spotok [2][4] = 0;\r
+\r
+}\r
+\r
+//=========================================================================\r
+\r
+\r
+////////////////\r
+//\r
+// drawpanel\r
+// Draws everything inside the control panel window.\r
+// Used to refresh under dialogs.\r
+//\r
+////////////////\r
+\r
+void drawpanel (void)\r
+{\r
+  leftedge=1;\r
+\r
+  xormask = 0;\r
+\r
+  sx=8;\r
+  sy=2;\r
+  print ("       Control Panel      \n\r");\r
+\r
+  getconfig ();\r
+\r
+  sy=rowy[0]+2;\r
+  sx=2;\r
+  print ("VIDEO:");\r
+  drawpic (collumnx[0]*8,rowy[0]*8,0);\r
+  if (_egaok)\r
+    drawpic (collumnx[1]*8,rowy[0]*8,1);\r
+  else\r
+    drawpic (collumnx[1]*8,rowy[0]*8,3);\r
+\r
+  sy=rowy[1]+2;\r
+  sx=2;\r
+  print ("SOUND:");\r
+  drawpic (collumnx[0]*8,rowy[1]*8,5);\r
+  drawpic (collumnx[1]*8,rowy[1]*8,6);\r
+\r
+  sy=rowy[2]+2;\r
+  sx=2;\r
+  print ("CONTROL:");\r
+  drawpic (collumnx[0]*8,rowy[2]*8,7);\r
+  if (mouseok)\r
+    drawpic (collumnx[1]*8,rowy[2]*8,10);\r
+  else\r
+    drawpic (collumnx[1]*8,rowy[2]*8,12);\r
+  if (joy1ok)\r
+    drawpic (collumnx[2]*8,rowy[2]*8,8);\r
+  else\r
+    drawpic (collumnx[2]*8,rowy[2]*8,11);\r
+  if (joy2ok)\r
+    drawpic (collumnx[3]*8,rowy[2]*8,9);\r
+  else\r
+    drawpic (collumnx[3]*8,rowy[2]*8,11);\r
+\r
+  drawchar(collumnx[(int) newgrmode -1]+1,rowy[0]+3,15);\r
+  drawchar(collumnx[(int) newsoundmode]+1,rowy[1]+3,15);\r
+  drawchar(collumnx[(int) newplayermode[1]]+1,rowy[2]+3,15);\r
+\r
+  sy=21;\r
+  sx=1;\r
+  print ("  Move the cursor with the arrow keys \n\r");\r
+  print ("   Make decisions with the ENTER key  \n\r");\r
+  print ("       ESC to return to your game     \n\r");\r
+}\r
+\r
+//=========================================================================\r
+\r
+////////////////\r
+//\r
+// controlpanel\r
+//\r
+////////////////\r
+\r
+void controlpanel (void)\r
+{\r
+  int chf;\r
+  char chl,chh;\r
+  int oldcenterx,oldcentery;\r
+\r
+  clearkeys ();                        // clear out the F2 and other crap\r
+\r
+  PauseSound ();               // pause any sound that is playing\r
+\r
+//\r
+// save off current settings so we can tell what changed\r
+//\r
+  newgrmode = oldgrmode = grmode;\r
+  newsoundmode = oldsoundmode = soundmode;\r
+  newplayermode[1] = oldplayermode[1] = playermode[1];\r
+  newplayermode[2] = oldplayermode[2] = playermode[2];\r
+  oldcenterx=screencenterx;\r
+  oldcentery=screencentery;\r
+\r
+  screencenterx = 19;\r
+  screencentery = 11;\r
+\r
+  setscreenmode (grmode);\r
+\r
+//\r
+// draw the screen\r
+//\r
+  drawwindow (0,0,39,24);\r
+  drawpanel ();\r
+\r
+  row = 0;\r
+  collumn = (int) grmode-1;\r
+\r
+  do\r
+  {\r
+//////////////////////////////////////\r
+\r
+    sx=collumnx[collumn]+2;\r
+    sy=rowy[row]+3;\r
+    chf = get ();\r
+    chl = chf % 256;\r
+    chh = chf / 256;\r
+\r
+    if (chh==0x48)     // up arrow\r
+      if (--row<0)\r
+       row = 2;\r
+\r
+    if (chh==0x50)     // down arrow\r
+      if (++row>2)\r
+       row = 0;\r
+\r
+    //\r
+    // see if up or down took you to a bad spot\r
+    //\r
+    while (!spotok[row][collumn])\r
+      collumn--;\r
+\r
+    if (chh==0x4b)     // left arrow\r
+    {\r
+      if (collumn==0)\r
+       collumn=4;\r
+      while (!spotok[row][--collumn]);\r
+    }\r
+\r
+    if (chh==0x4d)     // right arrow\r
+    {\r
+      while (!spotok[row][++collumn] || collumn>3)\r
+       if (collumn==4)\r
+         collumn=-1;\r
+    }\r
+\r
+\r
+    if (chl==13)               // return\r
+    {\r
+      switch (row)\r
+      {\r
+       case 0:\r
+              if ( (int) newgrmode == collumn+1 )\r
+                break;                 // don't reload if not needed\r
+              drawchar(collumnx[(int) newgrmode-1]+1,rowy[row]+3,32);\r
+              grmode = newgrmode = (grtype) collumn + 1;       // becuase TEXT is 0\r
+              loadgrfiles ();\r
+              setscreenmode (grmode);\r
+              if (grmode==EGAgr)\r
+                moveega();\r
+              drawwindow (0,0,39,24);\r
+              drawpanel ();\r
+              break;\r
+       case 1:\r
+              drawchar(collumnx[(int) newsoundmode]+1,rowy[row]+3,32);\r
+              newsoundmode = (soundtype) collumn;\r
+              break;\r
+       case 2:\r
+              drawchar(collumnx[(int) newplayermode[1]]+1,rowy[row]+3,32);\r
+              newplayermode[1] = (inputtype) collumn;\r
+              if (newplayermode[1]==keyboard)\r
+                calibratekeys ();\r
+              else if (newplayermode[1]==mouse)\r
+                calibratemouse ();\r
+              else if (newplayermode[1]==joystick1)\r
+                calibratejoy (1);\r
+              else if (newplayermode[1]==joystick2)\r
+                calibratejoy (2);\r
+              drawpanel ();\r
+              break;\r
+      }\r
+      drawchar(collumnx[collumn]+1,rowy[row]+3,15);\r
+    }\r
+\r
+//////////////////////////////////////\r
+\r
+  } while (chl!=27);   // ESC to quit\r
+\r
+//\r
+// done, so return to game\r
+//\r
+  playermode[1] = newplayermode[1];\r
+  playermode[2] = newplayermode[2];\r
+\r
+  grmode = newgrmode;\r
+\r
+  screencenterx = oldcenterx;\r
+  screencentery = oldcentery;\r
+\r
+  soundmode = newsoundmode;\r
+  repaintscreen ();            // have the game redraw everything\r
+  ContinueSound ();            // continue any sound that was playing\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= installgrfile\r
+=\r
+= Loads a PC-arcade graphic file\r
+= grmode must be set so it knows if it is an EGA planed file\r
+=\r
+= Can be a normal uncompressed file or an RLE'd file\r
+=\r
+===================\r
+*/\r
+\r
+unsigned egaplane[4];                  // main memory paragraph of plane image\r
+spritetype image, spritetable[NUMSPRITES];     // grfile headers\r
+pictype pictable[NUMPICS];\r
+void far *lastgrpic;\r
+\r
+int numchars,numtiles,numpics,numsprites;\r
+\r
+void installgrfile (char *filename, int unpack,void huge *inmem)\r
+{\r
+  int i;\r
+  unsigned long a,b,c,d;\r
+  typedef pictype ptype[NUMPICS];\r
+  typedef spritetype stype[NUMSPRITES];\r
+\r
+  typedef struct {void huge *charptr;\r
+                 void huge *tileptr;\r
+                 void huge *picptr;\r
+                 void huge *spriteptr;\r
+                 ptype huge *pictableptr;\r
+                 stype huge *spritetableptr;\r
+                 void huge *plane[4];\r
+                 int numchars,numtiles,numpics,numsprites;\r
+                } picfiletype;\r
+\r
+  picfiletype huge *picfile;\r
+\r
+  stype huge *(spriteinfile);\r
+  ptype huge *(picinfile);\r
+\r
+\r
+  if (!filename[0])\r
+       {\r
+        picfile=(picfiletype huge *)inmem;\r
+       }\r
+  else\r
+       {\r
+        if ( (long)lastgrpic )\r
+          farfree ((void far *)lastgrpic); // so new graphics modes will free it up\r
+\r
+        if (unpack)\r
+          picfile = (picfiletype huge *) bloadin /* LZW */ (filename);\r
+        else\r
+          picfile = (picfiletype huge *) bloadin (filename);\r
+\r
+        lastgrpic = (void far *) lastparalloc;\r
+       }\r
+\r
+  numchars = picfile->numchars;\r
+  numtiles = picfile->numtiles;\r
+  numpics = picfile->numpics;\r
+  numsprites = picfile->numsprites;\r
+\r
+\r
+  if (grmode==EGAgr)           // EGA is special because of bit plane grief!\r
+  {\r
+    charptr = MK_FP(EGADATASTART,0);\r
+    tileptr = MK_FP(EGADATASTART+FP_SEG(picfile->tileptr)-FP_SEG(picfile->charptr),0);\r
+    picptr = MK_FP(EGADATASTART+FP_SEG(picfile->picptr)-FP_SEG(picfile->charptr),0);\r
+    spriteptr = MK_FP(EGADATASTART+FP_SEG(picfile->spriteptr)-FP_SEG(picfile->charptr),0);\r
+\r
+    for (i=0;i<4;i++)\r
+    {\r
+      egaplane[i] = FP_SEG(picfile->plane[i])+FP_SEG(picfile);\r
+      egaspriteptr[i] = MK_FP(FP_SEG(picfile)+FP_SEG(picfile->plane[i]) +\r
+       FP_SEG(picfile->spriteptr) - FP_SEG(picfile->charptr),0);\r
+    }\r
+  }\r
+  else\r
+  {\r
+    charptr = MK_FP(FP_SEG(picfile)+FP_SEG(picfile->charptr),0);\r
+    tileptr = MK_FP(FP_SEG(picfile)+FP_SEG(picfile->tileptr),0);\r
+    picptr = MK_FP(FP_SEG(picfile)+FP_SEG(picfile->picptr),0);\r
+    spriteptr = MK_FP(FP_SEG(picfile)+FP_SEG(picfile->spriteptr),0);\r
+  }\r
+  //\r
+  // copy tables into data segment\r
+  //\r
+  picinfile = MK_FP(FP_SEG(picfile->pictableptr)+FP_SEG(picfile)\r
+    ,FP_OFF(picfile->pictableptr)+FP_OFF(picfile));\r
+  spriteinfile = MK_FP(FP_SEG(picfile->spritetableptr)+FP_SEG(picfile)\r
+    ,FP_OFF(picfile->spritetableptr)+FP_OFF(picfile));\r
+  for (i=0; i<NUMPICS; i++)\r
+    pictable[i] = (*picinfile)[i];\r
+  for (i=0; i<NUMSPRITES; i++)\r
+    spritetable[i] = (*spriteinfile)[i];\r
+\r
+}\r
+\r
+\r
+\r
+\r
+/*=========================================================================*/\r
+\r
+//////////////////////////\r
+//\r
+// moveega\r
+// moves the standard stuff into EGA memory\r
+// needs to be called after each setgrmode to ega to refill memory\r
+//\r
+//////////////////////////\r
+void moveega (void)\r
+{\r
+  int plane;\r
+\r
+  for (plane=0;plane<4;plane++)\r
+  {\r
+    outportb (SCindex,SCmapmask);\r
+    outportb (SCindex+1,1<<plane);     // write plane #\r
+\r
+    movedata (egaplane[plane],0,EGADATASTART,0,0xffff-EGADATASTART);\r
+  }\r
+  outportb (SCindex,SCmapmask);                // read map select\r
+  outportb (SCindex+1,15);     // all planes\r
+}\r
+\r
diff --git a/NGRABCA2.H b/NGRABCA2.H
new file mode 100644 (file)
index 0000000..653d798
--- /dev/null
@@ -0,0 +1,36 @@
+/* The Catacomb Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// header file created by NGRAB v 1.5\r
+\r
+#define CGAPIC 0\r
+#define EGAPIC 1\r
+#define VGAPIC 2\r
+#define NOEGAPIC 3\r
+#define NOVGAPIC 4\r
+#define NOSOUNDPIC 5\r
+#define SPKRPIC 6\r
+#define KBDPIC 7\r
+#define JOY1PIC 8\r
+#define JOY2PIC 9\r
+#define MOUSEPIC 10\r
+#define NOJOYPIC 11\r
+#define NOMOUSEPIC 12\r
+#define SIDEPIC 13\r
+#define TITLEPIC 14\r
+#define ENDPIC 15\r
diff --git a/OBJECTS.C b/OBJECTS.C
new file mode 100644 (file)
index 0000000..67efab2
--- /dev/null
+++ b/OBJECTS.C
@@ -0,0 +1,324 @@
+/* The Catacomb Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// initialize object info\r
+//\r
+\r
+void initobjects()\r
+{\r
+\r
+// with objdef[player] do\r
+\r
+    objdef[player].think = playercmd;\r
+    objdef[player].contact = benign;\r
+    objdef[player].solid = true;\r
+    objdef[player].firstchar = tile2s;\r
+    objdef[player].size  = 2;\r
+    objdef[player].stages  = 4;\r
+    objdef[player].dirmask = 3;\r
+    objdef[player].speed = 256;\r
+    objdef[player].hitpoints = 12;\r
+    objdef[player].damage  = 0;\r
+    objdef[player].points  = 0;\r
+\r
+\r
+// with objdef[goblin]. do\r
+\r
+    objdef[goblin].think = ramstraight;\r
+    objdef[goblin].contact = monster;\r
+    objdef[goblin].solid = true;\r
+    objdef[goblin].firstchar = tile2s+64;\r
+    objdef[goblin].size  = 2;\r
+    objdef[goblin].stages  = 4;\r
+    objdef[goblin].dirmask = 3;\r
+    objdef[goblin].speed = 75;\r
+    objdef[goblin].hitpoints = 1;\r
+    objdef[goblin].damage  = 1;\r
+    objdef[goblin].points  = 50;\r
+\r
+\r
+// with objdef[skeleton]. do\r
+\r
+    objdef[skeleton].think = ramdiag;\r
+    objdef[skeleton].contact = monster;\r
+    objdef[skeleton].solid = true;\r
+    objdef[skeleton].firstchar = tile2s+128;\r
+    objdef[skeleton].size  = 2;\r
+    objdef[skeleton].stages  = 4;\r
+    objdef[skeleton].dirmask = 3;\r
+    objdef[skeleton].speed = 130;\r
+    objdef[skeleton].hitpoints = 1;\r
+    objdef[skeleton].damage  = 1;\r
+    objdef[skeleton].points  = 150;\r
+\r
+\r
+// with objdef[ogre]. do\r
+\r
+    objdef[ogre].think = ramstraight;\r
+    objdef[ogre].contact = monster;\r
+    objdef[ogre].solid = true;\r
+    objdef[ogre].firstchar = tile3s;\r
+    objdef[ogre].size  = 3;\r
+    objdef[ogre].stages  = 4;\r
+    objdef[ogre].dirmask = 3;\r
+    objdef[ogre].speed = 75;\r
+    objdef[ogre].hitpoints = 5;\r
+    objdef[ogre].damage  = 2;\r
+    objdef[ogre].points  = 250;\r
+\r
+\r
+// with objdef[gargoyle]. do\r
+\r
+    objdef[gargoyle].think = gargcmd;\r
+    objdef[gargoyle].contact = monster;\r
+    objdef[gargoyle].solid = true;\r
+    objdef[gargoyle].firstchar = tile4s;\r
+    objdef[gargoyle].size  = 4;\r
+    objdef[gargoyle].stages  = 4;\r
+    objdef[gargoyle].dirmask = 3;\r
+    objdef[gargoyle].speed = 175;\r
+    objdef[gargoyle].hitpoints = 10;\r
+    objdef[gargoyle].damage  = 3;\r
+    objdef[gargoyle].points  = 500;\r
+\r
+\r
+// with objdef[dragon]. do\r
+\r
+    objdef[dragon].think = dragoncmd;\r
+    objdef[dragon].contact = monster;\r
+    objdef[dragon].solid = true;\r
+    objdef[dragon].firstchar = tile5s;\r
+    objdef[dragon].size  = 5;\r
+    objdef[dragon].stages  = 4;\r
+    objdef[dragon].dirmask = 3;\r
+    objdef[dragon].speed = 100;\r
+    objdef[dragon].hitpoints = 75;\r
+    objdef[dragon].damage  = 5;\r
+    objdef[dragon].points  = 1000;\r
+\r
+\r
+// with objdef[turbogre]. do\r
+\r
+    objdef[turbogre].think = ramstraight;\r
+    objdef[turbogre].contact = monster;\r
+    objdef[turbogre].solid = true;\r
+    objdef[turbogre].firstchar = tile3s+19*9;\r
+    objdef[turbogre].size  = 3;\r
+    objdef[turbogre].stages  = 4;\r
+    objdef[turbogre].dirmask = 3;\r
+    objdef[turbogre].speed = 255;\r
+    objdef[turbogre].hitpoints = 5;\r
+    objdef[turbogre].damage  = 2;\r
+    objdef[turbogre].points  = 500;\r
+\r
+\r
+\r
+// with objdef[wallhit]. do\r
+\r
+    objdef[wallhit].think = fade;\r
+    objdef[wallhit].contact = benign;\r
+    objdef[wallhit].solid = true;\r
+    objdef[wallhit].firstchar = 26;\r
+    objdef[wallhit].size  = 1;\r
+    objdef[wallhit].stages  = 3;\r
+    objdef[wallhit].dirmask = 0;\r
+    objdef[wallhit].speed = 80;\r
+    objdef[wallhit].hitpoints = 0;\r
+    objdef[wallhit].damage  = 0;\r
+    objdef[wallhit].points  = 0;\r
+\r
+\r
+// with objdef[dead1]. do\r
+\r
+    objdef[dead1].think = explode;\r
+    objdef[dead1].contact = benign;\r
+    objdef[dead1].solid = false;\r
+    objdef[dead1].firstchar = 29;\r
+    objdef[dead1].size  = 1;\r
+    objdef[dead1].stages  = 3;\r
+    objdef[dead1].dirmask = 0;\r
+    objdef[dead1].speed = 80;\r
+    objdef[dead1].hitpoints = 0;\r
+    objdef[dead1].damage  = 0;\r
+    objdef[dead1].points  = 0;\r
+\r
+\r
+// with objdef[dead2]. do\r
+\r
+    objdef[dead2].think = fade;\r
+    objdef[dead2].contact = benign;\r
+    objdef[dead2].solid = false;\r
+    objdef[dead2].firstchar = tile2s+224;\r
+    objdef[dead2].size  = 2;\r
+    objdef[dead2].stages  = 3;\r
+    objdef[dead2].dirmask = 0;\r
+    objdef[dead2].speed = 80;\r
+    objdef[dead2].hitpoints = 0;\r
+    objdef[dead2].damage  = 0;\r
+    objdef[dead2].points  = 0;\r
+\r
+\r
+// with objdef[dead3]. do\r
+\r
+    objdef[dead3].think = fade;\r
+    objdef[dead3].contact = benign;\r
+    objdef[dead3].solid = false;\r
+    objdef[dead3].firstchar = tile3s + 9*16;\r
+    objdef[dead3].size  = 3;\r
+    objdef[dead3].stages  = 3;\r
+    objdef[dead3].dirmask = 0;\r
+    objdef[dead3].speed = 80;\r
+    objdef[dead3].hitpoints = 0;\r
+    objdef[dead3].damage  = 0;\r
+    objdef[dead3].points  = 0;\r
+\r
+\r
+// with objdef[dead4]. do\r
+\r
+    objdef[dead4].think = fade;\r
+    objdef[dead4].contact = benign;\r
+    objdef[dead4].solid = false;\r
+    objdef[dead4].firstchar = tile4s + 16*16;\r
+    objdef[dead4].size  = 4;\r
+    objdef[dead4].stages  = 3;\r
+    objdef[dead4].dirmask = 0;\r
+    objdef[dead4].speed = 80;\r
+    objdef[dead4].hitpoints = 0;\r
+    objdef[dead4].damage  = 0;\r
+    objdef[dead4].points  = 0;\r
+\r
+\r
+// with objdef[dead5]. do\r
+\r
+    objdef[dead5].think = fade;\r
+    objdef[dead5].contact = benign;\r
+    objdef[dead5].solid = false;\r
+    objdef[dead5].firstchar = tile5s + 25*16;\r
+    objdef[dead5].size  = 5;\r
+    objdef[dead5].stages  = 3;\r
+    objdef[dead5].dirmask = 0;\r
+    objdef[dead5].speed = 80;\r
+    objdef[dead5].hitpoints = 0;\r
+    objdef[dead5].damage  = 0;\r
+    objdef[dead5].points  = 0;\r
+\r
+\r
+// with objdef[shot]. do\r
+\r
+    objdef[shot].think = straight;\r
+    objdef[shot].contact = pshot;\r
+    objdef[shot].solid = false;\r
+    objdef[shot].firstchar = 154;\r
+    objdef[shot].size  = 1;\r
+    objdef[shot].stages  = 2;\r
+    objdef[shot].dirmask = 3;\r
+    objdef[shot].speed = 256;\r
+    objdef[shot].hitpoints = 0;\r
+    objdef[shot].damage  = 1;\r
+    objdef[shot].points  = 0;\r
+\r
+\r
+// with objdef[guns]. do\r
+\r
+    objdef[guns].think = gunthinks;\r
+    objdef[guns].contact = benign;\r
+    objdef[guns].solid = true;\r
+    objdef[guns].firstchar = tile3s-8;\r
+    objdef[guns].size  = 2;\r
+    objdef[guns].stages  = 1;\r
+    objdef[guns].dirmask = 0;\r
+    objdef[guns].speed = 10;\r
+    objdef[guns].hitpoints = 255;\r
+    objdef[guns].damage  = 0;\r
+    objdef[guns].points  = 0;\r
+\r
+\r
+// with objdef[gune]. do\r
+\r
+    objdef[gune].think = gunthinke;\r
+    objdef[gune].contact = benign;\r
+    objdef[gune].solid = true;\r
+    objdef[gune].firstchar = tile3s-4;\r
+    objdef[gune].size  = 2;\r
+    objdef[gune].stages  = 1;\r
+    objdef[gune].dirmask = 0;\r
+    objdef[gune].speed = 10;\r
+    objdef[gune].hitpoints = 255;\r
+    objdef[gune].damage  = 0;\r
+    objdef[gune].points  = 0;\r
+\r
+\r
+// with objdef[rock]. do\r
+\r
+    objdef[rock].think = straight;\r
+    objdef[rock].contact = mshot;\r
+    objdef[rock].solid = false;\r
+    objdef[rock].firstchar = 153;\r
+    objdef[rock].size  = 1;\r
+    objdef[rock].stages  = 2;\r
+    objdef[rock].dirmask = 0;\r
+    objdef[rock].speed = 256;\r
+    objdef[rock].hitpoints = 0;\r
+    objdef[rock].damage  = 1;\r
+    objdef[rock].points  = 0;\r
+\r
+\r
+// with objdef[bigshot]. do\r
+\r
+    objdef[bigshot].think = straight;\r
+    objdef[bigshot].contact = nukeshot;\r
+    objdef[bigshot].solid = false;\r
+    objdef[bigshot].firstchar = tile2s+192;\r
+    objdef[bigshot].size  = 2;\r
+    objdef[bigshot].stages  = 2;\r
+    objdef[bigshot].dirmask = 3;\r
+    objdef[bigshot].speed = 256;\r
+    objdef[bigshot].hitpoints = 0;\r
+    objdef[bigshot].damage  = 1;\r
+    objdef[bigshot].points  = 0;\r
+\r
+\r
+// with objdef[teleporter]. do\r
+\r
+    objdef[teleporter].think = idle;\r
+    objdef[teleporter].contact = benign;\r
+    objdef[teleporter].solid = false;\r
+    objdef[teleporter].firstchar = tile2s+236;\r
+    objdef[teleporter].size  = 2;\r
+    objdef[teleporter].stages  = 5;\r
+    objdef[teleporter].dirmask = 0;\r
+    objdef[teleporter].speed = 200;\r
+    objdef[teleporter].hitpoints = 0;\r
+    objdef[teleporter].damage  = 0;\r
+    objdef[teleporter].points  = 0;\r
+\r
+// with objdef[secretgate]. do\r
+\r
+    objdef[secretgate].think = idle;\r
+    objdef[secretgate].contact = benign;\r
+    objdef[secretgate].solid = false;\r
+    objdef[secretgate].firstchar = tile3s-12;\r
+    objdef[secretgate].size  = 2;\r
+    objdef[secretgate].stages  = 1;\r
+    objdef[secretgate].dirmask = 0;\r
+    objdef[secretgate].speed = 200;\r
+    objdef[secretgate].hitpoints = 0;\r
+    objdef[secretgate].damage  = 0;\r
+    objdef[secretgate].points  = 0;\r
+}\r
diff --git a/OLDCAT.C b/OLDCAT.C
new file mode 100644 (file)
index 0000000..61ff1fd
--- /dev/null
+++ b/OLDCAT.C
@@ -0,0 +1,1211 @@
+/* The Catacomb Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+** Catacomb II -- The C translation...\r
+*/\r
+\r
+#include "PCRLIB.H"\r
+\r
+#define maxpics 2047\r
+#define numtiles 24*24-1   /*number of tiles displayed on screen*/\r
+#define numlevels 30\r
+#define maxobj 400           /*maximum possible active objects*/\r
+#define solidwall 129\r
+#define blankfloor 128\r
+#define leftoff 11\r
+#define topoff 11\r
+#define tile2s 256          /*tile number where the 2*2 pictures start*/\r
+#define tile3s tile2s+64*4\r
+#define tile4s tile3s+19*9\r
+#define tile5s tile4s+19*16\r
+#define lasttile tile5s+19*25\r
+\r
+typedef enum {nosnd,blockedsnd,itemsnd,treasuresnd,bigshotsnd,shotsnd,\r
+    tagwallsnd,tagmonsnd,tagplayersnd,killmonsnd,killplayersnd,opendoorsnd,\r
+    potionsnd,spellsnd,noitemsnd,gameoversnd,highscoresnd,leveldonesnd,\r
+    foundsnd} soundenum;\r
+\r
+typedef enum {playercmd,gargcmd,dragoncmd,ramstraight,ramdiag,straight,idle,\r
+    fade,explode} thinktype;\r
+\r
+typedef enum {benign,monster,pshot,mshot,nukeshot} tagtype;\r
+\r
+typedef enum {nothing,player,goblin,skeleton,ogre,gargoyle,dragon,wallhit,\r
+    shot,bigshot,rock,dead1,dead2,dead3,dead4,dead5,dead6,teleporter,\r
+    torch,lastclass} classtype;\r
+\r
+typedef struct {\r
+  boolean active;      /*if false, the object has not seen the player yet*/\r
+  classtype  class;\r
+  byte  x,y,           /*location of upper left corner in world*/\r
+    stage,             /*animation frame being drawn*/\r
+    delay;             /*number of frames to pause without doing anything*/\r
+  dirtype  dir;                /*direction facing*/\r
+  byte hp,             /*hit points*/\r
+    oldx,oldy;         /*position where it was last drawn*/\r
+  int oldtile;         /*origin tile when last drawn*/\r
+  char filler[1];      /*pad to 16 bytes*/\r
+   } activeobj;\r
+\r
+typedef struct {       /*holds a copy of ActiveObj, and its class info*/\r
+  boolean  active;     /*if false, the object has not seen the player yet*/\r
+  classtype  class;\r
+  byte  x,y,           /*location of upper left corner in world*/\r
+    stage,             /*animation frame being drawn*/\r
+    delay;             /*number of frames to pause without doing anything*/\r
+  dirtype  dir;                /*direction facing*/\r
+  byte hp,             /*hit points*/\r
+    oldx,oldy;         /*position where it was last drawn*/\r
+  int oldtile;         /*origin tile when last drawn*/\r
+  char filler[1];      /*pad to 16 bytes*/\r
+\r
+  thinktype think;\r
+  tagtype contact;\r
+  boolean  solid;\r
+  word  firstchar;\r
+  byte  size;\r
+  byte  stages;\r
+  byte  dirmask;\r
+  word  speed;\r
+  byte  hitpoints;\r
+  byte  damage;\r
+  word  points;\r
+  char filler2[1];     /*pad to 32 bytes*/\r
+  } objdesc;\r
+\r
+\r
+/*=================*/\r
+/*                */\r
+/* typed constants */\r
+/*                        */\r
+/*=================*/\r
+  char altmeters[14][13] = {\r
+ {0,0,0,0,0,0,0,0,0,0,0,0,0},\r
+ {190,0,0,0,0,0,0,0,0,0,0,0,0},\r
+ {190,192,0,0,0,0,0,0,0,0,0,0,0},\r
+ {190,191,192,0,0,0,0,0,0,0,0,0,0},\r
+ {190,191,191,192,0,0,0,0,0,0,0,0,0},\r
+ {190,191,191,191,192,0,0,0,0,0,0,0,0},\r
+ {190,191,191,191,191,192,0,0,0,0,0,0,0},\r
+ {190,191,191,191,191,191,192,0,0,0,0,0,0},\r
+ {190,191,191,191,191,191,191,192,0,0,0,0,0},\r
+ {190,191,191,191,191,191,191,191,192,0,0,0,0},\r
+ {190,191,191,191,191,191,191,191,191,192,0,0,0},\r
+ {190,191,191,191,191,191,191,191,191,191,192,0,0},\r
+ {190,191,191,191,191,191,191,191,191,191,191,192,0},\r
+ {190,191,191,191,191,191,191,191,191,191,191,191,193} };\r
+\r
+  char meters[14][13] = {\r
+ {0,0,0,0,0,0,0,0,0,0,0,0,0},\r
+ {194,0,0,0,0,0,0,0,0,0,0,0,0},\r
+ {194,196,0,0,0,0,0,0,0,0,0,0,0},\r
+ {194,195,196,0,0,0,0,0,0,0,0,0,0},\r
+ {194,195,195,196,0,0,0,0,0,0,0,0,0},\r
+ {194,195,195,195,196,0,0,0,0,0,0,0,0},\r
+ {194,195,195,195,195,196,0,0,0,0,0,0,0},\r
+ {194,195,195,195,195,195,196,0,0,0,0,0,0},\r
+ {194,195,195,195,195,195,195,196,0,0,0,0,0},\r
+ {194,195,195,195,195,195,195,195,196,0,0,0,0},\r
+ {194,195,195,195,195,195,195,195,195,196,0,0,0},\r
+ {194,195,195,195,195,195,195,195,195,195,196,0,0},\r
+ {194,195,195,195,195,195,195,195,195,195,195,196,0},\r
+ {194,195,195,195,195,195,195,195,195,195,195,195,193} };\r
+\r
+ dirtype opposite[9] =\r
+    {south,west,north,east,southwest,northwest,northeast,southeast,nodir};\r
+\r
+\r
+/*==================*/\r
+/*                 */\r
+/* global variables */\r
+/*                 */\r
+/*==================*/\r
+  enum {game,demogame,demosave,editor} playmode;\r
+  enum {quited,killed,reseted,victorious} gamexit; /*determines what to do after playloop*/\r
+\r
+  int oldtiles [numtiles];             /*tile displayed last refresh*/\r
+  int background[87][86];              /*base map*/\r
+  int view[87][86];                    /*base map with objects drawn in*/\r
+  int originx, originy;                        /*current world location of UL corn*/\r
+  byte priority [maxpics+1];           /*tile draw overlap priorities*/\r
+\r
+  int items[6];\r
+  int shotpower;                       /*0-13 characters in power meter*/\r
+  int side;                            /*which side shots come from*/\r
+  int boltsleft;                       /*number of shots left in a bolt*/\r
+\r
+  activeobj o[maxobj+1];               /*everything that moves is here*/\r
+  objdesc obj , altobj;                        /*total info about objecton and alt*/\r
+  int altnum;                          /*o[#] of altobj*/\r
+  int numobj,objecton;                 /*number of objects in O now*/\r
+\r
+  struct {\r
+    thinktype think;                   /*some of these sizes are for the*/\r
+    tagtype contact;                   /*convenience of the assembly routines*/\r
+    boolean solid;\r
+    word firstchar;\r
+    byte size;\r
+    byte stages;\r
+    byte dirmask;\r
+    word speed;\r
+    byte hitpoints;\r
+    byte damage;\r
+    word points;\r
+    byte filler[2];\r
+  } ObjDef [lastclass];\r
+\r
+\r
+  int i,j,k,x,y,z;\r
+  boolean playdone, leveldone;\r
+\r
+  boolean tempb;\r
+  char far *tempp;\r
+\r
+  int chkx,chky,chkspot;               /*spot being checked by WALK*/\r
+\r
+  word frameon;\r
+  char far *grmem;\r
+  classtype clvar;\r
+\r
+/****************************************************************************/\r
+\r
+//////////////////////////////////\r
+//\r
+// function prototypes\r
+//\r
+//////////////////////////////////\r
+\r
+void extern DrawObj (void);\r
+void extern EraseObj (void);\r
+void extern DoAll (void);\r
+void extern EGAmove (void);\r
+void extern CGArefresh (void);\r
+void extern EGArefresh (void);\r
+\r
+\r
+\r
+/*==============================*/\r
+/*                             */\r
+/* xxxREFRESH                   */\r
+/* Refresh the changed areas of */\r
+/* the tiles map in the various */\r
+/* graphics modes.              */\r
+/*                             */\r
+/*==============================*/\r
+\r
+char demowin [5][16] = {\r
+  {14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16},\r
+  {17,' ','-','-','-',' ','D','E','M','O',' ','-','-','-',' ',18},\r
+  {17,'S','P','A','C','E',' ','T','O',' ','S','T','A','R','T',18},\r
+  {17,'F','1',' ','T','O',' ','G','E','T',' ','H','E','L','P',18},\r
+  {19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21} };\r
+\r
+void refresh (void)\r
+{\r
+  int x,y,basex,basey;\r
+  word underwin [5][16];\r
+\r
+  basex=originx+4;\r
+  basey=originy+17;\r
+  if (playmode==demogame)\r
+    for (y=0; x<=4; y++)\r
+      for (x=0; x<=15; x++)\r
+       {\r
+         underwin[y][x]=view[y+basey][x+basex];\r
+         view[y+basey][x+basex]=demowin[y][x+1];\r
+       };\r
+\r
+  WaitVBL ();\r
+  if (grmode==CGAgr)\r
+    CGArefresh();\r
+  else\r
+    EGArefresh();\r
+\r
+  if (playmode==demogame)\r
+    for (y=0; x<=4; y++)\r
+      for (x=0; x<=15; x++)\r
+       view[y+basey][x+basex]=underwin[y][x];\r
+  WaitVBL ();\r
+}\r
+\r
+\r
+void simplerefresh(void)\r
+{\r
+  WaitVBL ();\r
+  if (grmode==CGAgr)\r
+    CGArefresh();\r
+  else\r
+    EGArefresh();\r
+\r
+}\r
+\r
+\r
+/*======================================*/\r
+/*                                     */\r
+/* RESTORE                              */\r
+/* Redraws every tile on the tiled area */\r
+/* by setting oldtiles to -1.  Used to  */\r
+/* erase any temporary windows.         */\r
+/*                                     */\r
+/*======================================*/\r
+\r
+void clearold (void)\r
+{\r
+  memset (&oldtiles,0xff,sizeof(oldtiles)); /*clear all oldtiles*/\r
+};\r
+\r
+\r
+void restore (void)\r
+{\r
+  clearold ();\r
+  SimpleRefresh ();\r
+};\r
+\r
+\r
+/*      */\r
+/* Help */\r
+/*      */\r
+boolean wantmore (void)\r
+{\r
+  sx=2;\r
+  sy=20;\r
+  Print ("(SPACE for more/ESC)");\r
+  sx=12;\r
+  sy=21;\r
+  ch = get ();\r
+  if (ch==chr(27))\r
+    return false;\r
+\r
+  return true;\r
+};\r
+\r
+\r
+/*        */\r
+/* charpic */\r
+/*        */\r
+void charpic(int x,int y, classtype c, dirtype dir, int stage)\r
+{\r
+  int xx,yy,size,tilenum;\r
+\r
+  size=ObjDef[c].size;\r
+  tilenum=ObjDef[c].firstchar+size*size\r
+    * ((integer(dir) & ObjDef[c].dirmask)*ObjDef[c].stages+stage);\r
+\r
+  for (yy=y;yy<=y+size-1;yy++)\r
+    for (xx=x;xx<=x+size-1;xx++)\r
+      {\r
+       charout (xx,yy,tilenum);\r
+       inc(tilenum);\r
+      };\r
+};\r
+\r
+void help (void)\r
+{\r
+  int x,y;\r
+\r
+  CenterWindow (20,20);\r
+  Print ("  C A T A C O M B   \n");\r
+  Print ("   - - - - - - -    \n");\r
+  Print (" By John Carmack &  \n");\r
+  Print ("     PC Arcade      \n");\r
+  Print ("\n");\r
+  Print ("F1 = Help           \n");\r
+  Print ("F2 = Sound on / off \n");\r
+  Print ("F3 = Controls       \n");\r
+  Print ("F4 = Game reset     \n");\r
+  Print ("F9 = Pause          \n");\r
+  Print ("F10= Quit           \n");\r
+  Print ("\n");\r
+  Print ("Watch the demo for  \n");\r
+  Print ("a play example.     \n");\r
+  Print ("\n");\r
+  Print ("Hit fire at the demo\n");\r
+  Print ("to begin playing.   \n");\r
+  if (!Wantmore())\r
+    return;\r
+\r
+  CenterWindow (20,20);\r
+  Print ("\nKeyboard controls:  \n\n");\r
+  Print ("Move    : Arrows    \n");\r
+  Print ("Button1 : Ctrl      \n");\r
+  Print ("Button2 : Alt       \n");\r
+  Print ("\nTo switch to mouse \n");\r
+  Print ("or joystick control,\n");\r
+  Print ("hit F3.             \n");\r
+\r
+  if (!Wantmore())\r
+    return;\r
+\r
+  CenterWindow (20,20);\r
+  Print ("Button 1 / CTRL key:\n");\r
+  Print ("Builds shot power.  \n");\r
+  Print ("If the shot power   \n");\r
+  Print ("meter is full when  \n");\r
+  Print ("the button is       \n");\r
+  Print ("released, a super   \n");\r
+  Print ("shot will be        \n");\r
+  Print ("launched.           \n");\r
+  Print ("\n");\r
+  for (y=11; y<=18; y++)\r
+    for (x=3; x<=20; x++)\r
+      Charout (x,y,128);\r
+\r
+  charpic (4,14,player,east,2);\r
+  charpic (19,15,shot,east,1);\r
+  charpic (17,14,shot,east,0);\r
+  charpic (15,15,shot,east,1);\r
+  charpic (8,14,bigshot,east,0);\r
+\r
+  if (!Wantmore())\r
+    return;\r
+\r
+  CenterWindow (20,20);\r
+  Print ("Button 2 / ALT key:\n");\r
+  Print ("Allows you to move  \n");\r
+  Print ("without changing the\n");\r
+  Print ("direction you are   \n");\r
+  Print ("facing.  Good for   \n");\r
+  Print ("searching walls and \n");\r
+  Print ("fighting retreats.  \n");\r
+  for (y=11; y<=18; y++)\r
+    for (x=3; x<=20; x++)\r
+      if (y==15)\r
+       charout (x,y,129);\r
+      else if (y==16)\r
+       charout (x,y,131);\r
+      else\r
+       charout (x,y,128);\r
+  charpic (6,13,player,south,2);\r
+  sx=6;\r
+  sy=15;\r
+  print ("\35\35\36\36\37\37");\r
+\r
+  if (!Wantmore())\r
+    return;\r
+\r
+  CenterWindow (20,20);\r
+  Print (""P"" or ""SPACE"" will \n");\r
+  Print ("take a healing      \n");\r
+  Print ("potion if you have  \n");\r
+  Print ("one.  This restores \n");\r
+  Print ("the body meter to   \n");\r
+  Print ("full strength.  Keep\n");\r
+  Print ("a sharp eye on the  \n");\r
+  Print ("meter, because when \n");\r
+  Print ("it runs out, you are\n");\r
+  Print ("dead!               \n\n");\r
+  Print ("""B"" will cast a bolt\n");\r
+  Print ("spell if you have   \n");\r
+  Print ("any.  You can mow   \n");\r
+  Print ("down a lot of       \n");\r
+  Print ("monsters with a bit \n");\r
+  Print ("of skill.           \n");\r
+\r
+  if (!Wantmore())\r
+    return;\r
+\r
+  CenterWindow (20,20);\r
+  Print ("""N"" or ""ENTER"" will \n");\r
+  Print ("cast a nuke spell.  \n");\r
+  Print ("This usually wipes  \n");\r
+  Print ("out all the monsters\n");\r
+  Print ("near you.  Consider \n");\r
+  Print ("it a panic button   \n");\r
+  Print ("when you are being  \n");\r
+  Print ("mobbed by monsters! \n\n");\r
+  Print ("              \200\200\200\n");\r
+  Print ("Potions:       \200\242\200\n");\r
+  Print ("              \200\200\200\n");\r
+  Print ("Scrolls:       \200\243\200\n");\r
+  Print (" (bolts/nukes) \200\200\200\n");\r
+  Print ("Treasure:      \200\247\200\n");\r
+  Print (" (points)      \200\200\200\n");\r
+  Print ("              \200\200\200\n");\r
+\r
+  Wantmore();\r
+\r
+};\r
+\r
+/*       */\r
+/* Reset */\r
+/*       */\r
+void reset(void)\r
+{\r
+  CenterWindow (18,1);\r
+  Print ("Reset game (Y/N)?");\r
+  ch= get ();\r
+  if (ch=='Y')\r
+    {\r
+      gamexit=killed;\r
+      playdone=true;\r
+    };\r
+};\r
+\r
+\r
+/*===========================*/\r
+/*                          */\r
+/* CHECKKEYS                 */\r
+/* If a key has been pressed */\r
+/* it will be assigned to CH/*/\r
+/* altkey, and if it is an F */\r
+/* key, it will be processed.*/\r
+/*                          */\r
+/*===========================*/\r
+\r
+void checkkeys (void)\r
+\r
+{\r
+};\r
+\r
+\r
+/*=========================================================================*/\r
+\r
+\r
+/*==============================*/\r
+/*                             */\r
+/* LOADLEVEL / SAVELEVEL        */\r
+/* Loads map LEVEL into memory, */\r
+/* and sets everything up.      */\r
+/*                             */\r
+/*==============================*/\r
+\r
+void loadlevel(void)\r
+{\r
+\r
+  classtype tokens[256-230]  =\r
+    {player,teleporter,goblin,skeleton,ogre,gargoyle,dragon,nothing,\r
+     nothing,nothing,nothing,nothing,nothing,nothing,nothing,nothing,\r
+     nothing,nothing,nothing,nothing,nothing,nothing,nothing,nothing,\r
+     nothing,nothing};\r
+\r
+  char filename[64],st[64];\r
+  int x,y,xx,yy,recs, btile;\r
+  char sm[4096];\r
+\r
+  strcpy (filename,"LEVEL");\r
+  itoa (level,st,10);\r
+  strcat (filename,level);\r
+\r
+  LoadFile (filename,sm);\r
+\r
+  numobj=0;\r
+  o[0].x=13;          /*just defaults if no player token is found*/\r
+  o[0].y=13;\r
+  o[0].stage=0;\r
+  o[0].delay=0;\r
+  o[0].dir=east;\r
+  o[0].oldx=0;\r
+  o[0].oldy=0;\r
+  o[0].oldtile=-1;\r
+\r
+\r
+  for (yy=0; yy<64; yy++)\r
+    for (xx=0; xx<64; xx++)\r
+      {\r
+       btile=sm[yy*64+xx];\r
+       if (btile<230)\r
+         background[yy+topoff][xx+leftoff]=btile;\r
+       else\r
+         {\r
+\r
+/*hit a monster token*/\r
+           background[yy+topoff][xx+leftoff]=blankfloor;\r
+           if (tokens[btile-230]==player)\r
+\r
+/*the player token determines where you start in level*/\r
+\r
+             {\r
+               o[0].x=xx+topoff;\r
+               o[0].y=yy+leftoff;\r
+             }\r
+            else\r
+\r
+/*monster tokens add to the object list*/\r
+\r
+             {\r
+               numobj++;\r
+               o[numobj].active=false;\r
+               o[numobj].class=tokens[btile-230];\r
+               o[numobj].x=xx+leftoff;\r
+               o[numobj].y=yy+topoff;\r
+               o[numobj].stage=0;\r
+               o[numobj].delay=0;\r
+               o[numobj].dir=(dirtype)(rndt()/64);  /*random 0-3*/\r
+               o[numobj].hp=ObjDef[o[numobj].class].hitpoints;\r
+               o[numobj].oldx=x;\r
+               o[numobj].oldy=y;\r
+               o[numobj].oldtile=-1;\r
+             };\r
+\r
+           };\r
+\r
+         };\r
+\r
+\r
+\r
+  originx = o[0].x-11;\r
+  originy = o[0].y-11;\r
+\r
+  shotpower=0;\r
+  for (y=topoff-1; y<65+topoff; y++)\r
+    for (x=leftoff-1; x<64+leftoff; x++)\r
+      view[y][x]=background[y][x];\r
+\r
+  sx=33;                  /*print the new level number on the right window*/\r
+  sy=1;\r
+  printint (level);\r
+  Print (" ");          /*in case it went from double to single digit*/\r
+  restore();\r
+};\r
+\r
+\r
+/*==========================================================================*/\r
+\r
+\r
+#include "cat_play.c"\r
+\r
+#include "objects.c"\r
+\r
+\r
+\r
+/*========================================*/\r
+/*                                       */\r
+/* Finished                               */\r
+/* SHows the } page...                    */\r
+/*                                       */\r
+/*========================================*/\r
+\r
+void finished()\r
+{\r
+  playsound (treasuresnd);\r
+  WaitEndSound();\r
+  playsound (treasuresnd);\r
+  WaitEndSound();\r
+  playsound (treasuresnd);\r
+  WaitEndSound();\r
+  playsound (treasuresnd);\r
+  WaitEndSound();\r
+\r
+  sx=20;\r
+  sy=24;\r
+  get ();\r
+\r
+};\r
+\r
+\r
+/*================================*/\r
+/*                               */\r
+/* PLAYSETUP                      */\r
+/* Set up all data for a new game */\r
+/* Does NOT start it playing      */\r
+/*                               */\r
+/*================================*/\r
+\r
+void playsetup()\r
+{\r
+  int i;\r
+\r
+  score=0;\r
+  shotpower=0;\r
+  level=1;\r
+  if (keydown [0x2E] || keydown [0x14])   /*hold down 'C' and 'T' to CheaT!*/\r
+  {\r
+    CenterWindow (16,2);\r
+    Print ("Warp to which\nlevel (1-99)?");\r
+    inputint (level);\r
+    if (level<1)\r
+      level=1;\r
+    if (level>30)\r
+      level=30;\r
+    restore();\r
+  };\r
+\r
+  for (i=1; i<6; i++)\r
+    items[i]=0;\r
+\r
+  o[0].active = true;\r
+  o[0].class = player;\r
+  o[0].hp = 13;\r
+  o[0].dir=west;\r
+  o[0].stage=0;\r
+  o[0].delay=0;\r
+\r
+  DrawWindow (24,0,38,23);  /*draw the right side window*/\r
+  Print ("  Level\n\nScore:\n\nTop  :\n\nK:\nP:\nB:\nN:\n\n\n");\r
+  Print (" Shot Power\n\n\n    Body\n\n\n");\r
+  printhighscore();\r
+  printbody();\r
+  printshotpower();\r
+\r
+/*give them a few items to start with*/\r
+\r
+  givenuke();\r
+  givenuke();\r
+  givebolt();\r
+  givebolt();\r
+  givebolt();\r
+  givepotion();\r
+  givepotion();\r
+  givepotion();\r
+\r
+};\r
+\r
+\r
+\r
+/*================================*/\r
+/*                               */\r
+/* GAMEOVER                       */\r
+/* Do a game over bit,  check     */\r
+/* for a high score,  return      */\r
+/* to demo.                       */\r
+/*                               */\r
+/*================================*/\r
+\r
+void gameover()\r
+{\r
+  int place,i,j;\r
+  char st[64];\r
+\r
+  WaitEndSound();\r
+  simplerefresh();\r
+};\r
+\r
+\r
+/****************************************************************************/\r
+\r
+\r
+#if 0\r
+\r
+/*====================*/\r
+/*                   */\r
+/* EDITORLOOP         */\r
+/* The editor mode... */\r
+/*                   */\r
+/*====================*/\r
+\r
+void editorloop()\r
+\r
+Label\r
+  cmdover;\r
+\r
+const\r
+  samplepics : array[1..12] of string[13] =\r
+    ("\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80",\r
+     "\x80\x80\x81\x81\x87\x80\x80\x80\x87\x87\xb1\x80\x80",\r
+     "\x80\x81\x81\x81\x81\x87\x80\x87\x87\x87\x87\xb1\x80",\r
+     "\x80\x81\x81\x81\x81\x82\x80\x87\xb2\xb3\xb4\xac\x80",\r
+     "\x80\x86\x81\x81\x85\x84\x80\xb0\x87\x87\xaf\xae\x80",\r
+     "\x80\x80\x86\x83\x84\x80\x80\x80\xb0\xad\xae\x80\x80",\r
+     "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80",\r
+     "\x80\xa2\x80\xa3\x80\xa4\x80\xa7\x80\xa5\x80\xa6\x80",\r
+     "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80",\r
+     "\x80\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\x80",\r
+     "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80",\r
+     "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80");\r
+\r
+var\r
+  drawtile:integer;\r
+  ltx,lty,ltt,x,y,i:integer;\r
+  dor: dirtype;\r
+  b1,b2: boolean;\r
+/*$i-*/\r
+\r
+/*                           */\r
+/*                           */\r
+/* LOADLEVEL                    */\r
+/* Loads map LEVEL into memory, */\r
+/* nothing more                 */\r
+/*                           */\r
+/*                           */\r
+\r
+Procedure LoadLevel;\r
+label\r
+  tryopen,fileread;\r
+Var\r
+  filename : string;\r
+  st: string[3];\r
+  x,y,xx,yy,recs, btile : Integer;\r
+  iofile: file;\r
+  tile: byte;\r
+  sm : array [0..4095] of byte;\r
+\r
+{\r
+  str(level:1,st);\r
+  filename=concat ('LEVEL',st,'.CAT');\r
+\r
+tryopen:\r
+\r
+  Assign (iofile,filename);\r
+  Reset (iofile,1);\r
+  If ioresult<>0\r
+/*create a blank level for the editor*/\r
+      {\r
+       for x=0 to 63 do\r
+         for y=0 to 63 do\r
+           background[y+topoff,x+leftoff]=blankfloor;\r
+       for x=0 to 63 do\r
+         {\r
+           background[topoff,x]=131;     /*perspective north wall*/\r
+           background[topoff+63,x]=129;  /*solid south wall*/\r
+           background[x,leftoff]=130;    /*perspective west wall*/\r
+           background[x,leftoff+63]=129; /*solid east wall*/\r
+         };\r
+       background [topoff,leftoff]=133;  /*perspective NW corner*/\r
+        goto fileread;\r
+      }\r
+\r
+    else\r
+\r
+  BlockRead (iofile,sm,4096,recs);\r
+  Close (iofile);\r
+\r
+  numobj=0;\r
+\r
+  for yy=0 to 63 do\r
+    for xx=0 to 63 do\r
+      {\r
+       tile=sm[yy*64+xx];\r
+\r
+/*if tile is an exploding block, change it to a special icon for editor*/\r
+\r
+       if (tile>=136) and (tile<=145)\r
+         tile=tile+35;\r
+       background[yy+topoff,xx+leftoff]=tile;\r
+      };\r
+\r
+fileread:\r
+\r
+  for y=topoff to 63+topoff do\r
+    for x=leftoff to 63+leftoff do\r
+      view[y,x]=background[y,x];\r
+  sx=33;                  /*print the new level number on the right window*/\r
+  sy=1;\r
+  shortnum (level);\r
+  Print (' ');          /*in case it went from double to single digit*/\r
+  restore;\r
+};\r
+\r
+\r
+\r
+/*         */\r
+/* Save Level */\r
+/*         */\r
+Procedure Saveit;\r
+Var\r
+  iofile : file;\r
+  filename : string;\r
+  x,y,recs : Integer;\r
+  tile: byte;\r
+  st: string[3];\r
+  sm : array [0..4095] of byte;\r
+{\r
+  CenterWindow (9,1);\r
+  Print ('Saving...');\r
+  For y=0 to 63 do\r
+    for x=0 to 63 do\r
+      {\r
+       tile=background[y+topoff,x+leftoff] and $00FF;\r
+\r
+/*if the tile was an exploding block, change back to undetectable*/\r
+\r
+       if (tile>=171) and (tile<=180)\r
+         tile=tile-35;\r
+       sm[y*64+x]=tile;\r
+      };\r
+  str(level:1,st);\r
+  filename=concat ('LEVEL',st,'.CAT');\r
+  Assign (iofile,filename);\r
+  Rewrite (iofile,1);\r
+  BlockWrite (iofile,sm,4096,recs);\r
+  Close (iofile);\r
+  restore;\r
+};\r
+\r
+\r
+\r
+/*           */\r
+/* Select Level */\r
+/*           */\r
+function SelectLevel:boolean;\r
+Var\r
+  err:integer;\r
+  lv:string;\r
+{\r
+  selectlevel=false;              /*editor won't reload a level if false*/\r
+  CenterWindow (16,2);\r
+  Print ('Edit which level](1-99):');\r
+  input (lv,2);\r
+  if lv[1]=chr(27)            /*allow ESC to quit editor mode*/\r
+    {\r
+      leveldone=true;\r
+      playdone=true;\r
+    };\r
+  val (lv,level,err);\r
+  If level>=1\r
+    selectlevel=true;\r
+  restore;\r
+};\r
+\r
+\r
+/*           */\r
+/* Toggle Block */\r
+/*           */\r
+Procedure ToggleBlock;\r
+Var\r
+  x,y,block:integer;\r
+{\r
+  x=originx+topoff;\r
+  y=originy+leftoff;\r
+  block=background [y,x];\r
+\r
+  If block=blankfloor\r
+    block=solidwall\r
+  else\r
+    block=blankfloor;\r
+\r
+  background [y,x]=block;\r
+  view [y,x]=block;\r
+};\r
+\r
+/*        */\r
+/* Print map */\r
+/*        */\r
+Procedure PrintMap;\r
+var\r
+  x,y,block:integer;\r
+  ch: char;\r
+{\r
+  writeln (lst);\r
+  Writeln (lst,'CATACOMB level ',level);\r
+  for y=0 to 65 do\r
+    {\r
+      for x=0 to 65 do\r
+       {\r
+         block=background[topoff-1+y,leftoff-1+x];\r
+         case block of\r
+           0..127: ch=chr(block);      /*ASCII*/\r
+           128: ch=' ';                /*floor*/\r
+           129..135: ch='#';           /*walls*/\r
+           171..177: ch='*';           /*exploding*/\r
+           178..180: ch='!';           /*hidden stuff*/\r
+           162: ch='p';                /*potion*/\r
+           163: ch='s';                /*scroll*/\r
+           164: ch='k';                /*key*/\r
+           165: ch='|';                /*door*/\r
+           166: ch='-';                /*door*/\r
+           167: ch='$';                /*treasure*/\r
+           230..238: ch=chr(ord('0')+block-229); /*tokens*/\r
+           else ch='?';\r
+         };\r
+         write (lst,ch);\r
+      };\r
+    writeln (lst);\r
+  };\r
+  writeln (lst,chr(12));\r
+};\r
+\r
+/*==================================*/\r
+\r
+{\r
+\r
+  regs.ax=0;\r
+  intr($33,regs);    /*show the mouse cursor*/\r
+\r
+  DrawWindow (24,0,38,23);  /*draw the right side window*/\r
+  Print ('  Level]] Map editor]]F4=exit]F7=Load]F8=Save]^P=Print');\r
+\r
+  sx=25;\r
+  leftedge=sx;\r
+  sy=10;\r
+  for i=1 to 12 do\r
+    Print (samplepics[i]+']');\r
+\r
+  drawtile=solidwall;\r
+  ltx=28;\r
+  lty=13;\r
+  ltt=solidwall;\r
+  xormask=$FFFF;\r
+  charout (ltx,lty,ltt);         /*highlight the new block*/\r
+  xormask=0;\r
+\r
+  level=1;\r
+  playdone=false;\r
+\r
+  Repeat\r
+    leveldone=false;\r
+    originx=0;\r
+    originy=0;\r
+\r
+    If selectlevel  /*let them choose which level to edit*/\r
+      loadlevel\r
+    else\r
+      goto cmdover;     /*so if they pressed ESC, they can leave*/\r
+\r
+    repeat\r
+      SimpleRefresh;\r
+\r
+      regs.ax=1;\r
+      intr($33,regs);    /*show the mouse cursor*/\r
+      waitvbl;           /*make sure it gets seen*/\r
+      waitvbl;\r
+\r
+      Repeat\r
+       regs.ax=3;\r
+       intr($33,regs);  /*mouse status*/\r
+      Until keypressed or (regs.bx and 3>0);\r
+\r
+      sx=regs.cx div 16;   /*tile on screen mouse is over*/\r
+      sy=regs.dx div 8;\r
+\r
+      regs.ax=2;\r
+      intr($33,regs);    /*hide the mouse cursor*/\r
+\r
+      Checkkeys;       /*handles F keys and returns a keypress*/\r
+\r
+      ch=chr(0);\r
+      altkey=false;\r
+      if keypressed\r
+       {\r
+         ch=upcase(readkey);\r
+         if ch=chr(0)\r
+           {\r
+             altkey=true;\r
+             ch=readkey;\r
+           }\r
+       };\r
+\r
+      if (sx<24) and (sy<24)\r
+/*buttons pressed in tile map*/\r
+       {\r
+         x=originx+sx;\r
+         y=originy+sy;\r
+          if (x>=leftoff) and (x<leftoff+64) and\r
+            (y>=topoff) and (y<topoff+64)\r
+           {\r
+             if (regs.bx and 1>0)\r
+\r
+/*left button places/deletes a DRAWTILE*/\r
+\r
+               {\r
+                 background[y,x]=drawtile;\r
+                 view[y,x]=drawtile;\r
+               };\r
+\r
+             if (regs.bx and 2>0)   /*right button places a BLANKFLOOR*/\r
+               {\r
+                 background[y,x]=blankfloor;\r
+                 view[y,x]=blankfloor;\r
+               };\r
+\r
+             if (not altkey) and ((ch>='A') and (ch<='Z')\r
+             or ((ch>='0') and (ch<='9') ) )\r
+               {\r
+                 if (ch>='0') and (ch<='9')\r
+                   background[y,x]=ord(ch)+161   /*map numbers are later*/\r
+                 else\r
+                   background[y,x]=ord(ch)+32; /*map letters are lowercase*/\r
+                 view[y,x]=background[y,x];\r
+                 regs.ax=4;\r
+                 regs.cx=regs.cx+16;\r
+                 intr ($33,regs);        /*move the mouse over*/\r
+               };\r
+\r
+             if (not altkey) and (ch=' ')   /*space makes a solidwall*/\r
+               {\r
+                 background[y,x]=solidwall;\r
+                 view[y,x]=solidwall;\r
+                 regs.ax=4;\r
+                 regs.cx=regs.cx+16;\r
+                 intr ($33,regs);        /*move the mouse over*/\r
+               };\r
+\r
+           };\r
+       };\r
+\r
+\r
+      x=sx-24;\r
+      y=sy-9;\r
+      if  (regs.bx and 1>0) and (x>0) and (x<14) and (y>0) and (y<13)and\r
+       (samplepics[y][x]<>#128)\r
+/*button pressed in samplepics*/\r
+       {\r
+         charout (ltx,lty,ltt);         /*unhighlight the old DRAWTILE*/\r
+         drawtile=ord(samplepics[y][x]);\r
+         ltx=sx;\r
+         lty=sy;\r
+         ltt=drawtile;\r
+         xormask=$FFFF;\r
+         charout (ltx,lty,ltt);         /*highlight the new block*/\r
+         xormask=0;\r
+       };\r
+\r
+\r
+      Rd_Keyboard (dir,b1,b2);\r
+      case dir of\r
+       north: if originy>0\r
+               originy=originy-1\r
+              else\r
+               playsound (blockedsnd);\r
+       west: if originx>0\r
+\r
+               originx=originx-1\r
+              else\r
+               playsound(blockedsnd);\r
+       east: if originx<51+leftoff\r
+               originx=originx+1\r
+              else\r
+               playsound(blockedsnd);\r
+       south: if originy<51+topoff\r
+               originy=originy+1\r
+              else\r
+               playsound(blockedsnd);\r
+      };\r
+\r
+\r
+      If keydown[$19] and keydown[$1d]  /*control-P*/\r
+       PrintMap;\r
+\r
+      If keydown[$42]\r
+       {\r
+         keydown[$42]=false;\r
+         SaveIt;\r
+       };\r
+\r
+      If keydown[$41]\r
+       {\r
+         keydown[$41]=false;\r
+         leveldone=true;        /*so SELECTLEVEL will be called*/\r
+       };\r
+\r
+cmdover:\r
+\r
+    Until leveldone or playdone;\r
+  Until playdone;\r
+\r
+};\r
+\r
+#endif\r
+\r
+/***************************************************************************/\r
+/*=========================*/\r
+/*                        */\r
+/* M A I N   P R O G R A M */\r
+/*                        */\r
+/*=========================*/\r
+\r
+void main (void)\r
+{\r
+  initobjects();\r
+\r
+  memset (&priority,99,sizeof(priority));\r
+\r
+  priority[blankfloor]=0;\r
+  for (i=ObjDef[teleporter].firstchar; i<=ObjDef[teleporter].firstchar+20;i++)\r
+    priority[i]=0;\r
+  for (clvar=dead2; clvar<=dead5; clvar++)\r
+    for (i=ObjDef[clvar].firstchar; i<=ObjDef[clvar].firstchar+\r
+    ObjDef[clvar].size*ObjDef[clvar].size; i++)\r
+      priority[i]=0;           /*deadthing*/\r
+  for (i=152; i<=161; i++)\r
+    priority[i]=2;             /*shots*/\r
+  for (i=ObjDef[bigshot].firstchar; i<= ObjDef[bigshot].firstchar + 31; i++)\r
+    priority[i]=2;             /*bigshot*/\r
+  for (i=0; i<=tile2s-1; i++)\r
+    if (priority [i]==99)\r
+      priority[i]=3;           /*most 1*1 tiles are walls, etc*/\r
+  for (i=tile2s; i<=maxpics; i++)\r
+    if (priority[i]==99)\r
+      priority[i]=4;           /*most bigger tiles are monsters*/\r
+  for (i=ObjDef[player].firstchar; i<= ObjDef[player].firstchar + 63; i++)\r
+    priority[i]=5;             /*player*/\r
+\r
+\r
+  side=0;\r
+\r
+  for (x=0; x<=85; x++)\r
+    {\r
+      for (y=0; y<=topoff-1; y++)\r
+       {\r
+         view[x][y]=solidwall;\r
+         view[x][85-y]=solidwall;\r
+         background[x][y]=solidwall;\r
+         background[x][85-y]=solidwall;\r
+       };\r
+      view[86][x]=solidwall;\r
+    };\r
+  for (y=11; y<=74; y++)\r
+    for (x=0; x<=leftoff-1; x++)\r
+      {\r
+       view[x][y]=solidwall;\r
+       view[85-x][y]=solidwall;\r
+       background[x][y]=solidwall;\r
+       background[85-x][y]=solidwall;\r
+      };\r
+\r
+\r
+  playmode=demogame;\r
+\r
+\r
+/*\r
+    {\r
+      playmode=demosave;\r
+      playsound (bigshotsnd);\r
+      WaitEndSound;\r
+    };\r
+*/\r
+\r
+  while (1)                    // keep going until _quit is called\r
+  {\r
+    switch (playmode)\r
+    {\r
+      case game:\r
+       playsetup();\r
+       playloop();\r
+       If (gamexit==killed)\r
+         gameover();\r
+       if (gamexit==victorious)\r
+       {\r
+         finished();\r
+         gameover();\r
+       };\r
+       playmode= demogame;\r
+       break;\r
+\r
+\r
+      case demogame:\r
+       PlaySetup();\r
+       PlayLoop();\r
+       if (playmode==demogame)\r
+       {\r
+         score=0;      /*so demo doersn't get a high score*/\r
+         GameOver();   /*if entire demo has cycled, show highs*/\r
+       };\r
+       break;\r
+\r
+      case editor :\r
+       EditorLoop();\r
+       playmode=demogame;\r
+       break;\r
+\r
+    };\r
+\r
+  }\r
+\r
+}\r
+\r
diff --git a/PCRLIB.H b/PCRLIB.H
new file mode 100644 (file)
index 0000000..470e269
--- /dev/null
+++ b/PCRLIB.H
@@ -0,0 +1,321 @@
+/* The Catacomb Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include <dos.h>\r
+#include <mem.h>\r
+#include <sys\stat.h>\r
+#include <fcntl.h>\r
+#include <alloc.h>\r
+#include <io.h>\r
+#include <alloc.h>\r
+#include <string.h>\r
+\r
+typedef enum {false,true} boolean;\r
+typedef unsigned char byte;\r
+typedef unsigned int word;\r
+\r
+char extern ch,str[80];\r
+\r
+/*=========================================================================*/\r
+\r
+/*\r
+** Sound routines\r
+** Tied into INT 8, with a speeded up timer!\r
+*/\r
+\r
+typedef enum {off,spkr,sdlib} soundtype;\r
+\r
+typedef struct {word start;\r
+               byte priority;\r
+               byte samplerate;\r
+               char name[12];} spksndtype;\r
+\r
+typedef struct {char id[4];\r
+               word filelength;\r
+               word filler[5];\r
+               spksndtype sounds[63];\r
+               word freqdata[];} SPKRtable;\r
+\r
+\r
+soundtype extern soundmode;\r
+char extern huge *SoundData;\r
+\r
+int extern _dontplay;\r
+\r
+void StartupSound (void);\r
+void ShutdownSound (void);\r
+void PlaySound (int sound);\r
+void PauseSound (void);\r
+void ContinueSound (void);\r
+void StopSound (void);\r
+void WaitEndSound (void);\r
+\r
+/*=========================================================================*/\r
+\r
+/*\r
+** Control routines\r
+** Ties into INT 9 to intercept all key presses, but passes on to BIOS\r
+** The control panel handles all this stuff!\r
+*/\r
+\r
+typedef enum {north,east,south,west,northeast,southeast,southwest,\r
+             northwest,nodir} dirtype;\r
+\r
+typedef struct {dirtype dir;\r
+               boolean button1,button2;} ControlStruct;\r
+\r
+typedef enum {keyboard,mouse,joystick1,joystick2,demo} inputtype;\r
+\r
+inputtype extern playermode[3];\r
+boolean        extern keydown[128];\r
+int extern JoyXlow[3], JoyXhigh[3], JoyYlow [3], JoyYhigh [3]; // 1&2 are used\r
+int extern MouseSensitivity;\r
+char extern key[8], keyB1, keyB2;\r
+\r
+enum demoenum {notdemo,demoplay,recording};\r
+enum demoenum extern indemo;\r
+\r
+void extern interrupt (*oldint9) ();\r
+\r
+void SetupKBD ();\r
+void interrupt Int9ISR ();\r
+void ShutdownKBD ();\r
+\r
+void ReadJoystick (int joynum,int *xcount,int *ycount);\r
+\r
+ControlStruct ControlKBD ();\r
+ControlStruct ControlMouse ();\r
+ControlStruct ControlJoystick (int joynum);\r
+ControlStruct ControlPlayer (int player);\r
+\r
+void LoadDemo (int demonum);\r
+void SaveDemo (int demonum);\r
+\r
+/*========================================================================*/\r
+\r
+/*\r
+** Miscellaneous library routines\r
+*/\r
+\r
+void extern far *lastparalloc;\r
+\r
+void huge *paralloc (long size);\r
+long unsigned int LoadFile(char *filename,char huge *buffer);\r
+void SaveFile(char *filename,char huge *buffer, long size);\r
+void huge *bloadin (char *filename);\r
+void huge *bloadinLZW (char *filename);\r
+long RLEcompress (void far *source, long length, void far *dest);\r
+\r
+void initrndt (boolean randomize);\r
+int rndt (void);\r
+\r
+void clearkeys (void);\r
+\r
+unsigned extern timecall,timeax,timebx,timecx,timedx,timesi,timedi,timebp,timees;\r
+int timesub (int ticks);\r
+\r
+int _MouseInit(void);\r
+void _MouseHide(void);\r
+void _MouseShow(void);\r
+int _MouseButton(void);\r
+void _MouseCoords(int *x,int *y);\r
+\r
+long _Verify(char *filename);\r
+\r
+void _printhexb(unsigned char value);\r
+void _printhex(unsigned value);\r
+void _printbin(unsigned value);\r
+\r
+/*========================================================================*/\r
+\r
+/*\r
+** Graphic routines\r
+*/\r
+\r
+#define SCindex 0x3C4\r
+#define SCmapmask 2\r
+#define GCindex 0x3CE\r
+#define GCmode 5\r
+#define CRTCLINECOMPARE 0x18\r
+#define CRTCOVERFLOW 0x7\r
+#define CRTCMAXSCANLINE 0x9\r
+#define CRTCSTARTL 0xd\r
+#define CRTCSTARTH 0xc\r
+\r
+typedef enum {text,CGAgr,EGAgr,VGAgr} grtype;\r
+typedef enum {NOcard,MDAcard,CGAcard,EGAcard,MCGAcard,VGAcard,\r
+             HGCcard=0x80,HGCPcard,HICcard} cardtype;\r
+\r
+grtype extern grmode;\r
+\r
+cardtype extern _videocard;\r
+\r
+int extern sx,sy,leftedge,xormask;     // stuff for screen text output\r
+\r
+word extern CGAylookup [200],EGAylookup[200],VGAylookup[200];\r
+\r
+unsigned extern crtcaddr;\r
+\r
+void setscreenmode (grtype mode);\r
+void WaitVBL (void);\r
+void EGAplane (int plane);\r
+void EGAlatch (void);\r
+void EGAsplitscreen (int linenum);\r
+void crtcstart (unsigned start);\r
+cardtype VideoID (void);\r
+\r
+/*=========================================================================*/\r
+\r
+/*\r
+** PC-Arcade graphic file format stuff\r
+*/\r
+\r
+#define NUMPICS 64\r
+#define NUMSPRITES 10\r
+\r
+unsigned extern EGADATASTART;\r
+\r
+typedef struct {\r
+                int width;\r
+                int height;\r
+                void far *shapeptr;            // reletive to spriteptr\r
+                void far *maskptr;\r
+                int xl,yl,xh,yh;               // death box pixel offsets\r
+                char name[12];\r
+              } spritetype;\r
+\r
+typedef struct {\r
+                int width;\r
+                int height;\r
+                void far *shapeptr;\r
+                char name[8];\r
+              } pictype;\r
+\r
+\r
+int extern numchars,numtiles,numpics,numsprites;\r
+\r
+spritetype extern image, spritetable[NUMSPRITES];      // grfile headers\r
+pictype extern pictable[NUMPICS];\r
+\r
+void extern huge *charptr;             // 8*8 tileset\r
+void extern huge *tileptr;             // 16*16 tileset\r
+void extern huge *picptr;              // any size picture set\r
+void extern huge *spriteptr;           // any size masked and hit rect sprites\r
+void extern huge *egaspriteptr[4];     // spriteptr for each plane\r
+\r
+unsigned extern screenseg;             // loaded into ES in the draw routines\r
+                                       // should be adjusted after grmode\r
+                                       // switches, page flipping, and scrolls\r
+\r
+void moveega (void);\r
+void installgrfile (char *filename,int unpack,void huge *inmem);\r
+\r
+void drawchar (int x, int y, int charnum);\r
+void drawtile (int x, int y, int picnum);\r
+void drawpic (int x, int y, int picnum);\r
+\r
+/*=========================================================================*/\r
+\r
+/*\r
+** higher level graphic routines\r
+*/\r
+\r
+int extern screencenterx ,screencentery, _yshift;\r
+\r
+void controlpanel (void);\r
+int get (void);\r
+int _input(char *string,int max);\r
+unsigned _inputint(void);\r
+void print (const char *str);\r
+void _printc(char *string);\r
+void _printhexb(unsigned char value);\r
+void _printhex(unsigned value);\r
+void printint (int val);\r
+void printlong (long val);\r
+void drawwindow (int xl, int yl, int xh, int yh);\r
+void erasewindow (void);\r
+void bar (int xl,int yl, int xh, int yh, int ch);\r
+void centerwindow (int width, int height);\r
+void expwin (int width, int height);\r
+void expwinh (int width, int height);\r
+void expwinv (int width, int height);\r
+\r
+/*=========================================================================*/\r
+\r
+/*\r
+** necessary routiones\r
+*/\r
+\r
+void loadgrfiles (void);       // call installgrfile + any other files\r
+void repaintscreen (void);     // do any screen wierdness and redraw all\r
+\r
+/*========================================================================*/\r
+\r
+/*\r
+** game level routines\r
+*/\r
+\r
+long extern score;\r
+int extern level;\r
+\r
+typedef struct { int width;\r
+                int height;\r
+                int planes;\r
+                int screenx;\r
+                int screeny;\r
+                int screenw;\r
+                int screenh;\r
+                unsigned planesize;\r
+              } LevelDef;\r
+\r
+int    extern  _numlevels, _maxplayers;\r
+boolean        extern  _cgaok, _egaok, _vgaok;\r
+char   extern  *_extension;\r
+\r
+struct scores {\r
+        long score;\r
+        int level;\r
+        char initials[4];\r
+       };\r
+\r
+struct scores extern scoreswap, highscores[5];\r
+\r
+\r
+void _loadctrls (void);\r
+void _savectrls (void);\r
+void _loadhighscores (void);\r
+void _savehighscores (void);\r
+void _showhighscores (void);\r
+void _checkhighscore (void);\r
+\r
+void _setupgame (void);\r
+void _quit (char *);           // shuts everything down\r
+\r
+/*=========================================================================*/\r
+\r
+\r
+unsigned int extern RLECompress\r
+  (char far *source, long sourcelen, char far *dest);\r
+\r
+void extern RLEExpand\r
+  (char far *source, char far *dest, long origlen);\r
+\r
+\r
+\r
+//NOLAN ADDED\r
+extern boolean GODMODE;
\ No newline at end of file
diff --git a/PCRLIB_A.ASM b/PCRLIB_A.ASM
new file mode 100644 (file)
index 0000000..f636bc5
--- /dev/null
@@ -0,0 +1,2439 @@
+; The Catacomb Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+       IDEAL\r
+       MODEL SMALL,C\r
+\r
+;\r
+;offsets into .SPK file at segment SPKRfile, offset 0\r
+;\r
+;sound records each take up 16 bytes, and start at $10, and continue to $3F0\r
+\r
+snd_start      equ     0\r
+snd_priority   equ     2\r
+snd_samples    equ     3\r
+snd_name       equ     4\r
+\r
+\r
+;\r
+; EGA registers\r
+;\r
+SCindex                equ     3C4h\r
+SCmapmask      equ     2\r
+GCindex                equ     3CEh\r
+GCmode                 equ     5\r
+\r
+\r
+       DATASEG\r
+\r
+\r
+;\r
+; stuff for pics and sprites\r
+;\r
+STRUC  picstruct               ; sprite info extracted by refresh\r
+width          dw      ?\r
+height         dw      ?\r
+shapeptr       dw      ?\r
+ENDS\r
+\r
+       EXTRN pictable: picstruct       ; array of picture info\r
+       EXTRN charptr:WORD\r
+       EXTRN tileptr:WORD\r
+       EXTRN picptr:WORD\r
+       EXTRN spriteptr:WORD    ;paraligned pointers\r
+       EXTRN grmode:WORD\r
+       EXTRN crtcaddr:WORD\r
+\r
+;\r
+; stuff for pics and sprites\r
+;\r
+STRUC  picstruct               ; sprite info extracted by refresh\r
+width          dw      ?\r
+height         dw      ?\r
+shapeptr       dw      ?\r
+ENDS\r
+\r
+       EXTRN   imageinfo: picstruct    ; array of picture info\r
+\r
+SPKactive dw    0                   ;set non zero when started\r
+\r
+SoundData      dd      ?       ;two word pointer to SPKR file, PARA aligned\r
+soundmode      dw      1       ;0=nosound, 1=SPKR, 2= adlib...\r
+OldInt8                dd      ?       ;StartupSPK saves here, Shutdown restores\r
+ExtraInts      db      ?       ;number of PlaySPKR's to a regular int 8\r
+Intcount       db      ?       ;counter for extraints, call OldInt8 at 0\r
+sndspeed       dw      ?       ;timer count speed\r
+\r
+SndPtr         dw      ?       ;Pointer to frequency of current sound\r
+SndPriority    db      ?       ;current sound's priority\r
+\r
+pausesndptr    dw      ?\r
+pausepriority  db      ?\r
+pauseextraints db      ?\r
+pauseintcount  db      ?\r
+pausespeed     dw      ?\r
+\r
+_dontplay      dw      0       ;set to 1 to avoid all interrupt and timer stuff\r
+\r
+xormask                dw      0       ;to invert characters\r
+_yshift                dw      0       ;to shift char lines\r
+\r
+screenseg      dw      0a000h  ;changes with grmode, page flips, & scrolls\r
+\r
+       PUBLIC soundmode,SoundData,xormask,screenseg,_dontplay\r
+\r
+         CODESEG\r
+\r
+       PUBLIC CGAylookup,EGAylookup,VGAylookup\r
+\r
+;========\r
+;\r
+; YLOOKUP has the offsets from screen mem of each screen line (multiples of 8)\r
+;\r
+;========\r
+\r
+CGAylookup dw    0,8192,  80,8272, 160,8352, 240,8432, 320,8512, 400,8592, 480,8672\r
+    dw  560,8752, 640,8832, 720,8912, 800,8992, 880,9072, 960,9152,1040,9232\r
+    dw 1120,9312,1200,9392,1280,9472,1360,9552,1440,9632,1520,9712,1600,9792\r
+    dw 1680,9872,1760,9952,1840,10032,1920,10112,2000,10192,2080,10272,2160,10352\r
+    dw 2240,10432,2320,10512,2400,10592,2480,10672,2560,10752,2640,10832,2720,10912\r
+    dw 2800,10992,2880,11072,2960,11152,3040,11232,3120,11312,3200,11392,3280,11472\r
+    dw 3360,11552,3440,11632,3520,11712,3600,11792,3680,11872,3760,11952,3840,12032\r
+    dw 3920,12112,4000,12192,4080,12272,4160,12352,4240,12432,4320,12512,4400,12592\r
+    dw 4480,12672,4560,12752,4640,12832,4720,12912,4800,12992,4880,13072,4960,13152\r
+    dw 5040,13232,5120,13312,5200,13392,5280,13472,5360,13552,5440,13632,5520,13712\r
+    dw 5600,13792,5680,13872,5760,13952,5840,14032,5920,14112,6000,14192,6080,14272\r
+    dw 6160,14352,6240,14432,6320,14512,6400,14592,6480,14672,6560,14752,6640,14832\r
+    dw 6720,14912,6800,14992,6880,15072,6960,15152,7040,15232,7120,15312,7200,15392\r
+    dw 7280,15472,7360,15552,7440,15632,7520,15712,7600,15792,7680,15872,7760,15952\r
+    dw 7840,16032,7920,16112\r
+\r
+EGAylookup dw    0,  40,  80, 120, 160, 200, 240, 280, 320, 360, 400, 440, 480, 520\r
+    dw  560, 600, 640, 680, 720, 760, 800, 840, 880, 920, 960,1000,1040,1080\r
+    dw 1120,1160,1200,1240,1280,1320,1360,1400,1440,1480,1520,1560,1600,1640\r
+    dw 1680,1720,1760,1800,1840,1880,1920,1960,2000,2040,2080,2120,2160,2200\r
+    dw 2240,2280,2320,2360,2400,2440,2480,2520,2560,2600,2640,2680,2720,2760\r
+    dw 2800,2840,2880,2920,2960,3000,3040,3080,3120,3160,3200,3240,3280,3320\r
+    dw 3360,3400,3440,3480,3520,3560,3600,3640,3680,3720,3760,3800,3840,3880\r
+    dw 3920,3960,4000,4040,4080,4120,4160,4200,4240,4280,4320,4360,4400,4440\r
+    dw 4480,4520,4560,4600,4640,4680,4720,4760,4800,4840,4880,4920,4960,5000\r
+    dw 5040,5080,5120,5160,5200,5240,5280,5320,5360,5400,5440,5480,5520,5560\r
+    dw 5600,5640,5680,5720,5760,5800,5840,5880,5920,5960,6000,6040,6080,6120\r
+    dw 6160,6200,6240,6280,6320,6360,6400,6440,6480,6520,6560,6600,6640,6680\r
+    dw 6720,6760,6800,6840,6880,6920,6960,7000,7040,7080,7120,7160,7200,7240\r
+    dw 7280,7320,7360,7400,7440,7480,7520,7560,7600,7640,7680,7720,7760,7800\r
+    dw 7840,7880,7920,7960,8000,8040,8080,8120,8160,8200,8240,8280,8320,8360\r
+    dw 8400,8440,8480,8520,8560,8600,8640,8680,8720,8760,8800,8840,8880,8920\r
+    dw 8960,9000,9040,9080,9120,9160,9200,9240,9280,9320,9360,9400,9440,9480\r
+    dw 9520,9560,9600,9640,9680,9720,9760,9800,9840,9880,9920,9960,10000,10040\r
+    dw 10080,10120,10160,10200\r
+\r
+VGAylookup dw    0, 320, 640, 960,1280,1600,1920,2240,2560,2880,3200,3520,3840,4160\r
+    dw 4480,4800,5120,5440,5760,6080,6400,6720,7040,7360,7680,8000,8320,8640\r
+    dw 8960,9280,9600,9920,10240,10560,10880,11200,11520,11840,12160,12480,12800,13120\r
+    dw 13440,13760,14080,14400,14720,15040,15360,15680,16000,16320,16640,16960,17280,17600\r
+    dw 17920,18240,18560,18880,19200,19520,19840,20160,20480,20800,21120,21440,21760,22080\r
+    dw 22400,22720,23040,23360,23680,24000,24320,24640,24960,25280,25600,25920,26240,26560\r
+    dw 26880,27200,27520,27840,28160,28480,28800,29120,29440,29760,30080,30400,30720,31040\r
+    dw 31360,31680,32000,32320,32640,-32576,-32256,-31936,-31616,-31296,-30976,-30656,-30336,-30016\r
+    dw -29696,-29376,-29056,-28736,-28416,-28096,-27776,-27456,-27136,-26816,-26496,-26176,-25856,-25536\r
+    dw -25216,-24896,-24576,-24256,-23936,-23616,-23296,-22976,-22656,-22336,-22016,-21696,-21376,-21056\r
+    dw -20736,-20416,-20096,-19776,-19456,-19136,-18816,-18496,-18176,-17856,-17536,-17216,-16896,-16576\r
+    dw -16256,-15936,-15616,-15296,-14976,-14656,-14336,-14016,-13696,-13376,-13056,-12736,-12416,-12096\r
+    dw -11776,-11456,-11136,-10816,-10496,-10176,-9856,-9536,-9216,-8896,-8576,-8256,-7936,-7616\r
+    dw -7296,-6976,-6656,-6336,-6016,-5696,-5376,-5056,-4736,-4416,-4096,-3776,-3456,-3136\r
+    dw -2816,-2496,-2176,-1856\r
+\r
+\r
+;=======================================================================\r
+\r
+;========\r
+;\r
+; StartupSound\r
+;\r
+; Sets up the new INT 8 ISR and various internal pointers.\r
+; Assumes that the calling program has pointer SOUNDDATA to something\r
+; meaningful...\r
+;\r
+;========\r
+\r
+PROC StartupSound\r
+       PUBLIC  StartupSound\r
+\r
+       test    [_dontplay],0ffffh\r
+       je      @@dowork\r
+       ret\r
+@@dowork:\r
+       test    [SPKactive],0FFFFh      ;see if library is active\r
+       jne     @@started               ;library was allready started\r
+\r
+@@start:\r
+       call    NEAR PTR StopSound      ;make sure nothing is playing\r
+\r
+       mov     ax,3508h        ;call bios to get int 8\r
+       int     21h\r
+       mov     [WORD oldint8],bx\r
+       mov     ax,es\r
+       mov     [WORD oldint8+2],ax\r
+\r
+       mov     ax,1\r
+       mov     [extraints],al          ;the timer is still going at the\r
+       mov     [intcount],al           ;normal rate now\r
+\r
+       push    ds\r
+\r
+       push    cs\r
+       pop     ds\r
+       lea     dx,[UpdateSPKR]\r
+       mov     ax,2508h        ;call bios to set int 8\r
+       int     21h\r
+\r
+       pop     ds\r
+\r
+       inc     [SPKactive]     ;sound routines are now active\r
+\r
+@@started:\r
+       mov     ax,1\r
+       mov     [soundmode],ax ;set soundmode to SPKR\r
+       ret\r
+\r
+ENDP\r
+\r
+;=======================================================================\r
+\r
+;========\r
+;\r
+; ShutdownSound\r
+;\r
+;========\r
+\r
+PROC   ShutdownSound\r
+       PUBLIC ShutdownSound\r
+\r
+       test    [_dontplay],0ffffh\r
+       je      @@dowork\r
+       ret\r
+@@dowork:\r
+       mov     al,36h          ;tell the timer chip we are going to\r
+       out     43h,al          ;change the speed of timer 0\r
+       mov     al,0            ;system expects 0000 for rate\r
+       out     40h,al          ;low\r
+       out     40h,al          ;high\r
+\r
+       mov     ax,[SPKactive]\r
+       cmp     ax,0\r
+       je      @@done          ;sound library wasn't started...\r
+\r
+       push    ds\r
+\r
+       mov     dx,[WORD Oldint8]\r
+       mov     ax,[WORD Oldint8+2]\r
+       mov     ds,ax\r
+       mov     ax,2508h        ;call bios to set int 8\r
+       int     21h\r
+\r
+       pop     ds\r
+\r
+       mov     [SPKactive],0   ;sound routines are now inactive\r
+\r
+       in      al,61h          ;get peripheral (speaker) port value\r
+       and     al,11111101b    ;turn speaker off\r
+       out     61h,al\r
+\r
+@@done:\r
+       ret\r
+\r
+ENDP\r
+\r
+;=========================================================================\r
+\r
+;===========\r
+;\r
+; PlaySound (soundnum)\r
+;\r
+; If the sound's priority is >= the current priority, SoundPtr, SndPriority,\r
+; and the timer speed are changed\r
+;\r
+;===========\r
+\r
+PROC   PlaySound playnum:WORD\r
+       PUBLIC PlaySound\r
+\r
+       test    [_dontplay],0ffffh\r
+       je      @@dowork\r
+       ret\r
+@@dowork:\r
+       mov     ax,[playnum]    ;index into the sound headers\r
+       push    si\r
+\r
+       mov     si,ax\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+\r
+       mov     ax,[WORD SoundData+2]\r
+       mov     es,ax           ;point es: to the spkr file\r
+\r
+       mov     al,[es:si+snd_Priority] ;priority table (one byte each)\r
+       cmp     al,[SndPriority]\r
+       jb      @@playdone      ;current sound has higher priority\r
+       mov     [SndPriority],al\r
+       mov     ax,[es:si+snd_Start]    ;offset in .SPK file\r
+       mov     [SndPtr],ax     ;store it in the sound playing table\r
+\r
+       mov     bl,[es:si+snd_samples]  ;samples / regular timer tick (1-255)\r
+       mov     [extraints],bl  ;an OldInt8 will be called after this\r
+       mov     [intcount],bl   ;many UpdateSPKR times have been called\r
+\r
+       cmp     bl,1            ;sample rate of 0 or 1 = 0000 for timer\r
+       ja      @@oktodiv\r
+       xor     bx,bx\r
+       jmp     @@settimer\r
+\r
+@@oktodiv:\r
+       xor     bh,bh\r
+       xor     ax,ax\r
+       mov     dx,1\r
+       div     bx              ;$10000 / samples = timer rate\r
+\r
+       mov     bx,ax\r
+       mov     [sndspeed],bx   ;save off the timer rate\r
+\r
+@@settimer:\r
+       mov     al,36h          ;tell the timer chip we are going to\r
+       out     43h,al          ;change the speed of timer 0\r
+\r
+       mov     al,bl           ;low byte of sample rate\r
+       out     40h,al\r
+       mov     al,bh           ;high byte of sample rate\r
+       out     40h,al\r
+\r
+@@playdone:\r
+       pop     si\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;======================================================================\r
+\r
+;===========\r
+;\r
+; StopSound\r
+;\r
+;===========\r
+\r
+PROC   StopSound\r
+       PUBLIC  StopSound\r
+\r
+       test    [_dontplay],0ffffh\r
+       je      @@dowork\r
+       ret\r
+@@dowork:\r
+       xor     ax,ax           ;set to 0\r
+       mov     [SndPtr],ax\r
+       mov     [SndPriority],al\r
+       mov     [sndspeed],ax\r
+\r
+       in      al,61h          ;get peripheral (speaker) port value\r
+       and     al,11111101b    ;turn speaker off\r
+       out     61h,al\r
+\r
+       mov     al,36h          ;tell the timer chip we are going to\r
+       out     43h,al          ;change the speed of timer 0\r
+\r
+       mov     al,0            ;back to normal clock speed\r
+       out     40h,al\r
+       out     40h,al\r
+\r
+       inc     al\r
+       mov     [extraints],al  ;one bios int / int8\r
+       mov     [intcount],al\r
+\r
+       ret\r
+ENDP\r
+\r
+;======================================================================\r
+\r
+;===========\r
+;\r
+; PauseSound\r
+;\r
+;===========\r
+\r
+PROC   PauseSound\r
+       PUBLIC  PauseSound\r
+\r
+       test    [_dontplay],0ffffh\r
+       je      @@dowork\r
+       ret\r
+@@dowork:\r
+       mov     ax,[SndPtr]     ;save off the current values\r
+       mov     [pausesndptr],ax\r
+       mov     al,[SndPriority]\r
+       mov     [pausepriority],al\r
+       mov     al,[extraints]\r
+       mov     [pauseextraints],al\r
+       mov     al,[intcount]\r
+       mov     [pauseintcount],al\r
+       mov     ax,[sndspeed]\r
+       mov     [pausespeed],ax\r
+       call    StopSound\r
+       ret\r
+ENDP\r
+\r
+;======================================================================\r
+\r
+;===========\r
+;\r
+; ContinueSound\r
+;\r
+;===========\r
+\r
+PROC   ContinueSound\r
+       PUBLIC  ContinueSound\r
+\r
+       test    [_dontplay],0ffffh\r
+       je      @@dowork\r
+       ret\r
+@@dowork:\r
+       mov     ax,[pausesndptr]\r
+       mov     [SndPtr],ax     ;restore the old values\r
+       mov     al,[pausepriority]\r
+       mov     [SndPriority],al\r
+       mov     al,[pauseextraints]\r
+       mov     [extraints],al\r
+       mov     al,[pauseintcount]\r
+       mov     [intcount],al\r
+\r
+       mov     bx,[pausespeed]\r
+\r
+       mov     al,36h          ;tell the timer chip we are going to\r
+       out     43h,al          ;change the speed of timer 0\r
+\r
+       mov     al,bl           ;low byte of sample rate\r
+       out     40h,al\r
+       mov     al,bh           ;high byte of sample rate\r
+       out     40h,al\r
+\r
+\r
+       ret\r
+ENDP\r
+\r
+;======================================================================\r
+\r
+;========\r
+;\r
+; WaitendSound\r
+; Just waits around until the current sound stops playing\r
+;\r
+;========\r
+\r
+PROC   WaitEndSound\r
+       PUBLIC WaitEndSound\r
+\r
+       test    [_dontplay],0ffffh\r
+       je      @@dowork\r
+       ret\r
+@@dowork:\r
+       pushf\r
+       call FAR PTR UpdateSPKR ;in case a sound was just started and hasn't\r
+                               ;been hit by an INT yet\r
+@@wait:\r
+       mov     ax,[sndptr]\r
+       cmp     ax,0            ;when the ptr is 0, nothing is on\r
+       jne     @@wait\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+;=========================================================================\r
+\r
+;========\r
+;\r
+; UpdateSPKR\r
+; only called by interrupt $8!\r
+;\r
+;=========\r
+\r
+PROC   UpdateSPKR FAR\r
+\r
+       push    ax\r
+       push    bx\r
+       push    cx\r
+       push    si\r
+       push    ds\r
+       push    es\r
+\r
+       mov     ax,_DATA\r
+       mov     ds,ax           ;ds to this data segment\r
+       mov     ax,[WORD SoundData+2]\r
+       mov     es,ax           ;es to sound file\r
+\r
+       mov     al,20h\r
+       out     20h,al          ;we got the interrupt here\r
+\r
+       dec     [intcount]      ;see if it is time for a BIOS int 8...\r
+       jne     @@dosounds\r
+\r
+       mov     al,[extraints]\r
+       mov     [intcount],al   ;reset interrupt counter\r
+\r
+       pushf                   ;so the IRET from bios returns right\r
+       call    [OldInt8]       ;call the old BIOS timer routine\r
+\r
+@@dosounds:\r
+;\r
+; play the speaker\r
+;\r
+       mov     si,[SndPtr]\r
+       cmp     si,0\r
+       je      @@nosound       ;nothing playing\r
+\r
+       mov     bx,[es:si]\r
+       inc     [SndPtr]\r
+       inc     [SndPtr]\r
+\r
+       cmp     bx,0\r
+       je      @@nosound       ;a zero frequency is no sound, but don't stop\r
+\r
+       cmp     bx,-1           ;a -1 frequency is end of sound\r
+       jne     @@playfreq\r
+\r
+       call    StopSound\r
+       jmp     @@doneplay\r
+\r
+@@nosound:\r
+       in      al,61h          ;get peripheral (speaker) port value\r
+       and     al,11111100b    ;turn speaker off\r
+       out     61h,al\r
+       jmp     @@doneplay\r
+\r
+@@playfreq:\r
+       test    [soundmode],0FFh        ;if soundon=0, don't play anything\r
+       je      @@nosound\r
+\r
+       mov     al,10110110b    ;write to channel 2 (speaker) timer\r
+       out     43h,al\r
+       mov     al,bl\r
+       out     42h,al          ;low byte\r
+       mov     al,bh\r
+       out     42h,al          ;high byte\r
+\r
+       in      al,61h          ;get peripheral (speaker) port value\r
+       or      al,00000011b    ;turn speaker on to timer\r
+       out     61h,al\r
+\r
+@@doneplay:\r
+       pop     es\r
+       pop     ds\r
+       pop     si\r
+       pop     cx\r
+       pop     bx\r
+       pop     ax\r
+\r
+       iret\r
+\r
+\r
+ENDP\r
+\r
+;==========================================================================\r
+\r
+;========================================================================\r
+       DATASEG\r
+\r
+rndindex       dw      ?\r
+\r
+rndtable db    0,   8, 109, 220, 222, 241, 149, 107,  75, 248, 254, 140,  16,  66\r
+    db   74,  21, 211,  47,  80, 242, 154,  27, 205, 128, 161,  89,  77,  36\r
+    db   95, 110,  85,  48, 212, 140, 211, 249,  22,  79, 200,  50,  28, 188\r
+    db   52, 140, 202, 120,  68, 145,  62,  70, 184, 190,  91, 197, 152, 224\r
+    db  149, 104,  25, 178, 252, 182, 202, 182, 141, 197,   4,  81, 181, 242\r
+    db  145,  42,  39, 227, 156, 198, 225, 193, 219,  93, 122, 175, 249,   0\r
+    db  175, 143,  70, 239,  46, 246, 163,  53, 163, 109, 168, 135,   2, 235\r
+    db   25,  92,  20, 145, 138,  77,  69, 166,  78, 176, 173, 212, 166, 113\r
+    db   94, 161,  41,  50, 239,  49, 111, 164,  70,  60,   2,  37, 171,  75\r
+    db  136, 156,  11,  56,  42, 146, 138, 229,  73, 146,  77,  61,  98, 196\r
+    db  135, 106,  63, 197, 195,  86,  96, 203, 113, 101, 170, 247, 181, 113\r
+    db   80, 250, 108,   7, 255, 237, 129, 226,  79, 107, 112, 166, 103, 241\r
+    db   24, 223, 239, 120, 198,  58,  60,  82, 128,   3, 184,  66, 143, 224\r
+    db  145, 224,  81, 206, 163,  45,  63,  90, 168, 114,  59,  33, 159,  95\r
+    db   28, 139, 123,  98, 125, 196,  15,  70, 194, 253,  54,  14, 109, 226\r
+    db   71,  17, 161,  93, 186,  87, 244, 138,  20,  52, 123, 251,  26,  36\r
+    db   17,  46,  52, 231, 232,  76,  31, 221,  84,  37, 216, 165, 212, 106\r
+    db  197, 242,  98,  43,  39, 175, 254, 145, 190,  84, 118, 222, 187, 136\r
+    db  120, 163, 236, 249\r
+\r
+;\r
+; Random # Generator vars\r
+;\r
+indexi         dw      ?       ;Rnd#Generator\r
+indexj         dw      ?\r
+LastRnd                dw      ?\r
+RndArray       dw      17 dup (?)\r
+\r
+baseRndArray   dw      1,1,2,3,5,8,13,21,54,75,129,204\r
+               dw      323,527,850,1377,2227\r
+\r
+       CODESEG\r
+\r
+;=================================================\r
+;\r
+; Init RND generator\r
+; if randomize is false, the counter is set to 0\r
+;\r
+; 11-Sep-90    LR      FIX initialization to use TIME!\r
+;=================================================\r
+\r
+PROC   initrnd randomize:word\r
+       public  initrnd\r
+\r
+       push    si\r
+       push    di\r
+\r
+       mov     ax,ds\r
+       mov     es,ax\r
+       mov     di,offset RndArray\r
+       mov     si,offset baseRndArray\r
+       mov     cx,17\r
+       cld\r
+       rep     movsw           ;set up the table (which is constantly changed)\r
+\r
+       mov     [LastRnd],0\r
+       mov     [indexi],17*2\r
+       mov     [indexj],5*2\r
+\r
+       mov     ax,[randomize]\r
+       cmp     ax,0\r
+       je      @@setit         ;if randomize is true, really random\r
+\r
+       mov     ah,2ch\r
+       int     21h                     ;GetSystemTime\r
+\r
+       mov     [RndArray+34-2],dx\r
+       xor dx,cx                               ;init w/seconds values\r
+       mov     [RndArray+10-2],dx\r
+\r
+@@setit:\r
+       mov     ax,0ffffh\r
+       push    ax\r
+       call    Random                  ;warm up generator!\r
+       pop     ax\r
+\r
+       pop     di\r
+       pop     si\r
+       ret\r
+\r
+ENDP\r
+\r
+;=================================================\r
+;\r
+; Return a random # between 0-?\r
+; Exit : AX = 0-max value\r
+;\r
+; 11-Sep-90 LR -modify to save registers!\r
+;=================================================\r
+PROC   random maxval:WORD\r
+       public  random\r
+\r
+       push    si\r
+       push    bx                      ;save registers so we work OK!\r
+       push    cx\r
+       push    dx\r
+\r
+       mov     ax,[maxval]\r
+\r
+       push    ax                      ;save max value\r
+;\r
+; create a mask to cut down on the # of SUBTRACTS!\r
+;\r
+       mov     dx,0ffffh               ;full-mask\r
+@@0:\r
+       shl     ax,1\r
+       jc      @@0a\r
+       shr     dx,1\r
+       jmp     @@0\r
+@@0a:\r
+       mov     bx,[indexi]             ;this routine was converted from\r
+       mov     si,[indexj]             ;the Random macro on Merlin GS\r
+       mov     ax,[RndArray-2+bx]\r
+       adc     ax,[RndArray-2+si]\r
+       mov     [RndArray-2+bx],ax\r
+       add     ax,[LastRnd]\r
+       mov     [LastRnd],ax\r
+       dec     bx\r
+       dec     bx\r
+       jne     @@1\r
+       mov     bx,17*2\r
+@@1:\r
+       dec     si\r
+       dec     si\r
+       jne     @@2\r
+       mov     si,17*2\r
+@@2:\r
+       mov     [indexi],bx\r
+       mov     [indexj],si\r
+       pop     cx                      ;loop -- returns value in range\r
+       and     ax,dx                   ;AND our mask!\r
+@@3:\r
+       cmp     ax,cx                   ;SUBTRACT to be within range\r
+       jbe     @@4\r
+       shr     ax,1\r
+@@4:\r
+       pop     dx\r
+       pop     cx                              ;restore registers\r
+       pop     bx\r
+       pop     si\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+\r
+;===========================================================================\r
+\r
+;=================================================\r
+;\r
+; Init table based RND generator\r
+; if randomize is false, the counter is set to 0\r
+;\r
+;=================================================\r
+\r
+PROC   initrndt        randomize:word\r
+       uses    si,di\r
+       public  initrndt\r
+\r
+       mov     ax,[randomize]\r
+       cmp     ax,0\r
+;      jne     @@timeit                ;if randomize is true, really random\r
+\r
+       mov     dx,0                    ;set to a definate value\r
+       jmp     @@setit\r
+\r
+@@timeit:\r
+       mov     ah,2ch\r
+       int     21h                     ;GetSystemTime\r
+       and     dx,0ffh\r
+\r
+@@setit:\r
+       mov     [rndindex],dx\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+;=================================================\r
+;\r
+; Return a random # between 0-255\r
+; Exit : AX = value\r
+;\r
+;=================================================\r
+PROC   rndt\r
+       public  rndt\r
+\r
+       mov     bx,[rndindex]\r
+       inc     bx\r
+       and     bx,0ffh\r
+       mov     [rndindex],bx\r
+       mov     al,[rndtable+BX]\r
+       xor     ah,ah\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+\r
+;===========================================================================\r
+\r
+;========\r
+;\r
+; WAITVBL\r
+;\r
+;========\r
+\r
+PROC   WaitVBL\r
+       PUBLIC  WaitVBL\r
+\r
+       push    si\r
+       push    di\r
+\r
+       mov     dx,[crtcaddr]\r
+       add     dx,6\r
+\r
+waitvbl1:\r
+       in      al,dx\r
+       test    al,00001000b    ;look for vbl\r
+       jnz     waitvbl1\r
+\r
+waitvbl2:\r
+       in      al,dx\r
+       test    al,00001000b    ;look for vbl\r
+       jz      waitvbl2\r
+\r
+       pop     di\r
+       pop     si\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+;=======================================================================\r
+\r
+;====================\r
+;\r
+; EGAplane\r
+; Sets read/write mode 0 and selects the given plane (0-3)\r
+; for reading and writing\r
+;\r
+;====================\r
+\r
+PROC   EGAplane plane:WORD\r
+       PUBLIC  EGAplane\r
+\r
+       mov     dx,GCindex\r
+       mov     ax,GCmode\r
+       out     dx,ax           ;set read / write mode 0\r
+\r
+       mov     dx,GCindex\r
+       mov     al,4            ;read map select\r
+       mov     ah,[BYTE plane] ;read from this plane number\r
+       out     dx,ax\r
+       mov     dx,SCindex\r
+       mov     al,SCmapmask\r
+       mov     ah,1\r
+       mov     cl,[BYTE plane] ;write to this plane only\r
+       shl     ah,cl\r
+       out     dx,ax\r
+       ret\r
+\r
+ENDP\r
+\r
+;=======================================================================\r
+\r
+;====================\r
+;\r
+; EGAlatch\r
+; Sets write mode 1 with all planes selected\r
+;\r
+;====================\r
+\r
+PROC   EGAlatch plane:WORD\r
+       PUBLIC  EGAlatch\r
+\r
+       mov     dx,GCindex\r
+       mov     ax,1*256 + GCmode\r
+       out     dx,ax           ;set read 0 / write mode 1\r
+\r
+       mov     dx,SCindex\r
+       mov     ax,15*256 + SCmapmask   ;write to all planes\r
+       out     dx,ax\r
+       ret\r
+ENDP\r
+\r
+;=======================================================================\r
+\r
+;============\r
+;\r
+; drawchar\r
+; Calls CGAcharout / EGAcharout / VGAcharout\r
+; based on grmode\r
+;\r
+;============\r
+\r
+PROC   drawchar xcoordinate:WORD, ycoordinate:WORD, char:WORD\r
+       PUBLIC  drawchar\r
+\r
+       push    si\r
+       push    di\r
+\r
+       mov     ax,[screenseg]\r
+       mov     es,ax\r
+\r
+       mov     si,[char]               ;low level routines get stuff in registers\r
+       mov     di,[xcoordinate]\r
+       mov     bx,[ycoordinate]\r
+       shl     bx,1\r
+       shl     bx,1\r
+       shl     bx,1\r
+       shl     bx,1\r
+       add bx,[_yshift]                ;allow printing on non-char lines\r
+\r
+       mov     ax,[grmode]\r
+       cmp     ax,1            ;CGA mode\r
+       jne     @@notcga\r
+       call    CgaCharout\r
+       pop     di\r
+       pop     si\r
+       ret\r
+\r
+\r
+@@notcga:\r
+       cmp     ax,2            ;EGA mode\r
+       jne     @@notega\r
+       call    EgaCharout\r
+       pop     di\r
+       pop     si\r
+       ret\r
+\r
+\r
+@@notega:\r
+       cmp     ax,3            ;VGA mode\r
+       jne     @@done\r
+       call    VgaCharout\r
+@@done:\r
+       pop     di\r
+       pop     si\r
+       ret\r
+\r
+ENDP\r
+\r
+;===========================================================================\r
+\r
+;============\r
+;\r
+; CGAcharout\r
+; SI= character; DI= xcoordinate; BX= ycoordinate*2\r
+;\r
+;============\r
+\r
+PROC   CgaCharOut\r
+\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1            ;char * 16 = tile's offset in PICS\r
+\r
+       shl     di,1            ;x * 2 + ylookup[y] = screen location\r
+\r
+       add     di,[CGAylookup+bx]      ;BX is pointer into YLOOKUP\r
+\r
+       mov     bx,[xormask]    ;so chars can be inverted\r
+\r
+       mov     ax,[WORD charptr+2]\r
+       mov     ds,ax           ;segment of tile pictures (PARA'd)\r
+\r
+       cld\r
+\r
+       lodsw                   ;load in a row of the tile's picture\r
+       xor     ax,bx\r
+       stosw\r
+       add     di,1FFEh\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       sub     di,1FB2h\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       add     di,1FFEh\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       sub     di,1FB2h\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       add     di,1FFEh\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       sub     di,1FB2h\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       add     di,1FFEh\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+\r
+       mov     ax,_DATA\r
+       mov     ds,ax           ;restore turbo's data segment\r
+       ret\r
+ENDP\r
+\r
+\r
+;=======================================================================\r
+\r
+;============\r
+;\r
+; EGAcharout\r
+; SI= character; DI= xcoordinate; BX= ycoordinate\r
+;\r
+;============\r
+\r
+PROC   EgaCharOut\r
+\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1            ;char * 8 = tile's offset in PICS\r
+\r
+       add     di,[EGAylookup+bx]      ;BX is pointer into YLOOKUP\r
+\r
+       mov     cx,ds\r
+\r
+       mov     dx,GCindex\r
+       mov     ax,GCmode\r
+       out     dx,ax           ;set read / write mode 0\r
+\r
+       cld\r
+\r
+       mov     bx,si           ;so the planes can be drawn the same\r
+       mov     cx,di\r
+\r
+       mov     dx,GCindex\r
+       mov     ax,0*256+4      ;read map select\r
+       out     dx,ax\r
+       mov     dx,SCindex\r
+       mov     ax,1*256+SCmapmask\r
+       out     dx,ax\r
+       mov     dx,[xormask]    ;so chars can be inverted\r
+\r
+       mov     ax,[charptr+2]\r
+       mov     ds,ax           ;segment of tile pictures (PARA'd)\r
+\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+\r
+       mov     si,bx\r
+       mov     di,cx\r
+\r
+       push    dx\r
+       mov     dx,GCindex\r
+       mov     ax,1*256+4      ;read map select\r
+       out     dx,ax\r
+       mov     dx,SCindex\r
+       mov     ax,2*256+SCmapmask      ;write mask\r
+       out     dx,ax\r
+       pop     dx\r
+\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+\r
+       mov     si,bx\r
+       mov     di,cx\r
+\r
+       push    dx\r
+       mov     dx,GCindex\r
+       mov     ax,2*256+4      ;read map select\r
+       out     dx,ax\r
+       mov     dx,SCindex\r
+       mov     ax,4*256+SCmapmask      ;write mask\r
+       out     dx,ax\r
+       pop     dx\r
+\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+\r
+       mov     si,bx\r
+       mov     di,cx\r
+\r
+       push    dx\r
+       mov     dx,GCindex\r
+       mov     ax,3*256+4      ;read map select\r
+       out     dx,ax\r
+       mov     dx,SCindex\r
+       mov     ax,8*256+SCmapmask      ;write mask\r
+       out     dx,ax\r
+       pop     dx\r
+\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+       lodsb\r
+       xor     al,dl\r
+       stosb\r
+       add     di,39\r
+\r
+       mov     dx,SCindex\r
+       mov     ax,15*256 + SCmapmask   ;write to all planes\r
+       out     dx,ax\r
+\r
+       mov     ax,_DATA\r
+       mov     ds,ax           ;restore turbo's data segment\r
+       ret\r
+ENDP\r
+\r
+\r
+;=======================================================================\r
+\r
+;============\r
+;\r
+; VGAcharout\r
+; SI= character; DI= xcoordinate; BX= ycoordinate\r
+;\r
+;============\r
+\r
+PROC   VgaCharOut\r
+\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1            ;char * 64 = tile's offset in PICS\r
+\r
+       shl     di,1\r
+       shl     di,1\r
+       shl     di,1            ;x * 8 + ylookup[y] = screen location\r
+\r
+       add     di,[VGAylookup+bx]      ;BX is pointer into YLOOKUP\r
+\r
+       mov     bx,[xormask]    ;so chars can be inverted\r
+\r
+       mov     ax,[WORD charptr+2]\r
+       mov     ds,ax           ;segment of tile pictures (PARA'd)\r
+\r
+       cld\r
+\r
+       lodsw                   ;load in a row of the tile's picture\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+\r
+       add     di,312\r
+\r
+       lodsw                   ;load in a row of the tile's picture\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+\r
+       add     di,312\r
+\r
+       lodsw                   ;load in a row of the tile's picture\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+\r
+       add     di,312\r
+\r
+       lodsw                   ;load in a row of the tile's picture\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+\r
+       add     di,312\r
+\r
+       lodsw                   ;load in a row of the tile's picture\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+\r
+       add     di,312\r
+\r
+       lodsw                   ;load in a row of the tile's picture\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+\r
+       add     di,312\r
+\r
+       lodsw                   ;load in a row of the tile's picture\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+\r
+       add     di,312\r
+\r
+       lodsw                   ;load in a row of the tile's picture\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+       lodsw\r
+       xor     ax,bx\r
+       stosw\r
+\r
+       mov     ax,_DATA\r
+       mov     ds,ax           ;restore turbo's data segment\r
+       ret\r
+ENDP\r
+\r
+;=======================================================================\r
+\r
+;============\r
+;\r
+; drawtile\r
+; Just drops a 16*16 tile onto the screen\r
+; Calls CGAtileout / EGAtileout / VGAtileout\r
+; based on grmode\r
+;\r
+;============\r
+\r
+PROC   drawtile xcoordinate:WORD, ycoordinate:WORD, tile:WORD\r
+       PUBLIC  drawtile\r
+\r
+       push    si\r
+       push    di\r
+\r
+       cld\r
+\r
+       mov     ax,[screenseg]\r
+       mov     es,ax\r
+\r
+       mov     si,[tile]\r
+       mov     di,[xcoordinate] ;low level routines get stuff in registers\r
+       mov     bx,[ycoordinate]\r
+       shl     bx,1\r
+\r
+       mov     ax,[grmode]\r
+\r
+       mov     cx,[WORD tileptr+2]\r
+       mov     ds,cx           ;segment of pictures (PARA'd)\r
+\r
+       cmp     ax,1            ;CGA mode\r
+       jne     @@notcga\r
+       call    CGAtileout\r
+       jmp     @@done\r
+\r
+@@notcga:\r
+       cmp     ax,2            ;EGA mode\r
+       jne     @@notega\r
+       call    EGAtileout\r
+       jmp     @@done\r
+\r
+@@notega:\r
+       cmp     ax,3            ;VGA mode\r
+       jne     @@done\r
+       call    VGAtileout\r
+@@done:\r
+\r
+       mov     ax,_DATA\r
+       mov     ds,ax           ;restore turbo's data segment\r
+       pop     di\r
+       pop     si\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;=======================\r
+;\r
+; CGAtileout\r
+; SI= tile #, BX=y, DI=x\r
+;\r
+;=======================\r
+PROC   CGAtileout\r
+       PUBLIC  CGAtileout\r
+\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+\r
+       shl     bx,1\r
+\r
+       add     di,[CGAylookup + BX]    ;destination on screen\r
+\r
+       movsw\r
+       movsw\r
+       add     di,8188\r
+       movsw\r
+       movsw\r
+       sub     di,8116\r
+       movsw\r
+       movsw\r
+       add     di,8188\r
+       movsw\r
+       movsw\r
+       sub     di,8116\r
+       movsw\r
+       movsw\r
+       add     di,8188\r
+       movsw\r
+       movsw\r
+       sub     di,8116\r
+       movsw\r
+       movsw\r
+       add     di,8188\r
+       movsw\r
+       movsw\r
+       sub     di,8116\r
+       movsw\r
+       movsw\r
+       add     di,8188\r
+       movsw\r
+       movsw\r
+       sub     di,8116\r
+       movsw\r
+       movsw\r
+       add     di,8188\r
+       movsw\r
+       movsw\r
+       sub     di,8116\r
+       movsw\r
+       movsw\r
+       add     di,8188\r
+       movsw\r
+       movsw\r
+       sub     di,8116\r
+       movsw\r
+       movsw\r
+       add     di,8188\r
+       movsw\r
+       movsw\r
+\r
+       ret\r
+ENDP\r
+\r
+;=======================\r
+;\r
+; EGAtileout\r
+; SI= tile #, BX=y, DI=x\r
+;\r
+;=======================\r
+PROC   EGAtileout\r
+       PUBLIC  EGAtileout\r
+\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       add     di,[EGAylookup + BX]    ;destination on screen\r
+\r
+       mov     dx,SCindex\r
+       mov     al,SCmapmask\r
+       out     dx,al\r
+       mov     al,1111b\r
+       inc     dx\r
+       out     dx,al\r
+\r
+       mov     dx,GCindex\r
+       mov     al,GCmode\r
+       out     dx,al\r
+       mov     al,1\r
+       inc     dx\r
+       out     dx,al\r
+\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+       add     di,38\r
+       movsb\r
+       movsb\r
+\r
+       ret\r
+ENDP\r
+\r
+\r
+;=======================\r
+;\r
+; VGAtileout\r
+; SI= tile #, BX=y, DI=x\r
+;\r
+;=======================\r
+PROC   VGAtileout\r
+       PUBLIC  VGAtileout\r
+\r
+       mov     ah,al\r
+       xor     al,al                   ;fast multiply by 256\r
+       add     di,[VGAylookup + BX]    ;destination on screen\r
+\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       add     di,304\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       add     di,304\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       add     di,304\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       add     di,304\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       add     di,304\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+       movsw\r
+      &nb