[comp.sources.games] v04i096: xconq5 - version 5 of the strategy game for X-windows and curses, Part08/18

games@tekred.TEK.COM (07/01/88)

Submitted by: "Stanley T. Shebs" <shebs%defun@cs.utah.edu>
Comp.sources.games: Volume 4, Issue 96
Archive-name: xconq5/Part08



#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 8 (of 18)."
# Contents:  lib/conquist.per map.c xconq.c
# Wrapped by billr@saab on Wed Jun 29 08:55:41 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f lib/conquist.per -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"lib/conquist.per\"
else
echo shar: Extracting \"lib/conquist.per\" \(3557 characters\)
sed "s/^X//" >lib/conquist.per <<'END_OF_lib/conquist.per'
XXconq 0 -+---- Conquistadors
XPeriod 0
X
X"the age of discovery" period-name
X
X"c" "colonist" "peasants eager to make a new life" utype
X"s" "soldier" "soldiers to protect colonists and look for treasure" utype
X"E" "explorer" "should go with on expeditions" utype
X"P" "privateer" "leads expeditions to capture settlements" utype
X"C" "conquistador" "Spanish type" utype
X"M" "missionary" "should convert the natives" utype
X"v" "caravel" "a small ship" utype
X"r" "carrack" "a normal ship" utype
X"G" "galleon fleet" "a collection of large armed ships" utype
X"/" "settlement" "where colonists live" utype
X"*" "town" "more established places" utype
X"!" "treasure city" "obvious" utype
X"$" "gold mine" "a source of gold" utype
X"@" "city" "the capital city back home" utype
X
X"f" "food" "what everybody needs" rtype
X"g" "gold" "what everybody is looking for" rtype
X
X"." "sea" "sky blue" ttype
X"," "shallows" "cyan" ttype
X"+" "plains" "green" ttype
X"%" "forest" "forest green" ttype
X"~" "desert" "yellow" ttype
X"^" "mountains" "sienna" ttype
X"_" "ice" "white" ttype
X
Xsea default-terrain
X
X[   0  70  70  70  70  95  99 ] t* min-alt
X[  70  71  95  95  95  99 100 ] t* max-alt
X[   0   0  20  80   0   0   0 ] t* min-wet
X[ 100 100  80 100  20 100 100 ] t* max-wet
X
Xice edge-terrain
X
X"soldiers" c icon-name
X"infantry" s icon-name
X"man" E icon-name
X"man" P icon-name
X"man" C icon-name
X"man" M icon-name
X"bireme" v icon-name
X"frigate" r icon-name
X"twodecker" G icon-name
X"walltown" * icon-name
X"pyramid" ! icon-name
X;"" $ icon-name
X"city" @ icon-name
X
X[ c s ] "ground" define
X[ E P C M ] "persons" define
X[ v r G ] "ships" define
X[ ! * @ ] "cities" define
X
X[ plains forest desert mountains ] "land" define
X[ sea shallows ] "water" define
X
X10 $ density
X2 ! density
X200 / density
X100 plains cities favored
X100 land [ / ! $ ] favored
X10 country-min-distance
X20 country-max-distance
X5 country-size
X1000 known-radius
X1 @ in-country
X6 * in-country
X@ first-unit
Xs first-product
X0 gold u* stockpile
X10% gold [ * @ ] stockpile
X
X; 5000 * surrender
X
X
X;; land attrition rates are horrendous, ships only slightly better
X
X400 t* c attrition
X250 t* s attrition
X0 water ground attrition
X800 forest ground attrition
X1500 mountains ground attrition
X750 sea [ v r ] attrition
X1500 shallows [ v r ] attrition  ; seamanship wasn't the hottest back then
X
X"succumbs to disease" ground attrition-message
X"is damaged in a storm" [ v r ] attrition-message
X
X100 water [ v r ] accident
X
X"is lost at sea" [ v r ] accident-message
X
X1 u* [ * @ ] make
X; longer times for shipbuilding?
X6 G @ make
X24 * c make
X1 cities maker
X[ 4 4 8 40 ] gold [ s v r G ] to-make
X
X[ 0 1 5 ] gold cities produce
X[ 5 25 500 ] gold ships storage
X[ 500 50 200 ] gold cities storage
X
X; turn time is about one month
X
X[ 1 2 ] ground speed
X3 persons speed
X15 ships speed
X
X0 land ground moves
X-1 ice ground moves
X0 land persons moves
X0 water ships moves
X
X[ 1 1 ] ground caravel capacity
X[ 2 2 ] ground carrack capacity
X10 u* * capacity
X100 u* @ capacity
X[ 20 3 ] ground @ capacity
X[ 1 2 0 ] ships hold-volume
X100 cities hold-volume
X1 ground volume
X
X60 [ v r ] [ v r ] hit
X50 ground ground hit
X
X1 u* u* damage
X
X20 [ v r ] [ v r ] capture
X[ 1 10 1 ] cities s capture
X
X; 3 ships hit-time
X
X100 @ territory
X10 * territory
X
X; gold mines deplete randomly
X; can abandon mine to make neutral ('G')
X; "un-garrison" something to produce troops quickly?
X; hexes with colonists produce resources of food (and other things?)
X
Xbegin{notes}
XThis period is similar to the AH game "Conquistador", but with the
Xinevitable xconq distortions.
Xend{notes}
X
Xend
X
END_OF_lib/conquist.per
if test 3557 -ne `wc -c <lib/conquist.per`; then
    echo shar: \"lib/conquist.per\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f map.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"map.c\"
