Logo Search packages:      
Sourcecode: xconq version File versions  Download package

module.c

/* Game modules for Xconq.
   Copyright (C) 1991-1996, 1998-2000 Stanley T. Shebs.

Xconq is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.  See the file COPYING.  */

#include "conq.h"
#include "kernel.h"

extern char *readerrbuf;

static void do_one_variant(Module *module, struct a_variant *var,
                     Obj *varsetdata);

/* List of all known modules. Their descriptions can co-exist in memory,
   even if their contents cannot. */

Module *modulelist;

/* The main module defining the game in effect. */

Module *mainmodule;

char *moduledesigbuf = NULL;

/* Empty out the list of modules. */

void
clear_game_modules(void)
{
    modulelist = mainmodule = NULL;
}

/* Create a brand-new game module. */

Module *
create_game_module(char *name)
{
    Module *module = (Module *) xmalloc(sizeof(Module));

    /* Fill in nonzero slots. */
    copy_module(module, NULL);
    /* The module's name must never be NULL. */
    if (name == NULL)
      name = "";
    module->name = copy_string(name);
    /* Add to front of module list. */
    module->next = modulelist;
    modulelist = module;
    return module;
}

Module *
find_game_module(char *name)
{
    Module *module;

    if (name != NULL) {
      for_all_modules(module) {
          if (module->name && strcmp(name, module->name) == 0)
            return module;
      }
    }
    return NULL;
}


/* Produce a module of the given name, either by finding it or creating it. */

Module *
get_game_module(char *name)
{
    Module *module = find_game_module(name);

    if (module != NULL)
      return module;
    return create_game_module(name);
}

/* Make a game module for the given name and maybe bolt it into the include
   list of another module. */

Module *
add_game_module(char *name, Module *includer)
{
    Module *module = get_game_module(name), *other;

    if (includer) {
      /* Add to the end of the list of include files. */
      if (includer->include == NULL) {
          includer->include = includer->lastinclude = module;
      } else {
          for (other = includer->include; other != NULL; other = other->nextinclude) {
            /* already here, just return it. */
            if (module == other)
              return module;
          }
          includer->lastinclude->nextinclude = module;
          includer->lastinclude = module;
      }
    } else {
      /* an error? */
    }
    return module;
}

/* Sometimes we find ourselves lacking a game to provide meaning and
   context for interpretation; this routine loads the standard game
   (or a specified alternative default) immediately, but only makes
   it the main module if none defined. */

void
load_default_game(void)
{
    extern char *standard_game_name;
    char *defaultname = standard_game_name;
    Module *module, *module2;

    /* If we have a different default module, use it instead. */
    if (mainmodule != NULL
      && !empty_string(mainmodule->defaultbasemodulename)) {
      defaultname = mainmodule->defaultbasemodulename;
    }
    module = get_game_module(defaultname);
    if (mainmodule == NULL)
      mainmodule = module;
    load_game_description(module);
    /* Recurse one level of default base module. */
    /* (should recurse indefinitely, or not?) */
    if (!empty_string(module->defaultbasemodulename)) {
      module2 = get_game_module(module->defaultbasemodulename);
      load_game_module(module2, TRUE);
    }
    load_game_module(module, TRUE);
}

/* Attempt to read just the first form in a module and use it as a
   description of the module.  Return true if this worked, false
   otherwise. */

int
load_game_description(Module *module)
{
    Obj *form, *thecar;
    char *name;

    /* Module is already open, don't mess with it. */
    if (module->open)
      return FALSE;
    if (open_module(module, FALSE)) {
      if ((form = read_form(module->fp,
                        &(module->startlineno),
                        &(module->endlineno)))
          != lispeof) {
          if (consp(form) && symbolp(thecar = car(form))) {
            name = c_string(thecar);
            if (keyword_code(name) == K_GAME_MODULE) {
                interp_game_module(form, module);
                close_module(module);
                /* Note that module is still not considered "loaded". */
                return TRUE;
            }
          }
      }
      close_module(module);
    }
    return FALSE;
}

/* Game files can live in library directories or somewhere else.  This
   function tries to find a file, open it, and load the contents. */

