v05i023: golddig -- A game for X11, Part01/02

Dan Heller argv at island.uu.net
Mon Nov 13 06:18:30 AEST 1989


Submitted-by: Alexander Siegel <siegel at cs.cornell.edu>
Posting-number: Volume 5, Issue 23
Archive-name: golddig/part01

[ This was originally sent to comp.sources.games -- unless this newsgroup
  gets overrun with traffic, X11 sources, even if they're games, should be
  submitted to comp.sources.x.  Also, I still get people mailing me asking
  questions about sources because they think I wrote everything posted to
  this group.  Please, folks, remember: the AUTHOR is on the Submitted-by:
  line shown above.  --dan ]


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	golddig
# This archive created: Fri Oct 13 14:50:06 1989
export PATH; PATH=/bin:$PATH
if test ! -d 'golddig1.0'
then
	mkdir 'golddig1.0'
fi
cd 'golddig1.0'
if test -f 'badguy.c'
then
	echo shar: will not over-write existing file "'badguy.c'"
else
cat << \SHAR_EOF > 'badguy.c'
/* This program was written by Alexander Siegel in September of 1989   */
/* at Cornell University.  It may may copied freely for private use or */
/* public dispersion provided that this comment is not removed.  This  */
/* program, any portion of this program, or any derivative of this     */
/* program may not be sold or traded for financial gain.               */

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include "golddig.h"

#include "bitmap/badguy.bits"
#include "bitmap/badguy2.bits"
#include "bitmap/badguy3.bits"

long random();

int numbadguy = 0;       /* Total number of bad guys. */
struct thing_s badguys[MAXBADGUY];  /* Array describing state of all */
                                    /* bad guys */
char movetree[MAXLEVEL]; /* Direction that badguy should go at each location */

/* Graphics cursors for drawing the possible states of the badguys */
GC badguy1gc,badguy2gc,badguy3gc;

/* Initialize graphics cursors for bad guys */
void init_badguy()
{
  badguy1gc = makegc(GXor,badguy_bits);
  badguy2gc = makegc(GXor,badguy2_bits);
  badguy3gc = makegc(GXor,badguy3_bits);
}

/* Initialize data structure for bad guys from level data */
void start_badguy()
{
  register int x,y,pos;

  /* Initialize number of bad guys to 0 */
  numbadguy = 0;
  pos = 0;
  /* Iterate through each position in level array */
  for(x=0;x<xsize;++x)
    for(y=0;y<ysize;++y) {
      /* Check if level holds a bad guys block at that position */
      if(level[pos] == BADGUY) {
        /* Replace bad guys block with space */
        level[pos] = SPACE;
        if(numbadguy < MAXBADGUY) {
          /* Add bad guy to global array.  Note the multiplication by */
          /* 2 when producing position. */
          badguys[numbadguy].xstart = badguys[numbadguy].xpos = x << 1;
          badguys[numbadguy].ystart = badguys[numbadguy].ypos = y << 1;
          badguys[numbadguy].dir = STAND;
          /* Bad guys start holding nothing */
          badguys[numbadguy].hold = SPACE;
          numbadguy ++;
        }
      }
      pos ++;
    }
}

/* Draw all the bad guys out to the game window */
void draw_badguys()
{
  register int i;
  GC drawgc;

  /* Iterate through each bad guy */
  for(i=0;i<numbadguy;++i) {
    /* If bad guy is horizontally offset, use the second graphics */
    /* cursor */
    if(badguys[i].xpos & 1)
      drawgc = badguy2gc;
    /* If bad guy is vertically offset, use the third graphics cursor */
    else if(badguys[i].ypos & 1)
      drawgc = badguy3gc;
    /* Otherwise use the normal graphics cursor */
    else
      drawgc = badguy1gc;
    /* Fill rectangle surrounding bad guy */
    XFillRectangle(disp,wind,drawgc,badguys[i].xpos << 3,
                   badguys[i].ypos << 3,16,16);
  }
}

/* Return 1 if the specified position overlaps a bad guy, 0 otherwise. */
/* Another parameter is provided so that this routine can ignore one */
/* bad guy. */
int overlap_badguy(x,y,num)
register int x,y;   /* Position which will be checked for overlap. */
register int num;   /* Number of bad guy to ignore.  Use a -1 for no */
                    /* ignoring. */
{
  register int i,d;

  /* Iterate through each bad guy */
  for(i=0;i<numbadguy;++i) {
    /* If this bad guy is the one that I am supposed to ignore, */
    /* continue to the next */
    if(i == num)
      continue;
    /* Compute horizontal distance between position and bad guy and */
    /* check it.  Things are two positions wide in each direction */
    d = x - badguys[i].xpos;
    if(d < -1 || d > 1)
      continue;
    /* Check vertical distance */
    d = y - badguys[i].ypos;
    if(d < -1 || d > 1)
      continue;
    /* Found overlap, return a 1 */
    return(1);
  }
  /* Could not find overlap */
  return(0);
}

/* Erase and redraw all badguys who have moved in the previous */
/* movement.  Movement is detected when (xpos,ypos) is different from */
/* (xold,yold). */
void drawmove_badguys()
{
  register int x,y,i;
  GC drawgc;

  /* iterate through each bad guy */
  for(i=0;i<numbadguy;++i) {
    /* do not erase bad guys who did not move */
    if(! badguys[i].redraw)
      continue;
    /* get position of bad guy in normal coordinates */
    x = badguys[i].xold >> 1;
    y = badguys[i].yold >> 1;
    /* draw block underneath bad guy */
    draw_block(x,y);
    /* if horizontally offset, draw block to the right */
    if(badguys[i].xold & 1)
      draw_block(x + 1,y);
    /* if vertically offset, draw block below */
    if(badguys[i].yold & 1)
      draw_block(x,y + 1);
  }

  /* iterate through each bad guy */
  for(i=0;i<numbadguy;++i) {
    /* do not draw bad guys who did not move */
    if(! badguys[i].redraw)
      continue;
    /* Put badguy coordinates in registers */
    x = badguys[i].xpos;
    y = badguys[i].ypos;
    /* if bad guy is horizontally offset, use the second graphics */
    /* cursor */
    if(x & 1)
      drawgc = badguy2gc;
    /* If bad guy is vertically offset, use the third graphics cursor */
    else if(y & 1)
      drawgc = badguy3gc;
    /* Otherwise use the normal graphics cursor */
    else
      drawgc = badguy1gc;
    /* Fill rectangle surrounding bad guy */
    XFillRectangle(disp,wind,drawgc,x << 3,y << 3,16,16);
  }
}

/* Maximum size for FIFO queue used by breadth first search algorithm */
#define QSIZE 1000
/* Add a suggested movement direction to the FIFO queue */
#define ADDGOAL(dx,dy,ndir)  { goals[tail].x = goals[head].x + dx; \
                             goals[tail].y = goals[head].y + dy; \
                             goals[tail++].dir = ndir; \
                             if(tail == QSIZE) tail = 0; }

/* Regenerate the movement tree used by bad guys when determining */
/* movement.  The basic algorithm is a breadth first search of the */
/* graph produced by interpreting the moveallow array as a directed */
/* graph.  The root of the tree is the player position.  The result of */
/* this algorithm is that the shortest path from each position to the */
/* player is described by the directions in the movement tree. */
void regen_tree()
{
  register int head,tail,lpos,dist;
  /* This array is used for the queue of graph nodes.  The head and */
  /* tail variables describe the head and tail of the queue in the */
  /* array. */
  struct goals_s {
    short x,y,dir;
  } goals[QSIZE];

  /* Do a fast pre-initialization of the movement tree */
  dist = xsize * ysize;
  for(lpos=0;lpos<dist;++lpos) {
    /* Zero out each position.  A 0 in a position signifies that the */
    /* direction of movement for that position is unknown. */
    movetree[lpos] = 0;
    /* If can only move in one direction, set movetree value */
    /* immediately.  This screws up the search algorithm and the */
    /* result is that bad guys will not jump off of things.  It makes */
    /* the search run much faster, and the bad guys were too smart */
    /* otherwise. */
    if(moveallow[lpos] == MOVEDOWN)
      movetree[lpos] = MOVEDOWN;
    /* If no movement is possible, set a -1 in the movement tree. */
    if(moveallow[lpos] == 0)
      movetree[lpos] = -1;
  }
  /* Initialize the head and tail of the FIFO queue. */
  head = 0;
  tail = 1;
  /* Set the first goal node to be the player */
  goals[0].x = player.xpos >> 1;
  goals[0].y = player.ypos >> 1;
  goals[0].dir = -1;    /* Make sure every direction is possible */
  /* While there are still goal nodes, continue */
  while(head != tail) {
    /* Compute position of position of goal */
    lpos = goals[head].x * ysize + goals[head].y;
    /* If the suggested movement direction is valid and the movement */
    /* has for that position has not been set, set it. */
    if(movetree[lpos] == 0 && (moveallow[lpos] & goals[head].dir) != 0) {
      movetree[lpos] = goals[head].dir;
      /* Suggest that the block to the left has to rightward pointer */
      if(goals[head].x > 0 && movetree[lpos - ysize] == 0)
        ADDGOAL(-1,0,MOVERIGHT)
      /* Suggest that the block to the right has a leftware pointer */
      if(goals[head].x < xsize - 1 && movetree[lpos + ysize] == 0)
        ADDGOAL(1,0,MOVELEFT)
      /* Suggest that the block above has a downward pointer */
      if(goals[head].y > 0 && movetree[lpos - 1] == 0)
        ADDGOAL(0,-1,MOVEDOWN)
      /* Suggest that the block below has an upward pointer */
      if(goals[head].y < ysize - 1 && movetree[lpos + 1] == 0)
        ADDGOAL(0,1,MOVEUP)
    }
    /* Remove current goal node from head of queue */
    head ++;
    if(head == QSIZE)
      head = 0;
  }
}