else
echo shar: Extracting \"map.c\" \(28721 characters\)
sed "s/^X//" >map.c <<'END_OF_map.c'
X/* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
X/* This program may be used, copied, modified, and redistributed freely */
X/* for noncommercial purposes, so long as this notice remains intact. */
X
X/* RCS $Header: map.c,v 1.1 88/06/21 12:30:19 shebs Exp $ */
X
X/* Code relating to the reading and writing of mapfiles.  A mapfile comes */
X/* in several independent sections, each of whose presence is flagged in the */
X/* header line.  A mapfile may also specify the inclusion of other mapfiles. */
X
X#include "config.h"
X#include "misc.h"
X#include "dir.h"
X#include "period.h"
X#include "side.h"
X#include "unit.h"
X#include "map.h"
X#include "global.h"
X
X/* A mapfile header has various pieces that we need to save. */
X
Xtypedef struct a_header {
X    int numincls;               /* number of included files */
X    char *includes[MAXINCLUDES];  /* raw names of included files */
X    int numnotes;               /* number of lines in designer notes */
X    char *notes[MAXMAPNOTES];   /* designer notes for mapfile */
X    char *sections;             /* +/- string of mapfile sections */
X    int sdetail;
X    int udetail;
X} Header;
X
X/* Sections of a mapfile have their recognizers. */
X
X#define vsect(s) (s[0] == '+')
X#define psect(s) (s[1] == '+')
X#define msect(s) (s[2] == '+')
X#define gsect(s) (s[3] == '+')
X#define ssect(s) (s[4] == '+')
X#define usect(s) (s[5] == '+')
X
X/* These vars tell init code what it has to do itself. */
X
Xextern bool periodmade, mapmade, globalsmade, sidesmade, unitsmade;
X
Xextern int nextid;
X
XWorld world;                    /* the world structure itself! */
X
XHeader *mainhead = NULL;        /* the "top-level" map header - needs saving */
X
Xint nummaps;                    /* Number of mapfiles loaded */
X
Xint curgiven;                   /* tmp for side creation */
Xint nux, nuy;                   /* where to put a newly created unit */
Xint nusn;                       /* Number of unit's side */
Xint nut;                        /* Number of the Unit Transport */
X
Xchar *mapfilenames[MAXLOADED];  /* Names of loaded files */
Xchar *perfilename = NULL;
X
X/* Malloc just enough space for the map. */
X
Xallocate_map()
X{
X    world.terrain = (Hex *) malloc(world.width * world.height * sizeof(Hex));
X}
X
X/* Game files can live in library directories or somewhere else.  This */
X/* function tries to find a file, open it, and load the contents. */
X
Xload_mapfile(name)
Xchar *name;
X{
X    bool loaded = FALSE;
X    int i;
X    FILE *fp = NULL;
X
X    make_pathname(XCONQLIB, name, "", spbuf);
X    for (i = 0; i < nummaps; ++i) {
X	if (strcmp(name,  mapfilenames[i]) == 0) { loaded = TRUE; break; }
X	if (strcmp(spbuf, mapfilenames[i]) == 0) { loaded = TRUE; break; }
X    }
X    if (loaded) {
X	fprintf(stderr, "\"%s\" is already loaded.\n", name);
X    } else if ((fp = fopen(spbuf, "r")) != NULL) {
X	if (Debug) printf("Reading \"%s\" ...\n", spbuf);
X	mapfilenames[nummaps++] = copy_string(spbuf);
X    } else if ((fp = fopen(name, "r")) != NULL) {
X	if (Debug) printf("Reading \"%s\" ...\n", name);
X	mapfilenames[nummaps++] = copy_string(name);
X    } else {
X	fprintf(stderr, "Neither \"%s\" or \"%s\" could be opened!\n",
X		spbuf, name);
X	exit(1);
X    }
X    if (fp != NULL) {
X	load_sections(fp);
X	fclose(fp);
X    }
X    if (periodmade) {
X	perfilename = name;
X	periodmade = FALSE;
X    }
X}
X
X/* Grab up all the components of a mapfile, and recurse if any submapfiles. */
X/* The header line tells us which sections to try to load.  For some */
X/* sections, flag them as loaded so we know not to synthesize replacements. */
X
Xload_sections(fp)
XFILE *fp;
X{
X    char sect[BUFSIZE], *tmp;
X    int numfiles, i;
X    Header *head;
X
X    /* Read and interpret the header */
X    head = (Header *) malloc(sizeof(Header));
X    if (mainhead == NULL) mainhead = head;
X    fscanf(fp, "Xconq %d %s", &numfiles, sect);
X    head->numincls = numfiles;
X    head->sections = copy_string(sect);
X    head->sdetail = head->udetail = 0;
X    tmp = read_line(fp);
X    head->notes[0] = tmp;
X    head->numnotes = 1;
X    if (tmp[strlen(tmp)-1] == ';') {
X	for (i = 1; i < MAXMAPNOTES; ++i) {
X	    head->notes[i] = read_line(fp);
X	    if (strcmp(head->notes[i], ".") == 0) {
X		head->numnotes = i;
X		break;
X	    }
X	    if (Debug) printf("%s\n", head->notes[i]);
X	}
X    }
X    for (i = 0; i < numfiles; ++i) {
X	head->includes[i] = read_line(fp);
X	if (Debug) printf("Including %s ...\n", head->includes[i]);
X	load_mapfile(head->includes[i]);
X    }
X    /* Suck in all the sections */
X    if (vsect(sect)) {
X	read_version(fp);
X    }
X    if (psect(sect)) {
X	read_period(fp);
X	periodmade = TRUE;
X    }
X    if (msect(sect)) {
X	read_map(fp);
X	mapmade = TRUE;
X    }
X    if (gsect(sect)) {
X	read_globals(fp);
X	globalsmade = TRUE;
X    }
X    if (ssect(sect)) {
X	head->sdetail = read_sides(fp);
X	sidesmade = TRUE;
X    }
X    if (usect(sect)) {
X	head->udetail = read_units(fp);
X	unitsmade = TRUE;
X    }
X}
X
X/* Read the version line and test it against the program version. */
X/* Continuation after failure is likely to result in core dump, so leave. */
X
Xread_version(fp)
XFILE *fp;
X{
X    char *tmpver;
X
X    if (Debug) printf("Will try to read version ...\n");
X    tmpver = read_line(fp);
X    if (strcmp(tmpver, version) != 0) {
X	fprintf(stderr, "Sorry, mapfile is for some other version!\n");
X	fprintf(stderr, "Version should be \"%s\", not \"%s\".\n",
X		tmpver, version);
X	exit(1);
X    }
X    if (Debug) printf("... Done reading version.\n");
X}
X
X/* Read the map section, starting with header, then working through data. */
X/* The map data may be partly run-length encoded; this is recognized by the */
X/* presence of digit strings (of arbitrary length) followed by the run char. */
X/* Any or all of the data may be so encoded, otherwise the chars are 1-1 */
X/* with map hexes; newlines are always at the end of each map row. */
X
Xread_map(fp)
XFILE *fp;
X{
X    char ch;
X    int extension, x, y, terr, run;
X
X    fscanf(fp, "Map %d %d %d %d %d\n",
X	   &world.width, &world.height,
X	   &world.scale, &world.known, &extension);
X    if (Debug) printf("Will try to read %dx%d map ...\n",
X		      world.width, world.height);
X    allocate_map();
X    for (y = world.height-1; y >= 0; --y) {
X	for (x = 0; x < world.width; /* incr below */) {
X	    ch = getc(fp);
X	    if (isdigit(ch)) {
X		run = ch - '0';
X		while (isdigit(ch = getc(fp))) {
X		    run = run * 10 + ch - '0';
X		}
X	    } else {
X		run = 1;
X	    }
X	    terr = find_terr(ch);
X	    if (terr < 0) terr = period.defaultterrain;
X	    if (terr < 0) {
X		fprintf(stderr, "'%c' is not valid terrain in this period!\n",
X			ch);
X		exit(1);
X	    }
X	    while (run-- > 0) {
X		set_terrain_at(x, y, terr);
X		set_people_at(x, y, NOBODY);
X		set_unit_at(x, y, NULL);
X		++x;
X	    }
X	}
X	fscanf(fp, "\n");
X    }
X    if (Debug) printf("... Done reading map.\n");
X}
X
X/* This should be more efficient.  Fortunately, run-length encoded maps */
X/* don't call it for every single hex! */
X
Xfind_terr(ch)
Xchar ch;
X{
X    register int t;
X
X    for_all_terrain_types(t) if (ch == ttypes[t].tchar) return t;
X    return (-1);
X}
X
X/* Period reading is complicated, and therefore lives in its own file. */
X
X/* Read the globals section.  Most info of interest is in the header, but */
X/* win/lose conditions need extra reads. */
X
Xread_globals(fp)
XFILE *fp;
X{
X    int extension, i, u, r;
X    Condition *cond;
X
X    fscanf(fp, "Globals %d %d %d %d %d %d\n",
X	   &global.time, &global.endtime, &global.setproduct,
X	   &global.leavemap, &global.numconds, &extension);
X    if (Debug) printf("Will try to read globals ...\n");
X    for (i = 0; i < global.numconds; ++i) {
X	cond = &(global.winlose[i]);
X	fscanf(fp, "%d %d %d %d %d\n",
X	       &(cond->win), &(cond->type),
X	       &(cond->starttime), &(cond->endtime), &(cond->sidesn));
X	switch (cond->type) {
X	case TERRITORY:
X	    fscanf(fp, "%d\n", &(cond->n));
X	    break;
X	case UNITCOUNT:
X	    for_all_unit_types(u) {
X		fscanf(fp, "%d", &(cond->units[u]));
X	    }
X	    fscanf(fp, "\n");
X	    break;
X	case RESOURCECOUNT:
X	    for_all_resource_types(r) {
X		fscanf(fp, "%d", &(cond->resources[r]));
X	    }
X	    fscanf(fp, "\n");
X	    break;
X	case POSSESSION:
X	    fscanf(fp, "%d %d %d\n", &(cond->x), &(cond->y), &(cond->n));
X	    break;
X	default:
X	    case_panic("condition type", cond->type);
X	    break;
X	}
X    }
X    if (Debug) printf("... Done reading globals.\n");
X}
X
X/* Read in the entire sides section.  Sides have several levels of detail, */
X/* since sides' views of the world can be pretty large and we don't always */
X/* need to remember them. */
X
Xint sidedetail;
X
Xread_sides(fp)
XFILE *fp;
X{
X    int numtoread, detail, extension, i;
X    Side *side;
X
X    curgiven = 0;
X    fscanf(fp, "Sides %d %d %d\n", &numtoread, &detail, &extension);
X    if (detail < 0 || detail > SIDEALL) case_panic("detail", detail);
X    sidedetail = detail;
X    if (Debug) printf("Will try to read %d sides at detail %d ...\n",
X		      numtoread, detail);
X    for (i = 0; i < numtoread; ++i) {
X	if (detail >= SIDEBASIC) side = read_basic_side(fp);
X	if (side != NULL) {
X	    if (detail >= SIDESLOTS) read_side_attributes(side, numtoread, fp);
X	    if (detail >= SIDEVIEW)  read_side_view(side, fp);
X	    if (detail >= SIDEMISC) {
X		read_side_misc(side, fp);
X		read_side_statistics(side, fp);
X	    }
X	    if (Debug) printf("Got side named \"%s\",\n", side->name);
X	}
X    }
X    if (Debug) printf("... Done reading sides.\n");
X    return detail;
X}
X
X/* Create a side, which only requires a name - person-ness and display come */
X/* from the command line if any were supplied. */
X
X/* Somebody should react coherently to side creation failure... */
X
XSide *
Xread_basic_side(fp)
XFILE *fp;
X{
X    bool human = FALSE;
X    char *host = NULL;
X    int j;
X
X    fscanf(fp, "%s\n", tmpbuf);
X    for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == '*') tmpbuf[j] = ' ';
X    if (curgiven < numgivens) {
X	human = humans[curgiven];
X	host = hosts[curgiven];
X	++curgiven;
X    }
X    return create_side(tmpbuf, human, host);
X}
X
X/* Read the most important attributes of a side. */
X
Xread_side_attributes(side, numtoread, fp)
XSide *side;
Xint numtoread;
XFILE *fp;
X{
X    int s, u;
X
X    fscanf(fp, "%hd", &(side->ideology));
X    for (s = 0; s < numtoread; ++s) fscanf(fp, "%hd", &(side->attitude[s]));
X    for_all_unit_types(u) fscanf(fp, "%hd", &(side->counts[u]));
X    fscanf(fp, "\n");
X}
X
X/* Read the goriest of details about a side - those things that are */
X/* relevant only to a particular game. */
X
Xread_side_misc(side, fp)
XSide *side;
XFILE *fp;
X{
X    bool human;
X    char host[BUFSIZE];
X
X    fscanf(fp, "%d %s %hd ",
X	   &human, host, &(side->lost));
X    fscanf(fp, "\n");
X}
X
X/* Read the performance statistics of a side. */
X
Xread_side_statistics(side, fp)
XSide *side;
XFILE *fp;
X{
X    int u, u2, i;
X
X    for_all_unit_types(u) {
X	for (i = 0; i < NUMREASONS; ++i) {
X	    fscanf(fp, "%hd", &(side->balance[u][i]));
X	}
X	fscanf(fp, "\n");
X	for_all_unit_types(u2) {
X	    fscanf(fp, "%hd", &(side->atkstats[u][u2]));
X	}
X	fscanf(fp, "\n");
X	for_all_unit_types(u2) {
X	    fscanf(fp, "%hd", &(side->hitstats[u][u2]));
X	}
X	fscanf(fp, "\n");
X    }
X}
X
X/* Read about what has been seen in the world. */
X
Xread_side_view(side, fp)
XSide *side;
XFILE *fp;
X{
X    char ch1, ch2;
X    int x, y, view;
X
X    for (y = 0; y < world.height; ++y) {
X	for (x = 0; x < world.width; ++x) {
X	    ch1 = getc(fp);  ch2 = getc(fp);
X	    if (ch1 == '?' && ch2 == '?')
X		view = UNSEEN;
X	    else if (ch1 == '.' && ch2 == '.')
X		view = EMPTY;
X	    else
X		view = buildview(ch2 - '0', find_unit_char(ch1));
X	    set_side_view(side, x, y, view);
X	}
X	fscanf(fp, "\n");
X    }
X}
X
X/* Read the entire units section.  Units also have different levels of */
X/* detail. */
X
Xread_units(fp)
XFILE *fp;
X{
X    int numtoread, detail, extension, i;
X    Unit *unit, *transport;
X
X    fscanf(fp, "Units %d %d %d\n", &numtoread, &detail, &extension);
X    if (detail < 0 || detail > UNITALL) case_panic("detail", detail);
X    if (Debug) printf("Will try to restore %d units at detail %d ...\n",
X		      numtoread, detail);
X    for (i = 0; i < numtoread; ++i) {
X	nut = -1;
X	if (detail >= UNITBASIC) {
X	    if ((unit = read_basic_unit(fp)) != NULL) {
X		if (detail >= UNITSLOTS) read_unit_attributes(unit, fp);
X		if (detail >= UNITORDERS) read_unit_orders(unit, fp);
X		if (nusn >= 0) {
X		    unit_changes_side(unit, side_n(nusn), -1, -1);
X		}
X		if (nut >= 0) {
X		    transport = find_unit(nut);
X		    if (transport != NULL) {
X			occupy_unit(unit, transport);
X		    } else {
X			occupy_hex(unit, nux, nuy);
X		    }
X		} else {
X		    occupy_hex(unit, nux, nuy);
X		}
X		if (Debug) printf("Got %s,\n", unit_handle(NULL, unit));
X	    }
X	}
X    }
X    if (Debug) printf("... Done reading units.\n");
X    return detail;
X}
X
X/* Read the barest info about a neutral unit - just type, name, and loc. */
X/* Do weird stuff to handle empty names represented by "*". */
X/* Give it full supplies (good idea?).  Can't place just yet, because it */
X/* might actually be an occupant of something else. */
X
XUnit *
Xread_basic_unit(fp)
XFILE *fp;
X{
X    char ch;
X    int j, u;
X    Unit *newunit;
X
X    fscanf(fp, "%c %s %d,%d %d\n", &ch, tmpbuf, &nux, &nuy, &nusn);
X    for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == '*') tmpbuf[j] = ' ';
X    if (strcmp(tmpbuf, " ") == 0) strcpy(tmpbuf, "");
X    if ((u = find_unit_char(ch)) != NOTHING) {
X	if ((newunit = create_unit(u, tmpbuf)) != NULL) {
X	    init_supply(newunit);
X	}
X    } else {
X	fprintf(stderr, "Unit '%c' is not of this period!\n", ch);
X	exit(1);
X    }
X    return newunit;
X}
X
X/* Read most of a unit's attributes, but not orders and suchlike. */
X
Xread_unit_attributes(unit, fp)
XUnit *unit;
XFILE *fp;
X{
X    int truesidenum, r;
X
X    fscanf(fp, "%hd %hd %d %hd %hd %hd %hd %hd %hd %hd %d",
X	   &(unit->id), &(unit->number), &truesidenum,
X	   &(unit->hp), &(unit->quality), &(unit->morale), &(unit->fatigue),
X	   &(unit->product), &(unit->schedule), &(unit->built), &nut);
X    for_all_resource_types(r) {
X	fscanf(fp, "%hd", &(unit->supply[r]));
X    }
X    fscanf(fp, "\n");
X    unit->trueside = side_n(truesidenum);
X    /* tricky way to ensure subsequent units will have unique ids */
X    nextid = max(nextid, unit->id + 1);
X}
X
X/* Read everything back in and try to recreate the unit's orders exactly. */
X/* The saved orders include a flag for the presence of standing orders. */
X
Xread_unit_orders(unit, fp)
XUnit *unit;
XFILE *fp;
X{
X    int i, more, uord[MAXUTYPES];
X
X    fscanf(fp, "%hd %hd", &(unit->lastdir), &(unit->awake));
X    read_one_order(fp, &(unit->orders));
X    fscanf(fp, "%d\n", &more);
X    if (more) {
X	unit->standing = (StandingOrder *) malloc(sizeof(StandingOrder));
X	for_all_unit_types(i) {
X	    fscanf(fp, "%d", &(uord[i]));
X	}
X	fscanf(fp, "\n");
X	for_all_unit_types(i) {
X	    if (uord[i]) {
X		unit->standing->orders[i] = (Order *) malloc(sizeof(Order));
X		read_one_order(fp, unit->standing->orders[i]);
X		fscanf(fp, "\n");
X	    }
X	}
X    }
X}
X
X/* The exact way to read an order depends on what type it is. */
X
Xread_one_order(fp, order)
XFILE *fp;
XOrder *order;
X{
X    int leadernum;
X
X    fscanf(fp, "%hd %hd %hd", &(order->type), &(order->rept), &(order->flags));
X    switch (orderargs[order->type]) {
X    case NOARG:
X	break;
X    case DIR:
X	fscanf(fp, "%hd", &(order->p.dir));
X	break;
X    case POS:
X	fscanf(fp, "%hd,%hd", &(order->p.pt[0].x), &(order->p.pt[0].y));
X	break;
X    case LEADER:
X	fscanf(fp, "%d", &leadernum);
X	/* should finish this */
X	break;
X    case WAYPOINTS:
X	fscanf(fp, "%hd,%hd %hd,%hd",
X	       &(order->p.pt[0].x), &(order->p.pt[0].y),
X	       &(order->p.pt[1].x), &(order->p.pt[1].y));
X	break;
X    default:
X	case_panic("order arg type", orderargs[order->type]);
X	break;
X    }
X}
X
X/* Output is quite similar to input of mapfiles, but of course everything */
X/* is reversed.  */
X
X/* A savefile has a particular format; it includes all sections except */
X/* period, which may or may not be referenced as an included file. */
X/* It is important that this routine not attempt to use graphics, since it */
X/* may be called when graphics code fails. */
X
Xwrite_savefile(fname)
Xchar *fname;
X{
X    Header *head;
X
X    head = (Header *) malloc(sizeof(Header));
X    head->numincls = (perfilename ? 1 : 0);
X    head->includes[0] = perfilename;
X    head->sections = "+-++++";
X    head->numnotes = 0;
X    head->sdetail = SIDEALL;
X    head->udetail = UNITALL;
X    return write_mapfile(fname, head);
X}
X
X/* A scenario is considerably more variable than a savefile, but the */
X/* principle is the same. */
X
Xwrite_scenario(fname, sections, sdetail, udetail)
Xchar *fname, *sections;
Xint sdetail, udetail;
X{
X    int i;
X    Header *head;
X
X    head = (Header *) malloc(sizeof(Header));
X    head->numincls = head->numnotes = 0;
X    if (mainhead) {
X	head->numnotes = mainhead->numnotes;
X	for (i = 0; i < head->numnotes; ++i) {
X	    head->notes[i] = mainhead->notes[i];
X	}
X	head->numincls = mainhead->numincls;
X	for (i = 0; i < head->numincls; ++i) {
X	    head->includes[i] = mainhead->includes[i];
X	}
X    }
X    head->sections = copy_string(sections);
X    head->sdetail = sdetail;
X    head->udetail = udetail;
X    return write_mapfile(fname, head);
X}
X
X/* Given a file name and a header telling what to put in, do the putting. */
X/* Returns true if file opened successfully. */
X
Xwrite_mapfile(fname, head)
Xchar *fname;
XHeader *head;
X{
X    int i;
X    FILE *fp;
X
X    if ((fp = fopen(fname, "w")) != NULL) {
X	fprintf(fp, "Xconq %d %s %s%s\n",
X		head->numincls, head->sections,
X		(head->numnotes > 0 ? head->notes[0] : ""),
X		(head->numnotes > 1 ? ";" : ""));
X	if (head->numnotes > 1) {
X	    for (i = 1; i < head->numnotes; ++i) {
X		fprintf(fp, "%s\n", head->notes[i]);
X	    }
X	    fprintf(fp, ".\n");
X	}
X	for (i = 0; i < head->numincls; ++i) {
X	    fprintf(fp, "%s\n", head->includes[i]);
X	}
X	if (vsect(head->sections)) write_version(fp);
X	/* no period writing */
X	if (msect(head->sections)) write_map(fp);
X	if (gsect(head->sections)) write_globals(fp);
X	if (ssect(head->sections)) write_sides(fp, head->sdetail);
X	if (usect(head->sections)) write_units(fp, head->udetail);
X	fclose(fp);
X	return TRUE;
X    } else {
X	return FALSE;
X    }
X}
X
X/* Writing out the version is pretty easy. */
X
Xwrite_version(fp)
XFILE *fp;
X{
X    fprintf(fp, "%s\n", version);
X}
X
X/* Write the map section.  Try to find runs of the same type and make a more */
X/* compact output. */
X
Xwrite_map(fp)
XFILE *fp;
X{
X    int extension = 0, x, y, run, runterr, terr, i;
X
X    fprintf(fp, "Map %d %d %d %d %d\n",
X	    world.width, world.height, world.scale, world.known, extension);
X    for (y = world.height-1; y >= 0; --y) {
X	run = 0;
X	runterr = terrain_at(0, y);
X	for (x = 0; x < world.width; ++x) {
X	    terr = terrain_at(x, y);
X	    if (terr == runterr) {
X		run++;
X	    } else {
X		if (run >= 3) {
X		    fprintf(fp, "%d%c", run, ttypes[runterr].tchar);
X		} else {
X		    for (i = 0; i < run; ++i) putc(ttypes[runterr].tchar, fp);
X		}
X		runterr = terr;
X		run = 1;
X	    }
X	}
X	fprintf(fp, "%d%c\n", run, ttypes[terr].tchar);
X    }
X}
X
X/* Write the globals section, which mostly consists of win/lose conditions. */
X
Xwrite_globals(fp)
XFILE *fp;
X{
X    int extension = 0, i, u, r;
X    Condition *cond;
X
X    fprintf(fp, "Globals %d %d %d %d %d %d\n",
X	    global.time, global.endtime, global.setproduct,
X	    global.leavemap, global.numconds, extension);
X    for (i = 0; i < global.numconds; ++i) {
X	cond = &(global.winlose[i]);
X	fprintf(fp, "%d %d %d %d %d\n",
X		cond->win, cond->type,
X		cond->starttime, cond->endtime, cond->sidesn);
X	switch (cond->type) {
X	case TERRITORY:
X	    fprintf(fp, "%d\n", cond->n);
X	    break;
X	case UNITCOUNT:
X	    for_all_unit_types(u) {
X		fprintf(fp, "%d ", cond->units[u]);
X	    }
X	    fprintf(fp, "\n");
X	    break;
X	case RESOURCECOUNT:
X	    for_all_resource_types(r) {
X		fprintf(fp, "%d ", cond->resources[r]);
X	    }
X	    fprintf(fp, "\n");
X	    break;
X	case POSSESSION:
X	    fprintf(fp, "%d %d %d\n", cond->x, cond->y, cond->n);
X	    break;
X	default:
X	    case_panic("condition type", cond->type);
X	    break;
X	}
X    }
X}
X
X/* Write the sides section, at the given level of detail. */
X
Xwrite_sides(fp, detail)
XFILE *fp;
Xint detail;
X{
X    int extension = 0;
X    Side *side;
X
X    fprintf(fp, "Sides %d %d %d\n", numsides, detail, extension);
X    if (detail < 0 || detail > SIDEALL) case_panic("detail", detail);
X    if (Debug) printf("Will try to write %d sides at detail %d ...\n",
X		      numsides, detail);
X    for_all_sides(side) {
X	if (detail >= SIDEBASIC) write_basic_side(side, fp);
X	if (detail >= SIDESLOTS) write_side_attributes(side, fp);
X	if (detail >= SIDEVIEW)  write_side_view(side, fp);
X	if (detail >= SIDEMISC) {
X	    write_side_misc(side, fp);
X	    write_side_statistics(side, fp);
X	}
X	if (Debug) printf("Wrote side \"%s\",\n", side->name);
X    }
X    if (Debug) printf("... Done writing sides.\n");
X}
X
X/* A side can be saved with or without the entire view, which is a fairly */
X/* large sort of thing.  Machine sides without displays have their names */
X/* written as "*" - let us hope and pray that such a perverted name never */
X/* appears as a valid X host/display name. */
X
Xwrite_basic_side(side, fp)
XSide *side;
XFILE *fp;
X{
X    int j;
X
X    strcpy(tmpbuf, side->name);
X    for (j = 0; j < strlen(tmpbuf); ++j) if (tmpbuf[j] == ' ') tmpbuf[j] = '*';
X    fprintf(fp, "%s\n", tmpbuf);
X}
X
X/* Write the random but important attributes of a side. */
X
Xwrite_side_attributes(side, fp)
XSide *side;
XFILE *fp;
X{
X    int u, s;
X
X    fprintf(fp, "%d  ", side->ideology);
X    for (s = 0; s < numsides; ++s) fprintf(fp, "%d ", side->attitude[s]);
X    fprintf(fp, " ");
X    for_all_unit_types(u) fprintf(fp, "%d ", side->counts[u]);
X    fprintf(fp, "\n");
X}
X
X/* Write about what has been seen in the world. (should be more compact) */
X
Xwrite_side_view(side, fp)
XSide *side;
XFILE *fp;
X{
X    char ch1, ch2;
X    int x, y, view;
X
X    for (y = 0; y < world.height; ++y) {
X	for (x = 0; x < world.width; ++x) {
X	    view = side_view(side, x, y);
X	    if (view == UNSEEN)
X		ch1 = ch2 = '?';
X	    else if (view == EMPTY)
X		ch1 = ch2 = '.';
X	    else {
X		ch1 = utypes[vtype(view)].uchar;
X		ch2 = vside(view) + '0';
X	    }
X	    putc(ch1, fp);  putc(ch2, fp);
X	}
X	fprintf(fp, "\n");
X    }
X}
X
X/* More volatile things, generally only of interest for saved games. */
X
Xwrite_side_misc(side, fp)
XSide *side;
XFILE *fp;
X{
X    fprintf(fp, "%d %s %d \n",
X	    side->humanp, (side->host ? side->host : "*"),
X	    side->lost, side->timeleft);
X}
X
X/* Write the performance statistics of a side. (the unit record may be */
X/* crucial to deciding win/lose conditions.) */
X
Xwrite_side_statistics(side, fp)
XSide *side;
XFILE *fp;
X{
X    int u, u2, i;
X
X    for_all_unit_types(u) {
X	for (i = 0; i < NUMREASONS; ++i) {
X	    fprintf(fp, "%d ", side->balance[u][i]);
X	}
X	fprintf(fp, "\n");
X	for_all_unit_types(u2) {
X	    fprintf(fp, "%d ", side->atkstats[u][u2]);
X	}
X	fprintf(fp, "\n");
X	for_all_unit_types(u2) {
X	    fprintf(fp, "%d ", side->hitstats[u][u2]);
X	}
X	fprintf(fp, "\n");
X    }
X}
X
X/* Write the unit section of a mapfile.  Each level of detail has its own */
X/* line, while standing orders will take one line per unit type. */
X/* Get rid of dead units and sort everything, so as to make sure that */
X/* transports are always written before occupants. */
X
Xwrite_units(fp, detail)
XFILE *fp;
Xint detail;
X{
X    int extension = 0;
X    Unit *unit;
X
X    flush_dead_units();
X    sort_units();
X    if (detail < 0 || detail > UNITALL) case_panic("detail", detail);
X    fprintf(fp, "Units %d %d %d\n", numunits, detail, extension);
X    if (Debug) printf("Writing %d units at detail %d ...\n", numunits, detail);
X    if (detail >= UNITBASIC) {
X	for_all_units(unit) {
X	    if (detail >= UNITBASIC) write_basic_unit(unit, fp);
X	    if (detail >= UNITSLOTS) write_unit_attributes(unit, fp);
X	    if (detail >= UNITORDERS) write_unit_orders(unit, fp);
X	    if (Debug) printf("Wrote %s,\n", unit_handle(NULL, unit));
X	}
X    }
X    if (Debug) printf("... Done writing units.\n");
X}
X
X/* Write only the minimal info about a unit - type, name, and position. */
X/* To make scanf happy, spaces in the name are replaced with stars. */
X/* Fortunately, names never seem to have stars in them. */
X/* Unnamed units get a star all by itself in the name position. */
X
Xwrite_basic_unit(unit, fp)
XUnit *unit;
XFILE *fp;
X{
X    int j;
X
X    if (unit->name == NULL || strlen(unit->name) < 1) {
X	sprintf(tmpbuf, "*");
X    } else {
X	strcpy(tmpbuf, unit->name);
X	for (j = 0; j < strlen(tmpbuf); ++j)
X	    if (tmpbuf[j] == ' ') tmpbuf[j] = '*';
X    }
X    fprintf(fp, "%c %s %d,%d %d\n",
X	    utypes[unit->type].uchar, tmpbuf, unit->x, unit->y,
X	    side_number(unit->side));
X}
X
X/* Write the most interesting attributes of a unit.  Just a long list of */
X/* numbers, no special tricks needed. */
X
Xwrite_unit_attributes(unit, fp)
XUnit *unit;
XFILE *fp;
X{
X    int i;
X
X    fprintf(fp, "%d %d %d  %d  %d %d %d  %d %d %d  %d  ",
X	    unit->id, unit->number, side_number(unit->trueside), 
X	    unit->hp, unit->quality, unit->morale, unit->fatigue,
X	    unit->product, unit->schedule, unit->built,
X	    (unit->transport ? unit->transport->id : -1));
X    for_all_resource_types(i) {
X	fprintf(fp, "%d ", unit->supply[i]);
X    }
X    fprintf(fp, "\n");
X}
X
X/* Write the unit's orders and standing orders out. */
X/* This is usually for game saving, although I suppose it has other uses. */
X
Xwrite_unit_orders(unit, fp)
XUnit *unit;
XFILE *fp;
X{
X    int i;
X
X    fprintf(fp, "%d  %d ", unit->lastdir, unit->awake);
X    write_one_order(fp, &(unit->orders));
X    fprintf(fp, " %d\n", (unit->standing != NULL));
X    if (unit->standing != NULL) {
X	for_all_unit_types(i) {
X	    fprintf(fp, "%d ", (unit->standing->orders[i] != NULL));
X	}
X	fprintf(fp, "\n");
X	for_all_unit_types(i) {
X	    if (unit->standing->orders[i] != NULL) {
X		write_one_order(fp, unit->standing->orders[i]);
X		fprintf(fp, "\n");
X	    }
X	}
X    }
X}
X
X/* Write a single order object, which may be for a unit or a standing order. */
X
Xwrite_one_order(fp, order)
XFILE *fp;
XOrder *order;
X{
X    fprintf(fp, " %d %d %d ", order->type, order->rept, order->flags);
X    switch (orderargs[order->type]) {
X    case NOARG:
X	break;
X    case DIR:
X	fprintf(fp, "%d", order->p.dir);
X	break;
X    case POS:
X	fprintf(fp, "%d,%d", order->p.pt[0].x, order->p.pt[0].y);
X	break;
X    case LEADER:
X	fprintf(fp, "%d", order->p.leader->id);
X	break;
X    case WAYPOINTS:
X	fprintf(fp, "%d,%d %d,%d",
X		order->p.pt[0].x, order->p.pt[0].y,
X		order->p.pt[1].x, order->p.pt[1].y);
X	break;
X    default:
X	case_panic("order arg type", orderargs[order->type]);
X	break;
X    }
X}
X
X/* Display the mapfile header info. */
X
Xdescribe_mapfiles(side)
XSide *side;
X{
X    int i;
X
X    wprintf(side, "The world is %d hexes around by %d hexes high.",
X	    world.width, world.height);
X    wprintf(side, "");
X    if (mainhead != NULL && mainhead->numnotes > 0) {
X	for (i = 0; i < mainhead->numnotes; ++i) {
X	    wprintf(side, "%s", mainhead->notes[i]);
X	}
X    } else {
X	wprintf(side, "(No other documentation is available.)");
X    }
X}
X
X/* Generalized area search routine.  It starts in the immediately adjacent */
X/* hexes and expands outwards.  The basic structure is to examine successive */
X/* "rings" out to the max distance; within each ring, we must scan each of */
X/* six faces (picking a random one to start with) by iterating along that */
X/* face, in a direction 120 degrees from the direction out to one corner of */
X/* the face.  Draw a picture if you want to understand it... */
X
X/* Dumb use of "limit" means hexes at edge of map may be searched repeatedly */
X/* A good place to optimize things... */
X
Xsearch_area(x0, y0, maxdist, pred, rxp, ryp)
Xint x0, y0, maxdist, (*pred)(), *rxp, *ryp;
X{
X    int clockwise, dist, dd, d, dir, x1, y1, i, dir2, x, y;
X
X    maxdist = max(min(maxdist, world.width), min(maxdist, world.height));
X    clockwise = (flip_coin() ? 1 : -1);
X    for (dist = 1; dist <= maxdist; ++dist) {
X	dd = random_dir();
X	for_all_directions(d) {
X	    dir = (d + dd) % NUMDIRS;
X	    x1 =  wrap(x0 + dist * dirx[dir]);
X	    y1 = limit(y0 + dist * diry[dir]);
X	    for (i = 0; i < dist; ++i) {
X		dir2 = opposite_dir(dir + clockwise);
X		x =  wrap(x1 + i * dirx[dir2]);
X		y = limit(y1 + i * diry[dir2]);
X		if ((*pred)(x, y)) {
X		    *rxp = x;  *ryp = y;
X		    return TRUE;
X		}
X	    }
X	}
X    }
X    return FALSE;
X}
X
X/* Apply a function to every hex within the given radius, being careful (for */
X/* both safety and efficiency reasons) not to go past edges.  Note that the */
X/* distance is inclusive, and that distance of 0 means x0,y0 only. */
X/* This routine should be avoided in time-critical code. */
X
Xapply_to_area(x0, y0, dist, fn)
Xint x0, y0, dist, (*fn)();
X{
X    int x, y, y1 = y0 - dist, y2 = y0 + dist, x1, x2;
X
X    for (y = y1; y <= y2; ++y) {
X	if (between(0, y, world.height-1)) {
X	    x1 = x0 - (y < y0 ? (y - y1) : dist);
X	    x2 = x0 + (y > y0 ? (y2 - y) : dist);
X	    for (x = x1; x <= x2; ++x) {
X		((*fn)(wrap(x), y));
X	    }
X	}
X    }
X}
END_OF_map.c
if test 28721 -ne `wc -c <map.c`; then
    echo shar: \"map.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f xconq.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"xconq.c\"
