1 /* Game modules for Xconq.
2    Copyright (C) 1991-1996, 1998-2000 Stanley T. Shebs.
3 
4 Xconq is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.  See the file COPYING.  */
8 
9 #include "conq.h"
10 #include "kernel.h"
11 
12 extern char *readerrbuf;
13 
14 static void do_one_variant(Module *module, struct a_variant *var, Obj *varsetdata);
15 static FILE *open_module_library_file(Module *module);
16 static FILE *open_module_saved_game(Module *module);
17 
18 /* List of all known modules. Their descriptions can co-exist in memory,
19    even if their contents cannot. */
20 
21 Module *modulelist;
22 
23 /* The main module defining the game in effect. */
24 
25 Module *mainmodule;
26 
27 char *moduledesigbuf = NULL;
28 
29 /* Empty out the list of modules. */
30 
31 void
clear_game_modules(void)32 clear_game_modules(void)
33 {
34     modulelist = mainmodule = NULL;
35 }
36 
37 /* Create a brand-new game module. */
38 
39 Module *
create_game_module(char * name)40 create_game_module(char *name)
41 {
42     Module *module = (Module *) xmalloc(sizeof(Module));
43 
44     /* Fill in nonzero slots. */
45     copy_module(module, NULL);
46     /* The module's name must never be NULL. */
47     if (name == NULL)
48       name = "";
49     module->name = copy_string(name);
50     /* Add to front of module list. */
51     module->next = modulelist;
52     modulelist = module;
53     return module;
54 }
55 
56 Module *
find_game_module(char * name)57 find_game_module(char *name)
58 {
59     Module *module;
60 
61     if (name != NULL) {
62 	for_all_modules(module) {
63 	    if (module->name && strcmp(name, module->name) == 0)
64 	      return module;
65 	}
66     }
67     return NULL;
68 }
69 
70 
71 /* Produce a module of the given name, either by finding it or creating it. */
72 
73 Module *
get_game_module(char * name)74 get_game_module(char *name)
75 {
76     Module *module = find_game_module(name);
77 
78     if (module != NULL)
79       return module;
80     return create_game_module(name);
81 }
82 
83 /* Make a game module for the given name and maybe bolt it into the include
84    list of another module. */
85 
86 Module *
add_game_module(char * name,Module * includer)87 add_game_module(char *name, Module *includer)
88 {
89     Module *module = NULL, *other = NULL;
90 
91     module = get_game_module(name);
92     if (!module) {
93 	init_error("Could not include module %s", name);
94 	return NULL;
95     }
96     if (includer) {
97 	/* Add to the end of the list of include files. */
98 	if (includer->include == NULL) {
99 	    /* Avoid creating cycles. */
100 	    for (other = module->nextinclude; other != NULL;
101 		 other = other->nextinclude) {
102 		if (!strcmp(includer->name, other->name))
103 		  return module;
104 	    }
105 	    includer->include = includer->lastinclude = module;
106 	} else {
107 	    for (other = includer->include; other != NULL;
108 		 other = other->nextinclude) {
109 		/* already here, just return it. */
110 		if (module == other)
111 		  return module;
112 	    }
113 	    includer->lastinclude->nextinclude = module;
114 	    includer->lastinclude = module;
115 	}
116     } else {
117 	/* an error? */
118     }
119     return module;
120 }
121 
122 /* Sometimes we find ourselves lacking a game to provide meaning and
123    context for interpretation; this routine loads the standard game
124    (or a specified alternative default) immediately, but only makes
125    it the main module if none defined. */
126 
127 void
load_default_game(void)128 load_default_game(void)
129 {
130     extern char *standard_game_name;
131     char *defaultname = standard_game_name;
132     Module *module, *module2;
133 
134     /* If we have a different default module, use it instead. */
135     if (mainmodule != NULL
136 	&& !empty_string(mainmodule->defaultbasemodulename)) {
137     	defaultname = mainmodule->defaultbasemodulename;
138     }
139     module = get_game_module(defaultname);
140     if (mainmodule == NULL)
141       mainmodule = module;
142     load_game_description(module);
143     /* Recurse one level of default base module. */
144     /* (should recurse indefinitely, or not?) */
145     if (!empty_string(module->defaultbasemodulename)) {
146 	module2 = get_game_module(module->defaultbasemodulename);
147 	load_game_module(module2, TRUE);
148     }
149     load_game_module(module, TRUE);
150 }
151 
152 /* Attempt to read just the first form in a module and use it as a
153    description of the module.  Return true if this worked, false
154    otherwise. */
155 
156 int
load_game_description(Module * module)157 load_game_description(Module *module)
158 {
159     Obj *form, *thecar;
160     char *name;
161 
162     /* Module is already open, don't mess with it. */
163     if (module->open)
164       return FALSE;
165     if (open_module(module, FALSE)) {
166 	if ((form = read_form(module->fp,
167 			      &(module->startlineno),
168 			      &(module->endlineno)))
169 	    != lispeof) {
170 	    if (consp(form) && symbolp(thecar = car(form))) {
171 		name = c_string(thecar);
172 		if (keyword_code(name) == K_GAME_MODULE) {
173 		    interp_game_module(form, module);
174 		    close_module(module);
175 		    /* Note that module is still not considered "loaded". */
176 		    return TRUE;
177 		}
178 	    }
179 	}
180 	close_module(module);
181     }
182     return FALSE;
183 }
184 
185 /* Game files can live in library directories or somewhere else.  This
186    function tries to find a file, open it, and load the contents. */
187 
188 void
load_game_module(Module * module,int dowarn)189 load_game_module(Module *module, int dowarn)
190 {
191     char ch;
192     Module *module2;
193 
194     if (numutypes == 0) {
195 	load_game_description(module);
196 	if (!empty_string(module->defaultbasemodulename)) {
197 	    module2 = get_game_module(module->defaultbasemodulename);
198 	    load_game_module(module2, TRUE);
199 	}
200     }
201     if (open_module(module, dowarn)) {
202 	if (module->fp) {
203 	    /* Peek at the first character - was 'X' in old format files. */
204 	    ch = getc(module->fp);
205 	    ungetc(ch, module->fp);
206 	    if (ch == 'X') {
207 		init_error("\"%s\" is probably an obsolete Xconq file; in any case, it cannot be used.",
208 			   module->filename);
209 	    } else {
210 		/* New format, read it all. */
211 		read_forms(module);
212 	    }
213 	} else {
214 	    /* (should be able to read from contents string) */
215 	}
216 	/* We're done, can close. */
217 	close_module(module);
218 	/* Mark the module as having been loaded - note that this will
219 	   happen even if there were horrible errors. */
220 	module->loaded = TRUE;
221 	/* If the turn number has been set explicitly to a positive
222 	   value, assume that a saved game is being restored into the
223 	   middle of the turn. */
224 	if (g_turn() > 0) {
225 		midturnrestore = TRUE;
226 	}
227 	/* If the random state has been set explicitly to a
228 	   nonnegative value, use it to reseed the generator. */
229 	if (g_random_state() >= 0) {
230 		init_xrandom(g_random_state());
231 	}
232    }
233 }
234 
235 void
load_base_module(Module * module)236 load_base_module(Module *module)
237 {
238     char *basename = module->basemodulename;
239     Module *basemodule;
240 
241     if (!empty_string(basename)) {
242 	basemodule = find_game_module(basename);
243 	if (basemodule == NULL)
244 	  basemodule = add_game_module(basename, module);
245 	if (basemodule->loaded) {
246 	    Dprintf("Base module `%s' already loaded.\n", basename);
247 	} else {
248 	    Dprintf("Loading base module `%s' ...\n", basename);
249 	    load_game_module(basemodule, FALSE);
250 	    Dprintf("... Done loading `%s'.\n", basename);
251 	}
252     }
253 }
254 
255 /* Given a module, attempt to open it. */
256 
257 int
open_module(Module * module,int dowarn)258 open_module(Module *module, int dowarn)
259 {
260     FILE *fp = NULL;
261 
262     /* Don't open more than once. */
263     if (module->open) {
264     	if (dowarn) {
265 		init_warning("Module \"%s\" is already open, ignoring attempt to reopen",
266 			     module->name);
267 	}
268 	return FALSE;
269     }
270     /* Don't open if already loaded. */
271     if (module->loaded) {
272     	if (dowarn) {
273 		init_warning("Module \"%s\" is already loaded, ignoring attempt to reload",
274 		     	module->name);
275 	}
276 	return FALSE;
277     }
278     /* Uninterpreted contents already available, init the ptr. */
279     if (module->contents) {
280 	module->sp = module->contents;
281 	Dprintf("Reading module \"%s\" from string ...\n", module->name);
282     /* First check if this is a saved game. */
283     } else if ((fp = open_module_saved_game(module)) != NULL) {
284 	Dprintf("Reading module \"%s\" from saved game file \"%s\" ...\n",
285 		module->name, module->filename);
286 	module->fp = fp;
287     /* Then look for a library module. */
288     } else if ((fp = open_module_library_file(module)) != NULL) {
289 	Dprintf("Reading module \"%s\" from the library ...\n",
290 		module->name);
291 	module->fp = fp;
292     } else {
293 	if (dowarn) {
294 	    if (module->name) {
295 	    	init_warning("Can't find module \"%s\" anywhere",
296 			     module->name);
297 	    } else {
298 	    	init_warning("Can't find unnamed module anywhere");
299 	    }
300 	}
301 	return FALSE;
302     }
303     /* It worked, mark this module as open. */
304     module->open = TRUE;
305     return TRUE;
306 }
307 
308 /* Attempt to open a saved game. */
309 
310 FILE *
open_module_saved_game(Module * module)311 open_module_saved_game(Module *module)
312 {
313 	char fullnamebuf[PATH_SIZE];
314 	LibraryPath *p;
315 	FILE *fp;
316 
317 	/* Can't open saved games without a filename. */
318 	if (empty_string(module->filename)) {
319 		return NULL;
320 	}
321 	/* Search the working dir first in case we picked a game file in a
322 	non-standard location. Only do this in single player games.*/
323 	if (numremotes == 0) {
324 		fp = open_file(module->filename, "r");
325 		if (fp) {
326 		    return fp;
327 		}
328 	}
329 	/* First search the saved games directory for filename. */
330 	make_pathname(game_homedir(), module->filename, NULL, fullnamebuf);
331 	/* Now try to open the file. */
332 	fp = open_file(fullnamebuf, "r");
333 	if (fp) {
334 		return fp;
335 	}
336 	/* Then search the library paths for filename. */
337 	for_all_library_paths(p) {
338 		make_pathname(p->path, module->filename, NULL, fullnamebuf);
339 		/* Now try to open the file. */
340 		fp = open_file(fullnamebuf, "r");
341 		if (fp) {
342 			return fp;
343 		}
344 	}
345 	return NULL;
346 }
347 
348 /* Attempt to open a library file. */
349 
350 FILE *
open_module_library_file(Module * module)351 open_module_library_file(Module *module)
352 {
353 	char fullnamebuf[PATH_SIZE];
354 	LibraryPath *p;
355 	FILE *fp;
356 
357 	/* Can't open anonymous library modules. */
358 	if (empty_string(module->name)) {
359 		return NULL;
360 	}
361 	/* Search the working dir first in case we picked a game file in a
362 	non-standard location. Only do this in single player games.*/
363 	if (numremotes == 0) {
364 		/* First look for "name.g". */
365 		make_pathname("", module->name, "g", fullnamebuf);
366 		fp = open_file(fullnamebuf, "r");
367 		if (fp) {
368 		    	return fp;
369 		}
370 		/* Then look for "name" in case the game desinger did not
371 		use the g file extension. */
372 		make_pathname("", module->name, NULL, fullnamebuf);
373 		/* Now try to open the file. */
374 		fp = open_file(fullnamebuf, "r");
375 		if (fp) {
376 		    	return fp;
377 		}
378 	}
379 	/* First search the library paths for "name.g". */
380 	for_all_library_paths(p) {
381 		make_pathname(p->path, module->name, "g", fullnamebuf);
382 		/* Now try to open the file. */
383 		fp = open_file(fullnamebuf, "r");
384 		if (fp) {
385 		    	return fp;
386 		}
387 	}
388 	/* Then search the library paths for "name" in case the game
389 	designer did not use the g extension. */
390 	for_all_library_paths(p) {
391 		make_pathname(p->path, module->name, NULL, fullnamebuf);
392 		/* Now try to open the file. */
393 		fp = open_file(fullnamebuf, "r");
394 		if (fp) {
395 		    	return fp;
396 		}
397 	}
398 	return NULL;
399 }
400 
401 /* Read info about a side's preferences and setup. */
402 
403 /* This assumes one form only, probably too restrictive. */
404 /* should read all the forms, use the relevant ones. */
405 /* (how does this interact with other defaults?) */
406 /* (should be delayed until player can confirm it...) */
407 
408 /* (update to work like other module stuff? then can use resources etc) */
409 /* (fix so that correct name can be found reliably) */
410 
411 int
load_side_config(Side * side)412 load_side_config(Side *side)
413 {
414 #if 0
415     FILE *fp;
416     Obj *config;
417     Module *module;
418 
419     /* (should incorp config name somehow, also be sys-dependent) */
420     module = create_game_module(side->player->name);
421 
422     /* Don't use fopen directly, use open_module which indirectly calls
423     open_file to ensure mac/unix cross-platform compatibility. */
424     if (open_module(module, FALSE)) {
425 	if ((config = read_form(module->fp,
426 				&(module->startlineno),
427 				&(module->endlineno)))
428 	    != lispeof) {
429 	    /* interpret the config */
430 	    Dprintf("Interpreting %s config form", side_desig(side));
431 	    Dprintlisp(config);
432 	    Dprintf("\n");
433 	    fill_in_side(side, config, TRUE);
434 	} else {
435 	    /* no config form in the file */
436 	}
437     } else {
438 	init_warning("Module \"%s\" could not be opened", module->name);
439 	/* Not a disaster, keep going */
440     }
441 #endif
442     return FALSE;
443 }
444 
445 /* Read an entire file, attempting to pick up objects in it. */
446 
447 /* (does this interp game-module form twice if description previously
448    loaded?) */
449 
450 void
read_forms(Module * module)451 read_forms(Module *module)
452 {
453     Obj *form;
454 
455     Dprintf("Trying to read a new format file \"%s\"...\n", module->name);
456     if (module->fp != NULL) {
457 	while ((form = read_form(module->fp,
458 			     &(module->startlineno),
459 			     &(module->endlineno)))
460 		!= lispeof) {
461 	    interp_form(module, form);
462 	}
463     } else {
464 	while ((form = read_form_from_string(module->sp,
465 			     &(module->startlineno),
466 			     &(module->endlineno),
467 			     &(module->sp)))
468 		!= lispeof) {
469 	    interp_form(module, form);
470 	}
471     }
472     /* Clean up after any print forms that might have been done. */
473     end_printing_forms();
474     Dprintf("... Done reading \"%s\".\n", module->name);
475 }
476 
477 void
set_variant_value(int which,int v1,int v2,int v3)478 set_variant_value(int which, int v1, int v2, int v3)
479 {
480     Variant *var;
481 
482     var = &(mainmodule->variants[which]);
483     var->newvalues[0] = v1;
484     var->newvalues[1] = v2;
485     var->newvalues[2] = v3;
486 }
487 
488 /* Interpret the given list of variants, using the given list to
489    fill in values not already supplied in the variant. */
490 
491 void
do_module_variants(Module * module,Obj * lis)492 do_module_variants(Module *module, Obj *lis)
493 {
494     int i, found, didsome;
495     Obj *restset, *varset;
496     Variant *var;
497 
498     if (module->variants == NULL)
499       return; /* error? */
500     didsome = FALSE;
501     for_all_list(lis, restset) {
502 	varset = car(restset);
503 	found = FALSE;
504 	for (i = 0; module->variants[i].id != lispnil; ++i) {
505 	    var = &(module->variants[i]);
506 	    if (equal(car(varset), var->id)) {
507 		do_one_variant(module, var, cdr(varset));
508 		found = TRUE;
509 		didsome = TRUE;
510 	    }
511 	}
512 	if (!found) {
513 	    read_warning("Mystifying variant");
514 	}
515     }
516     /* Now implement all the defaults. */
517     for (i = 0; module->variants[i].id != lispnil; ++i) {
518 	var = &(module->variants[i]);
519 	if (!var->used) {
520 	    do_one_variant(module, var, lispnil);
521 	    didsome = TRUE;
522 	}
523     }
524     if (didsome) {
525 	/* Recheck everything, the variants might have broken something. */
526 	check_game_validity();
527     }
528 }
529 
530 /* Implement the effect of the single given variant. */
531 
532 static void
do_one_variant(Module * module,Variant * var,Obj * varsetdata)533 do_one_variant(Module *module, Variant *var, Obj *varsetdata)
534 {
535     int val, caseval;
536     int width = 0, height = 0, circumference /*, latitude, longitude*/;
537     int rtime, rtimeperturn, rtimeperside;
538     char *vartypename = c_string(var->id);
539     Obj *restcases, *headcase, *rest, *filler, *rawcaseval;
540 
541     if (Debug) {
542 	if (readerrbuf == NULL)
543 	  readerrbuf = (char *) xmalloc(BUFSIZE);
544 	if (varsetdata != lispnil)
545 	  sprintlisp(readerrbuf, varsetdata, BUFSIZE);
546 	else
547 	  sprintf(readerrbuf, "%d %d %d",
548 		  var->newvalues[0], var->newvalues[1], var->newvalues[2]);
549     	Dprintf("Module %s variant %s being set to `%s'\n",
550 	    	module_desig(module), vartypename, readerrbuf);
551     }
552     switch (keyword_code(vartypename)) {
553       case K_WORLD_SEEN:
554 	val = var->newvalues[0];
555 	if (varsetdata != lispnil)
556 	  val = c_number(eval(car(varsetdata)));
557 	var->hasintvalue = TRUE;
558 	var->intvalue = val;
559 	set_g_terrain_seen(val);
560 	break;
561       case K_SEE_ALL:
562 	val = var->newvalues[0];
563 	if (varsetdata != lispnil)
564 	  val = c_number(eval(car(varsetdata)));
565 	var->hasintvalue = TRUE;
566 	var->intvalue = val;
567 	set_g_see_all(val);
568 	break;
569       case K_SEQUENTIAL:
570 	val = var->newvalues[0];
571 	if (varsetdata != lispnil)
572 	  val = c_number(eval(car(varsetdata)));
573 	var->hasintvalue = TRUE;
574 	var->intvalue = val;
575 	set_g_use_side_priority(val);
576 	break;
577       case K_PEOPLE:
578 	val = var->newvalues[0];
579 	if (varsetdata != lispnil)
580 	  val = c_number(eval(car(varsetdata)));
581 	var->hasintvalue = TRUE;
582 	var->intvalue = val;
583 	set_g_people(val);
584 	break;
585       case K_ECONOMY:
586 	val = var->newvalues[0];
587 	if (varsetdata != lispnil)
588 	  val = c_number(eval(car(varsetdata)));
589 	var->hasintvalue = TRUE;
590 	var->intvalue = val;
591 	set_g_economy(val);
592 	break;
593       case K_SUPPLY:
594 	val = var->newvalues[0];
595 	if (varsetdata != lispnil)
596 	  val = c_number(eval(car(varsetdata)));
597 	var->hasintvalue = TRUE;
598 	var->intvalue = val;
599 	set_g_supply(val);
600 	break;
601       case K_WORLD_SIZE:
602       	filler = lispnil;
603 	if (varsetdata != lispnil) {
604 	    filler = varsetdata;
605 	}
606 	/* Pick the width and height out of the list. */
607 	if (filler != lispnil) {
608 	    width = c_number(eval(car(filler)));
609 	    filler = cdr(filler);
610 	} else {
611 	    width = var->newvalues[0];
612 	}
613 	if (filler != lispnil) {
614 	    height = c_number(eval(car(filler)));
615 	    filler = cdr(filler);
616 	} else {
617 	    height = var->newvalues[1];
618 	}
619 	/* Pick up a circumference if given. */
620 	if (filler != lispnil) {
621 	    circumference = c_number(eval(car(filler)));
622 	    filler = cdr(filler);
623 	} else {
624 	    circumference = var->newvalues[2];
625 	}
626 	/* This is more useful after the circumference has been set. */
627 	if (width > 0 && height > 0)
628 	  set_area_shape(width, height, TRUE);
629 	set_world_circumference(circumference, TRUE);
630 #if 0 /* (redo as separate variant) */
631 	/* Pick up latitude and longitude if given. */
632 	if (filler != lispnil) {
633 	    latitude = c_number(eval(car(filler)));
634 	    /* (should use a setter routine?) */
635 	    area.latitude = latitude;
636 	    filler = cdr(filler);
637 	}
638 	if (filler != lispnil) {
639 	    longitude = c_number(eval(car(filler)));
640 	    /* (should use a setter routine?) */
641 	    area.longitude = longitude;
642 	    filler = cdr(filler);
643 	}
644 #endif
645 	/* (should record settings somehow) */
646 	break;
647       case K_REAL_TIME:
648       	filler = lispnil;
649 	if (varsetdata != lispnil) {
650 	    filler = varsetdata;
651 	}
652 	if (filler != lispnil) {
653 	    rtime = c_number(eval(car(filler)));
654 	    filler = cdr(filler);
655 	} else {
656 	    rtime = var->newvalues[0];
657 	}
658 	if (filler != lispnil) {
659 	    rtimeperside = c_number(eval(car(filler)));
660 	    filler = cdr(filler);
661 	} else {
662 	    rtimeperside = var->newvalues[1];
663 	}
664 	if (filler != lispnil) {
665 	    rtimeperturn = c_number(eval(car(filler)));
666 	    filler = cdr(filler);
667 	} else {
668 	    rtimeperturn = var->newvalues[2];
669 	}
670 	/* If the values were specified, tweak the official
671 	   realtime globals. */
672 	if (rtime > 0)
673 	  set_g_rt_for_game(rtime);
674 	if (rtimeperside > 0)
675 	  set_g_rt_per_side(rtimeperside);
676 	if (rtimeperturn > 0)
677 	  set_g_rt_per_turn(rtimeperturn);
678 	/* (should record settings somehow) */
679 	break;
680       default:
681 	/* This is the general case. */
682 	val = var->newvalues[0];
683 	if (varsetdata != lispnil)
684 	  val = c_number(eval(car(varsetdata)));
685 	var->hasintvalue = TRUE;
686 	var->intvalue = val;
687 	for_all_list(var->cases, restcases) {
688 	    headcase = car(restcases);
689 	    rawcaseval = eval(car(headcase));
690 	    if (numberp(rawcaseval))
691 	      caseval = c_number(rawcaseval);
692 	    if (numberp(rawcaseval) && caseval == val) {
693 		for_all_list(cdr(headcase), rest) {
694 		    interp_form(module, car(rest));
695 	    	}
696 	    }
697 	}
698 	/* Clean up after printing, might have been print forms in variant. */
699 	end_printing_forms();
700 	break;
701     }
702     /* Flag the variant as having been specified. */
703     var->used = TRUE;
704 }
705 
706 /* Given a module and a original, copy over properties of the original
707    module. */
708 
709 void
copy_module(Module * module,Module * origmodule)710 copy_module(Module *module, Module *origmodule)
711 {
712     char *name;
713     Module *next, *incl, *nextincl, *lastincl;
714 
715     /* No matter what, preserve the existing name. */
716     name = module->name;
717     if (origmodule != NULL) {
718 	/* Save away the module's links to other modules. */
719 	next = module->next;
720 	incl = module->include;
721 	nextincl = module->nextinclude;
722 	lastincl = module->lastinclude;
723 	memcpy(module, origmodule, sizeof(Module));
724 	/* By default, we copy everything through; but keep a separate
725 	   copy of the original name and variants, because the calling
726 	   routine may overwrite with other values. */
727 	/* (should test each property individually here?) */
728 	if (empty_string(origmodule->origmodulename)) {
729 	    /* If no original module, use the main module. */
730 	    module->origmodulename = origmodule->name;
731 	    module->origvariants = origmodule->variants;
732 	    module->origversion = origmodule->version;
733 	}
734 	/* Kill off caches that should not be replicated. */
735 	module->contents = NULL;
736 	module->sp = NULL;
737 	module->fp = NULL;
738 	module->open = FALSE;
739 	module->loaded = FALSE;
740 	/* Restore links. */
741 	module->next = next;
742 	module->include = incl;
743 	module->nextinclude = nextincl;
744 	module->lastinclude = lastincl;
745     } else {
746 	/* If there is no original from which to copy, just do some
747 	   generic clearing/init. */
748 	memset(module, 0, sizeof(Module));
749 	module->blurb = lispnil;
750 	module->instructions = lispnil;
751 	module->notes = lispnil;
752 	module->designnotes = lispnil;
753 	module->startlineno = 1;
754 	module->endlineno = 1;
755     }
756     module->name = name;
757 }
758 
759 void
init_module_reshape(Module * module)760 init_module_reshape(Module *module)
761 {
762     /* Seed all the reshaping parameters with reasonable values. */
763     module->maybe_reshape = TRUE;
764     module->subarea_width = area.width;
765     module->subarea_height = area.height;
766     module->subarea_x = module->subarea_y = 0;
767     module->final_subarea_width = area.width;
768     module->final_subarea_height = area.height;
769     module->final_subarea_x = module->final_subarea_y = 0;
770     module->final_width = area.width;  module->final_height = area.height;
771     module->final_circumference = world.circumference;
772 }
773 
774 /* This is true if any actual reshaping is required. */
775 
776 int
reshape_the_output(Module * module)777 reshape_the_output(Module *module)
778 {
779     return (module->maybe_reshape
780 	    && (module->subarea_width != area.width
781 		|| module->subarea_height != area.height
782 		|| module->subarea_x != 0
783 		|| module->subarea_y != 0
784 		|| module->final_subarea_width != area.width
785 		|| module->final_subarea_height != area.height
786 		|| module->final_subarea_x != 0
787 		|| module->final_subarea_y != 0
788 		|| module->final_width != area.width
789 		|| module->final_height != area.height
790 		|| module->final_circumference != world.circumference));
791 }
792 
793 /* Check if the proposed reshape will actually work. */
794 
795 int
valid_reshape(Module * module)796 valid_reshape(Module *module)
797 {
798     /* (should check hexagon shaping) */
799     if (module->subarea_width > area.width
800 	|| module->subarea_height > area.height)
801       return FALSE;
802     /* (should check other offsets) */
803     if (module->final_width < 3 || module->final_height < 3)
804       return FALSE;
805     return TRUE;
806 }
807 
808 /* Close the module. */
809 
810 void
close_module(Module * module)811 close_module(Module *module)
812 {
813     if (module->sp) {
814 	module->sp = NULL;
815     }
816     if (module->fp) {
817 	fclose(module->fp);
818 	module->fp = NULL;
819     }
820     module->open = FALSE;
821 }
822 
823 /* Return a description of the module. */
824 
825 char *
module_desig(Module * module)826 module_desig(Module *module)
827 {
828     if (moduledesigbuf == NULL)
829       moduledesigbuf = (char *)xmalloc(BUFSIZE);
830     sprintf(moduledesigbuf, "module %s (%s)",
831 	    module->name, (module->title ? module->title : "no title"));
832     return moduledesigbuf;
833 }
834 
835 char *
saved_game_filename(void)836 saved_game_filename(void)
837 {
838     char *str, name[BUFSIZE];
839 
840     sprintf(name, "%s-%d.xcq", mainmodule->name, g_turn());
841     str = game_filename("XCONQSAVEFILE", name);
842     return str;
843 }
844 
845 char *
checkpoint_filename(int n)846 checkpoint_filename(int n)
847 {
848     char *str, name[BUFSIZE];
849 
850     sprintf(name, "check%d.xcq", n);
851     str = game_filename(NULL, name);
852     return str;
853 }
854 
855 char *
statistics_filename(void)856 statistics_filename(void)
857 {
858     /* No need to cache name, will only get requested once. */
859     return game_filename("XCONQSTATSFILE", STATSFILE);
860 }
861 
862 char *
preferences_filename(void)863 preferences_filename(void)
864 {
865     return game_filename("XCONQPREFERENCES", PREFERENCESFILE);
866 }
867 
868 /* Legacy filename support. */
869 char *
old_preferences_filename(void)870 old_preferences_filename(void)
871 {
872     return game_filename("XCONQPREFERENCES", OLD_PREFERENCESFILE);
873 }
874 
875 /* (random code below, should be sent to better places) */
876 
877 /* If a special symbol, we might not have to fail.  Note that although
878    this looks like Lisp-level code, it knows about unit types and so
879    forth, so is higher-level. */
880 
881 int
lazy_bind(Obj * sym)882 lazy_bind(Obj *sym)
883 {
884     int u, m, t, a;
885     Obj *value;
886 
887     switch (keyword_code(c_string(sym))) {
888       case K_USTAR:
889 	value = lispnil;
890 	/* Since consing glues onto the front, iterate backwards
891 	   through the types. */
892 	for (u = numutypes - 1; u >= 0; --u)
893 	  value = cons(new_utype(u), value);
894 	break;
895       case K_MSTAR:
896 	value = lispnil;
897 	for (m = nummtypes - 1; m >= 0; --m)
898 	  value = cons(new_mtype(m), value);
899 	break;
900       case K_TSTAR:
901 	value = lispnil;
902 	for (t = numttypes - 1; t >= 0; --t)
903 	  value = cons(new_ttype(t), value);
904 	break;
905       case K_ASTAR:
906 	value = lispnil;
907 	for (a = numatypes - 1; a >= 0; --a)
908 	  value = cons(new_atype(a), value);
909 	break;
910       default:
911 	return FALSE;
912     }
913     setq(sym, value);
914     return TRUE;
915 }
916