/* Move all the bad guys one position */
void move_badguys()
{
  int i,x,y;
  register int lpos;
  enum directs dir;
  register char curchar,below;

  /* Iterate through all the bad guys */
  for(i=0;i<numbadguy;++i) {
    /* Get old position for bad guys */
    x = badguys[i].xold = badguys[i].xpos;
    y = badguys[i].yold = badguys[i].ypos;
    /* Initially assume that the badguy does not need to be redrawn */
    badguys[i].redraw = 0;
    /* Get the character underneath the bad guy */
    lpos = (x >> 1)*ysize + (y >> 1);
    curchar = level[lpos];
    /* Get the character below the bad guy.  If it is off the level, */
    /* assume it is a stone */
    if(y < (ysize - 1) << 1)
      below = level[lpos + 1];
    else
      below = STONE;

    /* If the current character is a killer block, move the bad guy to */
    /* its starting position */
    if(fast_lookup[curchar].code & KILLIN) {
      /* Increment the score */
      score += 2;
      /* Redraw the score line */
      draw_score();
      /* Move the bad guy back to its start */
      badguys[i].xpos = badguys[i].xstart;
      badguys[i].ypos = badguys[i].ystart;
      lpos = (badguys[i].xpos >> 1)*ysize + (badguys[i].ypos >> 1);
      /* Prevent a bad guy from forming on top of another */
      while(overlap_badguy(badguys[i].xpos,badguys[i].ypos,i) ||
            level[lpos] != SPACE) {
        badguys[i].xpos = random() % xsize;
        badguys[i].ypos = random() % ysize;
        lpos = (badguys[i].xpos >> 1)*ysize + (badguys[i].ypos >> 1);
      }
      /* Redraw bad guy */
      badguys[i].redraw = 1;
      /* Destroy whatever the bad guy was holding */
      if(badguys[i].hold != SPACE) {
        if(fast_lookup[badguys[i].hold].code & TREASURE) {
          goldleft --;
          /* If that was the last gold piece, escape ladder and other */
          /* stuff may need to appear. */
          if(goldleft == 0) {
            regen_allow();      /* Regenerate the allowable movement array */
            redrawall();        /* Refresh the entire screen */
          }
        }
        badguys[i].hold = SPACE;
      }
      /* Continue to the next bad guy */
      continue;
    }

    /* If the bad guy is stuck in a STOPBAD block, do not move him */
    if((x & 1) == 0 && (y & 1) == 0 &&
       (fast_lookup[curchar].code & STOPBAD)) {
      /* Redraw bad guy since underlying block gets redrawn occasionally */
      badguys[i].redraw = 1;      
      continue;
    }

    /* Check if the bad guy is above a hole. */
    if((x & 1) == 0 && (y & 1) == 0 && fast_lookup[below].code & STOPBAD) {
      /* If the hole is premature, fill it */
      if(below >= HOLE1 && below <= HOLE1+2)
        fill_hole(x >> 1,(y >> 1) + 1);
    }

    /* Complete previous movement */
    if((x & 1) != 0 || (y & 1) != 0)
      dir = badguys[i].dir;

    /* Can only drop things when at a non-offset position and when */
    /* there is space underneath */
    else if(curchar == SPACE && badguys[i].hold != SPACE && 
       /* Drop stuff with a 1 in 20 chance */
       random() % 20 == 0)
      dir = PUTDOWN;

    /* Try to move according to movement tree */
    else if(movetree[lpos] == MOVEUP)
      dir = UP;
    else if(movetree[lpos] == MOVEDOWN)
      dir = DOWN;
    else if(movetree[lpos] == MOVELEFT)
      dir = LEFT;
    else if(movetree[lpos] == MOVERIGHT)
      dir = RIGHT;

    /* Try circling clockwise around player */
    else if((moveallow[lpos] & MOVEUP) && badguys[i].xpos < player.xpos)
      dir = UP;
    else if((moveallow[lpos] & MOVEDOWN) && badguys[i].xpos > player.xpos)
      dir = DOWN;
    else if((moveallow[lpos] & MOVELEFT) && badguys[i].ypos > player.ypos)
      dir = LEFT;
    else if((moveallow[lpos] & MOVERIGHT) && badguys[i].ypos < player.ypos)
      dir = RIGHT;

    /* Try approaching player */
    else if((moveallow[lpos] & MOVEUP) && badguys[i].ypos > player.ypos)
      dir = UP;
    else if((moveallow[lpos] & MOVEDOWN) && badguys[i].ypos < player.ypos)
      dir = DOWN;
    else if((moveallow[lpos] & MOVELEFT) && badguys[i].xpos > player.xpos)
      dir = LEFT;
    else if((moveallow[lpos] & MOVERIGHT) && badguys[i].xpos < player.xpos)
      dir = RIGHT;

    /* Try moving in any possible direction */
    else if(moveallow[lpos] & MOVEUP)
      dir = UP;
    else if(moveallow[lpos] & MOVEDOWN)
      dir = DOWN;
    else if(moveallow[lpos] & MOVELEFT)
      dir = LEFT;
    else if(moveallow[lpos] & MOVERIGHT)
      dir = RIGHT;

    /* If totally stuck, just stand in place */
    else
      dir = STAND;

    /* Reverse bad guys direction is REVERSE item is held by player */
    if((fast_lookup[player.hold].code & REVERSE) &&
       (x & 1) == 0 && (y & 1) == 0)
      switch(dir) {
      case LEFT:  dir = RIGHT; break;
      case RIGHT: dir = LEFT;  break;
      case UP:    dir = DOWN;  break;
      case DOWN:  dir = UP;    break;
      }
    
    /* Execute computed movement. */
    if(movething(&(badguys[i]),dir,i) == 3) {
      /* If the bad guy dropped something, the block will have been */
      /* overwritten and now the bad guy needs to be redrawn */
      badguys[i].redraw = 1;
      break;
    }

    /* Back out of movement if it overlaps another bad guy */
    if(x != badguys[i].xpos || y != badguys[i].ypos) {
      if(overlap_badguy(badguys[i].xpos,badguys[i].ypos,i) &&
         /* Do not back into a position where there was already an overlap */
         ! overlap_badguy(x,y,i)) {
        /* Restore original bad guy position */
        badguys[i].xpos = x;
        badguys[i].ypos = y;
        badguys[i].dir = STAND;
      }
      /* Redraw bad guy if it has moved */
      else
        badguys[i].redraw = 1;
    }
  }
  /* Redraw bad guys in new positions */
  drawmove_badguys();
}
SHAR_EOF
if test 14297 -ne "`wc -c < 'badguy.c'`"
then
	echo shar: error transmitting "'badguy.c'" '(should have been 14297 characters)'
fi
fi # end of overwriting check
if test -f 'golddig.h'
then
	echo shar: will not over-write existing file "'golddig.h'"
else
cat << \SHAR_EOF > 'golddig.h'
/* This program was written by Alexander Siegel in September of 1989   */
/* at Cornell University.  It may may copied freely for private use or */
/* public dispersion provided that this comment is not removed.  This  */
/* program, any portion of this program, or any derivative of this     */
/* program may not be sold or traded for financial gain.               */

/* Start up default values */
#define DEFWORLD "goldlev"  /* Default world name */

