kent@ssbell.uu.net (Kent Landfield) (12/14/89)
Submitted-by: Alexander Siegel <siegel@cs.cornell.edu> Posting-number: Volume 5, Issue 48 Archive-name: golddig2/part04 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 4 (of 4)." # Contents: golddig2/golddig.c # Wrapped by kent@ssbell on Wed Dec 13 20:37:02 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'golddig2/golddig.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'golddig2/golddig.c'\" else echo shar: Extracting \"'golddig2/golddig.c'\" \(17923 characters\) sed "s/^X//" >'golddig2/golddig.c' <<'END_OF_FILE' X/* This program was written by Alexander Siegel in September of 1989 */ X/* at Cornell University. It may may copied freely for private use or */ X/* public dispersion provided that this comment is not removed. This */ X/* program, any portion of this program, or any derivative of this */ X/* program may not be sold or traded for financial gain. */ X X/* Modified by Josh Siegel to work with NeWS/X11 */ X X#include <stdio.h> X#include <X11/Xlib.h> X#include <X11/keysym.h> X#include <X11/Xutil.h> X#include <sys/time.h> X#include <signal.h> X#include "golddig.h" X Xextern char player_bits[]; X#include "bitmap/fly.bits" X#include "bitmap/hang1.bits" X#include "bitmap/hang2.bits" X#include "bitmap/up1.bits" X#include "bitmap/up2.bits" X#include "bitmap/left1.bits" X#include "bitmap/left2.bits" X#include "bitmap/right1.bits" X#include "bitmap/right2.bits" X Xlong random(); X X#define EVMASK KeyPressMask | ExposureMask | ButtonPressMask | FocusChangeMask X Xint newlevel = 0; /* Non-zero if a new level was just drawn */ Xstruct itimerval cycletime; /* Structure used when setting up timer */ X/* These are the graphics cursors used for drawing the player at */ X/* various times. */ XGC standgc,flygc,hang1gc,hang2gc,up1gc,up2gc; XGC left1gc,left2gc,right1gc,right2gc; X Xenum directs curorder = STAND; /* Current order which player has */ X /* typed at the keyboard. */ X X/* Plug into original block type definitions in shared.c */ Xextern struct symbs_s symbs[]; Xextern int numholes; /* Total number of holes */ X X/* This routine is called whenever the player dies. */ Xvoid died(whydie) Xchar *whydie; /* Textual description of reason for death */ X{ X /* Prevent timer from firing inside of sleep */ X cycletime.it_value.tv_sec = cycletime.it_interval.tv_sec = 10; X setitimer(ITIMER_REAL,&cycletime,(struct itimerval *) NULL); X signal(SIGALRM,SIG_DFL); /* Turn off timer signal */ X XSync(disp,False); /* Synchronize with display */ X if(strcmp(whydie,"was abandoned")) X sleep(2); /* Pause for 2 seconds to let player */ X /* see situation */ X xend(); /* Terminate X windows */ X /* Add score to high score list */ X add_score(whydie); X exit(0); X} X X/* Redraw the player. The graphics cursors all use the GXor function */ X/* so they will not erase what is underneath. */ Xvoid draw_player() X{ X GC drawgc; X register int lpos,code; X X /* Get position of level array of player */ X lpos = (player.xpos >> 1)*ysize + (player.ypos >> 1); X /* Get the control code describing block underneath player */ X code = fast_lookup[level[lpos]].code; X /* If the block is inactive, use the code for empty space */ X if((code & INACTIVE) && goldleft > 0) X code = fast_lookup[SPACE].code; X X /* Compute the graphics cursor appropriate to the player's current */ X /* state */ X drawgc = NULL; X /* Check if player is hanging from a rope */ X if((player.ypos & 1) == 0) { X if((code & DLEAVE) && ! (code & (ULEAVE | DFALL))) { X if(player.xpos & 1) X drawgc = hang2gc; X else X drawgc = hang1gc; X } X } X else if((player.ypos & 1) && (code & DFALL)) X drawgc = flygc; X X if(drawgc == NULL) X switch(player.dir) { X case UP: case DOWN: X if(player.ypos & 1) X drawgc = up2gc; X else X drawgc = up1gc; X break; X case LEFT: X if(player.xpos & 1) X drawgc = left2gc; X else X drawgc = left1gc; X break; X case RIGHT: X if(player.xpos & 1) X drawgc = right2gc; X else X drawgc = right1gc; X break; X case STAND: X if(code & ULEAVE) X drawgc = up1gc; X else X drawgc = standgc; X break; X default: X drawgc = standgc; X } X /* Fill the rectangle surrounding the player with the chosen */ X /* graphics cursor. */ X if(drawgc != NULL) X XFillRectangle(disp,wind,drawgc,player.xpos << 3,player.ypos << 3,16,16); X} X X/* Erase the player by redrawing the block(s) underneath him */ Xvoid drawmove_player() X{ X register int x,y; X X /* Do not erase redraw player if it is not nessary */ X if(! player.redraw) X return; X /* Draw block covering at least half of player */ X x = player.xold; X y = player.yold; X draw_block(x >> 1,y >> 1); X /* If player is offset horizontally, redraw block to the right */ X if(x & 1) X draw_block((x >> 1) + 1,y >> 1); X /* If player is offset vertically, redraw block below */ X if(y & 1) X draw_block(x >> 1,(y >> 1) + 1); X X draw_player(); X} X X/* Handle a key stroke by the user. */ Xvoid handle_key(keyhit) XKeySym keyhit; /* Key symbol for key stroke provided by X windows */ X{ X /* Now that a key is hit, really begin the level */ X newlevel = 0; X /* Do action depending on which key was hit */ X switch(keyhit) { X /* If it is a 'h', '?', or '/', print out a list of commands */ X case XK_H: case XK_h: case XK_question: case XK_slash: X puts("Control the player using keyboard keys or the mouse."); X puts("<space>,R11 - stop"); X puts("a,j,left arrow - move left"); X puts("d,l,right arrow - move right"); X puts("w,i,up arrow - move up"); X puts("s,k,down arrow - move down"); X puts("z,<,q,u,R13 - make hole left"); X puts("x,>,e,o,R15 - make hole right"); X puts("r,y,R7 - put down any held item"); X puts("1-9 - change the game speed"); X puts("\n^S,^Z - pause the game"); X puts("^Q,^Y - reactivate the game"); X puts("^C - kill the game"); X puts("^R - redraw the screen"); X break; X /* A space bar changes the command to STAND */ X case XK_space: case XK_R11: X curorder = STAND; break; X /* A 'z', ',', '<', 'q', or 'u' digs holes to the left */ X case XK_Z: case XK_comma: case XK_less: case XK_Q: case XK_U: X case XK_R13: case XK_z: case XK_q: case XK_u: X curorder = DIGLEFT; break; X /* A 'x', '.', '>', 'e', or 'o' digs holes to the right */ X case XK_X: case XK_period: case XK_greater: case XK_E: case XK_O: X case XK_R15: case XK_x: case XK_e: case XK_o: X curorder = DIGRIGHT; break; X /* A 'j' or 'a' changes the command to LEFT */ X case XK_J: case XK_A: case XK_Left: case XK_j: case XK_a: X curorder = LEFT; break; X /* A 'i' or 'w' changes the command to UP */ X case XK_I: case XK_W: case XK_Up: case XK_i: case XK_w: X curorder = UP; break; X /* A 'k' or 's' changes the command to DOWN */ X case XK_K: case XK_S: case XK_Down: case XK_k: case XK_s: X curorder = DOWN; break; X /* A 'l' or 'd' changes the command to RIGHT */ X case XK_L: case XK_D: case XK_Right: case XK_l: case XK_d: X curorder = RIGHT; break; X /* A 'r' or 'y' drops whatever is being held */ X case XK_R: case XK_Y: case XK_R7: case XK_r: case XK_y: X curorder = PUTDOWN; break; X } X} X X/* Redraw everything. This routine is called whenever something major */ X/* changes or the window is exposed. */ Xvoid redrawall() X{ X draw_level(); X draw_player(); X draw_badguys(); X XFlush(disp); X} X X/* Initialize a level from the current level file */ Xvoid init_level() X{ X register int x,y,pos; X X /* Allow level sizes to be changes by new level */ X xsize = ysize = -1; X /* Load the level data itself from the data file. */ X load_level(); X numholes = 0; X X /* Initialize player information */ X player.xpos = player.ypos = player.xstart = player.ystart = goldleft = 0; X player.dir = STAND; X player.hold = SPACE; X curorder = STAND; X pos = 0; X for(x=0;x<xsize;++x) X for(y=0;y<ysize;++y) { X /* Count the total number of treasures */ X if(fast_lookup[level[pos]].code & TREASURE) X goldleft ++; X /* Look for player blocks and remove them. The last one */ X /* encountered sets the player position. */ X if(level[pos] == PLAYER) { X player.xpos = player.xstart = x << 1; X player.ypos = player.ystart = y << 1; X level[pos] = SPACE; X } X pos ++; X } X printf("Collect %d gold dubloons.\n",goldleft); X X /* Initialize bad guy information and other things. */ X start_badguy(); X regen_allow(); X regen_tree(); X /* Freeze action until a key is pressed */ X newlevel = 1; X} X X/* Move player one movement */ Xvoid move_player() X{ X register int i,code; X X /* Attempt to move player according to his standing orders */ X code = movething(&player,curorder,-1); X /* If digging completed, or if the player fell, and he was trying to move */ X /* in the same direction, change the current order to STAND */ X if(code == 4 || (code == 2 && curorder == player.dir)) X curorder = STAND; X /* Redraw player if he dropped something (which will overwrite the */ X /* block) */ X if(code == 3) X player.redraw = 1; X /* If player is in the middle of a block, interesting things can */ X /* happen. */ X if((player.xpos & 1) == 0 && (player.ypos & 1) == 0) { X /* If the player has picked up a gold piece, consume it and */ X /* increment the score. */ X if(fast_lookup[player.hold].code & TREASURE) { X player.hold = SPACE; X score++; X goldleft--; X /* If that was the last gold piece, escape ladder and other */ X /* stuff may need to appear. */ X if(goldleft == 0) { X regen_allow(); /* Regenerate the allowable movement array */ X redrawall(); /* Refresh the entire screen */ X } X /* Redraw the score line */ X else X draw_score(); X } X /* Get the control code for the block direction underneath the */ X /* player */ X i = (player.xpos >> 1)*ysize + (player.ypos >> 1); X code = fast_lookup[level[i]].code; X /* If the control code shows an active UPLEVEL block, or the */ X /* player is at the top of the screen, and there is no more gold */ X /* left, goto the next level. */ X if((goldleft == 0 && X (player.ypos == 0 || (code & UPLEVEL))) || X ((code & UPLEVEL) && ! (code & INACTIVE))) { X /* Increment the level number */ X levelnum ++; X /* Load the next level in if the current one is done */ X init_level(); X /* Redraw the level */ X redrawall(); X /* Flush all the many X windows operations out to the server. This */ X /* is the only flush in all the operations in this procedure. */ X XFlush(disp); X return; X } X /* If the block is a killer block, kill the player */ X if(code & KILLIN) X died("was crushed"); X } X /* Do not let PUTDOWN order stay after movement has started */ X else if(curorder == PUTDOWN) X curorder = STAND; X} X X/* Move everything one movement (or less). This is the basic function */ X/* which is called on every timer signal. */ Xvoid moveall() X{ X /* Remember old position of player */ X player.xold = player.xpos; X player.yold = player.ypos; X /* Assume that the player does not need to be redrawn initially */ X player.redraw = 0; X /* Do player movement */ X move_player(); X /* If the level has changed, do not move other stuff */ X if(newlevel) X return; X /* Do secondary movement if player is sped up */ X if(fast_lookup[player.hold].code & SPEED) X move_player(); X /* If the level has changed, do not move other stuff */ X if(newlevel) X return; X /* Prevent time from advancing for bad guys if a TIMESTOP item is */ X /* held by player */ X if(! (fast_lookup[player.hold].code & TIMESTOP)) { X /* Regenerate bad guys movement tree periodically */ X if((curtick & 0xf) == 0) X regen_tree(); X /* Only move bad guys every other tick */ X if(curtick & 1) X move_badguys(); X } X /* Check if the player is overlapping one of the bad guys while not */ X /* holding armor. */ X if(! (fast_lookup[player.hold].code & ARMOR) && X overlap_badguy(player.xpos,player.ypos,-1)) X died("was eaten"); X /* Redraw player if he moved. Redraw occasionally anyway. */ X if(player.xpos != player.xold || player.ypos != player.yold || X (curtick & 0xf) == 0) X player.redraw = 1; X /* Erase and draw player if necessary */ X drawmove_player(); X /* Flush all the many X windows operations out to the server. This */ X /* is the only flush in all the operations in this procedure. */ X XFlush(disp); X} X X/* Function which is called whenever the timer signal goes off */ Xvoid ticker(sig) Xint sig; X{ X /* Ignore any signal which is not an alarm. Ignore alarm signals */ X /* after a new level has been drawn until a key is hit. */ X if(sig != SIGALRM || newlevel) X return; X X /* increment the tick counter if time is advancing */ X if(! (fast_lookup[player.hold].code & TIMESTOP)) X curtick ++; X X /* age all the holes */ X change_holes(); X X /* move the player and all the bad guys. */ X moveall(); X} X X/* main procedure for game */ Xvoid main(argc,argv) Xint argc; Xchar **argv; X{ X int keycount,i,gamestop = 0,gcfunc,firstevent; X static XEvent xev; X KeySym keyhit; X char buf[50]; X X printf("type h for help.\n"); X X /* set up level and world description defaults */ X worldname = DEFWORLD; X levelnum = 1; X score = 0; X speed = 5; X /* scan the command line for executing parameters and flags */ X for(i=1;i<argc;++i) { X if(argv[i][0] == '-') { X /* look for the level number */ X if(argv[i][1] == 'l') { X if(argv[i][2] == '\0' && i+1 < argc) { X sscanf(argv[i+1],"%d",&levelnum); X i++; X } X else X sscanf(argv[i]+2,"%d",&levelnum); X } X /* look for the level number */ X else if(argv[i][1] == 's') { X if(argv[i][2] == '\0' && i+1 < argc) { X sscanf(argv[i+1],"%d",&speed); X i++; X } X else X sscanf(argv[i]+2,"%d",&speed); X } X else { X printf("usage: golddig [-l <level>] [-s <speed 1-9>] [<world name>]\n"); X exit(1); X } X } X /* if it doesn't start with a -, it must be the name of the world */ X else { X worldname = argv[i]; X break; X } X } X /* remember what the starting level was */ X levelstart = levelnum; X X /* start up x windows and all graphics cursors for drawing level */ X xstart(EVMASK); X /* reassemble the graphics cursors to prepare for actual play */ X for(i=0;symbs[i].symb != '\0';++i) X fast_lookup[symbs[i].symb].gc = X fast_lookup[symbs[i].inplay].gc; X X /* Decide whether to use GXand or GXor depending on screen type */ X if((BlackPixel(disp,0) & WhitePixel(disp,0)) == BlackPixel(disp,0)) X gcfunc = GXand; X else X gcfunc = GXor; X /* compute all the graphics cursors for drawing the player in his */ X /* various states. */ X standgc = makegc(gcfunc,player_bits); X flygc = makegc(gcfunc,fly_bits); X hang1gc = makegc(gcfunc,hang1_bits); X hang2gc = makegc(gcfunc,hang2_bits); X up1gc = makegc(gcfunc,up1_bits); X up2gc = makegc(gcfunc,up2_bits); X left1gc = makegc(gcfunc,left1_bits); X left2gc = makegc(gcfunc,left2_bits); X right1gc = makegc(gcfunc,right1_bits); X right2gc = makegc(gcfunc,right2_bits); X /* initialize the bad guy's graphics cursors */ X init_badguy(); X /* name the game window */ X XStoreName(disp,wind,"gold digger 2.0"); X /* do the rest of the level initialization */ X init_level(); X X /* initialize timer structure according to speed */ X if(speed <= 0) X speed = 1; X if(speed <= 5) X cycletime.it_interval.tv_usec = (5-speed) * 50000 + 125000; X else X cycletime.it_interval.tv_usec = 625000 / speed; X cycletime.it_interval.tv_sec = 0; X cycletime.it_value = cycletime.it_interval; X /* start the system timer. the timer signal catcher will be set */ X /* after the first x event is received. */ X signal(SIGALRM,SIG_IGN); X setitimer(ITIMER_REAL,&cycletime,(struct itimerval *) NULL); X X /* main event loop */ X firstevent = 1; X while(1) { X /* get the next x window event */ X XWindowEvent(disp,wind,EVMASK,&xev); X /* suppress the timer to prevent race conditions */ X signal(SIGALRM,SIG_IGN); X /* If the window is exposed or the level is complete redraw the */ X /* entire level. Also redraw everything if it is the first window */ X /* event to handle window managers which capture expose events in */ X /* an unfriendly way. */ X if((xev.type == Expose && xev.xexpose.count == 0) || firstevent) { X /* Redraw the level */ X redrawall(); X /* Events after this are not the first event */ X firstevent = 0; X } X else if(xev.type == KeyPress) { X keycount = XLookupString(&xev,buf,50,&keyhit,(XComposeStatus *) NULL); X /* Check for special control command */ X if(xev.xkey.state & ControlMask) X switch(keyhit) { X /* ^S and ^Z freeze the game in place */ X case XK_S: case XK_Z: case XK_s: case XK_z: X gamestop = 1; X break; X /* ^Q and ^Y reactivate the game */ X case XK_Q: case XK_Y: case XK_q: case XK_y: X gamestop = 0; X break; X /* ^C, ^U, and ^/ kill the game */ X case XK_C: case XK_U: case XK_c: case XK_u: case XK_backslash: X goto game_over; X /* ^R redraws the level */ X case XK_R: case XK_r: X redrawall(); X break; X } X /* Pressing a number changes the game speed */ X else if(keyhit >= XK_1 && keyhit <= XK_9) { X speed = (int) (keyhit - XK_0); X /* Compute new cycle delay */ X if(speed <= 5) X cycletime.it_interval.tv_usec = (5-speed) * 50000 + 125000; X else X cycletime.it_interval.tv_usec = 625000 / speed; X cycletime.it_value = cycletime.it_interval; X /* Reset the timer cycle time */ X setitimer(ITIMER_REAL,&cycletime,(struct itimerval *) NULL); X /* Redraw score line with new speed */ X draw_score(); X } X /* If it was a normal key stroke, hand it off to the handle_key */ X /* procedure */ X else X handle_key(keyhit); X } X /* flush out pending x windows commands */ X XFlush(disp); X /* reenable the alarm signal if game should be active */ X if(! gamestop) X signal(SIGALRM,ticker); X } X X /* go to died procedure */ X game_over: X died("was abandoned"); X} END_OF_FILE if test 17923 -ne `wc -c <'golddig2/golddig.c'`; then echo shar: \"'golddig2/golddig.c'\" unpacked with wrong size! fi # end of 'golddig2/golddig.c' fi echo shar: End of archive 4 \(of 4\). cp /dev/null ark4isdone MISSING="" for I in 1 2 3 4 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 4 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0