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