int xsize,ysize;        /* Current level width and height */
int levelstart;         /* Level that player started at */
int levelnum;           /* Current level number */
int score;              /* Total score */
int goldleft;           /* Total number of treasure blocks left */
char *worldname;        /* Name of world (set of levels) */
char filename[300];     /* Current file name of this level */
int curtick;            /* Current clock tick number */

Display *disp;          /* X11 display of client */
Window wind;            /* X11 window where game is displayed */

/* Enumerated type to described direction or activity */
enum directs {
  STAND,UP,DOWN,LEFT,RIGHT,DIGLEFT,DIGRIGHT,PUTDOWN
};
/* Structure describing all stats of thing */
struct thing_s {
  int xstart,ystart;        /* Starting position of thing.  For both */
                            /* this pair and (xpos,ypos), the value is */
                            /* actually 2 times the expected value. */
                            /* This allows for describing when a thing */
                            /* is half way between one block and another. */
  int xpos,ypos;            /* Current position of thing */
  int xold,yold;            /* Previous position before last movement */
  int redraw;               /* Non-zero if thing should be redrawn */
  enum directs dir;         /* Current movement direction or action */
  char hold;                /* What is it carrying */
} player;   /* The player is a thing too */

/* These are used in the bit pattern for the generalized block */
/* description data structure which is in shared.c.  New bit */
/* descriptor can be added without modifying anything else. */
#define SUPPORT 0x1         /* Does this block prevent falling */
#define UPLEVEL 0x2         /* Jump to next level on contact */
#define ULEAVE  0x4         /* Can you leave this block upwards */
#define DLEAVE  0x8         /* Can you leave this block downwards */
#define HLEAVE  0x10        /* Can you leave this block horizontally */
#define HENTER  0x20        /* Can you enter this block horizontally */
#define VENTER  0x40        /* Can you enter this block vertically */
#define NOUPMV  0x78        /* All in and out movements except up */
#define ALLMOVE 0x7c        /* All in and out movements */
#define STOPBAD 0x80        /* Stops badguys from passing through. */
                            /* They must climb out. */
#define CANDIG  0x100       /* Can this block be dug */
#define INACTIVE 0x200      /* Does this block activate after treasure */
                            /* is gone, space is used otherwise */
#define TREASURE 0x400      /* Get points for contact, then turned */
                            /* into space */
#define KILLIN  0x800       /* Kills anyone standing in it */
#define NODRAW  0x1000      /* Can not draw it in editor */
#define DIGUND  0x2000      /* Can dig out underneath this block */
#define PICKUP  0x4000      /* Can pickup block and replace it with space */
#define TELEPORT 0x8000     /* Does this cause teleportation on contact */
#define ARMOR   0x10000     /* Grants invulnerability to player if held */
#define STOPFALL 0x20000    /* Prevent holder from falling */
#define NSHOVEL 0x40000     /* Holder can dig much bigger holes */
#define TIMESTOP 0x80000    /* Stop time for all except player if held */
#define REVERSE 0x100000    /* Reverse bad guy direction if player holds */

/* Predefined important block types.  Other types can be added WITHOUT */
/* adding to this list. */
#define SPACE ' '
#define BRICK '#'
#define STONE '@'
#define HOLE1 '1'
#define PLAYER 'p'
#define BADGUY 'b'

#define MAXBADGUY 30      /* Maximum number of bad guys */
#define MAXLEVEL 5000     /* Maximum size of a level */
char level[MAXLEVEL];     /* Array describing level using characters */
                          /* from above */
char moveallow[MAXLEVEL]; /* Array describing which directions can be moved */
                          /* out of any position in level */
/* Bit patterns for moveallow array */
#define MOVEUP    0x1
#define MOVEDOWN  0x2
#define MOVELEFT  0x4
#define MOVERIGHT 0x8

/* Array of block type descriptors.  The actual values for this array */
/* are in shared.c. */
struct symbs_s {
  char symb;    /* Character symbol representing block */
  char inplay;  /* Character describing what this block looks like */
                /* during actual play.  It is the symbol character */
                /* for this or another block type. */
  char *name;   /* Symbol name in English */
  char *bits;   /* Bitmap defined by a X11 bitmap file. */
  long code;    /* Bit code describing properties of block */
  KeySym xkey1,xkey2;   /* One or two X11 keyboard symbols for symbol */
};

/* Array for fast lookup of block types.  This array is index by the */
/* actual block character. */
struct fast_s {
  GC gc;        /* Graphics cursor used for drawing block */
  long code;    /* Code describing block properties */
} fast_lookup[256];

/* Global declarations of some functions */
GC makegc();
void xstart();
void xend();
void setchar();
void draw_block();
void draw_level();
void load_level();
void save_level();
int overlap_badguy();
void move_badguy();
void init_badguy();
void start_badguy();
void move_badguys();
void draw_badguys();
int in_hole();
int movething();
void fill_hole();
void regen_tree();
void regen_allow();
void add_score();
void draw_score();
SHAR_EOF
if test 5899 -ne "`wc -c < 'golddig.h'`"
then
	echo shar: error transmitting "'golddig.h'" '(should have been 5899 characters)'
fi
fi # end of overwriting check
if test -f 'makelev.c'
then
	echo shar: will not over-write existing file "'makelev.c'"
else
cat << \SHAR_EOF > 'makelev.c'
/* This program was written by Alexander Siegel in September of 1989   */
/* at Cornell University.  It may may copied freely for private use or */
/* public dispersion provided that this comment is not removed.  This  */
/* program, any portion of this program, or any derivative of this     */
/* program may not be sold or traded for financial gain.               */

/* Modified by Josh Siegel to work with NeWS/X11                       */

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include "golddig.h"

#define EVMASK KeyPressMask | ExposureMask | EnterWindowMask | ButtonMotionMask | ButtonPressMask

/* Plug into original block type definitions in shared.c */
extern struct symbs_s symbs[];

/* Current drawing character */
char curchar = ' ';

/* Redraw the entire level */
void redrawall()
{
  draw_level();
  XFlush(disp);
}

/* Set the score to be equal to the number of gold pieces */
void count_gold()
{
  register int i,levsize;

  levsize = xsize*ysize;
  score = 0;
  for(i=0;i<levsize;++i)
    if(fast_lookup[level[i]].code & TREASURE)
      score ++;
}

/* Main routine for editing levels */
void main(argc,argv)
int argc;
char **argv;
{
  register int i;
  static XEvent xev;
  KeySym *keyhit;
  int keycount;

  /* Set up default values for describing world */
  worldname = DEFWORLD;
  levelnum = -1;
  goldleft = 0;
  score = 0;
  /* Use size of default level as default level size */
  xsize = -1;
  ysize = -1;

  /* Read in command line options */
  for(i=1;i<argc;++i) {
    if(argv[i][0] == '-') {
      /* -w sets the level width */
      if(argv[i][1] == 'w') {
        if(argv[i][2] == '\0' && i+1 < argc) {
          sscanf(argv[i+1],"%d",&xsize);
          i++;
        }
        else
          sscanf(argv[i]+2,"%d",&xsize);
      }
      /* -h sets the level height */
      else if(argv[i][1] == 'h') {
        if(argv[i][2] == '\0' && i+1 < argc) {
          sscanf(argv[i+1],"%d",&ysize);
          i++;
        }
        else
          sscanf(argv[i]+2,"%d",&ysize);
      }
      /* -l sets the level number */
      else if(argv[i][1] == 'l') {
        if(argv[i][2] == '\0' && i+1 < argc) {
          sscanf(argv[i+1],"%d",&levelnum);
          i++;
        }
        else
          sscanf(argv[i]+2,"%d",&levelnum);
      }
      else {
        printf("usage: levelmake [-h <height>] [-w <width>] -l <level> [<world name>]\n");
        exit(1);
      }
    }
    /* If it doesn't start with a dash, it must a the world name */
    else {
      worldname = argv[i];
      break;
    }
  }
  /* Make sure some value was chosen for the level number.  This */
  /* discourages everybody editing the same level all the time. */
  if(levelnum == -1) {
    printf("usage: levelmake [-h <height>] [-w <width>] -l <level> [<world name>]\n");
    exit(1);
  }

  /* Load in level data from file. */
  load_level();

  printf("Welcome.  Type h for help.\n");

  /* Start up X windows and create all graphics cursors */
  xstart(EVMASK);
  /* Set the name of the output window */
  XStoreName(disp,wind,"Gold Digger Level Generator");

  /* Main event loop */
  do {
    /* Get the next X window event */
    XWindowEvent(disp,wind,EVMASK,&xev);
    /* If it was an enter event, raise the window */
    if(xev.type == EnterNotify) {
      XRaiseWindow(disp,wind);
      XSetInputFocus(disp,wind,RevertToParent,CurrentTime);
      XFlush(disp);
    }
    /* If it was an expose event, redraw everything */
    else if(xev.type == Expose) {
      /* Count the number of gold pieces in level */
      count_gold();
      /* Redraw the level */
      redrawall();
    }
    else if(xev.type == KeyPress) {
      keyhit = XGetKeyboardMapping(disp,xev.xkey.keycode,1,&keycount);
      /* If the 'h', '?' or '/' key was hit, print out the text */
      /* descriptions of each block type */
      if(*keyhit == XK_H || *keyhit == XK_question || *keyhit == XK_slash) {
        for(i=0;symbs[i].symb != '\0';++i)
          if(! (symbs[i].code & NODRAW))
            printf("%c - draw %ss\n",symbs[i].symb,symbs[i].name);
        puts("Type ^C to finish editing and save the level.");
        puts("Use the left mouse button to paint blocks.");
        putchar('\n');
      }
      else {
        /* Search through the block descriptions for one which has a */
        /* key code which matches the key code of the key which was */
        /* hit. */
        for(i=0;symbs[i].symb != '\0';++i)
          if(! (symbs[i].code & NODRAW))
            if(*keyhit == symbs[i].xkey1 || *keyhit == symbs[i].xkey2) {
              /* Change the current drawing character to the symbol */
              /* which was found. */
              curchar = symbs[i].symb;
              /* Count and display the number of gold pieces in level */
              count_gold();
              draw_score();
              break;
            }
      }
    }
    /* If the mouse moves with the button pressed, or the button is */
    /* pressed, draw the current block at that position */
    else if(xev.type == MotionNotify)
      setchar(xev.xmotion.x >> 4,xev.xmotion.y >> 4,curchar);
    else if(xev.type == ButtonPress)
      setchar(xev.xbutton.x >> 4,xev.xbutton.y >> 4,curchar);
    /* Flush the graphics commands out to the server */
    XFlush(disp);
  /* Loop until a control key is pressed */
  } while(xev.type != KeyPress ||
          xev.xkey.state != ControlMask);

  /* Terminated X windows connection */
  xend();
  /* Save level to data file */
  save_level();
  exit(0);
}
SHAR_EOF
if test 5513 -ne "`wc -c < 'makelev.c'`"
then
	echo shar: error transmitting "'makelev.c'" '(should have been 5513 characters)'
