1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <X11/Xlib.h>
4 #include <X11/Xutil.h>
5 #include <golddig.h>
6
7 extern struct thing_s badguys[]; /* Array describing state of all */
8 extern int numbadguy;
9
10 /* These are the graphics cursors used for drawing the player at */
11 /* various times. */
12 extern GC standgc,angelgc,angelugc,angellgc,flygc,hang1gc,hang2gc,up1gc,up2gc;
13 extern GC left1gc,left2gc,right1gc,right2gc;
14
15 /* Redraw the player. The graphics cursors all use the GXor function */
16 /* so they will not erase what is underneath. */
draw_player()17 void draw_player()
18 {
19 GC drawgc;
20 register int lpos,code;
21
22 /* Get position of level array of player */
23 lpos = (player.xpos >> 1)*ysize + (player.ypos >> 1);
24 /* Get the control code describing block underneath player */
25 code = fast_lookup[level[lpos]].code;
26 /* If the block is inactive, use the code for empty space */
27 if((code & INACTIVE) && goldleft > 0)
28 code = fast_lookup[SPACE].code;
29
30 /* Compute the graphics cursor appropriate to the player's current */
31 /* state */
32 drawgc = NULL;
33 if(angelleft) {
34 if(player.ypos & 1)
35 drawgc = angelugc;
36 else if(player.xpos & 1)
37 drawgc = angellgc;
38 else
39 drawgc = angelgc;
40 }
41 else {
42 /* Check if player is hanging from a rope */
43 if((player.ypos & 1) == 0) {
44 if((code & DLEAVE) && ! (code & (ULEAVE | DFALL))) {
45 if(player.xpos & 1)
46 drawgc = hang2gc;
47 else
48 drawgc = hang1gc;
49 }
50 }
51 else if((player.ypos & 1) && (code & DFALL))
52 drawgc = flygc;
53
54 if(drawgc == NULL)
55 switch(player.dir) {
56 case UP: case DOWN:
57 if(player.ypos & 1)
58 drawgc = up2gc;
59 else
60 drawgc = up1gc;
61 break;
62 case LEFT:
63 if(player.xpos & 1)
64 drawgc = left2gc;
65 else
66 drawgc = left1gc;
67 break;
68 case RIGHT:
69 if(player.xpos & 1)
70 drawgc = right2gc;
71 else
72 drawgc = right1gc;
73 break;
74 case STAND:
75 if(code & ULEAVE)
76 drawgc = up1gc;
77 else
78 drawgc = standgc;
79 break;
80 default:
81 drawgc = standgc;
82 }
83 }
84 /* Fill the rectangle surrounding the player with the chosen */
85 /* graphics cursor. */
86 if(drawgc != NULL)
87 XFillRectangle(disp,wind,drawgc,player.xpos << 3,player.ypos << 3,16,16);
88 }
89
90 /* Erase the player by redrawing the block(s) underneath him */
drawmove_player()91 void drawmove_player()
92 {
93 register int x,y;
94
95 /* Do not erase redraw player if it is not nessary */
96 if(! player.redraw)
97 return;
98 /* Draw block covering at least half of player */
99 x = player.xold;
100 y = player.yold;
101 draw_block(x >> 1,y >> 1);
102 /* If player is offset horizontally, redraw block to the right */
103 if(x & 1)
104 draw_block((x >> 1) + 1,y >> 1);
105 /* If player is offset vertically, redraw block below */
106 if(y & 1)
107 draw_block(x >> 1,(y >> 1) + 1);
108
109 draw_player();
110 }
111
112 /* Redraw everything. This routine is called whenever something major */
113 /* changes or the window is exposed. */
redrawall()114 void redrawall()
115 {
116 draw_level();
117 draw_player();
118 draw_badguys();
119 XFlush(disp);
120 }
121
122 /* Move player one movement */
move_player()123 void move_player()
124 {
125 register int i,code;
126 static int linepos = 0; /* Output script line position */
127
128 /* Read in input script character */
129 if(inscr != NULL) {
130 /* If input repetition factor is down to 0, read the next order */
131 /* from the script */
132 if(incount == 0) {
133 /* Loop through characters until a command is found */
134 do {
135 /* Read next script character */
136 i = getc(inscr);
137 /* Check for end of input script */
138 if(i == EOF) {
139 fclose(inscr);
140 inscr = NULL;
141 printf("Input script terminated. Press any key to continue.\n");
142 newlevel = 1;
143 curtick --; /* Reset clock back one tick */
144 score = 0;
145 draw_score();
146 return;
147 }
148 /* If character is a repitition count, add it to incount */
149 if(i >= 'a' && i <= 'z')
150 incount = 26 * incount + (i - 'a');
151 } while(i < '0' || i > '8');
152 /* Set next input order */
153 inorder = (enum directs) (i - '0');
154 /* Make sure input count is at least 1 if no multiplier was */
155 /* given */
156 if(incount == 0)
157 incount = 1;
158 }
159 curorder = inorder; /* Set current order to input order */
160 incount --; /* Decrement input order counter */
161 }
162 /* Write out output script character */
163 if(outscr != NULL) {
164 /* If current order has not changed, just increment output order */
165 /* count */
166 if(outorder == curorder)
167 outcount ++;
168 else {
169 /* Write output count multiplier */
170 if(outcount > 1) {
171 for(i=1;i <= outcount;i *= 26)
172 continue;
173 for(i /= 26;i > 0;i /= 26) {
174 if(putc(((char) (outcount / i)) + 'a',outscr) == EOF) {
175 perror("output script");
176 exit(3);
177 }
178 outcount -= (outcount / i) * i;
179 }
180 }
181 /* Write old output order */
182 if(outorder != UNMOVE)
183 if(putc(((char) outorder) + '0',outscr) == EOF) {
184 perror("output script");
185 exit(3);
186 }
187
188 /* Reset output order to new order */
189 outorder = curorder;
190 outcount = 1;
191
192 /* Periodically insert newlines to make the output pretty */
193 linepos ++;
194 if(linepos == 30) {
195 if(putc('\n',outscr) == EOF) {
196 perror("output script");
197 exit(3);
198 }
199 linepos = 0;
200 }
201 }
202 }
203
204 /* Attempt to move player according to his standing orders */
205 if(angelleft)
206 code = movedead(&player,curorder,-1);
207 else
208 code = movething(&player,curorder,-1);
209 /* If digging completed, or if the player fell, and he was trying to */
210 /* move in the same direction, change the current order to STAND */
211 if(code == 4 || (code == 2 && curorder == player.dir))
212 curorder = STAND;
213 /* Redraw player if he dropped something (which will overwrite the */
214 /* block) */
215 if(code == 3)
216 player.redraw = 1;
217 /* If player is in the middle of a block, interesting things can */
218 /* happen. */
219 if((player.xpos & 1) == 0 && (player.ypos & 1) == 0) {
220 /* If the player has picked up a gold piece, consume it and */
221 /* increment the score. */
222 if(fast_lookup[player.hold].code & TREASURE) {
223 player.hold = SPACE;
224 score++;
225 goldleft--;
226 /* If that was the last gold piece, escape ladder and other */
227 /* stuff may need to appear. */
228 if(goldleft == 0) {
229 regen_allow(); /* Regenerate the allowable movement array */
230 redrawall(); /* Refresh the entire screen */
231 }
232 /* Redraw the score line */
233 else
234 draw_score();
235 }
236 /* Get the control code for the block direction underneath the */
237 /* player */
238 i = (player.xpos >> 1)*ysize + (player.ypos >> 1);
239 code = fast_lookup[level[i]].code;
240 /* If the player is an angel, and he is not inside a killer block, */
241 /* decrement angelleft */
242 if(angelleft > 0)
243 if(! --angelleft) {
244 if((code & KILLIN) ||
245 overlap_badguy(player.xpos,player.ypos,-1))
246 ++angelleft;
247 else
248 draw_score();
249 }
250 /* If the control code shows an active UPLEVEL block, or the */
251 /* player is at the top of the screen, and there is no more gold */
252 /* left, goto the next level. */
253 if((goldleft == 0 &&
254 (player.ypos == 0 || (code & UPLEVEL))) ||
255 ((code & UPLEVEL) && ! (code & INACTIVE))) {
256 /* Increment the level number */
257 levelnum ++;
258 /* Load the next level in if the current one is done */
259 init_level();
260 /* Redraw the level */
261 redrawall();
262 /* Flush all the many X windows operations out to the server. This */
263 /* is the only flush in all the operations in this procedure. */
264 XFlush(disp);
265 return;
266 }
267 /* If the block is a killer block, kill the player */
268 if((code & KILLIN) && ! angelleft) {
269 if (lives--) {
270 angelleft = ANGELTIME;
271 draw_score();
272 }
273 else
274 died("was crushed");
275 }
276 }
277 /* Do not let PUTDOWN order stay after movement has started */
278 else if(curorder == PUTDOWN)
279 curorder = STAND;
280 }
281
282 /* Move everything one movement (or less). This is the basic function */
283 /* which is called on every timer signal. */
moveall()284 void moveall()
285 {
286 /* Remember old position of player */
287 player.xold = player.xpos;
288 player.yold = player.ypos;
289 /* Assume that the player does not need to be redrawn initially */
290 player.redraw = 0;
291 /* Do player movement */
292 move_player();
293 /* Do secondary movement if player is sped up */
294 if(fast_lookup[player.hold].code & SPEED)
295 move_player();
296 /* Prevent time from advancing for bad guys if a TIMESTOP item is */
297 /* held by player */
298 if(! (fast_lookup[player.hold].code & TIMESTOP)) {
299 /* Regenerate bad guys movement tree periodically */
300 if(! (curtick & 0xf))
301 regen_tree();
302 /* Only move bad guys every other tick */
303 if(! (curtick & 0x1))
304 move_badguys();
305 }
306 /* Check if the player is overlapping one of the bad guys while not */
307 /* holding armor. */
308 if(! angelleft && ! (fast_lookup[player.hold].code & ARMOR) &&
309 overlap_badguy(player.xpos,player.ypos,-1)) {
310 if (lives--) {
311 angelleft = ANGELTIME;
312 draw_score();
313 }
314 else
315 died("was eaten");
316 }
317 /* Redraw player if he moved. Redraw occasionally anyway. */
318 if(player.xpos != player.xold || player.ypos != player.yold ||
319 (! (curtick & 0xf)))
320 player.redraw = 1;
321 /* Erase and draw player if necessary */
322 drawmove_player();
323 /* Flush all the many X windows operations out to the server. This */
324 /* is the only flush in all the operations in this procedure. */
325 XFlush(disp);
326 }
327