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