fi
fi # end of overwriting check
if test -f 'shared.c'
then
	echo shar: will not over-write existing file "'shared.c'"
else
cat << \SHAR_EOF > 'shared.c'
/* This program was written by Alexander Siegel in September of 1989   */
/* at Cornell University.  It may may copied freely for private use or */
/* public dispersion provided that this comment is not removed.  This  */
/* program, any portion of this program, or any derivative of this     */
/* program may not be sold or traded for financial gain.               */

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <errno.h>
#include <string.h>
#include "golddig.h"

/* Include all the bitmaps for the terrain blocks */
#include "bitmap/player.bits"
#include "bitmap/badguy.bits"
#include "bitmap/ladder.bits"
#include "bitmap/space.bits"
#include "bitmap/wall.bits"
#include "bitmap/hole1.bits"
#include "bitmap/hole2.bits"
#include "bitmap/hole3.bits"
#include "bitmap/hole4.bits"
#include "bitmap/hole5.bits"
#include "bitmap/hole6.bits"
#include "bitmap/hole7.bits"
#include "bitmap/stone.bits"
#include "bitmap/ghost.bits"
#include "bitmap/gold.bits"
#include "bitmap/escape.bits"
#include "bitmap/rope.bits"
#include "bitmap/uplevel.bits"
#include "bitmap/invis.bits"
#include "bitmap/steplad.bits"
#include "bitmap/tube.bits"
#include "bitmap/chute.bits"
#include "bitmap/vrope.bits"
#include "bitmap/teleport.bits"
#include "bitmap/armor.bits"
#include "bitmap/parac.bits"
#include "bitmap/nshovel.bits"
#include "bitmap/hourgl.bits"
#include "bitmap/reverse.bits"

char *sprintf();    /* UNIX brain damage */

/* Structure describing all the characteristics of all blocks.  Refer */
/* to the defines and structure definition in golddig.h. */
struct symbs_s symbs[] = 
  {{SPACE,SPACE,"space",space_bits,NOUPMV | DIGUND,XK_space,0},
   {PLAYER,PLAYER,"player",player_bits,NOUPMV | DIGUND,XK_P,0},
   {BADGUY,BADGUY,"bad guy",badguy_bits,NOUPMV | DIGUND,XK_B,0},
   {BRICK,BRICK,"wall",wall_bits,CANDIG | KILLIN,XK_3,XK_numbersign},
   {HOLE1,HOLE1,"hole1",hole1_bits,NOUPMV | STOPBAD | DIGUND | NODRAW,0,0},
   {HOLE1+1,HOLE1+1,"hole2",hole2_bits,NOUPMV | STOPBAD | DIGUND | NODRAW,0,0},
   {HOLE1+2,HOLE1+2,"hole3",hole3_bits,NOUPMV | STOPBAD | DIGUND | NODRAW,0,0},
   {HOLE1+3,HOLE1+3,"hole4",hole4_bits,NOUPMV | STOPBAD | DIGUND | NODRAW,0,0},
   {HOLE1+4,HOLE1+4,"hole5",hole5_bits,NOUPMV | STOPBAD | DIGUND | NODRAW,0,0},
   {HOLE1+5,HOLE1+5,"hole6",hole6_bits,NOUPMV | STOPBAD | DIGUND | NODRAW,0,0},
   {HOLE1+6,HOLE1+6,"hole7",hole7_bits,NOUPMV | STOPBAD | DIGUND | NODRAW,0,0},
   {STONE,STONE,"stone",stone_bits,KILLIN,XK_2,XK_at},
   {'|','|',"ladder",ladder_bits,SUPPORT | ALLMOVE,XK_backslash,XK_bar},
   {'g',BRICK,"ghost brick",ghost_bits,VENTER | DLEAVE,XK_G,0},
   {'i',SPACE,"invisible block",invis_bits,KILLIN,XK_I,0},
   {'$','$',"gold",gold_bits,NOUPMV | TREASURE | DIGUND |
      PICKUP,XK_4,XK_dollar},
   {'!','|',"escape",escape_bits,SUPPORT | ALLMOVE | DIGUND |
      INACTIVE,XK_exclam,XK_1},
   {'-','-',"rope",rope_bits,SUPPORT | NOUPMV | DIGUND,XK_minus,0},
   {'u','u',"leave level",uplevel_bits,NOUPMV | DIGUND | UPLEVEL |
      INACTIVE,XK_U,0},
   {'s','s',"step ladder",steplad_bits,SUPPORT | ALLMOVE | PICKUP | DIGUND,
      XK_S,0},
   {'c','c',"chute",chute_bits,DLEAVE | VENTER,XK_C,0},
   {'=','=',"tube",tube_bits,SUPPORT | HLEAVE | HENTER,XK_equal,0},
   {'v','v',"vertical rope",vrope_bits,SUPPORT | ULEAVE | DLEAVE |
      HENTER | VENTER,XK_V,0},
   {'t','t',"teleporter",teleport_bits,NOUPMV | TELEPORT,XK_T,0},
   {'a','a',"armor",armor_bits,NOUPMV | ARMOR | DIGUND | PICKUP,XK_A,0},
   {'r','r',"parachute",parac_bits,NOUPMV | STOPFALL | DIGUND | PICKUP,XK_R,0},
   {'n','n',"nuclear shovel",nshovel_bits,NOUPMV | NSHOVEL | DIGUND |
      PICKUP,XK_N,0},
   {'z','z',"time stop",hourgl_bits,NOUPMV | TIMESTOP | DIGUND |
      PICKUP,XK_Z,0},
   {'e','e',"reverse monster",reverse_bits,NOUPMV | REVERSE | DIGUND
      | PICKUP, XK_E,0},

   /* List terminator */
   {'\0','\0',(char *) 0,(char *) 0,0,0,0}};

Font scorefont;     /* Font used to display score */
GC scoregc;         /* GC used to draw score */
GC blackgc;         /* Simple black foreground GC */

