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