1 /* This program was written by Alexander Siegel in September of 1989   */
2 /* at Cornell University.  It may may copied freely for private use or */
3 /* public dispersion provided that this comment is not removed.  This  */
4 /* program, any portion of this program, or any derivative of this     */
5 /* program may not be sold or traded for financial gain.               */
6 
7 /* Modified by Josh Siegel to work with NeWS/X11 */
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <X11/Xlib.h>
14 #include <X11/keysym.h>
15 #include <X11/Xutil.h>
16 #include <sys/time.h>
17 #include <signal.h>
18 #include <golddig.h>
19 
20 #define EVMASK KeyPressMask | ExposureMask | ButtonPressMask |\
21                FocusChangeMask | StructureNotifyMask
22 
23 /* Pre-initialize some variables */
24 int lives = STARTLIVES;
25 int newlevel = 0;
26 int angelleft = 0;
27 
28 static int gamestop = 0;
29 
30 /* Plug into original block type definitions in shared.c */
31 extern struct symbs_s symbs[];
32 extern int numholes;        /* Total number of holes */
33 
34 /* This routine is called whenever the player dies. */
died(whydie)35 void died(whydie)
36 char *whydie;       /* Textual description of reason for death */
37 {
38   register int i;
39 #ifndef VMS
40   static struct itimerval cycletime; /* Structure used when setting up timer */
41 #endif
42 
43 #ifdef VMS
44   signal(SIGALRM,SIG_IGN);
45 #else
46   /* Build cycletime structure used in setitimer */
47   cycletime.it_interval.tv_sec = 100;
48   cycletime.it_interval.tv_usec = 0;
49   cycletime.it_value = cycletime.it_interval;
50   /* Make timer not fire for 100 seconds.  This is necessary because */
51   /* sleep needs SIG_DFL to be set on the timer, but I don't want the */
52   /* timer to go off before sleep is called. */
53   setitimer(ITIMER_REAL,&cycletime,(struct itimerval *) NULL);
54   signal(SIGALRM,SIG_DFL);
55 #endif
56 
57   if(inscr != NULL)             /* Close input script file */
58     fclose(inscr);
59   /* Clean up output script */
60   if(outscr != NULL) {
61     /* Write output count multiplier */
62     if(outcount > 1) {
63       for(i=1;i <= outcount;i *= 26)
64         continue;
65       for(i /= 26;i > 0;i /= 26) {
66         if(putc(((char) (outcount / i)) + 'a',outscr) == EOF) {
67           perror("output script");
68           exit(3);
69         }
70         outcount -= (outcount / i) * i;
71       }
72     }
73     /* Write final output order */
74     if(outorder != UNMOVE)
75       if(putc(((char) outorder) + '0',outscr) == EOF) {
76         perror("output script");
77         exit(3);
78       }
79     putc('\n',outscr);          /* Append a final new line */
80     fclose(outscr);             /* Close output script file */
81   }
82   XSync(disp,False);            /* Synchronize with display */
83   if(strcmp(whydie,"was abandoned"))
84     sleep(2);                   /* Pause for 2 seconds to let player */
85                                 /* see situation */
86   xend();                       /* Terminate X windows */
87   /* Add score to high score list */
88   add_score(whydie);
89   exit(0);
90 }
91 
92 /* Handle a key stroke by the user. */
handle_key(keyhit)93 void handle_key(keyhit)
94 KeySym keyhit;     /* Key symbol for key stroke provided by X windows */
95 {
96   /* Now that a key is hit, really begin the level */
97   if(newlevel) {
98     drawmove_badguys();
99     newlevel = 0;
100   }
101   /* Do action depending on which key was hit */
102   switch(keyhit) {
103   /* If it is a 'h', '?', or '/', print out a list of commands */
104   case XK_H:    case XK_h:    case XK_question:   case XK_slash:
105     puts("Control the player using keyboard keys or the mouse.");
106     puts("<space>,R11 - stop");
107     puts("a,j,left arrow - move left");
108     puts("d,l,right arrow - move right");
109     puts("w,i,up arrow - move up");
110     puts("s,k,down arrow - move down");
111     puts("z,<,q,u,R13 - make hole left");
112     puts("x,>,e,o,R15 - make hole right");
113     puts("r,y,R7 - put down any held item");
114     puts("1-9 - change the game speed");
115     puts("\n^S,^Z - pause the game");
116     puts("^Q,^Y - reactivate the game");
117     puts("^C - kill the game");
118     puts("^R - redraw the screen");
119     puts("^K - kill yourself (go into angel mode)");
120     break;
121   /* A space bar changes the command to STAND */
122   case XK_space:    case XK_R11:
123     curorder = STAND; break;
124   /* A 'z', ',', '<', 'q', or 'u' digs holes to the left */
125   case XK_Z:    case XK_comma:  case XK_less:   case XK_Q: case XK_U:
126   case XK_R13:  case XK_z:      case XK_q:      case XK_u:
127     curorder = DIGLEFT; break;
128   /* A 'x', '.', '>', 'e', or 'o' digs holes to the right */
129   case XK_X:    case XK_period: case XK_greater: case XK_E: case XK_O:
130   case XK_R15:  case XK_x:      case XK_e: case XK_o:
131     curorder = DIGRIGHT; break;
132   /* A 'j' or 'a' changes the command to LEFT */
133   case XK_J:    case XK_A:  case XK_Left:   case XK_j:  case XK_a:
134     curorder = LEFT; break;
135   /* A 'i' or 'w' changes the command to UP */
136   case XK_I:    case XK_W:  case XK_Up:     case XK_i:  case XK_w:
137     curorder = UP; break;
138   /* A 'k' or 's' changes the command to DOWN */
139   case XK_K:    case XK_S:  case XK_Down:   case XK_k:  case XK_s:
140     curorder = DOWN; break;
141   /* A 'l' or 'd' changes the command to RIGHT */
142   case XK_L:    case XK_D:  case XK_Right:  case XK_l:  case XK_d:
143     curorder = RIGHT; break;
144   /* A 'r' or 'y' drops whatever is being held */
145   case XK_R:    case XK_Y:  case XK_R7:     case XK_r:  case XK_y:
146     curorder = PUTDOWN; break;
147   }
148 }
149 
150 /* Initialize a level from the current level file */
init_level()151 void init_level()
152 {
153   register int x,y,pos;
154 
155   /* Allow level sizes to be changes by new level */
156   xsize = ysize = -1;
157   /* Load the level data itself from the data file. */
158   load_level();
159   numholes = 0;
160 
161   /* Initialize player information */
162   player.xpos = player.ypos = player.xstart = player.ystart = goldleft = 0;
163   player.dir = STAND;
164   player.hold = SPACE;
165   curorder = STAND;
166   pos = 0;
167   lives++;  /* add one life for current level */
168   for(x=0;x<xsize;++x)
169     for(y=0;y<ysize;++y) {
170       /* Count the total number of treasures */
171       if(fast_lookup[level[pos]].code & TREASURE)
172         goldleft ++;
173       /* Look for player blocks and remove them.  The last one */
174       /* encountered sets the player position. */
175       if(level[pos] == PLAYER) {
176         player.xpos = player.xstart = x << 1;
177         player.ypos = player.ystart = y << 1;
178         level[pos] = SPACE;
179       }
180       pos ++;
181     }
182   printf("Collect %d gold dubloons.\n",goldleft);
183 
184   /* Initialize bad guy information and other things. */
185   start_badguy();
186   regen_allow();
187   regen_tree();
188   /* Freeze action until a key is pressed */
189   newlevel = 1;
190 }
191 
192 /* Function which is called whenever the timer signal goes off */
ticker(sig)193 void ticker(sig)
194 int sig;
195 {
196 #ifdef VMS
197 #define REARMSIG
198 #endif
199 #ifdef hpux
200 #define REARMSIG
201 #endif
202 #ifdef REARMSIG
203   signal(SIGALRM,ticker);     /* Re-arm the signal */
204 #endif
205   /* Ignore any signal which is not an alarm. */
206   if(sig != SIGALRM)
207     return;
208 
209   /* increment the tick counter if time is advancing */
210   if(! (fast_lookup[player.hold].code & TIMESTOP))
211     curtick ++;
212 
213   /* age all the holes */
214   change_holes();
215 
216   /* move the player and all the bad guys. */
217   moveall();
218 }
219 
220 /* main procedure for game */
main(argc,argv)221 int main(argc,argv)
222 int argc;
223 char **argv;
224 {
225   int keycount,i;
226   static XEvent xev;
227   KeySym keyhit;
228   char buf[50];
229   int usec;           /* Microseconds to wait per timer interval */
230 #ifndef VMS
231   static struct itimerval cycletime; /* Structure used when setting up timer */
232 #endif
233 
234   printf("type h for help.\n");
235 
236   /* set up level and world description defaults */
237   worldname = DEFWORLD;
238   levelnum = 1;
239   score = 0;
240   speed = 5;
241   inscr = outscr = NULL;
242   geom = NULL;
243   /* scan the command line for executing parameters and flags */
244   for(i=1;i<argc;++i) {
245     if(argv[i][0] == '-') {
246       /* look for the level number */
247       if(argv[i][1] == 'l') {
248         if(argv[i][2] == '\0' && i+1 < argc) {
249           sscanf(argv[i+1],"%d",&levelnum);
250           i++;
251         }
252         else
253           sscanf(argv[i]+2,"%d",&levelnum);
254       }
255       /* look for the level number */
256       else if(argv[i][1] == 's') {
257         if(argv[i][2] == '\0' && i+1 < argc) {
258           sscanf(argv[i+1],"%d",&speed);
259           i++;
260         }
261         else
262           sscanf(argv[i]+2,"%d",&speed);
263       }
264       /* look for input script file */
265       else if(argv[i][1] == 'i') {
266         if(argv[i][2] == '\0' && i+1 < argc) {
267           inscr = fopen(argv[i+1],"r");
268           i++;
269         }
270         else
271           inscr = fopen(argv[i]+2,"r");
272         if(inscr == NULL) {
273           perror("input script file");
274           exit(2);
275         }
276         printf("Input script started.\n");
277         inorder = UNMOVE;
278         incount = 0;
279       }
280       /* look for output script file */
281       else if(argv[i][1] == 'o') {
282         if(argv[i][2] == '\0' && i+1 < argc) {
283           outscr = fopen(argv[i+1],"w");
284           i++;
285         }
286         else
287           outscr = fopen(argv[i]+2,"w");
288         if(outscr == NULL) {
289           perror("output script file");
290           exit(2);
291         }
292         outorder = UNMOVE;
293         outcount = 0;
294       }
295       /* Look for geometry description */
296       else if (argv[i][1] == 'g') {
297         geom = argv[i + 1];
298         i++;
299       }
300       else {
301         printf("usage: golddig [-l <level>] [-s <speed 1-9>] [-i <input script>] [-o <output script>] [<world name>]\n");
302         exit(1);
303       }
304     }
305     /* if it doesn't start with a -, it must be the name of the world */
306     else {
307       worldname = argv[i];
308       break;
309     }
310   }
311   /* remember what the starting level was */
312   levelstart = levelnum;
313 
314   /* start up x windows and all graphics cursors for drawing level */
315   xstart(EVMASK, argc, argv);
316   /* reassemble the graphics cursors to prepare for actual play */
317   for(i=0;symbs[i].symb != '\0';++i)
318     fast_lookup[symbs[i].symb].gc  =
319       fast_lookup[symbs[i].inplay].gc;
320 
321   /* name the game window */
322   XStoreName(disp,wind,"Gold Digger 3.0");
323   XSetIconName(disp,wind,"GD 3.0");
324   /* do the rest of the level initialization */
325   init_level();
326 
327   curtick = 0;        /* Initialize tick count to 0 */
328 
329   /* Turn off default timer signal handler */
330   signal(SIGALRM,SIG_IGN);
331   /* initialize timer structure according to speed */
332   if(speed <= 0)
333     speed = 1;
334   if(speed <= 5)
335     usec = (5-speed) * 50000 + 125000;
336   else
337     usec = 625000 /speed;
338 #ifdef VMS
339   /* start the system timer.  the timer signal catcher will be set */
340   /* after the first x event is received. */
341   setitimer(usec);
342 #else
343   /* Build cycletime structure used in setitimer */
344   cycletime.it_interval.tv_sec = 0;
345   cycletime.it_interval.tv_usec = usec;
346   cycletime.it_value = cycletime.it_interval;
347   /* start the system timer.  the timer signal catcher will be set */
348   /* after the first x event is received. */
349   setitimer(ITIMER_REAL,&cycletime,(struct itimerval *) NULL);
350 #endif
351 
352   /* main event loop */
353   while(1) {
354     /* get the next x window event */
355     XWindowEvent(disp,wind,EVMASK,&xev);
356     /* suppress the timer to prevent race conditions */
357     signal(SIGALRM,SIG_IGN);
358     /* If the window is exposed or the level is complete redraw the */
359     /* entire level. */
360     if(xev.type == Expose && xev.xexpose.count == 0)
361       /* Redraw the level */
362       redrawall();
363     else if(xev.type == KeyPress) {
364       keycount = XLookupString((XKeyEvent *) &xev, buf, 50, &keyhit,
365 			       (XComposeStatus *) NULL);
366       /* Check for special control command */
367       if(xev.xkey.state & ControlMask)
368         switch(keyhit)  {
369         /* ^S and ^Z freeze the game in place */
370         case XK_S: case XK_Z: case XK_s: case XK_z:
371           gamestop = 1;
372           break;
373         /* ^Q and ^Y reactivate the game */
374         case XK_Q: case XK_Y: case XK_q: case XK_y:
375           gamestop = 0;
376           break;
377         /* ^C, ^U, and ^/ kill the game */
378         case XK_C: case XK_U: case XK_c: case XK_u: case XK_backslash:
379           goto game_over;
380         /* ^R redraws the level */
381         case XK_R: case XK_r:
382           redrawall();
383           break;
384         /* ^K commits suicide */
385         case XK_K: case XK_k:
386           if (lives--) {
387             angelleft = ANGELTIME;
388             draw_score();
389           }
390           else
391             died("committed suicide");
392         }
393       /* Pressing a number changes the game speed */
394       else if(keyhit >= XK_1 && keyhit <= XK_9) {
395         speed = (int) (keyhit - XK_0);
396         /* Compute new cycle delay */
397         if(speed <= 5)
398           usec = (5-speed) * 50000 + 125000;
399         else
400           usec = 625000 / speed;
401 #ifdef VMS
402         /* Reset the timer cycle time */
403         setitimer(usec);
404 #else
405         cycletime.it_value = cycletime.it_interval;
406         cycletime.it_interval.tv_usec = usec;
407         /* Reset the timer cycle time */
408         setitimer(ITIMER_REAL,&cycletime,(struct itimerval *) NULL);
409 #endif
410         /* Redraw score line with new speed */
411         draw_score();
412       }
413       /* If it was a normal key stroke, hand it off to the handle_key */
414       /* procedure */
415       else
416         handle_key(keyhit);
417     }
418     /*
419      * dce@sony.com - Iconify causes automatic pause.  Deiconify
420      * causes state to stay paused until a keypress happens.
421      */
422     else if(xev.type == UnmapNotify)
423       newlevel = 1;
424 
425     /* flush out pending x windows commands */
426     XFlush(disp);
427     /* reenable the alarm signal if game should be active */
428     if((! gamestop) && (! newlevel))
429       signal(SIGALRM,ticker);
430   }
431 
432   /* go to died procedure */
433  game_over:
434   died("was abandoned");
435   /* NOTREACHED */
436   return(0);
437 }
438