/* Manufaction a 16x16 graphics cursor used in a XFill... operation. */
GC makegc(func,bits)
int func;       /* Drawing function such as GXcopy or GXor. */
char bits[];    /* Bits describing fill pattern.  Produced in an X11 */
                /* bitmap file usually. */
{
  static XGCValues gcv;
  Pixmap pmap;

  /* Build X11 bitmap from data in bits */
  pmap = XCreatePixmapFromBitmapData(disp,wind,bits,16,16,BlackPixel(disp,0),
                                     WhitePixel(disp,0),DisplayPlanes(disp,0));
  /* Assign the graphics cursor parameters */
  gcv.function = func;
  gcv.foreground = BlackPixel(disp,0);
  gcv.background = WhitePixel(disp,0);
  gcv.tile = pmap;
  gcv.fill_style = FillTiled;
  /* Return the created graphics cursor */
  return(XCreateGC(disp,wind,GCFunction | GCForeground | GCBackground | 
                   GCTile | GCFillStyle,&gcv));
}

/* Start X11 and do some basic initialization */
void xstart(evmask)
long evmask;    /* Event mask which will be used in XSelectInput */
{
  register int i;
  XGCValues xgcv;

  /* Open up the display */
  disp = XOpenDisplay(NULL);
  /* Check to see if the open display succeeded */
  if(disp == NULL) {
    fprintf(stderr,"Display open failed.  Check DISPLAY environment variable.\n");
    exit(-1);
  }

  /* Create the game window */
  wind = XCreateSimpleWindow(disp,DefaultRootWindow(disp),20,20,
                             50 << 4,(30 << 4) + SCORESIZE,
                             2,WhitePixel(disp,0),BlackPixel(disp,0));
  /* Check to see if the open window succeeded */
  if(wind == 0) {
    fprintf(stderr,"Window open failed.\n");
    XCloseDisplay(disp);
    exit(-1);
  }
  /* Name and raise the window */
  XMapRaised(disp,wind);

  /* Clear fast block type lookup table */
  for(i=0;i<256;++i) {
    fast_lookup[i].gc = NULL;
    /* Everything starts out looking like a space */
    fast_lookup[i].code = NOUPMV | DIGUND;
  }
  /* Generate block type lookup table from symbs array defined above. */
  /* After this the symbs array will be used very rarely. */
  for(i=0;symbs[i].symb != '\0';++i) {
    fast_lookup[symbs[i].symb].gc  =
      makegc(GXcopy,symbs[i].bits);
    fast_lookup[symbs[i].symb].code = symbs[i].code;
  }
  /* Load in the font used to display the score */
  scorefont = XLoadFont(disp,SCOREFONT);
  /* Create GC which will be used from drawing score */
  xgcv.function = GXcopy;
  xgcv.font = scorefont;
  xgcv.foreground = WhitePixel(disp,0);
  xgcv.background = BlackPixel(disp,0);
  scoregc = XCreateGC(disp,wind,
                      GCFunction | GCFont | GCForeground | GCBackground,
                      &xgcv);
  /* Create GC which will be used for clearing score line */
  xgcv.function = GXcopy;
  xgcv.foreground = BlackPixel(disp,0);
  xgcv.background = WhitePixel(disp,0);
  blackgc = XCreateGC(disp,wind,
                      GCFunction | GCForeground | GCBackground,
                      &xgcv);

  /* Select the interesting window events */
  XSelectInput(disp,wind,evmask);
  /* Flush and synchronize the server */
  XFlush(disp);
  XSync(disp,False);
}

/* Gracefully shut X windows down.  It is not strictly necessary to */
/* call this function. */
void xend()
{
  XUnloadFont(disp,scorefont);
  XUnmapWindow(disp,wind);
  XDestroyWindow(disp,wind);
  XCloseDisplay(disp);
}

/* Draw a block from the level array in the output window. */
void draw_block(x,y)
int x,y;    /* Position of block in array */
{
  register char curchar;
  GC drawgc;

  /* Get the block character out of the level array */
  curchar = level[x*ysize + y];
  /* If there is gold left and this block is inactive, replace it with */
  /* a space. */
  if(goldleft > 0 && (fast_lookup[curchar].code & INACTIVE))
    curchar = SPACE;
  /* Get the graphics cursor */
  drawgc = fast_lookup[curchar].gc;
  /* Replace questionable characters with spaces */
  if(drawgc == NULL)
    drawgc = fast_lookup[SPACE].gc;
  /* Fill the block */
  XFillRectangle(disp,wind,drawgc,x << 4,y << 4,16,16);
}

/* Change a block character in the level array.  The block is redrawn. */
void setchar(x,y,ch)
int x,y;    /* Position of block character to change. */
char ch;    /* Character to change it to */
{
  if(level[x*ysize + y] != ch) {
    level[x*ysize + y] = ch;
    draw_block(x,y);
  }
}

/* Draw the score and level number */
void draw_score()
{
  char buf[50];

  /* Build the output string */
  sprintf(buf,"score: %d  level: %d",score,levelnum);
  /* Clear the current score line */
  XFillRectangle(disp,wind,blackgc,0,ysize << 4,xsize << 4,SCORESIZE);
  /* Actually draw the text */
  XDrawString(disp,wind,scoregc,0,(ysize << 4) + SCORESIZE - 1,buf,
              strlen(buf));
}

/* Redraw the entire level */
void draw_level()
{
  int x,y;

  /* Change the window size */
  XResizeWindow(disp,wind,xsize << 4,(ysize << 4) + SCORESIZE);
  /* Draw the score and level number */
  draw_score();
  /* Iterate through each block position and draw it */
  for(x=0;x < xsize;++x)
    for(y=0;y < ysize;++y)
      draw_block(x,y);
}

/* Load a level out of a file.  The global variables worldname and */
/* levelnum are used to produce the file name which is stored in */
/* filename. */
void load_level()
{
  FILE *levelfile;
  register int i,j;
  int x,y;
  char buf[300];

  /* Manufaction the file name by starting with the world name and */
  /* appending the level number to it. */
  strcpy(filename,LIB);
  strcat(filename,"/");
  strcat(filename,worldname);
  sprintf(filename + strlen(filename),"%03d",levelnum);
  /* Open level file for reading */
  levelfile = fopen(filename,"r");
  /* If level file does not exist, use the default level file. */
  if(levelfile == NULL) {
    /* Build the default level name */
    strcpy(buf,LIB);
    strcat(buf,"/default");
    /* Open default level file for reading */
    levelfile = fopen(buf,"r");
    if(levelfile == NULL) {
      perror(worldname);
      exit(1);
    }
  }

  /* Load the first line of the level file */
  fgets(buf,300,levelfile);
  /* Extract the level size */
  sscanf(buf,"%d %d",&x,&y);
  /* Change level size only if it is necessary */
  if(xsize == -1)
    xsize = x;
  if(ysize == -1)
    ysize = y;
  /* Carefully check the sanity of the size parameters */
  if(xsize < 5)
    xsize = 5;
  if(xsize > 250)
    xsize = 250;
  if(ysize < 5)
    ysize = 5;
  if(ysize > 250)
    ysize = 250;
  if(xsize * ysize > MAXLEVEL) {
    if(xsize > ysize)
      xsize = MAXLEVEL / ysize;
    else
      ysize = MAXLEVEL / xsize;
  }
  /* Iterate through each horizontal line */
  for(i=0;i<ysize;++i) {
    /* Load the next line from the file */
    if(fgets(buf,300,levelfile) != NULL) {
      /* Go through each horizontal position and copy the data into */
      /* the level array. */
      for(j=0;j<xsize;++j) {
        /* Break out if line ends prematurely */
        if(buf[j] == '\n' || buf[j] == '\0')
          break;
        level[j*ysize + i] = buf[j];
      }
    }
    else
      j = 0;
    /* Fill in rest of premature lines with spaces */
    for(;j<xsize;++j)
      level[j*ysize + i] = SPACE;
  }
  /* Close the level file */
  fclose(levelfile);
}

/* Save the current level back into a file.  The global variable */
/* filename is used to determine the file name. */
void save_level()
{
  FILE *levelfile;
  char buf[300];
  register int i,j;

  /* Open the data file */
  levelfile = fopen(filename,"w");
  if(levelfile == NULL) {
    perror(worldname);
    exit(1);
  }
  /* Write out the size of the level.  Normal text is used so that */
  /* levels can be easily copied across architectures. */
  fprintf(levelfile,"%d %d\n",xsize,ysize);
  /* Terminate the lines for writing out the horizontal level lines */
  buf[xsize] = '\n';
  buf[xsize+1] = '\0';
  /* Iterate through each vertical position */
  for(i=0;i<ysize;++i) {
    /* Copy each horizontal line into the output buffer */
    for(j=0;j<xsize;++j)
      buf[j] = level[j*ysize + i];
    /* Write the line out to the file */
    fputs(buf,levelfile);
  }
  /* Close the data file */
  fclose(levelfile);
}
SHAR_EOF
if test 12121 -ne "`wc -c < 'shared.c'`"
then
	echo shar: error transmitting "'shared.c'" '(should have been 12121 characters)'