void
load_game_module(Module *module, int dowarn)
{
    char ch;
    Module *module2;

    if (numutypes == 0) {
      load_game_description(module);
      if (!empty_string(module->defaultbasemodulename)) {
          module2 = get_game_module(module->defaultbasemodulename);
          load_game_module(module2, TRUE);
      }
    }
    if (open_module(module, dowarn)) {
      if (module->fp) {
          /* Peek at the first character - was 'X' in old format files. */
          ch = getc(module->fp);
          ungetc(ch, module->fp);
          if (ch == 'X') {
            init_error("\"%s\" is probably an obsolete Xconq file; in any case, it cannot be used.",
                     module->filename);
          } else {
            /* New format, read it all. */
            read_forms(module);
          }
      } else {
          /* (should be able to read from contents string) */
      }
      /* We're done, can close. */
      close_module(module);
      /* Mark the module as having been loaded - note that this will
         happen even if there were horrible errors. */
      module->loaded = TRUE;
      /* If the turn number has been set explicitly to a positive
         value, assume that a saved game is being restored into the
         middle of the turn. */
      if (g_turn() > 0)
        midturnrestore = TRUE;
      /* If the random state has been set explicitly to a
         nonnegative value, use it to reseed the generator. */
      if (g_random_state() >= 0)
        init_xrandom(g_random_state());
      /* Make all the cross-references right. */
      patch_object_references();
    }
}

void
load_base_module(Module *module)
{
    char *basename = module->basemodulename;
    Module *basemodule;

    if (!empty_string(basename)) {
      basemodule = find_game_module(basename);
      if (basemodule == NULL)
        basemodule = add_game_module(basename, module);
      if (basemodule->loaded) {    
          Dprintf("Base module `%s' already loaded.\n", basename);
      } else {
          Dprintf("Loading base module `%s' ...\n", basename);
          load_game_module(basemodule, FALSE);
          Dprintf("... Done loading `%s'.\n", basename);
      }
    }
}

/* Given a module, attempt to open it. */

int
open_module(Module *module, int dowarn)
{
    FILE *fp = NULL;

    /* Don't open more than once. */
    if (module->open && dowarn) {
      init_warning("Module \"%s\" is already open, ignoring attempt to reopen",
                 module->name);
      return FALSE;
    }
    /* Don't open if already loaded. */
    if (module->loaded && dowarn) {
      init_warning("Module \"%s\" is already loaded, ignoring attempt to reload",
                 module->name);
      return FALSE;
    }
    if (module->contents) {
      /* Uninterpreted contents already available, init the ptr. */
      module->sp = module->contents;
      Dprintf("Reading module \"%s\" from string ...\n", module->name);
    } else if ((fp = open_module_library_file(module)) != NULL) {
      /* Found the module in a library directory. */
      Dprintf("Reading module \"%s\" from library file \"%s\" ...\n",
            module->name, module->filename);
      module->fp = fp;
    } else if ((fp = open_module_explicit_file(module)) != NULL) {
      Dprintf("Reading module \"%s\" from file \"%s\" ...\n",
            module->name, module->filename);
      module->fp = fp;
    } else {
      if (dowarn) {
          if (module->name) {
            init_warning("Can't find module \"%s\" anywhere",
                       module->name);
          } else {
            init_warning("Can't find unnamed module anywhere");
          }
      }
      return FALSE;
    }
    /* It worked, mark this module as open. */
    module->open = TRUE;
    return TRUE;
}

/* Read info about a side's preferences and setup. */

/* This assumes one form only, probably too restrictive. */
/* should read all the forms, use the relevant ones. */
/* (how does this interact with other defaults?) */
/* (should be delayed until player can confirm it...) */

/* (update to work like other module stuff? then can use resources etc) */
/* (fix so that correct name can be found reliably) */

int
load_side_config(Side *side)
{
#if 0
    FILE *fp;
    Obj *config;
    Module *module;

    /* (should incorp config name somehow, also be sys-dependent) */
    module = create_game_module(side->player->name);

    /* Don't use fopen directly, use open_module which indirectly calls 
    open_file to ensure mac/unix cross-platform compatibility. */    
    if (open_module(module, FALSE)) {
      if ((config = read_form(module->fp,
                        &(module->startlineno),
                        &(module->endlineno)))
          != lispeof) {
          /* interpret the config */
          Dprintf("Interpreting %s config form", side_desig(side));
          Dprintlisp(config);
          Dprintf("\n");
          fill_in_side(side, config, TRUE);
      } else {
          /* no config form in the file */
      }
    } else {
      init_warning("Module \"%s\" could not be opened", module->name);
      /* Not a disaster, keep going */
    }
#endif
    return FALSE;
}

