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 #include <stdio.h>
8 #include <X11/Xlib.h>
9 #include <X11/keysym.h>
10 #include <golddig.h>
11
12 #define MAXHOLE 100 /* Maximum number of holes at once */
13
14 int holedecay[] = {2,4,6,62,66,70,74}; /* Number of ticks after which */
15 /* hole will change form. */
16 int numholes; /* Total number of holes */
17 /* Structure to describe a hole */
18 struct {
19 int x,y; /* Position of hole */
20 int ticmade; /* Value of curtick when hole was created. Used to */
21 /* time hole transitions. */
22 } holes[MAXHOLE]; /* Array for holes. */
23
24 /* Compute the allowable directions of movement out of a block. The */
25 /* value in the moveallow array is updated at that position. */
allow_posit(x,y)26 void allow_posit(x,y)
27 register int x,y; /* Position of block which is to be checked */
28 {
29 register int pos,code,code2,allow;
30
31 /* Get position of block in level array */
32 pos = x*ysize + y;
33 /* Initially assume that all directions of movement are acceptable */
34 allow = MOVEUP | MOVEDOWN | MOVELEFT | MOVERIGHT;
35 /* Prevent movement off of the level into nowhere land */
36 if(x == 0)
37 allow &= ~MOVELEFT;
38 if(y == 0)
39 allow &= ~MOVEUP;
40 if(x == xsize - 1)
41 allow &= ~MOVERIGHT;
42 if(y == ysize - 1)
43 allow &= ~MOVEDOWN;
44
45 /* Get the control code for the block at the position */
46 code = fast_lookup[level[pos]].code;
47 /* Use the control code for space if the block is inactive */
48 if((code & INACTIVE) && goldleft > 0)
49 code = fast_lookup[SPACE].code;
50 /* Use the control bits for the block to reduce the allowable */
51 /* movement directions */
52 if(! (code & ULEAVE))
53 allow &= ~MOVEUP;
54 if(! (code & DLEAVE))
55 allow &= ~MOVEDOWN;
56 if(! (code & LLEAVE))
57 allow &= ~MOVELEFT;
58 if(! (code & RLEAVE))
59 allow &= ~MOVERIGHT;
60
61 /* Check block to the left to make sure that it is possible to enter */
62 /* that block from the current position. */
63 if(x > 0) {
64 /* Put control code for block to the left into register */
65 code2 = fast_lookup[level[pos - ysize]].code;
66 if((code2 & INACTIVE) && goldleft > 0)
67 code2 = fast_lookup[SPACE].code;
68 /* Determine if it possible to enter */
69 if(! (code2 & HENTER))
70 allow &= ~MOVELEFT;
71 /* Prevent falling back into it if something can walk back from it */
72 if((code2 & RLEAVE) && ! (code2 & CANFALL))
73 code &= ~LFALL;
74 }
75 /* Check block to the right */
76 if(x < xsize - 1) {
77 code2 = fast_lookup[level[pos + ysize]].code;
78 if((code2 & INACTIVE) && goldleft > 0)
79 code2 = fast_lookup[SPACE].code;
80 /* Determine if it possible to enter */
81 if(! (code2 & HENTER))
82 allow &= ~MOVERIGHT;
83 /* Prevent falling back into it if something can walk back from it */
84 if((code2 & LLEAVE) && ! (code2 & CANFALL))
85 code &= ~RFALL;
86 }
87 /* Check block above */
88 if(y > 0) {
89 code2 = fast_lookup[level[pos - 1]].code;
90 if((code2 & INACTIVE) && goldleft > 0)
91 code2 = fast_lookup[SPACE].code;
92 /* Determine if it possible to enter */
93 if(! (code2 & VENTER))
94 allow &= ~MOVEUP;
95 /* Prevent falling back into it if something can walk back from it */
96 if((code2 & DLEAVE) && ! (code2 & CANFALL))
97 code &= ~UFALL;
98 }
99 /* Check block below */
100 if(y < ysize - 1) {
101 code2 = fast_lookup[level[pos + 1]].code;
102 if((code2 & INACTIVE) && goldleft > 0)
103 code2 = fast_lookup[SPACE].code;
104 /* Determine if it possible to enter */
105 if(! (code2 & VENTER))
106 allow &= ~MOVEDOWN;
107 /* Prevent falling back into it if something can walk back from it */
108 /* Note that things will stick up if there is upward gravity */
109 /* pointing into downward gravity. */
110 if(code2 & ULEAVE)
111 code &= ~DFALL;
112 }
113
114 /* Add gravity force */
115 if((code & DFALL) && (allow & MOVEDOWN))
116 allow |= FORCEDOWN;
117 if((code & UFALL) && (allow & MOVEUP))
118 allow |= FORCEUP;
119 if((code & LFALL) && (allow & MOVELEFT))
120 allow |= FORCELEFT;
121 if((code & RFALL) && (allow & MOVERIGHT))
122 allow |= FORCERIGHT;
123
124 /* Store value back into moveallow array */
125 moveallow[pos] = allow;
126 }
127
128 /* Call allow_posit on a position and all surrounding positions */
allow_area(x,y)129 void allow_area(x,y)
130 int x,y; /* Position to call allow_posit at */
131 {
132 allow_posit(x,y);
133 if(x < xsize - 1)
134 allow_posit(x+1,y);
135 if(x > 0)
136 allow_posit(x-1,y);
137 if(y < ysize - 1)
138 allow_posit(x,y+1);
139 if(y > 0)
140 allow_posit(x,y-1);
141 }
142
143 /* Regenerate entire moveallow array */
regen_allow()144 void regen_allow()
145 {
146 int x,y;
147
148 /* Iterate through every possible position and call allow_posit on */
149 /* it. */
150 for(x=0;x<xsize;++x)
151 for(y=0;y<ysize;++y)
152 allow_posit(x,y);
153 }
154
155 /* Form a hole. */
make_hole(x,y,code)156 void make_hole(x,y,code)
157 int x,y; /* Position where hole is to be formed */
158 long code; /* Code of item held while digging */
159 {
160 register int pos;
161
162 /* Compute position in level array where hole is to be created */
163 pos = x*ysize + y;
164 /* Make sure that the position is inside the level array */
165 if(x < 0 || x >= xsize || y < 1 || y >= ysize)
166 return;
167 /* Check for reverse shovel */
168 if(code & RSHOVEL) {
169 /* Make sure that there is space is the specified position */
170 if(level[pos] != SPACE)
171 return;
172 /* Replace space with brick */
173 setchar(x,y,BRICK);
174 }
175 else {
176 /* Make sure that the block can be dug */
177 if(! (fast_lookup[level[pos]].code & CANDIG) ||
178 /* Make sure that the block above it allows for digging */
179 /* underneath. */
180 ! (fast_lookup[level[pos - 1]].code & DIGUND))
181 return;
182
183 /* Check for power shovel */
184 if(code & PSHOVEL)
185 setchar(x,y,SPACE);
186 else {
187 /* Prevent the creation of too many holes */
188 if(numholes >= MAXHOLE)
189 return;
190 /* Replace the character at the position with the first hole block */
191 setchar(x,y,HOLE1);
192 /* Add that hole to the hole array */
193 holes[numholes].x = x;
194 holes[numholes].y = y;
195 holes[numholes].ticmade = curtick;
196 numholes ++;
197 }
198 }
199 /* Recompute the allowable movement direction for that position and */
200 /* all surrounding positions */
201 allow_area(x,y);
202 }
203
204 /* Fill up a hole with brick */
fill_hole(x,y)205 void fill_hole(x,y)
206 int x,y; /* Position specifying hole */
207 {
208 register int i;
209
210 /* Look through all the holes until the right one is found */
211 for(i=0;i<numholes;++i)
212 if(holes[i].x == x && holes[i].y == y) {
213 /* Change the block to a brick */
214 setchar(holes[i].x,holes[i].y,BRICK);
215 /* Recompute the allowable movement for the specified position */
216 /* and all surrounding positions. */
217 allow_area(holes[i].x,holes[i].y);
218 /* Remove the hole from the holes array */
219 holes[i] = holes[numholes - 1];
220 numholes --;
221 return;
222 }
223 }
224
225 /* Age all holes by one clock tick */
change_holes()226 void change_holes()
227 {
228 register int i,j;
229
230 /* Look for decaying holes. Iterate through each hole. */
231 for(i=0;i<numholes;++i) {
232 /* Check if the hole is exactly at any transition point */
233 for(j=0;j<7;++j)
234 if(holes[i].ticmade + holedecay[j] == curtick) {
235 /* If it is a normal transition point, just change the block */
236 /* type */
237 if(j < 6)
238 setchar(holes[i].x,holes[i].y,(char) (HOLE1 + j + 1));
239 /* If it is the last transition point, fill the hole in */
240 else {
241 fill_hole(holes[i].x,holes[i].y);
242 /* Back up one hole since the hole that was at this position */
243 /* has now been replaced by another. */
244 i --;
245 }
246 break;
247 }
248 }
249 }
250
251 /* Try to move a thing (player or bad guy) in a given direction. The */
252 /* structure describing the things is updated in place with the new */
253 /* position and apparent active command. A code value is returned */
254 /* describing what type of movement actually occurred. */
movething(thing,newdir,num)255 int movething(thing,newdir,num)
256 register struct thing_s *thing; /* Pointer to struct describing */
257 /* current state of thing */
258 enum directs newdir; /* New command being attempted */
259 int num; /* Number of bad guy or -1 for */
260 /* player */
261 {
262 register int lpos,code;
263 int nx,ny;
264
265 /* Compute useful local values */
266 lpos = (thing->xpos >> 1)*ysize + (thing->ypos >> 1);
267 code = fast_lookup[level[lpos]].code;
268 if((code & INACTIVE) && goldleft > 0)
269 code = fast_lookup[SPACE].code;
270
271 /* Complete previous initiated movement */
272 if((thing->xpos & 1) || (thing->ypos & 1)) {
273 /* Allow partial horizontal movement to complete */
274 if(thing->xpos & 1) {
275 /* Continue in old direction */
276 switch(thing->dir) {
277 case LEFT:
278 thing->xpos -= 1;
279 thing->dir = LEFT;
280 break;
281 default:
282 thing->xpos += 1;
283 thing->dir = RIGHT;
284 break;
285 }
286 }
287
288 /* Allow partial vertical movement to complete */
289 if(thing->ypos & 1) {
290 /* Continue in old direction */
291 switch(thing->dir) {
292 case UP:
293 thing->ypos -= 1;
294 thing->dir = UP;
295 break;
296 default:
297 thing->ypos += 1;
298 thing->dir = DOWN;
299 break;
300 }
301 }
302
303 /* Pickup things which are laying around */
304 lpos = (thing->xpos >> 1)*ysize + (thing->ypos >> 1);
305 code = fast_lookup[level[lpos]].code;
306 if(newdir != PUTDOWN && (code & PICKUP) && thing->hold == SPACE) {
307 thing->hold = level[lpos];
308 setchar(thing->xpos >> 1,thing->ypos >> 1,SPACE);
309 allow_area(thing->xpos >> 1,thing->ypos >> 1);
310 }
311
312 /* Activate teleporter if standing on one and not holding an */
313 /* anchor */
314 if((code & TELEPORT) &&
315 ! (fast_lookup[thing->hold].code & ANCHOR)) {
316 do {
317 lpos ++;
318 thing->ypos += 2;
319 if(thing->ypos >> 1 == ysize) {
320 thing->ypos = 0;
321 thing->xpos += 2;
322 if(thing->xpos >> 1 == xsize) {
323 lpos = 0;
324 thing->xpos = 0;
325 }
326 }
327 } while(! (fast_lookup[level[lpos]].code & TELEPORT));
328 }
329
330 /* Activate jump pad if standing on one and not holding an anchor */
331 if((code & UPTWO) &&
332 ! (fast_lookup[thing->hold].code & ANCHOR)) {
333 thing->ypos -= 4;
334 if(thing->ypos < 0)
335 thing->ypos = 0;
336 }
337
338 /* Activate super jump pad */
339 if(code & UPALL) {
340 thing->ypos -= 4;
341 /* Jump to the top of screen if not holding an anchor */
342 if(thing->ypos < 0 ||
343 ! (fast_lookup[thing->hold].code & ANCHOR))
344 thing->ypos = 0;
345 }
346
347 return(1);
348 }
349
350 /* Allow creature to fall */
351 if((moveallow[lpos] & FORCEALL) &&
352 ! (fast_lookup[thing->hold].code & STOPFALL)) {
353 /* Compute position offset in direction of fall */
354 nx = ny = 0;
355 if(moveallow[lpos] & FORCEDOWN) {
356 ny = 1;
357 thing->dir = DOWN;
358 }
359 if(moveallow[lpos] & FORCEUP) {
360 ny = -1;
361 thing->dir = UP;
362 }
363 if(moveallow[lpos] & FORCERIGHT) {
364 nx = 1;
365 thing->dir = RIGHT;
366 }
367 if(moveallow[lpos] & FORCELEFT) {
368 nx = -1;
369 thing->dir = LEFT;
370 }
371
372 /* Prevent falling into another thing */
373 if(! overlap_badguy(thing->xpos + nx + nx,thing->ypos + ny + ny,num)) {
374 /* Drop item behind if falling into a hole */
375 if(level[lpos] == SPACE && thing->hold != SPACE) {
376 /* Compute level position of space that thing is falling into */
377 lpos = ((thing->xpos >> 1)+nx)*ysize +
378 ((thing->ypos >> 1)+ny);
379 /* Check to see if next position is a hole */
380 if(fast_lookup[level[lpos]].code & STOPBAD) {
381 setchar(thing->xpos >> 1,thing->ypos >> 1,thing->hold);
382 thing->hold = SPACE;
383 allow_area(thing->xpos >> 1,thing->ypos >> 1);
384 }
385 }
386
387 /* Increment position */
388 thing->xpos += nx;
389 thing->ypos += ny;
390 return(2);
391 }
392 }
393
394 /* Slow movement down if holding an anchor */
395 if((fast_lookup[thing->hold].code & ANCHOR) && (curtick & 0x7))
396 newdir = STAND;
397
398 /* Continue previous movement if it was at right angles to intended */
399 /* movement, and intended movement is impossible */
400 switch(newdir) {
401 /* Check for possible upward movement. */
402 case UP:
403 if(! (moveallow[lpos] & MOVEUP)) {
404 if(thing->dir == LEFT || thing->dir == RIGHT)
405 newdir = thing->dir;
406 }
407 break;
408 /* Check for possible downward movement. */
409 case DOWN:
410 if(! (moveallow[lpos] & MOVEDOWN)) {
411 if(thing->dir == LEFT || thing->dir == RIGHT)
412 newdir = thing->dir;
413 }
414 break;
415 /* Check for possible left movement. */
416 case LEFT:
417 if(! (moveallow[lpos] & MOVELEFT)) {
418 if(thing->dir == UP || thing->dir == DOWN)
419 newdir = thing->dir;
420 }
421 break;
422 /* Check for possible right movement. */
423 case RIGHT:
424 if(! (moveallow[lpos] & MOVERIGHT)) {
425 if(thing->dir == UP || thing->dir == DOWN)
426 newdir = thing->dir;
427 }
428 break;
429 case STAND: case DIGLEFT: case DIGRIGHT: case UNMOVE: case PUTDOWN: break;
430 }
431
432 /* By default, the thing is standing in place */
433 thing->dir = STAND;
434 /* Try to execute the intended movement */
435 switch(newdir) {
436 /* Put something down if that is the order */
437 case PUTDOWN:
438 if(level[lpos] == SPACE && thing->hold != SPACE) {
439 setchar(thing->xpos >> 1,thing->ypos >> 1,thing->hold);
440 thing->hold = SPACE;
441 allow_area(thing->xpos >> 1,thing->ypos >> 1);
442 }
443 return(3);
444 /* Dig holes left or right if that is the command. The make_hole */
445 /* command will fail if it is impossible to dig a hole at the */
446 /* specified location. */
447 case DIGLEFT:
448 make_hole((thing->xpos >> 1) - 1,(thing->ypos >> 1) + 1,
449 fast_lookup[thing->hold].code);
450 if(fast_lookup[thing->hold].code & NSHOVEL) {
451 make_hole((thing->xpos >> 1) - 2,(thing->ypos >> 1) + 1,
452 fast_lookup[thing->hold].code);
453 make_hole((thing->xpos >> 1) - 1,(thing->ypos >> 1) + 2,
454 fast_lookup[thing->hold].code);
455 make_hole((thing->xpos >> 1) - 2,(thing->ypos >> 1) + 2,
456 fast_lookup[thing->hold].code);
457 }
458 return(4);
459 case DIGRIGHT:
460 make_hole((thing->xpos >> 1) + 1,(thing->ypos >> 1) + 1,
461 fast_lookup[thing->hold].code);
462 if(fast_lookup[thing->hold].code & NSHOVEL) {
463 make_hole((thing->xpos >> 1) + 2,(thing->ypos >> 1) + 1,
464 fast_lookup[thing->hold].code);
465 make_hole((thing->xpos >> 1) + 1,(thing->ypos >> 1) + 2,
466 fast_lookup[thing->hold].code);
467 make_hole((thing->xpos >> 1) + 2,(thing->ypos >> 1) + 2,
468 fast_lookup[thing->hold].code);
469 }
470 return(4);
471 /* Since the thing is not falling or completing a previous movement, */
472 /* it can start off in a new direction. The moveallow array is used */
473 /* to determine which directions are possible. */
474 /* Check for possible upward movement. */
475 case UP:
476 if(moveallow[lpos] & MOVEUP) {
477 thing->ypos -= 1;
478 thing->dir = UP;
479 }
480 break;
481 /* Check for possible downward movement. */
482 case DOWN:
483 if(moveallow[lpos] & MOVEDOWN) {
484 thing->ypos += 1;
485 thing->dir = DOWN;
486 }
487 break;
488 /* Check for possible left movement. */
489 case LEFT:
490 if(moveallow[lpos] & MOVELEFT) {
491 thing->xpos -= 1;
492 thing->dir = LEFT;
493 }
494 break;
495 /* Check for possible right movement. */
496 case RIGHT:
497 if(moveallow[lpos] & MOVERIGHT) {
498 thing->xpos += 1;
499 thing->dir = RIGHT;
500 }
501 break;
502 case STAND: case UNMOVE: break;
503 }
504 return(0);
505 }
506
507 /* Try to move an angel in a given direction. */
movedead(thing,newdir,num)508 int movedead(thing,newdir,num)
509 register struct thing_s *thing; /* Pointer to struct describing */
510 /* current state of thing */
511 enum directs newdir; /* New command being attempted */
512 int num; /* Number of bad guy or -1 for */
513 /* player */
514 {
515 register int lpos,code;
516
517 /* Compute useful local values */
518 lpos = (thing->xpos >> 1)*ysize + (thing->ypos >> 1);
519 code = fast_lookup[level[lpos]].code;
520 if((code & INACTIVE) && goldleft > 0)
521 code = fast_lookup[SPACE].code;
522
523 /* Complete previous initiated movement */
524 if((thing->xpos & 1) || (thing->ypos & 1)) {
525 /* Allow partial horizontal movement to complete */
526 if(thing->xpos & 1) {
527 /* Continue in old direction */
528 switch(thing->dir) {
529 case LEFT:
530 thing->xpos -= 1;
531 thing->dir = LEFT;
532 break;
533 default:
534 thing->xpos += 1;
535 thing->dir = RIGHT;
536 break;
537 }
538 }
539
540 /* Allow partial vertical movement to complete */
541 if(thing->ypos & 1) {
542 /* Continue in old direction */
543 switch(thing->dir) {
544 case UP:
545 thing->ypos -= 1;
546 thing->dir = UP;
547 break;
548 default:
549 thing->ypos += 1;
550 thing->dir = DOWN;
551 break;
552 }
553 }
554
555 /* Activate teleporter if standing on one */
556 if(code & TELEPORT) {
557 do {
558 lpos ++;
559 thing->ypos += 2;
560 if(thing->ypos >> 1 == ysize) {
561 thing->ypos = 0;
562 thing->xpos += 2;
563 if(thing->xpos >> 1 == xsize) {
564 lpos = 0;
565 thing->xpos = 0;
566 }
567 }
568 } while(! (fast_lookup[level[lpos]].code & TELEPORT));
569 }
570
571 return(1);
572 }
573
574 /* By default, the thing is standing in place */
575 thing->dir = STAND;
576 /* Try to execute the intended movement */
577 switch(newdir) {
578 /* Since the thing is not falling or completing a previous movement, */
579 /* it can start off in a new direction. */
580 case UP:
581 if(thing->ypos > 0) {
582 thing->ypos -= 1;
583 thing->dir = UP;
584 }
585 break;
586 /* Check for possible downward movement. */
587 case DOWN:
588 if((thing->ypos >> 1) < ysize - 1) {
589 thing->ypos += 1;
590 thing->dir = DOWN;
591 }
592 break;
593 /* Check for possible left movement. */
594 case LEFT:
595 if(thing->xpos > 0) {
596 thing->xpos -= 1;
597 thing->dir = LEFT;
598 }
599 break;
600 /* Check for possible right movement. */
601 case RIGHT:
602 if((thing->xpos >> 1) < xsize - 1) {
603 thing->xpos += 1;
604 thing->dir = RIGHT;
605 }
606 break;
607 case STAND: case DIGLEFT: case DIGRIGHT: case UNMOVE: case PUTDOWN: break;
608 }
609 return(0);
610 }
611