fi
fi # end of overwriting check
if test -f 'golddig.c'
then
	echo shar: will not over-write existing file "'golddig.c'"
else
cat << \SHAR_EOF > 'golddig.c'
/* This program was written by Alexander Siegel in September of 1989   */
/* at Cornell University.  It may may copied freely for private use or */
/* public dispersion provided that this comment is not removed.  This  */
/* program, any portion of this program, or any derivative of this     */
/* program may not be sold or traded for financial gain.               */

/* Modified by Josh Siegel to work with NeWS/X11 */

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <sys/time.h>
#include <signal.h>
#include "golddig.h"

#include "bitmap/player.bits"
#include "bitmap/fly.bits"
#include "bitmap/hang1.bits"
#include "bitmap/hang2.bits"
#include "bitmap/up1.bits"
#include "bitmap/up2.bits"
#include "bitmap/left1.bits"
#include "bitmap/left2.bits"
#include "bitmap/right1.bits"
#include "bitmap/right2.bits"

long random();

#define EVMASK KeyPressMask | ExposureMask | EnterWindowMask | ButtonPressMask | FocusChangeMask

int speed = 5;          /* Speed of game.  1 is slowest */
int newlevel = 0;       /* Non-zero if a new level was just drawn */
struct itimerval cycletime; /* Structure used when setting up timer */
/* These are the graphics cursors used for drawing the player at */
/* various times. */
GC standgc,flygc,hang1gc,hang2gc,up1gc,up2gc;
GC left1gc,left2gc,right1gc,right2gc;

enum directs curorder = STAND;  /* Current order which player has */
                                /* typed at the keyboard. */

/* Plug into original block type definitions in shared.c */
extern struct symbs_s symbs[];
extern int numholes;        /* Total number of holes */

/* This routine is called whenever the player dies. */
void died(whydie)
char *whydie;       /* Textual description of reason for death */
{
  signal(SIGALRM,SIG_IGN);      /* Turn off timer signal */
  sleep(1);                     /* Pause for a second */
  xend();                       /* Terminate X windows */
  /* Add score to high score list */
  add_score(whydie);
  exit(0);
}

/* Redraw the player.  The graphics cursors all use the GXor function */
/* so they will not erase what is underneath. */
void draw_player()
{
  GC drawgc;
  register int lpos,code;

  /* Get position of level array of player */
  lpos = (player.xpos >> 1)*ysize + (player.ypos >> 1);
  /* Get the control code describing block underneath player */
  code = fast_lookup[level[lpos]].code;
  /* If the block is inactive, use the code for empty space */
  if((code & INACTIVE) && goldleft > 0)
    code = fast_lookup[SPACE].code;

  /* Compute the graphics cursor appropriate to the player's current */
  /* state */
  drawgc = NULL;
  /* Check if player is hanging from a rope */
  if((player.ypos & 1) == 0) {
    if((code & SUPPORT) && ! (code & ULEAVE)) {
      if(player.xpos & 1)
        drawgc = hang2gc;
      else
        drawgc = hang1gc;
    }
  }
  if(drawgc == NULL)
    switch(player.dir) {
    case UP:    case DOWN:
      if(player.ypos & 1)
        drawgc = up2gc;
      else
        drawgc = up1gc;
      break;
    case LEFT:
      if(player.xpos & 1)
        drawgc = left2gc;
      else
        drawgc = left1gc;
      break;
    case RIGHT:
      if(player.xpos & 1)
        drawgc = right2gc;
      else
        drawgc = right1gc;
      break;
    case STAND:
      if(player.ypos & 1)
        drawgc = flygc;
      else if(code & ULEAVE)
        drawgc = up1gc;
      else
        drawgc = standgc;
      break;
    default:
      drawgc = standgc;
    }
  /* Fill the rectangle surrounding the player with the chosen */
  /* graphics cursor. */
  if(drawgc != NULL)
    XFillRectangle(disp,wind,drawgc,player.xpos << 3,player.ypos << 3,16,16);
}

/* Erase the player by redrawing the block(s) underneath him */
void drawmove_player()
{
  register int x,y; 

  /* Do not erase redraw player if it is not nessary */
  if(! player.redraw)
    return;
  /* Draw block covering at least half of player */
  x = player.xold;
  y = player.yold;
  draw_block(x >> 1,y >> 1);
  /* If player is offset horizontally, redraw block to the right */
  if(x & 1)
    draw_block((x >> 1) + 1,y >> 1);
  /* If player is offset vertically, redraw block below */
  if(y & 1)
    draw_block(x >> 1,(y >> 1) + 1);

  draw_player();
}

/* Handle a key stroke by the user. */
void handle_key(keyhit)
KeySym *keyhit;     /* Key symbol for key stroke provided by X windows */
{
  /* Now that a key is hit, really begin the level */
  newlevel = 0;
  /* Do action depending on which key was hit */
  switch(*keyhit) {
  /* If it is a 'h', '?', or '/', print out a list of commands */
  case XK_H:    case XK_question:   case XK_slash:
    puts("Control the player using keyboard keys or the mouse.");
    puts("<space>,R11 - stop");
    puts("a,j,left arrow - move left");
    puts("d,l,right arrow - move right");
    puts("w,i,up arrow - move up");
    puts("s,k,down arrow - move down");
    puts("z,<,q,u,R13 - make hole left");
    puts("x,>,e,o,R15 - make hole right");
    puts("r,y,R7 - put down any held item");
    break;
  /* A space bar changes the command to STAND */
  case XK_space:    case XK_R11:
    curorder = STAND; break;
  /* A 'z', ',', '<', 'q', or 'u' digs holes to the left */
  case XK_Z:    case XK_comma:  case XK_less:    case XK_Q: case XK_U:
  case XK_R13:
    curorder = DIGLEFT; break;
  /* A 'x', '.', '>', 'e', or 'o' digs holes to the right */
  case XK_X:    case XK_period: case XK_greater: case XK_E: case XK_O:
  case XK_R15:
    curorder = DIGRIGHT; break;
  /* A 'j' or 'a' changes the command to LEFT */
  case XK_J:    case XK_A:  case XK_Left:
    curorder = LEFT; break;
  /* A 'i' or 'w' changes the command to UP */
  case XK_I:    case XK_W:  case XK_Up:
    curorder = UP; break;
  /* A 'k' or 's' changes the command to DOWN */
  case XK_K:    case XK_S:  case XK_Down:
    curorder = DOWN; break;
  /* A 'l' or 'd' changes the command to RIGHT */
  case XK_L:    case XK_D:  case XK_Right:
    curorder = RIGHT; break;
  /* A 'r' or 'y' drops whatever is being held */
  case XK_R:    case XK_Y:  case XK_R7:
    curorder = PUTDOWN; break;
  }
}

/* Redraw everything.  This routine is called whenever something major */
/* changes or the window is exposed. */
void redrawall()
{
  draw_level();
  draw_player();
  draw_badguys();
  XFlush(disp);
}

/* Initialize a level from the current level file */
void init_level()
{
  register int x,y,pos;
  
  /* Allow level sizes to be changes by new level */
  xsize = ysize = -1;
  /* Load the level data itself from the data file. */
  load_level();
  numholes = 0;

  /* Initialize player information */
  player.xpos = player.ypos = player.xstart = player.ystart = goldleft = 0;
  player.dir = STAND;
  player.hold = SPACE;
  curorder = STAND;
  pos = 0;
  for(x=0;x<xsize;++x)
    for(y=0;y<ysize;++y) {
      /* Count the total number of treasures */
      if(fast_lookup[level[pos]].code & TREASURE)
        goldleft ++;
      /* Look for player blocks and remove them.  The last one */
      /* encountered sets the player position. */
      if(level[pos] == PLAYER) {
        player.xpos = player.xstart = x << 1;
        player.ypos = player.ystart = y << 1;
        level[pos] = SPACE;
      }
      pos ++;
    }
  printf("Collect %d gold dubloons.\n",goldleft);

  /* Initialize bad guy information and other things. */
  start_badguy();
  regen_allow();
  regen_tree();
  /* Freeze action until a key is pressed */
  newlevel = 1;
}