/* Read an entire file, attempting to pick up objects in it. */

/* (does this interp game-module form twice if description previously
   loaded?) */

void
read_forms(Module *module)
{
    Obj *form;

    Dprintf("Trying to read a new format file \"%s\"...\n", module->name);
    if (module->fp != NULL) {
      while ((form = read_form(module->fp,
                       &(module->startlineno),
                       &(module->endlineno)))
            != lispeof) {
          interp_form(module, form);
      }
    } else {
      while ((form = read_form_from_string(module->sp,
                       &(module->startlineno),
                       &(module->endlineno),
                       &(module->sp)))
            != lispeof) {
          interp_form(module, form);
      }
    }
    /* Clean up after any print forms that might have been done. */
    end_printing_forms();
    Dprintf("... Done reading \"%s\".\n", module->name);
}

void
set_variant_value(int which, int v1, int v2, int v3)
{
    Variant *var;

    var = &(mainmodule->variants[which]);
    var->newvalues[0] = v1;
    var->newvalues[1] = v2;
    var->newvalues[2] = v3;
}

/* Interpret the given list of variants, using the given list to
   fill in values not already supplied in the variant. */

void
do_module_variants(Module *module, Obj *lis)
{
    int i, found, didsome;
    Obj *restset, *varset;
    Variant *var;

    if (module->variants == NULL)
      return; /* error? */
    didsome = FALSE;
    for_all_list(lis, restset) {
      varset = car(restset);
      found = FALSE;
      for (i = 0; module->variants[i].id != lispnil; ++i) {
          var = &(module->variants[i]);
          if (equal(car(varset), var->id)) {
            do_one_variant(module, var, cdr(varset));
            found = TRUE;
            didsome = TRUE;
          }
      }
      if (!found) {
          read_warning("Mystifying variant");
      }
    }
    /* Now implement all the defaults. */
    for (i = 0; module->variants[i].id != lispnil; ++i) {
      var = &(module->variants[i]);
      if (!var->used) {
          do_one_variant(module, var, lispnil);
          didsome = TRUE;
      }
    }
    if (didsome) {
      /* Make all the cross-references right. */
      patch_object_references();
      /* Recheck everything, the variants might have broken something. */
      check_game_validity();
    }
}

/* Implement the effect of the single given variant. */

