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 <sys/types.h>
8 #include <sys/param.h>
9 #include <sys/stat.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <X11/Xlib.h>
13 #include <X11/keysym.h>
14 #include <X11/Xutil.h>
15 #include <errno.h>
16 #include <string.h>
17
18 #define GOLDDIG_EXTERN
19
20 #include <golddig.h>
21 /* Include all the bitmaps for the terrain blocks */
22 #include <bitmaps.h>
23
24 int savehighscore = 1; /* by default, do save highscores */
25
26 /* All in and out movements except up */
27 #define NOUPBITS DLEAVE | LLEAVE | RLEAVE | HENTER | VENTER
28 /* All in and out movements */
29 #define MOVEBITS NOUPBITS | ULEAVE
30 /* Standard bit pattern for empty space */
31 #define SPACEBITS NOUPBITS | DIGUND | DFALL
32 /* Generic item which can be picked up */
33 #define ITEMBITS SPACEBITS | PICKUP
34 /* Bit pattern used for dug holes */
35 #define HOLEBITS SPACEBITS | STOPBAD | NODRAW
36
37 /* Structure describing all the characteristics of all blocks. Refer */
38 /* to the defines and structure definition in golddig.h. */
39 struct symbs_s symbs[] =
40 {{SPACE,SPACE,"space",space_bits,SPACEBITS,XK_space,0, "black"},
41 {'!','|',"escape",escape_bits,MOVEBITS | DIGUND | INACTIVE,XK_exclam,XK_1, "blue"},
42 {BRICK,BRICK,"wall",wall_bits,CANDIG | KILLIN,XK_3,XK_numbersign, "firebrick"},
43 {'$','$',"gold",gold_bits,ITEMBITS | TREASURE,XK_4,XK_dollar, "gold"},
44 {'-','-',"rope",rope_bits,NOUPBITS | DIGUND,XK_minus,0, "wheat"},
45 {HOLE1,HOLE1,"hole1",hole1_bits,HOLEBITS,0,0, "firebrick"},
46 {HOLE1+1,HOLE1+1,"hole2",hole2_bits,HOLEBITS,0,0, "firebrick"},
47 {HOLE1+2,HOLE1+2,"hole3",hole3_bits,HOLEBITS,0,0, "firebrick"},
48 {HOLE1+3,HOLE1+3,"hole4",hole4_bits,HOLEBITS,0,0, "firebrick"},
49 {HOLE1+4,HOLE1+4,"hole5",hole5_bits,HOLEBITS,0,0, "firebrick"},
50 {HOLE1+5,HOLE1+5,"hole6",hole6_bits,HOLEBITS,0,0, "firebrick"},
51 {HOLE1+6,HOLE1+6,"hole7",hole7_bits,HOLEBITS,0,0, "firebrick"},
52 {'<','<',"left-arrow",larrow_bits,LLEAVE | HENTER | VENTER | LFALL | DIGUND,
53 XK_comma,XK_less, "orange"},
54 {'=','=',"tube",tube_bits,RLEAVE | LLEAVE | HENTER,XK_equal,0, "purple"},
55 {'>','>',"right-arrow",rarrow_bits,RLEAVE | HENTER | VENTER | RFALL |
56 DIGUND,XK_period,XK_greater, "orange"},
57 {STONE,STONE,"stone",stone_bits,KILLIN,XK_2,XK_at, "dimgrey"},
58 {'^','^',"anti-space",anti_bits,ULEAVE | LLEAVE | RLEAVE | HENTER |
59 VENTER | DIGUND | UFALL,XK_6,XK_asciicircum, "PaleGreen"},
60 {'a','a',"armor",armor_bits,ITEMBITS | ARMOR,XK_A,XK_a, "yellow"},
61 {BADGUY,BADGUY,"bad-guy",badguy_bits,SPACEBITS,XK_B,XK_b, "white"},
62 {'c','c',"chute",chute_bits,DLEAVE | DFALL | VENTER,XK_C,XK_c, "purple"},
63 {'d','d',"down-arrow",darrow_bits,DLEAVE | HENTER | VENTER | DIGUND |
64 DFALL,XK_D,XK_d, "orange"},
65 {'e','e',"reverse-monster",reverse_bits,ITEMBITS | REVERSE, XK_E,XK_e, "ForestGreen"},
66 {'f','f',"up-arrow",uarrow_bits,ULEAVE | HENTER | VENTER | UFALL |
67 DIGUND,XK_F,XK_f, "orange"},
68 {'g',BRICK,"ghost-brick",ghost_bits,(SPACEBITS) & ~HENTER,XK_G,XK_g, "firebrick"},
69 {'i',SPACE,"invisible-block",invis_bits,KILLIN,XK_I,XK_i,"firebrick"},
70 {'j','j',"jump-pad",jump_bits,NOUPBITS | UPTWO,XK_J,XK_j, "IndianRed"},
71 {'k','k',"kill-zone",kill_bits,HENTER | VENTER | DIGUND |
72 KILLIN,XK_K,XK_k, "green"},
73 {'l','l',"power-shovel",pshovel_bits,ITEMBITS | PSHOVEL,XK_L,XK_l, "SandyBrown"},
74 {'m','m',"super-jump-pad",sjump_bits,NOUPBITS | UPALL,XK_M,XK_m, "red"},
75 {'n','n',"nuclear-shovel",nshovel_bits,ITEMBITS | NSHOVEL,XK_N,XK_n, "tan"},
76 {'o','o',"anchor",anchor_bits,ITEMBITS | ANCHOR,XK_O,XK_o, "DodgerBlue"},
77 {PLAYER,PLAYER,"player",player_bits,SPACEBITS,XK_P,XK_p, "white"},
78 {'q','q',"speed-boot",speed_bits,ITEMBITS | SPEED,XK_Q,XK_q, "sienna"},
79 {'r','r',"parachute",parac_bits,ITEMBITS | STOPFALL,XK_R,XK_r, "LightCyan"},
80 {'s','s',"step-ladder",steplad_bits,MOVEBITS | PICKUP,XK_S,XK_s, "turquoise"},
81 {'t','t',"teleporter",teleport_bits,SPACEBITS | TELEPORT,XK_T,XK_t, "VioletRed"},
82 {'u','u',"leave-level",uplevel_bits,SPACEBITS | UPLEVEL |
83 INACTIVE,XK_U,XK_u, "brown"},
84 {'v','v',"vertical-rope",vrope_bits,ULEAVE | DLEAVE |
85 HENTER | VENTER,XK_V,XK_v, "wheat"},
86 {'w','w',"window",window_bits,SPACEBITS | UPLEVEL,XK_W,XK_w, "cyan"},
87 {'x','x',"extra-brick",rshovel_bits,ITEMBITS | RSHOVEL,XK_X,XK_x, "tomato"},
88 {'y','y',"heavy-fog",fog_bits,SPACEBITS,XK_Y,XK_y, "DarkSlateGrey"},
89 {'z','z',"time-stop",hourgl_bits,ITEMBITS | TIMESTOP,XK_Z,XK_z, "magenta"},
90 {'|','|',"ladder",ladder_bits,MOVEBITS,XK_backslash,XK_bar, "CornflowerBlue"},
91 {'~','-',"portable-rope",portable_bits,NOUPBITS | DIGUND |
92 PICKUP,XK_asciitilde,XK_quoteleft, "wheat"},
93
94 /* List terminator */
95 {'\0','\0',(char *) 0,(char *) 0,0,0,0}};
96
97 Font scorefont; /* Font used to display score */
98 XFontStruct *scorestruct;
99 int scoresize; /* Size of score font */
100 GC scoregc; /* GC used to draw score */
101 GC blackgc; /* Simple black foreground GC */
102 GC livesgc; /* For drawing player in score line */
103
104 /* These are the graphics cursors used for drawing the player at */
105 /* various times. */
106 GC standgc,angelgc,angelugc,angellgc,flygc,hang1gc,hang2gc,up1gc,up2gc;
107 GC left1gc,left2gc,right1gc,right2gc;
108 /* Graphics cursors for drawing the possible states of the badguys */
109 GC badguy1gc,badguy2gc,badguy3gc;
110
111 /* Manufaction a 16x16 graphics cursor used in a XFill... operation. */
112 /* The FillTiled style is used. */
113
fill_gc(func,bits,item,color)114 static GC fill_gc(func, bits, item, color)
115 int func; /* Drawing function such as GXcopy or GXor. */
116 char *bits; /* Bits describing fill pattern. Produced in an X11 */
117 /* bitmap file usually. */
118 char *item, *color; /* item and color */
119
120 {
121 XGCValues gcv;
122 XColor got, hardw;
123 Pixmap pmap;
124
125 /* Build X11 bitmap from data in bits */
126
127 if (DisplayCells(disp, scrn) <= 2) {
128
129 /*
130 * monochrome
131 */
132
133 pmap = XCreatePixmapFromBitmapData
134 (disp,wind,bits,16,16,BlackPixel(disp,scrn),WhitePixel(disp,scrn),
135 DisplayPlanes(disp,scrn));
136 gcv.foreground = BlackPixel(disp,scrn);
137 gcv.background = WhitePixel(disp,scrn);
138
139 } else {
140
141 /*
142 * color
143 */
144
145 if (!XAllocNamedColor(disp, XDefaultColormap(disp, scrn), color,
146 &got, &hardw)) {
147 printf("XAllocNamedColor failed for %s color %s!\n", item, color);
148 printf("using WhitePixel\n");
149 hardw.pixel = WhitePixel(disp, scrn);
150 }
151
152 pmap = XCreatePixmapFromBitmapData
153 (disp,wind,bits,16,16, hardw.pixel, background,
154 DisplayPlanes(disp,scrn));
155
156 gcv.foreground = hardw.pixel;
157 gcv.background = background;
158
159 }
160
161 /* Assign the graphics cursor parameters */
162 gcv.function = func;
163 gcv.tile = pmap;
164 gcv.fill_style = FillTiled;
165 /* Return the created graphics cursor */
166 return(XCreateGC(disp,wind,GCFunction | GCForeground | GCBackground |
167 GCTile | GCFillStyle,&gcv));
168 }
169
170 /* Manufaction a 16x16 graphics cursor used in a XFill... operation. */
171 /* The FillStippled style is used. */
stip_gc(func,bits,pix)172 static GC stip_gc(func, bits, pix)
173 int func, pix;
174 char bits[];
175 {
176 XGCValues gcv;
177 Pixmap pmap;
178
179 /* Build X11 bitmap from data in bits */
180 pmap = XCreateBitmapFromData(disp,wind,bits,16,16);
181 /* Assign the graphics cursor parameters */
182 gcv.function = func;
183 gcv.foreground = pix;
184 gcv.fill_style = FillStippled;
185 gcv.stipple = pmap;
186 /* Return the created graphics cursor */
187 return(XCreateGC(disp,wind,GCFunction | GCFillStyle |
188 GCForeground | GCStipple,&gcv));
189 }
190
191 /* Start X11 and do some basic initialization */
xstart(evmask,argc,argv)192 void xstart(evmask, argc, argv)
193 long evmask; /* Event mask which will be used in XSelectInput */
194 int argc;
195 char **argv;
196 {
197 register int i;
198 XGCValues xgcv;
199 XWMHints wmhints;
200 XSetWindowAttributes xswa;
201 unsigned int xswamask;
202 XSizeHints hints;
203 int x,y;
204 unsigned int w,h;
205 Window rootW;
206 char *resource;
207 XColor hardw, got;
208 unsigned long tmp, score_fg, score_bg, mecol, badcol;
209
210 /* Open up the display */
211 disp = XOpenDisplay(NULL);
212 /* Check to see if the open display succeeded */
213 if(disp == NULL) {
214 fprintf(stderr,"Display open failed. Check DISPLAY environment variable.\n");
215 exit(-1);
216 }
217
218 /* Set the default screen number */
219 scrn = DefaultScreen(disp);
220 /* Get the root window */
221 rootW = RootWindow(disp,scrn);
222 /* Get the window position, width, and height */
223 x = y = w = h = 1;
224 i = XParseGeometry (geom, &x, &y, &w, &h);
225
226 /* Set values of fields in hints structure */
227 if(i & WidthValue) hints.width = w; else hints.width = 50 << 4;
228 if(i & HeightValue) hints.height = h; else hints.height = (30 << 4)
229 + scoresize;
230 if(i & XValue) {
231 if(i & XNegative)
232 hints.x = XDisplayWidth(disp,scrn) - hints.width - abs(x);
233 else
234 hints.x = x;
235 }
236 else
237 hints.x = 20;
238 if(i & YValue) {
239 if(i & YNegative)
240 hints.y = XDisplayHeight(disp,scrn) - hints.height - abs(y);
241 else
242 hints.y = y;
243 }
244 else
245 hints.y = 20;
246 hints.max_width = DisplayWidth(disp,scrn);
247 hints.max_height = DisplayHeight(disp,scrn);
248 hints.flags = PMaxSize | PPosition | PSize;
249
250 /* Build window attributes */
251 xswa.background_pixel = BlackPixel(disp,scrn);
252 xswa.border_pixel = WhitePixel(disp,scrn);
253 xswamask = CWBackPixel | CWBorderPixel;
254 /* Create Window */
255 wind = XCreateWindow(disp, rootW, hints.x, hints.y, hints.width,
256 hints.height, 2, 0, CopyFromParent,
257 CopyFromParent, xswamask, &xswa);
258 /* Check to see if the open window succeeded */
259 if(wind == 0) {
260 fprintf(stderr,"Window open failed.\n");
261 XCloseDisplay(disp);
262 exit(-1);
263 }
264 /* Set other properties on window */
265 XSetStandardProperties(disp,wind,"Golddig","Golddig",None,argv,argc,&hints);
266
267 if (DisplayCells(disp, scrn) <= 2)
268 background = WhitePixel(disp, scrn);
269 else {
270
271 resource = XGetDefault(disp, "Golddig", "background");
272 if (resource == NULL)
273 resource = "black";
274 if (!XAllocNamedColor(disp, XDefaultColormap(disp, scrn), resource,
275 &got, &hardw)) {
276 printf("XAllocNamedColor failed for background color %s!\n", resource);
277 printf("using BlackPixel\n");
278 hardw.pixel = BlackPixel(disp, scrn);
279 }
280 background = hardw.pixel;
281 }
282
283 /* Clear fast block type lookup table */
284 for(i=0;i<256;++i) {
285 fast_lookup[i].gc = NULL;
286 /* Everything starts out looking like a space */
287 fast_lookup[i].code = SPACEBITS;
288 }
289 /* Generate block type lookup table from symbs array defined above. */
290 /* After this the symbs array will be used very rarely. */
291 for(i=0;symbs[i].symb != '\0';++i) {
292 fast_lookup[symbs[i].symb].gc =
293 fill_gc(GXcopy,symbs[i].bits, symbs[i].name,
294 ((resource = XGetDefault(disp, "Golddig", symbs[i].name)) == NULL) ?
295 symbs[i].dcolor: resource);
296 fast_lookup[symbs[i].symb].code = symbs[i].code;
297 }
298 /* Load in the font used to display the score */
299 scorefont = XLoadFont(disp,SCOREFONT);
300 scorestruct = XQueryFont(disp, scorefont);
301 scoresize = scorestruct->max_bounds.ascent +
302 scorestruct->max_bounds.descent;
303
304 /* Create GC which will be used from drawing score */
305
306 xgcv.function = GXcopy;
307 xgcv.font = scorefont;
308
309 if (DisplayCells(disp, scrn) <= 2) {
310
311 score_fg = xgcv.foreground = WhitePixel(disp,scrn);
312 score_bg = xgcv.background = BlackPixel(disp,scrn);
313
314 } else {
315
316 resource = XGetDefault(disp, "Golddig", "score-fg");
317 if (resource == NULL)
318 resource = "black";
319 if (!XAllocNamedColor(disp, XDefaultColormap(disp, scrn), resource,
320 &got, &hardw)) {
321 printf("XAllocNamedColor failed for score-fg color %s!\n", resource);
322 printf("using BlackPixel\n");
323 hardw.pixel = BlackPixel(disp, scrn);
324 }
325 score_fg = xgcv.foreground = hardw.pixel;
326 resource = XGetDefault(disp, "Golddig", "score-bg");
327 if (resource == NULL)
328 resource = "grey";
329 if (!XAllocNamedColor(disp, XDefaultColormap(disp, scrn), resource,
330 &got, &hardw)) {
331 printf("XAllocNamedColor failed for score-bg color %s!\n", resource);
332 printf("using WhitePixel\n");
333 hardw.pixel = WhitePixel(disp, scrn);
334 }
335 score_bg = xgcv.background = hardw.pixel;
336
337 }
338
339 scoregc = XCreateGC(disp,wind,
340 GCFunction | GCFont | GCForeground | GCBackground,
341 &xgcv);
342
343 /* Create GC which will be used for clearing score line */
344
345 xgcv.function = GXcopy;
346 tmp = xgcv.foreground;
347 xgcv.foreground = score_bg;
348 xgcv.background = score_fg;
349 blackgc = XCreateGC(disp,wind,
350 GCFunction | GCForeground | GCBackground,
351 &xgcv);
352 /* Create GC used for drawing men in score line */
353 livesgc = stip_gc(GXcopy,player_bits, score_fg);
354
355 if (DisplayCells(disp, scrn) <= 2) {
356 mecol = badcol = BlackPixel(disp, scrn);
357 } else {
358 resource = XGetDefault(disp, "Golddig", "mycolor");
359 if (resource == NULL)
360 resource = "white";
361 if (!XAllocNamedColor(disp, XDefaultColormap(disp, scrn), resource,
362 &got, &hardw)) {
363 printf("XAllocNamedColor failed for mycolor (color %s).\n", resource);
364 printf("using WhitePixel\n");
365 hardw.pixel = WhitePixel(disp, scrn);
366 }
367 mecol = hardw.pixel;
368
369 resource = XGetDefault(disp, "Golddig", "badcol");
370 if (resource == NULL)
371 resource = "white";
372 if (!XAllocNamedColor(disp, XDefaultColormap(disp, scrn), resource,
373 &got, &hardw)) {
374 printf("XAllocNamedColor failed for badcol (color %s).\n", resource);
375 printf("using WhitePixel\n");
376 hardw.pixel = WhitePixel(disp, scrn);
377 }
378 badcol = hardw.pixel;
379 }
380
381 /* compute all the graphics cursors for drawing the player in his */
382 /* various states. */
383 standgc = stip_gc(GXcopy,player_bits, mecol);
384 angelgc = stip_gc(GXcopy,angel_bits, mecol);
385 angelugc = stip_gc(GXcopy,angelu_bits,mecol);
386 angellgc = stip_gc(GXcopy,angell_bits,mecol);
387 flygc = stip_gc(GXcopy,fly_bits,mecol);
388 hang1gc = stip_gc(GXcopy,hang1_bits,mecol);
389 hang2gc = stip_gc(GXcopy,hang2_bits,mecol);
390 up1gc = stip_gc(GXcopy,up1_bits,mecol);
391 up2gc = stip_gc(GXcopy,up2_bits,mecol);
392 left1gc = stip_gc(GXcopy,left1_bits,mecol);
393 left2gc = stip_gc(GXcopy,left2_bits,mecol);
394 right1gc = stip_gc(GXcopy,right1_bits,mecol);
395 right2gc = stip_gc(GXcopy,right2_bits,mecol);
396
397 /* Generate graphics cursors for drawing bad guy */
398 badguy1gc = stip_gc(GXcopy,badguy_bits,badcol);
399 badguy2gc = stip_gc(GXcopy,badguy2_bits,badcol);
400 badguy3gc = stip_gc(GXcopy,badguy3_bits,badcol);
401
402 /* Tell the WM that we want input... */
403 wmhints.input = True;
404 wmhints.flags = InputHint;
405 XSetWMHints(disp, wind, &wmhints);
406
407 /* Select the interesting window events */
408 XSelectInput(disp,wind,evmask);
409
410 /* Name and raise the window */
411 XMapRaised(disp,wind);
412
413 /* Flush and synchronize the server */
414 XFlush(disp);
415 XSync(disp,False);
416 }
417
418 /* Gracefully shut X windows down. It is not strictly necessary to */
419 /* call this function. */
xend()420 void xend()
421 {
422 XUnloadFont(disp,scorefont);
423 XUnmapWindow(disp,wind);
424 XDestroyWindow(disp,wind);
425 XCloseDisplay(disp);
426 }
427
428 /* Draw a block from the level array in the output window. */
draw_block(x,y)429 void draw_block(x,y)
430 int x,y; /* Position of block in array */
431 {
432 register unsigned char curchar;
433 GC drawgc;
434
435 /* Get the block character out of the level array */
436 curchar = level[x*ysize + y];
437 /* If there is gold left and this block is inactive, replace it with */
438 /* a space. */
439 if(goldleft > 0 && (fast_lookup[curchar].code & INACTIVE))
440 curchar = SPACE;
441 /* Get the graphics cursor */
442 drawgc = fast_lookup[curchar].gc;
443 /* Replace questionable characters with spaces */
444 if(drawgc == NULL)
445 drawgc = fast_lookup[SPACE].gc;
446 /* Fill the block */
447 XFillRectangle(disp,wind,drawgc,x << 4,y << 4,16,16);
448 }
449
450 /* Change a block character in the level array. The block is redrawn. */
setchar(x,y,ch)451 void setchar(x,y,ch)
452 int x,y; /* Position of block character to change. */
453 char ch; /* Character to change it to */
454 {
455 if(level[x*ysize + y] != ch) {
456 level[x*ysize + y] = ch;
457 draw_block(x,y);
458 }
459 }
460
461 /* Draw the score and level number */
draw_score()462 void draw_score()
463 {
464 char buf[50];
465 int textwidth,i;
466
467 /* Build the output string */
468 sprintf(buf,"score: %d level: %d speed: %d%s",
469 score, levelnum, speed,angelleft ? " You are dead! " : "");
470 /* Clear the current score line */
471 XFillRectangle(disp,wind,blackgc,0,ysize << 4,xsize << 4,scoresize);
472 /* Actually draw the text */
473 i = strlen(buf);
474 XDrawString(disp,wind,scoregc,0,(ysize << 4) + scoresize - 1,buf, i);
475 /* find size of string we just drew */
476 textwidth = XTextWidth(scorestruct, buf, i);
477 i = (textwidth + 16) & ~15; /* starting point */
478 textwidth = lives * 16;
479 XFillRectangle(disp,wind,livesgc, i, (ysize << 4), textwidth, 16);
480 }
481
482 /* Redraw the entire level */
draw_level()483 void draw_level()
484 {
485 int x,y;
486
487 /* Change the window size */
488 XResizeWindow(disp,wind,xsize << 4,(ysize << 4) + scoresize);
489 /* Draw the score and level number */
490 draw_score();
491 /* Iterate through each block position and draw it */
492 for(x=0;x < xsize;++x)
493 for(y=0;y < ysize;++y)
494 draw_block(x,y);
495 }
496
497 /* Load a level out of a file. The global variables worldname and */
498 /* levelnum are used to produce the file name which is stored in */
499 /* filename. */
load_level()500 void load_level()
501 {
502 FILE *levelfile;
503 register int i,j;
504 int x,y;
505 char buf[MAXPATHLEN];
506
507 /* Manufaction the file name by starting with the world name and */
508 /* appending the level number to it. */
509 /* Try looking in ~/.golddig first, then in the global levels. */
510 snprintf(buf, sizeof(buf), "%s/.golddig/%s%03d", getenv("HOME"),
511 worldname, levelnum);
512 /* Open level file for reading */
513 levelfile = fopen(buf,"r");
514
515 if (levelfile == NULL) {
516 /* If local file doesn't exist, try global one. */
517 snprintf(buf, sizeof(buf), "%s/%s%03d", LIB, worldname, levelnum);
518 /* Open level file for reading */
519 levelfile = fopen(buf,"r");
520 } else {
521 printf("Using %s\n", buf);
522 savehighscore = 0;
523 }
524
525 /* If level file does not exist, use the default level file. */
526 if(levelfile == NULL) {
527 /* Build the default level name */
528 snprintf(buf, sizeof(buf), "%s/default", LIB);
529 /* Open default level file for reading */
530 levelfile = fopen(buf,"r");
531 if(levelfile == NULL) {
532 perror(buf);
533 exit(1);
534 }
535 }
536
537 /* Load the first line of the level file */
538 if(fgets(buf,300,levelfile) == NULL) {
539 x = 50;
540 y = 30;
541 }
542 else {
543 /* Extract the level size */
544 sscanf(buf,"%d %d",&x,&y);
545 }
546 /* Change level size only if it is necessary */
547 if(xsize == -1)
548 xsize = x;
549 if(ysize == -1)
550 ysize = y;
551 /* Carefully check the sanity of the size parameters */
552 if(xsize < 5)
553 xsize = 5;
554 if(xsize > 250)
555 xsize = 250;
556 if(ysize < 5)
557 ysize = 5;
558 if(ysize > 250)
559 ysize = 250;
560 if(xsize * ysize > MAXLEVEL) {
561 if(xsize > ysize)
562 xsize = MAXLEVEL / ysize;
563 else
564 ysize = MAXLEVEL / xsize;
565 }
566 /* Iterate through each horizontal line */
567 for(i=0;i<ysize;++i) {
568 /* Load the next line from the file */
569 if(fgets(buf,300,levelfile) != NULL) {
570 /* Go through each horizontal position and copy the data into */
571 /* the level array. */
572 for(j=0;j<xsize;++j) {
573 /* Break out if line ends prematurely */
574 if(buf[j] == '\n' || buf[j] == '\0')
575 break;
576 level[j*ysize + i] = buf[j];
577 }
578 }
579 else
580 j = 0;
581 /* Fill in rest of premature lines with spaces */
582 for(;j<xsize;++j)
583 level[j*ysize + i] = SPACE;
584 }
585 /* Close the level file */
586 fclose(levelfile);
587 }
588
589 /* Save the current level back into a file. The global variables */
590 /* worldname and levelnum are used to determine the file name. */
save_level()591 void save_level()
592 {
593 FILE *levelfile;
594 char buf[MAXPATHLEN+1];
595 char dir[MAXPATHLEN+1];
596 register int i,j;
597 struct stat sb;
598
599 /* Check existence of $HOME/.golddig; create if not existant. */
600 snprintf(dir, sizeof(dir), "%s/.golddig", getenv("HOME"));
601 if ((stat(dir, &sb) == -1)
602 && (mkdir(dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) == -1)) {
603 perror("error accessing or creating ~/.golddig");
604 exit(1);
605 }
606
607 /* Write levels in ~/.golddig. */
608 snprintf(buf, sizeof(buf), "%s/%s%03d", dir, worldname, levelnum);
609 /* Open level file for writing */
610 levelfile = fopen(buf,"w");
611
612 if (levelfile == NULL) {
613 /* If local file doesn't exist, try global one. */
614 snprintf(buf, sizeof(buf), "%s/%s%03d", LIB, worldname, levelnum);
615 /* Open level file for reading */
616 levelfile = fopen(buf,"r");
617 }
618
619 /* Open the data file */
620 levelfile = fopen(buf,"w");
621 if(levelfile == NULL) {
622 perror(buf);
623 exit(1);
624 }
625 /* Write out the size of the level. Normal text is used so that */
626 /* levels can be easily copied across architectures. */
627 fprintf(levelfile,"%d %d\n",xsize,ysize);
628 /* Terminate the lines for writing out the horizontal level lines */
629 buf[xsize] = '\n';
630 buf[xsize+1] = '\0';
631 /* Iterate through each vertical position */
632 for(i=0;i<ysize;++i) {
633 /* Copy each horizontal line into the output buffer */
634 for(j=0;j<xsize;++j)
635 buf[j] = level[j*ysize + i];
636 /* Write the line out to the file */
637 fputs(buf,levelfile);
638 }
639 /* Close the data file */
640 fclose(levelfile);
641 }
642