/* Move everything one movement (or less).  This is the basic function */
/* which is called on every timer signal. */
void moveall()
{
  register int i,code;

  /* Remember old position of player */
  player.xold = player.xpos;
  player.yold = player.ypos;
  /* Assume that the player does not need to be redrawn initially */
  player.redraw = 0;
  /* Attempt to move player according to his standing orders */
  code = movething(&player,curorder,-1);
  /* If digging completed, or if the player fell,
     and he was trying to move */
  /* down, change the current order to STAND */
  if(code == 4 || (code == 2 && curorder == MOVEDOWN))
    curorder = STAND;
  /* Redraw player if he dropped something (which will overwrite the */
  /* block) */
  if(code == 3)
    player.redraw = 1;
  /* If player is in the middle of a block, interesting things can */
  /* happen. */
  if((player.xpos & 1) == 0 && (player.ypos & 1) == 0)  {
    /* If the player has picked up a gold piece, consume it and */
    /* increment the score. */
    if(fast_lookup[player.hold].code & TREASURE) {
      player.hold = SPACE;
      score++;
      goldleft--;
      /* If that was the last gold piece, escape ladder and other */
      /* stuff may need to appear. */
      if(goldleft == 0) {
        regen_allow();      /* Regenerate the allowable movement array */
        redrawall();        /* Refresh the entire screen */
      }
      /* Redraw the score line */
      else
        draw_score();
    }
    /* Get the control code for the block direction underneath the */
    /* player */
    i = (player.xpos >> 1)*ysize + (player.ypos >> 1);
    code = fast_lookup[level[i]].code;
    /* If the control code shows an active UPLEVEL block, or the */
    /* player is at the top of the screen, and there is no more gold */
    /* left, goto the next level. */
    if((goldleft == 0 && 
    (player.ypos == 0 || (code & UPLEVEL))) ||
       ((code & UPLEVEL) && ! (code & INACTIVE)))  {
      /* Increment the level number */
      levelnum ++;
      /* Load the next level in if the current one is done */
      init_level();
      /* Redraw the level */
      redrawall();
      /* Flush all the many X windows operations out to the server.  This */
      /* is the only flush in all the operations in this procedure. */
      XFlush(disp);
      return;
    }
    /* If the block is a killer block, kill the player */
    if(code & KILLIN)
      died("was crushed");
  }
  /* Prevent time from advancing for bad guys if a TIMESTOP item is */
  /* held by player */
  if(! (fast_lookup[player.hold].code & TIMESTOP)) {
    /* Regenerate bad guys movement tree periodically */
    if((curtick & 0xf) == 0)
      regen_tree();
    /* Only move bad guys every other tick */
    if(curtick & 1)
      move_badguys();
  }
  /* Check if the player is overlapping one of the bad guys while not */
  /* holding armor. */
  if(! (fast_lookup[player.hold].code & ARMOR) &&
     overlap_badguy(player.xpos,player.ypos,-1))
    died("was eaten");
  /* Redraw player if he moved.  Redraw occasionally anyway. */
  if(player.xpos != player.xold || player.ypos != player.yold ||
     (curtick & 0xf) == 0)
    player.redraw = 1;
  /* Erase and draw player if necessary */
  drawmove_player();
  /* Flush all the many X windows operations out to the server.  This */
  /* is the only flush in all the operations in this procedure. */
  XFlush(disp);
}

/* Function which is called whenever the timer signal goes off */
void ticker(sig)
int sig;
{
  /* Ignore any signal which is not an alarm.  Ignore alarm signals */
  /* after a new level has been drawn until a key is hit. */
  if(sig != SIGALRM || newlevel)
    return;
  
  /* increment the tick counter if time is advancing */
  if(! (fast_lookup[player.hold].code & TIMESTOP))
    curtick ++;

  /* age all the holes */
  change_holes();

  /* move the player and all the bad guys. */
  moveall();
}

/* main procedure for game */
void main(argc,argv)
int argc;
char **argv;
{
  int keycount,i,gamestop = 0;
  static XEvent xev;
  KeySym *keyhit;

  printf("type h for help.\n");

  /* set up level and world description defaults */
  worldname = DEFWORLD;
  levelnum = 1;
  score = 0;
  /* scan the command line for executing parameters and flags */
  for(i=1;i<argc;++i) {
    if(argv[i][0] == '-') {
      /* look for the level number */
      if(argv[i][1] == 'l') {
        if(argv[i][2] == '\0' && i+1 < argc) {
          sscanf(argv[i+1],"%d",&levelnum);
          i++;
        }
        else
          sscanf(argv[i]+2,"%d",&levelnum);
      }
      /* look for the level number */
      else if(argv[i][1] == 's') {
        if(argv[i][2] == '\0' && i+1 < argc) {
          sscanf(argv[i+1],"%d",&speed);
          i++;
        }
        else
          sscanf(argv[i]+2,"%d",&speed);
      }
      else {
        printf("usage: golddig [-l <level>] [-s <speed 1-9>] [<world name>]\n");
        exit(1);
      }
    }
    /* if it doesn't start with a -, it must be the name of the world */
    else {
      worldname = argv[i];
      break;
    }
  }
  /* remember what the starting level was */
  levelstart = levelnum;

  /* start up x windows and all graphics cursors for drawing level */
  xstart(EVMASK);
  /* reassemble the graphics cursors to prepare for actual play */
  for(i=0;symbs[i].symb != '\0';++i)
    fast_lookup[symbs[i].symb].gc  =
      fast_lookup[symbs[i].inplay].gc;

  /* compute all the graphics cursors for drawing the player in his */
  /* various states. */
  standgc = makegc(GXor,player_bits);
  flygc = makegc(GXor,fly_bits);
  hang1gc = makegc(GXor,hang1_bits);
  hang2gc = makegc(GXor,hang2_bits);
  up1gc = makegc(GXor,up1_bits);
  up2gc = makegc(GXor,up2_bits);
  left1gc = makegc(GXor,left1_bits);
  left2gc = makegc(GXor,left2_bits);
  right1gc = makegc(GXor,right1_bits);
  right2gc = makegc(GXor,right2_bits);
  /* initialize the bad guy's graphics cursors */
  init_badguy();
  /* name the game window */
  XStoreName(disp,wind,"gold digger");
  /* do the rest of the level initialization */
  init_level();
 
  /* initialize timer structure according to speed */
  if(speed <= 0)
    speed = 1;
  if(speed <= 5)
    cycletime.it_interval.tv_usec = (5-speed) * 50000 + 125000;
  else
    cycletime.it_interval.tv_usec = 625000 / speed;
  cycletime.it_interval.tv_sec = 0;
  cycletime.it_value = cycletime.it_interval;
  /* start the system timer.  the timer signal catcher will be set */
  /* after the first x event is received. */
  signal(SIGALRM,SIG_IGN);
  setitimer(ITIMER_REAL,&cycletime,(struct itimerval *) NULL);

  /* main event loop */
  while(1) {
    /* get the next x window event */
    XWindowEvent(disp,wind,EVMASK,&xev);
    /* suppress the timer to prevent race conditions */
    signal(SIGALRM,SIG_IGN);
    /* raise the window if it has been entered */
    if(xev.type == EnterNotify) {
      XRaiseWindow(disp,wind);
      XSetInputFocus(disp,wind,RevertToParent,CurrentTime);
    }
    /* If the window is exposed or the level is complete redraw the */
    /* entire level. */
    else if(xev.type == Expose && xev.xexpose.count == 0)
      /* Redraw the level */
      redrawall();
    else if(xev.type == KeyPress) {
      keyhit = XGetKeyboardMapping(disp,xev.xkey.keycode,1,&keycount);
      /* Check for special control command */
      if(xev.xkey.state & ControlMask)
        switch(*keyhit)  {
        /* ^S and ^Z freeze the game in place */
        case XK_S: case XK_Z:
          gamestop = 1;
          break;
        /* ^Q and ^Y reactivate the game */
        case XK_Q: case XK_Y:
          gamestop = 0;
          break;
        /* ^C, ^U, and ^/ kill the game */
        case XK_C: case XK_U: case XK_backslash:
          goto game_over;
        /* ^R redraws the level */
        case XK_R:
          redrawall();
          break;
        }
      /* If it was a normal key stroke, hand it off to the handle_key */
      /* procedure */
      else
        handle_key(keyhit);
    }
    /* flush out pending x windows commands */
    XFlush(disp);
    /* reenable the alarm signal if game should be active */
    if(! gamestop)
      signal(SIGALRM,ticker);
  }

  /* go to died procedure */
 game_over:
  died("was abandoned");
}
SHAR_EOF
if test 15684 -ne "`wc -c < 'golddig.c'`"
then
	echo shar: error transmitting "'golddig.c'" '(should have been 15684 characters)'
fi
fi # end of overwriting check
if test -f 'golddig.p'
then
	echo shar: will not over-write existing file "'golddig.p'"