static void
do_one_variant(Module *module, Variant *var, Obj *varsetdata)
{
    int val, caseval;
    int width = 0, height = 0, circumference /*, latitude, longitude*/;
    int rtime, rtimeperturn, rtimeperside;
    char *vartypename = c_string(var->id);
    Obj *restcases, *headcase, *rest, *filler, *rawcaseval;

    if (Debug) {
      if (readerrbuf == NULL)
        readerrbuf = (char *) xmalloc(BUFSIZE);
      if (varsetdata != lispnil)
        sprintlisp(readerrbuf, varsetdata, BUFSIZE);
      else
        sprintf(readerrbuf, "%d %d %d",
              var->newvalues[0], var->newvalues[1], var->newvalues[2]);
      Dprintf("Module %s variant %s being set to `%s'\n",
            module_desig(module), vartypename, readerrbuf);
    }
    switch (keyword_code(vartypename)) {
      case K_WORLD_SEEN:
      val = var->newvalues[0];
      if (varsetdata != lispnil)
        val = c_number(eval(car(varsetdata)));
      var->hasintvalue = TRUE;
      var->intvalue = val;
      set_g_terrain_seen(val);
      break;
      case K_SEE_ALL:
      val = var->newvalues[0];
      if (varsetdata != lispnil)
        val = c_number(eval(car(varsetdata)));
      var->hasintvalue = TRUE;
      var->intvalue = val;
      set_g_see_all(val);
      break;
      case K_SEQUENTIAL:
      val = var->newvalues[0];
      if (varsetdata != lispnil)
        val = c_number(eval(car(varsetdata)));
      var->hasintvalue = TRUE;
      var->intvalue = val;
      set_g_use_side_priority(val);
      break;
      case K_PEOPLE:
      val = var->newvalues[0];
      if (varsetdata != lispnil)
        val = c_number(eval(car(varsetdata)));
      var->hasintvalue = TRUE;
      var->intvalue = val;
      set_g_people(val);
      break;
      case K_ECONOMY:
      val = var->newvalues[0];
      if (varsetdata != lispnil)
        val = c_number(eval(car(varsetdata)));
      var->hasintvalue = TRUE;
      var->intvalue = val;
      set_g_economy(val);
      break;
      case K_SUPPLY:
      val = var->newvalues[0];
      if (varsetdata != lispnil)
        val = c_number(eval(car(varsetdata)));
      var->hasintvalue = TRUE;
      var->intvalue = val;
      set_g_supply(val);
      break;
      case K_WORLD_SIZE:
            filler = lispnil;
      if (varsetdata != lispnil) {
          filler = varsetdata;
      }
      /* Pick the width and height out of the list. */
      if (filler != lispnil) {
          width = c_number(eval(car(filler)));
          filler = cdr(filler);
      } else {
          width = var->newvalues[0];
      }
      if (filler != lispnil) {
          height = c_number(eval(car(filler)));
          filler = cdr(filler);
      } else {
          height = var->newvalues[1];
      }
      /* Pick up a circumference if given. */
      if (filler != lispnil) {
          circumference = c_number(eval(car(filler)));
          filler = cdr(filler);
      } else {
          circumference = var->newvalues[2];
      }
      /* This is more useful after the circumference has been set. */
      if (width > 0 && height > 0)
        set_area_shape(width, height, TRUE);
      set_world_circumference(circumference, TRUE);
#if 0 /* (redo as separate variant) */
      /* Pick up latitude and longitude if given. */
      if (filler != lispnil) {
          latitude = c_number(eval(car(filler)));
          /* (should use a setter routine?) */
          area.latitude = latitude;
          filler = cdr(filler);
      }
      if (filler != lispnil) {
          longitude = c_number(eval(car(filler)));
          /* (should use a setter routine?) */
          area.longitude = longitude;
          filler = cdr(filler);
      }
#endif
      /* (should record settings somehow) */
      break;
      case K_REAL_TIME:
            filler = lispnil;
      if (varsetdata != lispnil) {
          filler = varsetdata;
      }
      if (filler != lispnil) {
          rtime = c_number(eval(car(filler)));
          filler = cdr(filler);
      } else {
          rtime = var->newvalues[0];
      }
      if (filler != lispnil) {
          rtimeperside = c_number(eval(car(filler)));
          filler = cdr(filler);
      } else {
          rtimeperside = var->newvalues[1];
      }
      if (filler != lispnil) {
          rtimeperturn = c_number(eval(car(filler)));
          filler = cdr(filler);
      } else {
          rtimeperturn = var->newvalues[2];
      }
      /* If the values were specified, tweak the official
         realtime globals. */
      if (rtime > 0)
        set_g_rt_for_game(rtime);
      if (rtimeperside > 0)
        set_g_rt_per_side(rtimeperside);
      if (rtimeperturn > 0)
        set_g_rt_per_turn(rtimeperturn);
      /* (should record settings somehow) */
      break;
      default:
      /* This is the general case. */
      val = var->newvalues[0];
      if (varsetdata != lispnil)
        val = c_number(eval(car(varsetdata)));
      var->hasintvalue = TRUE;
      var->intvalue = val;
      for_all_list(var->cases, restcases) {
          headcase = car(restcases);
          rawcaseval = eval(car(headcase));
          if (numberp(rawcaseval))
            caseval = c_number(rawcaseval);
          if (numberp(rawcaseval) && caseval == val) {
            for_all_list(cdr(headcase), rest) {
                interp_form(module, car(rest));
            }
          }
      }
      /* Clean up after printing, might have been print forms in variant. */
      end_printing_forms();
      break;
    }
    /* Flag the variant as having been specified. */
    var->used = TRUE;
}

/* Given a module and a original, copy over properties of the original
   module. */