else
echo shar: Extracting \"xconq.c\" \(18966 characters\)
sed "s/^X//" >xconq.c <<'END_OF_xconq.c'
X/* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
X/* This program may be used, copied, modified, and redistributed freely */
X/* for noncommercial purposes, so long as this notice remains intact. */
X
X/* RCS $Header: xconq.c,v 1.2 88/06/28 10:38:57 shebs Exp $ */
X
X/* Unlike most multi-player network programs, xconq does not use multiple */
X/* processes.  Instead, it relies on the network capability inherent in  */
X/* some graphical interfaces (such as X) to open up multiple displays. */
X
X#include "config.h"
X#include "misc.h"
X#include "period.h"
X#include "side.h"
X#include "unit.h"
X#include "map.h"
X#include "global.h"
X#include "version.h"
X
X/* This is for extremely simple menus to be used with novice players. */
X
Xtypedef struct menu_entry {
X    char *name;
X    char *description;
X} MenuEntry;
X
Xextern int nummaps;     /* needed to initialize number of maps read */
X
X/* Definitions of globally global variables. */
X
XGlobal global;          /* important (i.e. saved/restored) globals */
X
XMenuEntry mapmenu[MAXMAPMENU];  /* list of mapfiles to suggest to players */
X
Xbool givenseen = FALSE; /* true if world known */
Xbool Debug = FALSE;     /* the debugging flag itself. */
Xbool Build = FALSE;     /* magic flag for scenario-builders */
Xbool Freeze = FALSE;    /* keeps machine players from moving */
Xbool humans[MAXSIDES];  /* flags for which players are human or machine */
X
Xchar *programname;      /* name of the main program */
Xchar *rawfilenames[MAXLOADED];  /* names specified on cmd line */
Xchar version[] = VERSION;  /* version string */
Xchar *hosts[MAXSIDES];  /* names of displays for each side */
Xchar spbuf[BUFSIZE];    /* main printing buffer */
Xchar tmpbuf[BUFSIZE];   /* a temp buffer */
X
Xint numfiles = 0;       /* number of data files asked for */
Xint numgivens = 0;      /* number of sides given on cmd line */
Xint numhumans = 0;      /* number of bona-fide human players */
Xint givenwidth = DEFAULTWIDTH;    /* requested width for a random map */
Xint givenheight = DEFAULTHEIGHT;  /* requested height for a random map */
Xint giventime = 0;      /* requested time for individual turns */
X
X/* Indices of groups of mapfile types mentioned in collection of mapfiles. */
X
Xint scnstart, scnend, perstart, perend, mapstart, mapend;
X
XSide *winner = NULL;    /* the winning side (allies are also winners) */
X
X/* Where it all begins... the main program's primary duty is command line */
X/* interpretation, it hands off for everything else. */
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    bool eopt = FALSE, ask = FALSE;
X    char ch;
X    int i, numenemies;
X
X    programname = argv[0];
X    nummaps = 0;
X    for (i = 0; i < MAXLOADED; ++i) rawfilenames[i] = "";
X    add_default_player();
X    for (i = 1; i < argc; ++i) {
X	if ((argv[i])[0] == '-') {
X	    ch = (argv[i])[1];
X	    if (Debug) printf("-%c\n", ch);
X	    switch (ch) {
X	    case 'A':
X		if (i+1 >= argc) usage_error();
X		add_player(FALSE, argv[++i]);
X		break;
X	    case 'B':
X		Build = TRUE;
X		Freeze = TRUE;
X		break;
X	    case 'D':
X		Debug = TRUE;
X		break;
X	    case 'e':
X		if (i+1 >= argc) usage_error();
X		eopt = TRUE;
X		numenemies = atoi(argv[++i]);
X		while (numenemies-- > 0) add_player(FALSE, NULL);
X		break;
X	    case 'm':
X		if (i+1 >= argc) usage_error();
X		make_pathname(NULL, argv[++i], "map", spbuf);
X		rawfilenames[numfiles++] = copy_string(spbuf);
X		break;
X	    case 'M':	
X		if (i+2 >= argc) usage_error();
X		givenwidth = atoi(argv[++i]);
X		givenheight = atoi(argv[++i]);
X		break;
X	    case 'p':
X		if (i+1 >= argc) usage_error();
X		make_pathname(NULL, argv[++i], "per", spbuf);
X		rawfilenames[numfiles++] = copy_string(spbuf);
X		break;
X	    case 'r':
X		if (numgivens > 1) {
X		    fprintf(stderr, "Warning: -r is resetting the list of\n");
X		    fprintf(stderr, "players already given in the command.\n");
X		}
X		numgivens = numhumans = 0;
X		break;
X	    case 's':
X		if (i+1 >= argc) usage_error();
X		make_pathname(NULL, argv[++i], "scn", spbuf);
X		rawfilenames[numfiles++] = copy_string(spbuf);
X		break;
X	    case 't':
X		if (i+1 >= argc) usage_error();
X		/* Converting to seconds for internal use */
X		giventime = 60 * atoi(argv[++i]);
X		break;
X	    case 'v':
X		givenseen = TRUE;
X		break;
X	    case 'x':
X		ask = TRUE;
X		break;
X	    default:
X		usage_error();
X	    }
X	} else {
X	    /* We just have a host name for a human player */
X	    add_player(TRUE, argv[i]);
X	}
X    }
X    /* Put in a single machine opponent if no -e and no human opponents */
X    if (!eopt && numgivens == 1) add_player(FALSE, NULL);
X    /* Say hi to the user */
X    printf("\n              Welcome to XCONQ version %s\n\n", version);
X    maybe_dump_news();
X    /* If no command-line options supplied, work interactively */
X    if (ask) {
X	if (read_menu_file()) {
X	    ask_about_mapfiles();
X	} else {
X	    printf("No period/map/scenario menus available.\n");
X	}
X	ask_about_players();
X    }
X    /* While away the hours setting everything up */
X    init_random(-1);
X    init_sides();
X    init_units();
X    init_game();
X    init_movement();
X    init_displays();
X    init_mplayers();
X    init_sighandlers();
X    /* Relatively low chance of screwup now, so safe to delete saved game */
X    if (saved_game()) remove_saved_game();
X    /* Play! */
X    play();
X}
X
X/* Complain and leave if command is garbage. */
X
Xusage_error()
X{
X    fprintf(stderr, "Usage: %s [display] [-A display] [-B] [-e n] [-m name]\n",
X	    programname);
X    fprintf(stderr, "  [-M w h] [-p name] [-r] [-s name] [-t n] [-v] [-x]\n");
X    exit(1);
X}
X
X/* Add a player into the array of players, making sure of no overflow. */
X/* Fail if so, players probably made a typo on command line - help them */
X/* out with a list of what players had been included. */
X
Xadd_player(ahuman, host)
Xbool ahuman;
Xchar *host;
X{
X    if (numgivens >= MAXSIDES) {
X	fprintf(stderr, "At most %d player sides allowed!\n", MAXSIDES);
X	fprintf(stderr, "(Valid ones were ");
X	list_players(stderr);
X	fprintf(stderr, ")\n");
X	exit(1);
X    }
X    hosts[numgivens] = host;
X    humans[numgivens] = ahuman;
X    numgivens++;
X    if (ahuman) numhumans++;
X    if (Debug) printf("Added %s player (%s)\n",
X		      (ahuman ? "human" : "machine"), (host ? host : ""));
X}
X
X/* The main loop that cycles through the turns and the phases. */
X/* Always departs through game end phase (or by core dump :-( ). */
X
Xplay()
X{
X    while (TRUE) {
X	init_turn_phase();
X	spy_phase();
X	disaster_phase();
X	build_phase();
X	supply_phase();
X	sort_units();
X	movement_phase();
X	consumption_phase();
X	flush_dead_units();
X	game_end_phase();
X    }
X}
X
X/* Do random small things to get the turn started, including resetting */
X/* some side vars that could get things confused, if set wrong. */
X/* Most movement-related things should get set later, at beginning of */
X/* move phase. */
X
Xinit_turn_phase()
X{
X    Side *side;
X
X    ++global.time;
X    if (Debug) printf("##### TURN %d #####\n", global.time);
X    for_all_sides(side) {
X	wedge_mode(side);
X	show_timemode(side);
X	if (giventime > 0) update_clock(side);
X	flush_input(side);
X	flush_output(side);
X    }
X}
X
X/* The win/lose phase assesses the sides' performance during that turn, */
X/* and can end the game.  In fact, it is the only way to get out, except */
X/* for the quick exit command.  With multiple mixed players, there are a */
X/* number of possibilities at game end, such as an all machine win.  Also, */
X/* it is possible for all players to lose simultaneously! */
X
Xgame_end_phase()
X{
X    bool enemiesleft = FALSE;
X    int sidesleft = 0, testrslt;
X    Side *side, *side2, *survivor;
X
X    if (Debug) printf("Entering game end phase\n");
X    /* See if any sides have won or lost */
X    if (global.time >= global.endtime) {
X	notify_all("The end of the world has arrived!");
X	for_all_sides(side) side_loses(side, NULL);
X    } else if (Build) {
X	/* No winning or losing while building */
X    } else {
X	for_all_sides(side) {
X	    if (!side->lost) {
X		if (side->timedout) {
X		    notify_all("The %s ran out of time!",
X			       plural_form(side->name));
X		    side_loses(side, NULL);
X		} else if (all_units_gone(side)) {
X		    notify_all("The %s have been wiped out!",
X			       plural_form(side->name));
X		    side_loses(side, NULL);
X		} else {
X		    testrslt = condition_true(side);
X		    if (testrslt > 0) side_wins(side);
X		    if (testrslt < 0) side_loses(side, NULL);
X		}
X	    }
X	}
X    }
X    /* See if any opposing sides left */
X    for_all_sides(side) {
X	for_all_sides(side2) {
X	    if (!side->lost && !side2->lost && enemy_side(side, side2))
X		enemiesleft = TRUE;
X	}
X	if (!side->lost) {
X	    survivor = side;
X	    sidesleft++;
X	}
X    }
X    /* Decide if the game is over */
X    if (numsides == 1) {
X	/* A one-player game can't end */
X    } else if (sidesleft == 0) {
X        exit_xconq();
X    } else if (!enemiesleft) {
X	winner = survivor;
X	if (sidesleft == 1) {
X	    notify(winner, "You have prevailed over your enemies!");
X	} else {
X	    for_all_sides(side) {
X		if (!side->lost) {
X		    notify(side, "Your alliance has defeated its enemies!");
X		}
X	    }
X	}
X	wait_to_exit(winner);
X	exit_xconq();
X    } else {
X        /* Game isn't over yet */
X    }
X}
X
X/* Quicky test for absence of all units. */
X
Xall_units_gone(side)
XSide *side;
X{
X    int u;
X
X    for_all_unit_types(u) if (side->units[u] > 0) return FALSE;
X    return TRUE;
X}
X
X/* Check out all the winning and losing conditions, returning a flag */
X/* on which, if any, are true.  The three return values are 1 for win, */
X/* -1 for lose, and 0 for undecided. */
X
Xcondition_true(side)
XSide *side;
X{
X    int i, u, r, amt = 0;
X    Unit *unit;
X    Condition *cond;
X
X    for (i = 0; i < global.numconds; ++i) {
X	cond = &(global.winlose[i]);
X	if ((between(cond->starttime, global.time, cond->endtime)) &&
X	    (cond->sidesn == -1 || cond->sidesn == side_number(side))) {
X	    switch (cond->type) {
X	    case TERRITORY:
X		for_all_unit_types(u) {
X		    amt += side->units[u] * utypes[u].territory;
X		}
X		if (cond->win) {
X		    if (amt >= cond->n) {
X			notify_all("The %s have enough territory to win!",
X				   plural_form(side->name));
X			return 1;
X		    }
X		} else {
X		    if (amt < cond->n) {
X			notify_all("The %s don't have enough territory!",
X				   plural_form(side->name));
X			return -1;
X		    }
X		}
X		break;
X	    case UNITCOUNT:
X		if (cond->win) {
X		    for_all_unit_types(u) {
X			if (side->units[u] < cond->units[u]) return 0;
X		    }
X		    notify_all("The %s have enough units to win!",
X			       plural_form(side->name));
X		    return 1;
X		} else {
X		    for_all_unit_types(u) {
X			if (side->units[u] > cond->units[u]) return 0;
X		    }
X		    notify_all("The %s don't have the required units!",
X			       plural_form(side->name));
X		    return -1;
X		}
X		break;
X	    case RESOURCECOUNT:
X		if (cond->win) {
X		    for_all_resource_types(r) {
X			if (side->resources[r] < cond->resources[r]) return 0;
X		    }
X		    notify_all("The %s have enough resources to win!",
X			       plural_form(side->name));
X		    return 1;
X		} else {
X		    for_all_resource_types(r) {
X			if (side->resources[r] > cond->resources[r]) return 0;
X		    }
X		    notify_all("The %s don't have the required resources!",
X			       plural_form(side->name));
X		    return -1;
X		}
X		break;
X	    case POSSESSION:
X		unit = unit_at(cond->x, cond->y);
X		if (cond->win) {
X		    if (unit != NULL && unit->side == side) {
X			notify_all("The %s have got hex %d,%d!",
X				   plural_form(side->name), cond->x, cond->y);
X			return 1;
X		    }
X		} else {
X		    if (unit == NULL || unit->side != side) {
X			notify_all("The %s don't have hex %d,%d!",
X				   plural_form(side->name), cond->x, cond->y);
X			return -1;
X		    }
X		}
X		break;
X	    default:
X		case_panic("condition type", cond->type);
X		break;
X	    }
X	}
X    }
X    return 0;
X}
X
X/* Winning is defined as not losing :-) */
X
Xside_wins(side)
XSide *side;
X{
X    Side *side2;
X
X    for_all_sides(side2) {
X	if (!allied_side(side, side2)) side_loses(side2, NULL);
X    }
X}
X
X/* Be rude to losers and give all their units to their defeaters (might */
X/* not be one, so units become neutral or dead).  Give the loser a chance */
X/* to study the screen, mark the side as lost, then pass on this detail to */
X/* all the other sides and remove spurious leftover images of units. */
X
Xside_loses(side, victor)
XSide *side, *victor;
X{
X    int ux, uy;
X    Unit *unit;
X    Side *side2;
X
X    notify(side, "You lost, %s!", (flip_coin() ? "sucker" : "turkey"));
X    if (active_display(side)) {
X	wait_to_exit(side);
X	close_display(side);
X	wedge_mode(side);
X    }
X    for_all_units(unit) {
X	if (alive(unit) && unit->side == side) {
X	    ux = unit->x;  uy = unit->y;
X	    unit_changes_side(unit, victor, CAPTURE, SURRENDER);
X	    all_see_hex(ux, uy);
X	}
X    }
X    side->lost = TRUE;
X    for_all_sides(side2) {
X	if (active_display(side2)) {
X	    remove_images(side2, side_number(side));
X	    if (side != side2) show_all_sides(side2);
X	}
X    }
X}
X
X/* Leave screen up so everybody can study it, and allow any input to take */
X/* it down.  OK to freeze all the other sides at once here. */
X
Xwait_to_exit(side)
XSide *side;
X{
X    if (humanside(side) && active_display(side)) {
X	sprintf(side->promptbuf, "[Do anything to exit]");
X	show_prompt(side);
X	freeze_wait(side);
X    }
X}
X
X/* This routine should be called before any sort of non-death exit. */
X/* Among other things, it updates the statistics. */
X
Xexit_xconq()
X{
X    int n = 0;
X    Unit *unit;
X    Side *side;
X
X    close_displays();
X    /* Need to kill off all units to finish up statistics */
X    for_all_units(unit) kill_unit(unit, (winner ? VICTOR : SURRENDER));
X    print_statistics();
X    /* Offer a one-line comment on the game and then leave */
X    for_all_sides(side) if (!side->lost) n++;
X    switch (n) {
X    case 0:
X	printf("\nEverybody lost!\n\n");
X	break;
X    case 1:
X	printf("\nThe %s (%s) won!\n\n",
X	       plural_form(winner->name),
X	       (winner->host ? winner->host : "machine"));
X	break;
X    default:
X	sprintf(spbuf, "");
X	for_all_sides(side) {
X	    if (!side->lost) {
X		if (strlen(spbuf) > 0) {
X		    sprintf(tmpbuf, "/");
X		    strcat(spbuf, tmpbuf);
X		}
X		sprintf(tmpbuf, "%s(%s)",
X			side->name, (side->host ? side->host : ""));
X		strcat(spbuf, tmpbuf);
X	    }
X	}
X	printf("\nThe %s alliance won!\n\n", spbuf);
X	break;
X    }
X    exit(0);
X}
X
X/* Shut down displays - should be done before any sort of exit. */
X
Xclose_displays()
X{
X    Side *side;
X
X    for_all_sides(side) if (active_display(side)) close_display(side);
X}
X
X/* This is to find out how everybody did. */
X
Xprint_statistics()
X{
X    Side *side;
X    FILE *fp;
X
X#ifdef STATISTICS
X    if ((fp = fopen(STATSFILE, "w")) != NULL) {
X	for_all_sides(side) {
X	    print_side_results(fp, side);
X	    print_unit_record(fp, side);
X	    print_combat_results(fp, side);
X	    if (side->next != NULL) fprintf(fp, "\f\n");
X	}
X	fclose(fp);
X    } else {
X	fprintf(stderr, "Can't open statistics file \"%s\"\n", STATSFILE);
X    }
X#endif STATISTICS
X}
X
X/* Read in a file in a special format - basically lines describing mapfiles */
X/* with markers for various types in between.  Returns true if the file */
X/* was actually found. */
X
Xread_menu_file()
X{
X    int i, j;
X    char *tmp;
X    FILE *fp;
X
X    scnstart = scnend = perstart = perend = mapstart = mapend = 0;
X    make_pathname(XCONQLIB, MAPFILEFILE, NULL, spbuf);
X    if ((fp = fopen(spbuf, "r")) != NULL) {
X	for (i = 0; i < MAXMAPMENU; ++i) {
X	    fscanf(fp, "%s", tmpbuf);
X	    mapmenu[i].name = copy_string(tmpbuf);
X	    tmp = read_line(fp);
X	    for (j = 0; tmp[j] != '\0'; ++j) if (tmp[j] != ' ') break;
X	    mapmenu[i].description = tmp + j;
X	    if (strcmp(mapmenu[i].name, "Scenarios") == 0) {
X		scnstart = i+1;
X	    } else if (strcmp(mapmenu[i].name, "Periods") == 0) {
X		scnend = i-1;
X		perstart = i+1;
X	    } else if (strcmp(mapmenu[i].name, "Maps") == 0) { 
X		perend = i-1;
X		mapstart = i+1;
X	    } else if (strcmp(mapmenu[i].name, "End") == 0) {
X		mapend = i-1;
X		break;
X	    }
X	}
X	fclose(fp);
X	return TRUE;
X    } else {
X	return FALSE;
X    }
X}
X
X/* Display menus of mapfiles.  Player can either choose a single scenario */
X/* or a period + map (but no guarantees that map will work with period!) */
X
Xask_about_mapfiles()
X{
X    char *name;
X
X    if (file_menu(scnstart, scnend, "scenario",
X		  "Enter a number, or hit return to look at maps and periods.",
X		  &name)) {
X	make_pathname(NULL, name, "scn", spbuf);
X	rawfilenames[numfiles++] = copy_string(spbuf);
X    } else {
X	if (file_menu(perstart, perend, "historical period",
X		      "Enter a number, or hit return for the standard period.",
X		      &name)) {
X	    make_pathname(NULL, name, "per", spbuf);
X	    rawfilenames[numfiles++] = copy_string(spbuf);
X	} else {
X	    printf("\nYou will get the standard period.\n");
X	}	
X	if (file_menu(mapstart, mapend, "map",
X		      "Enter a number, or hit return for a random map.",
X		      &name)) {
X	    make_pathname(NULL, name, "map", spbuf);
X	    rawfilenames[numfiles++] = copy_string(spbuf);
X	} else {
X	    printf("\nYou will get a random map.\n");
X	}
X    }
X}
X
X/* Given an array of names, return success or failure of choice and maybe */
X/* one of the names. */
X
Xfile_menu(start, end, prompt, help, rslt)
Xint start, end;
Xchar *prompt, *help, **rslt;
X{
X    int i, ans;
X
X    printf("\n");
X    if (start > end) return FALSE;
X    for (i = start; i <= end; ++i) {
X	printf("[%d]  %-14s  %s\n",
X	       i - start + 1, mapmenu[i].name, mapmenu[i].description);
X    }
X    while (TRUE) {
X	printf("\nChoose a %s: ", prompt);
X	gets(spbuf);
X	sscanf(spbuf, "%d", &ans);
X	ans += start - 1;
X	if (ans >= start && ans <= end) {
X	    *rslt = mapmenu[ans].name;
X	    return TRUE;
X	} else if (iindex('?', spbuf) >= 0) {
X	    printf("%s\n", help);
X	} else if (strlen(spbuf) == 0) {
X	    return FALSE;
X	} else {
X	    printf("Garbled answer!  Try again...\n");
X	}
X    }
X}
X
X/* This allows fairly general setup for a collection of players.  There */
X/* should be facilities for removing as well as adding... */
X
Xask_about_players()
X{
X    int i, n;
X
X    while (TRUE) {
X	printf("\nPlayers so far: ");
X	list_players(stdout);
X	printf("\n\nAny others? [number or hostname]: ");
X	gets(spbuf);
X	if (strlen(spbuf) == 0) {
X	    return;
X	} else if (strcmp(spbuf, "r") == 0) {
X	    numgivens = 0;
X	} else if (iindex('?', spbuf) >= 0) {
X	    printf("You can make various combinations of players,\n");
X	    printf("by giving either a number of machine or a host name.\n");
X	    printf("`r' clears the list; hit return when done.\n");
X	} else if (numgivens < MAXSIDES) {
X	    if (sscanf(spbuf, "%d", &n) == 1) {
X		n = max(0, min(n, MAXSIDES - numgivens));
X		for (i = 0; i < n; ++i) add_player(FALSE, NULL);
X	    } else {
X		add_player(TRUE, copy_string(spbuf));
X	    }
X	} else {
X	    printf("Can't add anybody else; 'r' starts over.\n");
X	}
X    }
X}
X
X/* List all the specified players briefly. */
X
Xlist_players(fp)
XFILE *fp;
X{
X    int i;
X
X    if (numgivens > 0) {
X	fprintf(fp, "%s", (hosts[0] ? hosts[0] : "machine"));
X	for (i = 1; i < numgivens; ++i) {
X	    fprintf(fp, ", %s", (hosts[i] ? hosts[i] : "machine"));
X	}
X    } else {
X	fprintf(fp, "no players defined.");
X    }
X}
END_OF_xconq.c
if test 18966 -ne `wc -c <xconq.c`; then
    echo shar: \"xconq.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 8 \(of 18\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 18 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0