else
cat << \SHAR_EOF > 'golddig.p'
.TH GOLDDIG P "September 29 1989"
.UC 4
.SH NAME
golddig - Getting the Gold and Avoiding Death
.SH
.B golddig
[
.B \-l
.I level
]
[
.B \-s
.I speed
]
[
.I world
]
.br
.B makelev
.B \-l
.I level
[
.B \-w
.I width
]
[
.B \-h
.I height
]
[
.I world
]
.SH DESCRIPTION
.PP
.I golddig
is a fast action game designed for use with the X window system.
.I makelev
is used to create and edit new levels which can be used by
.I golddig.
.PP
The goal in
.I golddig
is to pick up all the gold pieces and then go to the next level
without getting eaten by one of the bad guys.  The gold pieces are
distinctive small circular coins.  After picking up all of the gold,
the player must get to an open door or the top row of the level.
After accomplishing this, the game will freeze, and any key will start
the next level.
.PP
The
.B "\-l"
option sets the level number for editing or for starting the game.
This may be any integer greater than 0.  By default, the game starts
at level 1.  The
.B "\-s"
options sets the game speed.  A value of 1 is very slow and a value of
10 is fast.  The default value is 5.  The
.B "\-w"
and
.B "\-h"
options set the width and height of the level when creating a new
level.  The
.B world
option is used to select a world name.  A world is a distinct set of
numbered levels.  Any valid file name may be used as the world name.
Different worlds may be used to allow users to control private sets of
levels.
.PP
When playing
.I golddig
the following keyboard commands may be used:
.PP
<space>,R11 \- stop
.br
a,j,left arrow \- move left
.br
d,l,right arrow \- move right
.br
w,i,up arrow \- move up
.br
s,k,down arrow \- move down
.br
z,<,q,u,R13 \- make hole left
.br
x,>,e,o,R15 \- make hole right
.br
r,y,R7 \- put down any held item
.PP
In addition, some control keys can be used:
.PP
^S,^Z \- freeze the game
.br
^Q,^Y \- reactivate the game
.br
^C,^U,^\\ \- kill the game
.br
^R - redraw the level
.PP
The mouse must be placed in the game window to use the keyboard
commands.
.PP
.I makelev
is used as a graphics editor.  Each level is divided into a grid of
blocks.  The blocks are "painted" by pressing the left mouse button
down and moving the mouse.  A new block type is selected by a keyboard
key.  Currently, the following block types are supported:
.PP
<space>  - draw spaces
.br
p - draw players
.br
b - draw bad guys
.br
# - draw walls
.br
@ - draw stones
.br
| - draw ladders
.br
g - draw ghost bricks
.br
i - draw invisible blocks
.br
$ - draw golds
.br
! - draw escapes
.br
- - draw ropes
.br
u - draw leave levels
.br
s - draw step ladders
.br
c - draw chutes
.br
= - draw tubes
.br
v - draw vertical ropes
.br
t - draw teleporters
.br
a - draw armors
.br
r - draw parachutes
.br
n - draw nuclear shovels
.br
z - draw time stops
.br
e - draw reverse monsters
.PP
A player block sets where the player begins in the level.  If more
than one is used in a level, all but one will be ignored.  Bad guy
blocks set where the bad guys begin the level.  There is a limit to
the total number of bad guys, and that number is compiled into the game.
Pressing any control key inside the editor window will stop editing
and save the level.  The properties of the rest of the block can be
determined with experimentation.
.PP
A really good level has the several properties.  The first important
property is efficiency.  A good level makes careful use of all of the
available space unless it is trying to achieve a specific effect.
Another critical property is fairness.  A player should always be
forewarned or have an escape if trouble comes up.  Nothing is more
irritating than a killer trap where the user had no way of guessing that
it was coming up.  It is also important to avoid tedious and repetative
levels.  Really difficult traps should be rewarded with lots of gold.
.SH AUTHORS
Alex Siegel at Cornell University during September of 1989.
.SH
/usr/games/golddig - golddig executable
.br
/usr/games/makelev - level editor executable
.br
/usr/games/lib/golddig - directory for storing files which are private to
.I golddig
.br
/usr/games/lib/golddig/scores - file for storing high score list
.br
/usr/games/lib/golddig/default - default level for use when no
generated level is available
.br
/usr/games/lib/golddig/goldlev### - default levels used when no world
name is specified
.SH BUGS
This game is the first totally bug free program ever written.
SHAR_EOF
if test 4380 -ne "`wc -c < 'golddig.p'`"
then
	echo shar: error transmitting "'golddig.p'" '(should have been 4380 characters)'
fi
fi # end of overwriting check
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# This program was written by Alexander Siegel in September of 1989   
# at Cornell University.  It may may copied freely for private use or 
# public dispersion provided that this comment is not removed.  This  
# program, any portion of this program, or any derivative of this     
# program may not be sold or traded for financial gain.

# Name of game executable
GAME = golddig
# Name of level editor executable
EDITOR = makelev
# Directory where games binaries are stored
BIN = /usr/public/${HOSTTYPE}/games
# Directory where the library for this game will be stored
LIB = /usr/public/sun3/games/lib/${GAME}
# Directory where manual pages are kept
MANEXT = p
MAN = /usr/public/man/man${MANEXT}
# Font used to display score
SCOREFONT = -adobe-times-bold-r-normal--18-180-75-75-p-99-iso8859-1
# Height of SCOREFONT
SCORESIZE = 18

# Common compilers
#CC = cc
CC = gcc

# Some common choices for CFLAGS.
#CFLAGS = -O2
#CFLAGS = -O
CFLAGS = -O -W -Wreturn-type -Wunused -g -finline-functions -fstrength-reduce
#CFLAGS = -g
LDFLAGS = -g

all: ${GAME} ${EDITOR}

install: all
	-mkdir ${LIB}
	cp ${GAME} ${EDITOR} ${BIN}
# The next line sets up the level files so that anyone can edit all of
# the levels.  Remove this line to have better security.
	chmod 4755 ${BIN}/${EDITOR}
	touch ${LIB}/scores
# Allow anyone to modify the high score file.  An alternative is to
# change the mode bits of ${GAME} to be 4755.
	chmod 666 ${LIB}/scores
	cp goldlev* default ${LIB}
	cp golddig.p ${MAN}/golddig.${MANEXT}

update: all
	cp ${GAME} ${EDITOR} ${BIN}
# The next line sets up the level files so that anyone can edit all of
# the levels.  Remove this line to have better security.
	chmod 4755 ${BIN}/${EDITOR}

clean:
	rm -f *.o *~ core ${GAME} ${EDITOR}

SRCS = golddig.c shared.c badguy.c movement.c scores.c
OBJS = golddig.o shared.o badguy.o movement.o scores.o

lint:
	lint -DLIB=\"${LIB}\" ${SRCS}

${GAME}: ${OBJS}
	${CC} ${LDFLAGS} -o ${GAME} ${OBJS} -lX11

${EDITOR}: makelev.o shared.o
	${CC} ${LDFLAGS} -o ${EDITOR} makelev.o shared.o -lX11

.c.o:
	${CC} ${CFLAGS} -c -DLIB=\"${LIB}\" -DSCOREFONT=\"${SCOREFONT}\" \
                       -DSCORESIZE=${SCORESIZE} $<
SHAR_EOF
if test 2170 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 2170 characters)'
fi
fi # end of overwriting check
if test -f 'goldlev001'
then
	echo shar: will not over-write existing file "'goldlev001'"
else
cat << \SHAR_EOF > 'goldlev001'
50 30
                         !                        
 $$ $$----------------   !                        
 @@@@@                |  !  p  $  ----------      
                      |##########|                
                      |          |          $$ $$ 
                      |          |          @@@@@ 
                   $  |       @@@@@|              
                |#########         |              
                |             $    |              
                ||@@@@@@@    ######|              
                 |                 |              
           $     |                 |              
        |##########       ###########|            
        |                            |            
        |                            |            
        |@@@@@     ##########    $ ######|        
        |                                |        
        |       $                        |        
        ||#########                     $|        
         |             @@@@@@    #########|       
         |                          $     |       
         |      ##########                |       
         |                                |       
       $ |                 #######    @@@@@|      
   |########                               |      
   |         $                             | $    
   |      @@@@@@@     ##########      ########|   
   |                              $           |   
   |                                          |   
   |        b          b            b         |   
SHAR_EOF
if test 1536 -ne "`wc -c < 'goldlev001'`"
then
	echo shar: error transmitting "'goldlev001'" '(should have been 1536 characters)'
fi
fi
cd ..
#	End of shell archive
exit 0



More information about the Comp.sources.x mailing list