void
copy_module(Module *module, Module *origmodule)
{
    char *name;
    Module *next, *incl, *nextincl, *lastincl;

    /* No matter what, preserve the existing name. */
    name = module->name;
    if (origmodule != NULL) {
      /* Save away the module's links to other modules. */
      next = module->next;
      incl = module->include;
      nextincl = module->nextinclude;
      lastincl = module->lastinclude;
      memcpy(module, origmodule, sizeof(Module));
      /* By default, we copy everything through; but keep a separate
         copy of the original name and variants, because the calling
         routine may overwrite with other values. */
      /* (should test each property individually here?) */
      if (empty_string(origmodule->origmodulename)) {
          /* If no original module, use the main module. */
          module->origmodulename = origmodule->name;
          module->origvariants = origmodule->variants;
          module->origversion = origmodule->version;
      }
      /* Kill off caches that should not be replicated. */
      module->contents = NULL;
      module->sp = NULL;
      module->fp = NULL;
      module->open = FALSE;
      module->loaded = FALSE;
      /* Restore links. */
      module->next = next;
      module->include = incl;
      module->nextinclude = nextincl;
      module->lastinclude = lastincl;
    } else {
      /* If there is no original from which to copy, just do some
         generic clearing/init. */
      memset(module, 0, sizeof(Module));
      module->instructions = lispnil;
      module->notes = lispnil;
      module->designnotes = lispnil;
      module->startlineno = 1;
      module->endlineno = 1;
    }
    module->name = name;
}

void
init_module_reshape(Module *module)
{
    /* Seed all the reshaping parameters with reasonable values. */
    module->maybe_reshape = TRUE;
    module->subarea_width = area.width;
    module->subarea_height = area.height;
    module->subarea_x = module->subarea_y = 0;
    module->final_subarea_width = area.width;
    module->final_subarea_height = area.height;
    module->final_subarea_x = module->final_subarea_y = 0;
    module->final_width = area.width;  module->final_height = area.height;
    module->final_circumference = world.circumference;
}

/* This is true if any actual reshaping is required. */

int
reshape_the_output(Module *module)
{
    return (module->maybe_reshape
          && (module->subarea_width != area.width
            || module->subarea_height != area.height
            || module->subarea_x != 0
            || module->subarea_y != 0
            || module->final_subarea_width != area.width
            || module->final_subarea_height != area.height
            || module->final_subarea_x != 0
            || module->final_subarea_y != 0
            || module->final_width != area.width
            || module->final_height != area.height
            || module->final_circumference != world.circumference));
}

/* Check if the proposed reshape will actually work. */

int
valid_reshape(Module *module)
{
    /* (should check hexagon shaping) */
    if (module->subarea_width > area.width
      || module->subarea_height > area.height)
      return FALSE;
    /* (should check other offsets) */
    if (module->final_width < 3 || module->final_height < 3)
      return FALSE;
    return TRUE;
}

/* Close the module. */

void
close_module(Module *module)
{
    if (module->sp) {
      module->sp = NULL;
    }
    if (module->fp) {
      fclose(module->fp);
      module->fp = NULL;
    }
    module->open = FALSE;
}

/* Return a description of the module. */

char *
module_desig(Module *module)
{
    if (moduledesigbuf == NULL)
      moduledesigbuf = xmalloc(BUFSIZE);
    sprintf(moduledesigbuf, "module %s (%s)",
          module->name, (module->title ? module->title : "no title"));
    return moduledesigbuf;
}

/* (random code below, should be sent to better places) */

/* If a special symbol, we might not have to fail.  Note that although
   this looks like Lisp-level code, it knows about unit types and so
   forth, so is higher-level. */

int
lazy_bind(Obj *sym)
{
    int u, m, t, a;
    Obj *value;

    switch (keyword_code(c_string(sym))) {
      case K_USTAR:
      value = lispnil;
      /* Since consing glues onto the front, iterate backwards
         through the types. */
      for (u = numutypes - 1; u >= 0; --u)
        value = cons(new_utype(u), value);
      break;
      case K_MSTAR:
      value = lispnil;
      for (m = nummtypes - 1; m >= 0; --m)
        value = cons(new_mtype(m), value);
      break;
      case K_TSTAR:
      value = lispnil;
      for (t = numttypes - 1; t >= 0; --t)
        value = cons(new_ttype(t), value);
      break;
      case K_ASTAR:
      value = lispnil;
      for (a = numatypes - 1; a >= 0; --a)
        value = cons(new_atype(a), value);
      break;
      default:
      return FALSE;
    }
    setq(sym, value);
    return TRUE;
}

Generated by  Doxygen 1.6.0   Back to index