[comp.sources.games] v01i084: xconq - multiplayer strategy game for X-windows, Part04/07

games-request@tekred.UUCP (07/10/87)

Submitted by: "Stanley T. Shebs" <shebs%orion@cs.utah.edu>
Comp.sources.games: Volume 1, Issue 84
Archive-name: xconq/Part04


#! /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 4 (of 7)."
# Contents:  X.c atk.c city.c mplay.c
# Wrapped by billr@tekred on Thu Jul  9 17:45:44 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f X.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"X.c\"
else
echo shar: Extracting \"X.c\" \(12854 characters\)
sed "s/^X//" >X.c <<'END_OF_X.c'
X/* Copyright (c) 1987  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: X.c,v 1.9 87/06/08 21:44:52 shebs Exp $ */
X
X#include "xconq.h"
X
X/* Random bitmaps that we want to relegate to a separate file. */
X
X#include "bitmaps.h"
X
X/* Default size of the view window.  Making other sizes work would take */
X/* some fiddling around I think. */
X
X#define VWID 35
X#define VHGT 35
X
X/* This is the common size of all bitmaps, tiles, etc.  This number is so */
X/* wired in, it makes one ashamed for using X. */
X
X#define SQ 16
X
X#define MAPWINSIZE (SQ*VWID)
X
X/* The unit/city icon font is different for each period, and is defined */
X/* in the period description file. */
X
Xextern char fontname[];
X
X/* Wishful thinking about what colors are available... */
X
Xchar *terrcolors[] = { "sky blue", "cyan", "green", "forest green", 
X		       "yellow", "sienna", "white" };
X
Xint mapmag = 1;      /* size of blobs on world map */
X
Xint vwid, vhgt;      /* height of window screen */
Xint vwid2, vhgt2;    /* half-heights rounded down */
Xint fw, fh;          /* font size info */
X
X/* Put starting city at center (or close to it) of first view.  I guess we */
X/* can't use put_on_screen because not enough stuff is inited yet. */
X
Xinit_curxy(side)
XSide *side;
X{
X    City *city;
X
X    for (city = citylist; city != NULL; city = city->next) {
X	if (city->side == side) {
X	    side->vcx = city->x;
X	    side->vcy = min(max(city->y, vwid2+1), (worldheight-1)-vhgt2);
X	}
X    }
X}
X
X#ifdef XWINDOWS
X
X/* The very first step in using X is to open up all the desired displays. */
X/* In our case, there are many displays each with many windows. */
X/* The world map is magnified to a scale guaranteed to fit in the alloted */
X/* space, but be as large as will fit (but not too large). */
X
Xopen_displays()
X{
X    Side *side;
X
X    mapmag = min(5, (MAXWIDTH / worldwidth));
X    vwid = min(worldwidth, VWID);
X    vhgt = min(worldheight, VHGT);
X    vwid2 = vwid/2;
X    vhgt2 = vhgt/2;
X
X    for_all_sides(side) {
X	if (side->host != NULL) {
X	    open_display(side);
X	}
X    }
X}
X
X/* Open display and all the windows we'll need.  Note that each window will */
X/* be attached to the current display, which is the one just opened. */
X/* All the windows start out mapped and non-overlapping, except the help */
X/* window, which exactly overlays the main map and therefore gets unmapped. */
X
Xopen_display(side)
XSide *side;
X{
X    int i, mainwidth, mainheight;
X    Window mainwin;
X
X    if ((side->display = XOpenDisplay(side->host)) == NULL) {
X	fprintf(stderr, "Display %s could not be opened!\n", side->host);
X	exit(0);
X    }
X    XSetDisplay(side->display);
X
X    get_fonts(side);
X    get_colors(side);
X    store_bitmaps(side);
X
X    mainwidth = MAPWINSIZE + MAXWIDTH + 1;
X    mainheight = MAPWINSIZE + 13*fh + 3;
X
X    side->bordertile = XMakeTile(side->bdcolor);
X    /* kludge test - why doesn't this work for microvaxen? */
X    if (!side->fewcolors) {
X	side->dotpix = XMakePixmap(side->dots, side->graycolor, side->bgcolor);
X    } else {
X        side->dotpix = BlackPixmap;
X    }
X    side->main = XCreateWindow(RootWindow, 50, 3, mainwidth, mainheight,
X			       3, side->bordertile, side->dotpix);
X    mainwin = side->main;
X
X    side->msg = XCreateWindow(mainwin, 0, 0, mainwidth, 10*fh,
X			      1, side->bordertile, BlackPixmap);
X    side->info = XCreateWindow(mainwin, 0, 10*fh+1, mainwidth, 2*fh,
X			       1, side->bordertile, BlackPixmap);
X    side->prompt = XCreateWindow(mainwin, 0, 12*fh+2, mainwidth, 1*fh,
X				 1, side->bordertile, BlackPixmap);
X    side->map = XCreateWindow(mainwin, 0, 13*fh+3, MAPWINSIZE, MAPWINSIZE,
X			      1, side->bordertile, BlackPixmap);
X    side->time = XCreateWindow(mainwin, MAPWINSIZE+fw, 13*fh+3+7, 9*fw, fh,
X			       1, side->bordertile, BlackPixmap);
X    side->clock = XCreateWindow(mainwin,
X				MAPWINSIZE+fw+9*fw+fw, 13*fh+3+7, 2*fw, fh,
X				1, side->bordertile, BlackPixmap);
X    side->mode = XCreateWindow(mainwin,
X			       MAPWINSIZE+12*fw+3*fw, 13*fh+3+7, 11*fw, fh,
X			       1, side->bordertile, BlackPixmap);
X    side->state = XCreateWindow(mainwin, MAPWINSIZE+1, 13*fh+3+7+fh+7,
X				MAXWIDTH, 2*fh,
X				1, side->bordertile, BlackPixmap);
X    side->sides = XCreateWindow(mainwin, MAPWINSIZE+1, 13*fh+3+7+fh+7+2*fh+7,
X				MAXWIDTH, numsides*fh,
X				1, side->bordertile, BlackPixmap);
X    side->world = XCreateWindow(mainwin,
X				MAPWINSIZE+1, mainheight-mapmag*worldheight,
X				worldwidth*mapmag, worldheight*mapmag,
X				1, side->bordertile, BlackPixmap);
X    side->help = XCreateWindow(mainwin, 0, 13*fh+3, MAPWINSIZE, MAPWINSIZE,
X			       1, side->bordertile, BlackPixmap);
X
X    XStoreName(mainwin, programname);
X    XMapWindow(mainwin);
X    XMapSubwindows(mainwin);
X    XUnmapWindow(side->help);
X    XDefineCursor(side->map, side->curs);
X    XSelectInput(mainwin, KeyPressed|ButtonPressed|ExposeRegion|ExposeWindow);
X
X    /* initialize random parameters */
X    /* one-char strings simplify screen refresh */
X    for (i = 0; i < MAXNOTES; ++i) {
X	strcpy(side->noticebuf[i], " ");
X    }
X    for (i = 0; i < 1; ++i) {
X	strcpy(side->infobuf[i], " ");
X    }
X    side->lastvcx = -1;  side->lastvcy = -1;
X    side->lastx = -1;  side->lasty = -1;
X}
X
X/* Xconq needs two fonts - one for normal messages and one for pictures of */
X/* all the units, cities, etc.  The character font is nothing unusual, but */
X/* the icon fonts are not a standard part of X, so we look for them either */
X/* in the usual place or in the library directory. */
X
Xget_fonts(side)
XSide *side;
X{
X    char *font1, *font2;
X
X    if ((font1 = XGetDefault(programname, "BodyFont")) == NULL)
X	font1 = "9x15";
X    if ((side->msgfontinfo = XOpenFont(font1)) == NULL) {
X	perror(font1);
X	exit(1);
X    }
X    side->msgfont = side->msgfontinfo->id;
X    fw = side->msgfontinfo->width;
X    fh = side->msgfontinfo->height;
X
X    if ((font2 = XGetDefault(programname, "IconFont")) == NULL)
X	font2 = fontname;
X    if ((side->iconfontinfo = XOpenFont(font2)) == NULL) {
X	make_lib_pathname(fontname, "onx", spbuf);
X	font2 = spbuf;
X	if ((side->iconfontinfo = XOpenFont(font2)) == NULL) {
X	    fprintf(stderr,
X		    "Warning: Can't open icon font - substituting letters\n");
X	    if ((side->iconfontinfo = XOpenFont(font1)) == NULL) {
X		perror(font2);
X		exit(1);
X	    }
X	}
X    }
X    if (side->iconfontinfo->width != SQ || side->iconfontinfo->height != SQ) {
X	fprintf(stderr, "Warning: Icon font is %dx%d, should be %dx%d\n",
X		side->iconfontinfo->width, side->iconfontinfo->height,
X		SQ, SQ);
X    }
X    side->iconfont = side->iconfontinfo->id;
X}
X
X/* Acquire a set of colors.  There are too many to make it feasible to */
X/* customize these via .Xdefaults, so we don't even try.  On the other */
X/* hand, there is a feeble effort to handle fewer colors than the 16 or */
X/* or so that xconq would really like to have available.  The plan is to */
X/* set priorities on which colors are most important to have. */
X
Xget_colors(side)
XSide *side;
X{
X    int	i;
X
X    /* this set should work everywhere, though not well */
X    side->bgcolor = BlackPixel;
X    side->owncolor = BlackPixel;
X    side->curscolor = BlackPixel;
X    side->enemycolor = BlackPixel;
X    side->fgcolor = WhitePixel;
X    side->bdcolor = WhitePixel;
X    side->graycolor = WhitePixel;
X    if (Debug) printf("%d colors available\n", DisplayCells());
X    /* now try to get better colors if possible */
X    if (DisplayCells() > 2) {
X	side->enemycolor = request_color("red");
X	side->curscolor = request_color("maroon");
X    }
X    if (DisplayCells() > 4) {
X	side->bdcolor = request_color("blue");
X	side->graycolor = request_color("light gray");
X    }
X    if (DisplayCells() > 15 && !Debug) {
X	side->fewcolors = FALSE;
X	for (i = 0; i < NUMTERRTYPES; ++i) {
X	    side->sqcolor[i] = request_color(terrcolors[i]);
X	}
X    } else {
X	side->fewcolors = TRUE;
X	for (i = 0; i < NUMTERRTYPES; ++i) {
X	    side->sqcolor[i] = WhitePixel;
X	}
X    }
X}
X
X/* Get a color set up and warn if not getting what was asked for. */
X
Xrequest_color(name)
Xchar *name;
X{
X    Color truecolor, availcolor;
X
X    XGetColor(name, &availcolor, &truecolor);
X    XGetHardwareColor(&availcolor);
X    if (truecolor.red != availcolor.red ||
X	truecolor.green != availcolor.green ||
X	truecolor.blue != availcolor.blue) {
X	fprintf(stderr, "Warning: %s color not exact\n", name);
X	fprintf(stderr, "(%d %d %d instead of %d %d %d)\n",
X		availcolor.red, availcolor.green, availcolor.blue,
X		truecolor.red, truecolor.green, truecolor.blue);
X    }
X    return availcolor.pixel;
X}
X
X/* Do the rigmarole to convert all those short arrays into X-approved */
X/* Bitmaps.  Note that this has to be for *each* display separately (!) */
X/* Also get the cursor shape.  If the hardware can't hack the desired */
X/* cursor size, warn about it but try to keep going. */
X
Xstore_bitmaps(side)
XSide *side;
X{
X    int actualwidth, actualheight;
X
X    side->ccurs = XStoreBitmap(SQ, SQ, ccurs_bits);
X    side->cmask = XStoreBitmap(SQ, SQ, cmask_bits);
X    side->bombpics[0] = XStoreBitmap(bomb1_width, bomb1_height, bomb1_bits);
X    side->bombpics[1] = XStoreBitmap(bomb2_width, bomb2_height, bomb2_bits);
X    side->bombpics[2] = XStoreBitmap(bomb3_width, bomb3_height, bomb3_bits);
X    side->mcurs = XStoreBitmap(SQ, SQ, mcurs_bits);
X    side->mmask = XStoreBitmap(SQ, SQ, mmask_bits);
X    side->dots = XStoreBitmap(dots_width, dots_height, dots_bits);
X
X/*  Bobcats generate curious results on this...
X    XQueryCursorShape(SQ, SQ, &actualwidth, &actualheight);
X    if (actualwidth != SQ || actualheight != SQ) {
X	fprintf(stderr, "Warning: Display %s cannot support %dx%d cursors!\n",
X		side->host, SQ, SQ);
X    }
X*/
X    side->curs = XStoreCursor(side->mcurs, side->mmask, (SQ-1)/2, (SQ-1)/2,
X			      side->bgcolor, side->fgcolor, GXcopy);
X}
X
X/* Main funnel for input returns both mouse and keyboard events, and maybe */
X/* other kinds eventually.  Some events like window exposure are handled */
X/* strictly locally. */
X
X/* One thing this routine ought to do is look at queues from other displays */
X/* to see if any screen redrawing, quitting, or whatever should be done. */
X
Xget_input(keyp, xp, yp)
Xchar *keyp;
Xint *xp, *yp;
X{
X    XEvent evt;
X    char *buf;
X    int nchar, rawx, rawy;
X    Window dummy;
X
X    if (active_display(curside)) {
X	if (numhosts > 1 && !beepedplayer) {
X	    beep(curside);
X	    beepedplayer = TRUE;
X	}
X	while (TRUE) {
X	    XNextEvent(&evt);
X	    switch (evt.type) {
X	    case KeyPressed:
X		buf = XLookupMapping(&evt, &nchar);
X		if (nchar > 0) {
X		    if (Debug) printf("%c\n", *buf);
X		    *keyp = *buf;
X		    return KEYBOARD;
X		}
X		break;
X	    case ButtonPressed:
X		XQueryMouse(curside->map, &rawx, &rawy, &dummy);
X		*xp = wrap((rawx / SQ) + (curside->vcx - vwid2));
X		*yp = (curside->vcy + vhgt2) - (rawy / SQ);
X		if (Debug) printf("%d %d\n", *xp, *yp);
X		return MOUSE;
X		break;
X	    case ExposeRegion:
X	    case ExposeWindow:
X		if (evt.window == curside->main) {
X		    XSync(TRUE);  /* suppress extra exposures */
X		    redraw(curside);
X		}
X		break;
X	    default:
X		break;
X	    }
X	}
X    }
X    /* should have better error response... */
X    return -1;
X}
X
X/* Get rid of extra key/mouse clicks. */
X
Xflush_input()
X{
X    if (active_display(curside))
X	XSync(TRUE);
X}
X
X/* A predicate that tests whether our display can safely be written to. */
X/* If so, it also sets the display (so random routines don't need to). */
X
Xactive_display(side)
XSide *side;
X{
X    if (side != NULL && side->host != NULL &&
X	!side->lost && side->display != NULL) {
X	XSetDisplay(side->display);
X	return TRUE;
X    } else {
X	return FALSE;
X    }
X}
X
X/* Completely redo a screen, making no assumptions about appearance. */
X/* This one is used frequently, especially when a window is exposed. */
X
Xredraw(side)
XSide *side;
X{
X    if (active_display(side)) {
X	if (side->lastx > 0) erase_cursor(side, curx, cury);
X	if (side->lastvcx > 0) undraw_box(side);
X	show_info(side);
X	show_note(side);
X	show_time(side);
X	show_mode(side);
X	show_state(side);
X	show_all_sides(side);
X	show_curside(side);
X	show_prompt(side);
X	show_map(side);
X	show_world(side);
X	draw_cursor(side, curx, cury);
X	flush_output();
X    }
X}
X
X/* Trivial abstraction - sometimes other routines like to ensure all output */
X/* actually on the screen. */
X
Xflush_output()
X{
X    XFlush();
X}
X
X/* Shut all displays down.  As it happens, we now rely on windows shutting */
X/* when program exits. */
X
Xclose_displays()
X{
X}
X
X/* Shut a single window down (for when one player quits). */
X
Xclose_display(side)
XSide *side;
X{
X    if (active_display(side)) {
X	XCloseDisplay(side->display);
X    }
X}
X
X/* Get a boolean value from .Xdefaults - use for customization flags. */
X
Xboolean_default(prog, def)
Xchar *prog, *def;
X{
X    char *str;
X
X    return ((str = XGetDefault(prog, def)) != NULL && strcmp(str, "on") == 0);
X}
X
X#endif XWINDOWS
END_OF_X.c
if test 12854 -ne `wc -c <X.c`; then
    echo shar: \"X.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f atk.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"atk.c\"
else
echo shar: Extracting \"atk.c\" \(14041 characters\)
sed "s/^X//" >atk.c <<'END_OF_atk.c'
X/* Copyright (c) 1987  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: atk.c,v 1.4 87/06/04 13:01:54 shebs Exp $ */
X
X#include "xconq.h"
X
X/* Buffers for descriptions of units and cities from each other's */
X/* point of view. */
X
Xchar aabuf[BUFSIZE], aobuf[BUFSIZE], oabuf[BUFSIZE], oobuf[BUFSIZE];
X
X/* Various forms of verbs for use in messages. */
X
Xchar *actvoice[NUMDOMAINS] = { "defeats", "sinks", "shoots down" };
Xchar *passvoice[NUMDOMAINS] = { "destroyed", "sunk", "shot down" };
Xchar *gerund[NUMDOMAINS] = { "defeating", "sinking", "downing" };
X
X#define destruct(u) (actvoice[utypes[(u)->type].domain])
X
X#define passive(u) (passvoice[utypes[(u)->type].domain])
X
X#define gerundify(u) (gerund[utypes[(u)->type].domain])
X
X/* This routine is called anytime a unit attempts to move onto a */
X/* square occupied by another (unfriendly) unit.  If on opposite sides, */
X/* the units hit each other and then we sort out who gets destroyed */
X/* (0, 1, or 2  participants possibly). */
X/* The hairiest part is the messages since there are 2*3*3 = 18 possible, */
X/* depending on which side is getting message and whether each unit was */
X/* unharmed, damaged, or destroyed. */
X/* Return true if the attacker defeated the defender, and can therefore */
X/* try to move into the defender's old position. */
X
Xattack_unit(atker, other)
XUnit *atker, *other;
X{
X    int d1, d2;
X    int ax = atker->x, ay = atker->y, atype = atker->type;
X    int ox = other->x, oy = other->y, otype = other->type;
X    Side *as = atker->side, *os = other->side;
X
X    d1 = d2 = 0;
X    if (probability(utypes[atype].hittab[otype]))
X	d1 = utypes[atype].power;
X    if (probability(utypes[otype].hittab[atype]))
X	d2 = utypes[otype].power;
X    /* get credit for making the hit */
X    adjust_score(as, d1);
X    adjust_score(os, d2);
X    /* maybe used up some extra supply during the attack */
X    atker->supply -= utypes[atype].hitsupply;
X    other->supply -= utypes[otype].hitsupply;
X    /* record in the history books */
X    (as->atkstats[atype][otype])++;
X    (os->atkstats[otype][atype])++;
X    as->hitstats[atype][otype] += d1;
X    os->hitstats[otype][atype] += d2;
X    /* damaged units always become visible */
X    if (d2 > 0) atker->hidden = FALSE;
X    if (d1 > 0) other->hidden = FALSE;
X
X    strcpy(aabuf, unit_handle(as, atker));
X    strcpy(aobuf, unit_handle(as, other));
X    strcpy(oobuf, unit_handle(os, other));
X    strcpy(oabuf, unit_handle(os, atker));
X    if (atker->hp <= d2) {
X	if (other->hp <= d1) {
X	    notify(as, "%s is %s while %s %s!",
X		   aabuf, passive(atker), gerundify(other), aobuf);
X	    notify(os, "%s is %s while %s %s!",
X		   oabuf, passive(atker), gerundify(other), oobuf);
X	} else if (d1 > 0) {
X	    notify(as, "%s is %s while hitting %s!",
X		   aabuf, passive(atker), aobuf);
X	    notify(os, "%s is %s while hitting %s!",
X		   oabuf, passive(atker), oobuf);
X	} else {
X	    notify(as, "%s is %s by %s!", aabuf, passive(atker), aobuf);
X	    notify(os, "%s is %s by %s!", oabuf, passive(atker), oobuf);
X	}
X    } else if (d2 > 0) {
X	if (other->hp <= d1) {
X	    notify(as, "%s is hit while %s %s!",
X		   aabuf, gerundify(other), aobuf);
X	    notify(os, "%s is hit while %s %s!",
X		   oabuf, gerundify(other), oobuf);
X	} else if (d1 > 0) {
X	    notify(as, "%s and %s both hit!", aabuf, aobuf);
X	    notify(os, "%s and %s both hit!", oabuf, oobuf);
X	} else {
X	    notify(as, "%s is hit by %s!", aabuf, aobuf);
X	    notify(os, "%s is hit by %s!", oabuf, oobuf);
X	}
X    } else {
X	if (other->hp <= d1) {
X	    notify(as, "%s %s %s!", aabuf, destruct(other), aobuf);
X	    notify(os, "%s %s %s!", oabuf, destruct(other), oobuf);
X	} else if (d1 > 0) {
X	    notify(as, "%s hits %s!", aabuf, aobuf);
X	    notify(os, "%s hits %s!", oabuf, oobuf);
X	} else {
X	    notify(as, "%s and %s both miss!", aabuf, aobuf);
X	    notify(os, "%s and %s both miss!", oabuf, oobuf);
X	}
X    }
X
X    atker->hp -= d2;
X    other->hp -= d1;
X    /* unit is dead if hit points at or below zero */
X    if (!alive(atker)) {
X	adjust_morale(as, -5 * utypes[atype].schedule);
X	adjust_morale(os,  5 * utypes[atype].schedule);
X	kill_unit(atker, UNITDEFEAT);
X    }
X    if (!alive(other)) {
X	adjust_morale(as,  5 * utypes[otype].schedule);
X	adjust_morale(os, -5 * utypes[otype].schedule);
X	kill_unit(other, UNITDEFEAT);
X    }
X    update_view(as, ax, ay);
X    update_view(os, ox, oy);
X    /* if we won, can move into new square */
X    return (alive(atker) && !alive(other));
X}
X
X/* Assault of cities dispatches depending on what unit can do to cities. */
X/* This routine assumes unit and city are unfriendly. */
X
Xattack_city(unit, city)
XUnit *unit;
XCity *city;
X{
X    Side *us = unit->side;
X
X    /* suck some extra supply */
X    unit->supply -= utypes[unit->type].hitsupply;
X    /* record the attack, but only for enemy cities */
X    if (!neutral(city)) us->cityatks[unit->type][city->size]++;
X
X    switch (utypes[unit->type].vscity[city->size]) {
X    case NOWAY:
X	movesuccess = FALSE;
X	notify(us, "%s refuses to attack a %s!",
X	       unit_handle(us, unit), citynames[city->size]);
X	break;
X    case ASSAULT:
X	assault_city(unit, city);
X	break;
X    case BOMBARD:
X	bombard_city(unit, city);
X	break;
X    case FLATTEN:
X	/* atom bomb handled under bombardment now... */
X	break;
X    default:
X	case_panic("city attack type", utypes[unit->type].vscity[city->size]);
X    }
X}
X
X/* Units that can capture cities end up here.  When a city is */
X/* captured, some of its contents may change sides. */
X
X/* Idea: if unit captures,  and city destroys that unit simultaneously, the */
X/* city becomes neutral... */
X
Xassault_city(unit, city)
XUnit *unit;
XCity *city;
X{
X    int chance;
X    Unit *occ;
X    Side *cs = city->side, *us = unit->side;
X
X    chance = utypes[unit->type].powervscity[city->size];
X    /* better chance to capture neutral places */
X    if (neutral(city)) chance += (chance / 2);
X    /* better chance to capture damaged places */
X    chance += (ctypes[city->size].hp - city->hp);
X    /* worse chance to capture occupied places */
X    for (occ = city->occupant; occ != NULL; occ = occ->nexthere) {
X	chance -= occ->hp;
X    }
X    /* make sure is non-bogus percentage */
X    chance = max(1, min(100, chance));
X
X    if (probability(chance)) {
X	if (!neutral(city)) us->cityhits[unit->type][city->size]++;
X	capture_city(unit, city);
X	if (city->size > BASE) set_product(city, decide_product(city), FALSE);
X    } else if (probability(utypes[unit->type].cityvs[city->size])) {
X	city_hits_unit(city, unit);
X    } else {
X	adjust_morale(cs,  10);
X	adjust_morale(us, -10);
X	notify(us, "%s repulses %s!",
X	       city_handle(us, city), unit_handle(us, unit));
X	notify(cs, "%s repulses %s!",
X	       city_handle(cs, city), unit_handle(cs, unit));
X	/* riskier to attack from transports... */
X	if (unit->transport != NULL) {
X	    notify(us, "%s was slaughtered!", unit_handle(us, unit));
X	    kill_unit(unit, CITYDEFEAT);
X	}
X    }
X}
X
X/* There are many consequences of a city being captured. */
X/* It loses hit points equal to range of unit on open ground - this is */
X/* crude measure of unit's general destructiveness in capturing things. */
X
Xcapture_city(unit, city)
XUnit *unit;
XCity *city;
X{
X    int cx = city->x, cy = city->y, chp = city->hp, csize = city->size;
X    Side *cs = city->side, *us = unit->side;
X
X    notify(us, "You %s %s!",
X	   (neutral(city) ? "captured" : "liberated"), city_handle(us, city));
X    notify(cs, "%s has been captured by the %s!",
X	   city_handle(cs, city), plural_form(us->name));
X    if (hit_city(city, us, utypes[unit->type].range)) {
X	city_changes_side(city, us, CAPTURED);
X	while (city->occupant != NULL) {
X	    kill_unit(city->occupant, PRISONER);
X	}
X	if (utypes[unit->type].garrison) kill_unit(unit, GARRISON);
X	adjust_morale(us, 10 * csize);
X    }
X    update_view(cs, cx, cy);
X    update_view(us, cx, cy);
X    adjust_score(us, (cs == NULL ? -1 : 1) * chp);
X    adjust_score(cs, -1 * chp);
X}
X
X/* Bombarding a city is what some units can do even if they can't */
X/* assault it directly.  If done enough, city might get trashed. */
X
Xbombard_city(unit, city)
XUnit *unit;
XCity *city;
X{
X    int hit, cx = city->x, cy = city->y;
X    Side *us = unit->side, *cs = city->side;
X
X    notify(us, "%s %s %s", 
X	   unit_handle(us, unit), "bombards", city_handle(us, city));
X    notify(cs, "%s %s %s",
X	   unit_handle(cs, unit), "bombards", city_handle(cs, city));
X    hit = utypes[unit->type].powervscity[city->size];
X    /* record for posterity */
X    us->cityhits[unit->type][city->size] += hit;
X    /* city's chance to get back at unit */
X    if (probability(utypes[unit->type].cityvs[city->size])) {
X	city_hits_unit(city, unit);
X    }
X    hit_city(city, us, hit);
X    update_view(us, cx, cy);
X    update_view(cs, cx, cy);
X}
X
X/* Hitting a city is more complex than hitting units, because a sufficiently */
X/* large city will die spectacularly if hit hard enough.  Smaller hits will */
X/* cause flattening, while the smallest may only reduce it to the next */
X/* smaller type of city. */
X
Xhit_city(city, us, hit)
XCity *city;
XSide *us;
Xint hit;
X{
X    bool stillthere;
X    Side *cs = city->side;
X
X    if (hit > ctypes[CITY].hp) {
X	notify_all("An atomic bomb destroys %s!", city->name);
X	explode_nuke(cs, us, city->x, city->y);
X	adjust_morale(cs, -1000);
X	adjust_score(us, -1000);
X	city_changes_side(city, NULL, NUKED);
X	remove_city(city);
X	set_square(city->x, city->y, DESERT, RURAL);
X	stillthere = FALSE;
X    } else if (hit >= city->hp) {
X	notify(cs, "%s has been flattened!", city_handle(cs, city));
X	notify(us, "%s has been flattened!", city_handle(us, city));
X	adjust_morale(cs, -100);
X	city_changes_side(city, NULL, FLATTENED);
X	remove_city(city);
X	stillthere = FALSE;
X    } else {
X	city->hp -= hit;
X	if (city->hp <= ctypes[(city->size)-1].hp) {
X	    notify(cs, "%s has been reduced to a %s!",
X		   city_handle(cs, city), citynames[city->size-1]);
X	    notify(us, "%s has been reduced to a %s!",
X		   city_handle(us, city), citynames[city->size-1]);
X	    adjust_territory(cs, 0 - city_value(city->size), TRUE);
X	    city->size--;
X	    adjust_territory(cs, city_value(city->size), TRUE);
X	    adjust_morale(cs, -100);
X	}
X	adjust_morale(cs, -5 * hit);
X	stillthere = TRUE;
X    }
X    return stillthere;
X}
X
X/* Cities can also be captured by a siege.  If successful, one of the */
X/* besiegers is randomly chosen to be the occupier. */
X
Xcheck_siege(city)
XCity *city;
X{
X    int chance, dir;
X    Unit *unit;
X
X    chance = ctypes[city->size].siege;
X    if (neutral(city)) chance *= 2;
X    chance = max(1, min(100, chance));
X    if (probability(chance)) {
X	dir = random_dir();
X	unit = unit_at(city->x+dirx[dir], city->y+diry[dir]);
X	notify(unit->side, "%s has surrendered to %s!",
X	       city_handle(unit->side, city), unit_handle(unit->side, unit));
X	notify(city->side, "%s has surrendered to %s!",
X	       city_handle(city->side, city), unit_handle(city->side, unit));
X	capture_city(unit, city);
X    }
X}
X
X/* City can try to hit back.  If it fails to do any damage, no message */
X/* is displayed (why not?) */
X/* Amount of damage is presently one less than the size of the city. */
X
Xcity_hits_unit(city, unit)
XCity *city;
XUnit *unit;
X{
X    int d1, cx = city->x, cy = city->y;
X    Side *cs = city->side, *us = unit->side;
X
X    /* hit power is 2 for metropoli, 1 for towns, 0 for bases */
X    d1 = city->size - 1;
X
X    strcpy(aabuf, unit_handle(us, unit));
X    strcpy(aobuf, city_handle(us, city));
X    strcpy(oabuf, unit_handle(cs, unit));
X    strcpy(oobuf, city_handle(cs, city));
X    if (unit->hp <= d1) {
X	notify(us, "%s %s %s!", aobuf, destruct(unit), aabuf);
X	notify(cs, "%s %s %s!", oobuf, destruct(unit), oabuf);
X    } else if (d1 > 0) {
X	notify(us, "%s hits %s!", aobuf, aabuf);
X	notify(cs, "%s hits %s!", oobuf, oabuf);
X    }
X    unit->hp -= d1;
X    if (unit->hp <= 0) {
X	kill_unit(unit, CITYDEFEAT);
X	update_view(cs, cx, cy);
X	update_view(us, cx, cy);
X    }
X}
X
X/* Nearly-raw combat statistics are hard to interpret, but they provide */
X/* a useful check against subjective evaluation of performance. */
X
X#ifdef STATISTICS
Xprint_unit_results(fp, side)
XFILE *fp;
XSide *side;
X{
X    int atype, dtype, reason, sum;
X
X    fprintf(fp,
X	    "Unit Performance (successes and attacks against enemy by type\n");
X    fprintf(fp, "   ");
X    for (dtype = 0; dtype < numutypes; ++dtype) {
X	fprintf(fp, "   %c   ", unitchars[dtype]);
X    }
X    fprintf(fp, "\n");
X    for (atype = 0; atype < numutypes; ++atype) {
X	fprintf(fp, " %c ", unitchars[atype]);
X	for (dtype = 0; dtype < numutypes; ++dtype) {
X	    if (side->atkstats[atype][dtype] > 0) {
X		fprintf(fp, "%3.1f/%-2d ",
X			((float) side->hitstats[atype][dtype]) /
X			side->atkstats[atype][dtype],
X			side->atkstats[atype][dtype]);
X	    } else {
X		fprintf(fp, "       ");
X	    }
X	}
X	fprintf(fp, "\n");
X    }
X    fprintf(fp, "\n");
X    fprintf(fp, "Unit Record (ultimate fates by cause and unit type)\n");
X    fprintf(fp, "     %s  Total\n", REASONNAMES);
X    for (atype = 0; atype < numutypes; ++atype) {
X	sum = 0;
X	fprintf(fp, " %c  ", unitchars[atype]);
X	for (reason = 0; reason < MAXREASONS; ++reason) {
X	    if (side->losses[atype][reason] > 0) {
X		fprintf(fp, " %3d  ", side->losses[atype][reason]);
X		sum += side->losses[atype][reason];
X	    } else {
X		fprintf(fp, "      ");
X	    }
X	}
X	fprintf(fp, "  %3d\n", sum);
X    }
X    fprintf(fp, "\n");
X}
X
X/* Statistics about attacks on cities. */
X
Xprint_city_results(fp, side)
XFILE *fp;
XSide *side;
X{
X    int atype, ctype;
X
X    fprintf(fp, "City Results (successes / tries against cities)\n");
X    fprintf(fp, "      Base    Town    City\n");
X    for (atype = 0; atype < numutypes; ++atype) {
X	fprintf(fp, " %c  ", unitchars[atype]);
X	for (ctype = 1; ctype < numctypes; ++ctype) {
X	    if (side->cityatks[atype][ctype]) {
X		fprintf(fp, "%3d/%-3d ",
X			side->cityhits[atype][ctype],
X			side->cityatks[atype][ctype]);
X	    } else {
X		fprintf(fp, "        ");
X	    }
X	}
X	fprintf(fp, "\n");
X    }	
X    fprintf(fp, "\n");
X}
X#endif STATISTICS
END_OF_atk.c
if test 14041 -ne `wc -c <atk.c`; then
    echo shar: \"atk.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f city.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"city.c\"
else
echo shar: Extracting \"city.c\" \(12579 characters\)
sed "s/^X//" >city.c <<'END_OF_city.c'
X/* Copyright (c) 1987  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: city.c,v 1.4 87/06/07 11:37:02 shebs Exp $ */
X
X#include "xconq.h"
X
XCity cities[MAXCITIES]; /* The array of all cities */
XCity *citylist;         /* Head of the list of all cities */
X
Xint numcities;          /* the number of cities in existence */
X
Xchar citychars[MAXCTYPES];   /* cache of chars repning cities */
Xchar *citynames[MAXCTYPES];  /* cache of names of types of cities */
X
X/* Reset each entry in the big array of city structures. */
X
Xinit_cities()
X{
X    int i;
X
X    for (i = 0; i < MAXCITIES; ++i)
X	cities[i].size = RURAL;
X    citylist = NULL;
X}
X
X/* A city is specified by its size, position, and name.  As an optimization */
X/* the parameter "cityn" specifies where to start in the array - this */
X/* eliminates O(n^2) behavior when initing 400 cities... */
X/* This routine is also used during game restoration, so it has to set */
X/* almost all the slots. */
X     
XCity *
Xcreate_city(size, x, y, name, sidename, product, schedule, hp, cityn)
Xint size, x, y, product, schedule, hp, cityn;
Xchar *name, *sidename;
X{
X    int i, j;
X    
X    for (i = cityn; i < MAXCITIES; ++i) {
X	if (cities[i].size == RURAL) {
X	    cities[i].x = x;  cities[i].y = y;
X	    cities[i].size = size;
X	    cities[i].side = NULL;  /* neutral, true side asgned later */
X	    cities[i].name = malloc(strlen(name)+2);
X	    strcpy(cities[i].name, name);
X	    cities[i].sname = sidename;
X	    for (j = 0; j < numutypes; ++j) {
X		((cities[i].standing)[j]).type = NONE;
X		cities[i].produced[j] = 0;
X	    }
X	    cities[i].product = product;
X	    cities[i].schedule = schedule;
X	    cities[i].hp = hp;
X	    cities[i].next = citylist;
X	    citylist = &cities[i];
X	    /* tweak world map to include the city */
X	    set_square(x, y, terrain(x, y), size);
X	    set_city_at(x, y, citylist);
X	    return citylist;
X	}
X    }
X    /* will happen if scenario too big, or players build too many bases */
X    fprintf(stderr, "Cannot create more than %d cities!\n", MAXCITIES);
X    return NULL;
X}
X
X/* Find a good initial city for the given side.  Only requirement these */
X/* days is that it be a big city and not already used by somebody. */
X/* If enough tries, we're either extremely unlucky or something is wrong. */
X/* In either case, we don't want this game anymore. */
X
XCity *
Xrandom_start_city()
X{
X    int tries = 0;
X    City *city;
X
X    while (tries++ < MAXTRIES) {
X	city = &cities[random(numcities)];
X	if (neutral(city) && city->size == CITY)
X	    return city;
X    }
X    /* failure here is completely fatal */
X    fprintf(stderr, "Can't find a starting city!\n");
X    exit(1);
X}
X
X/* The main activity of cities is to produce units.  The code here has to */
X/* be a little tricky because players need to get opportunities to cancel */
X/* and to idle cities. */
X
Xproduce(city)
XCity *city;
X{
X    int tmp, prod;
X    Unit *new;
X    
X    if (!neutral(city) && city->size > BASE) {
X	city->side->production[city->size]++;
X	if (!random(city->side->morale) &&
X	    city->size == TOWN &&
X	    producing(city)) {
X	    city_revolts(city);
X	    return;
X	}
X	if (!human(city))
X	    set_product(city, decide_product(city), FALSE);
X	if (city->schedule-- <= 0) {
X	    if (producing(city)) {
X		prod = city->product;
X		if (create_new_unit(prod, city->side, city->x, city->y)) {
X		    notify(city->side, "%s produces a new %s",
X			   city_handle(city->side, city), unitnames[prod]);
X		    city->produced[prod]++;
X		    if (usecount) numinputs++;
X		    city->side->completion[city->size] += utypes[prod].schedule;
X		}
X		set_product(city, prod, TRUE);
X	    } else {
X		set_product(city, decide_product(city), FALSE);
X	    }
X	}
X    }
X}
X
X/* If a city is run by a player, then interact to find out what to produce, */
X/* error checking to we make sure we don't get battleships in the middle of */
X/* Siberia and other sillinesses. */
X
Xdecide_product(city)
XCity *city;
X{
X    int type;
X
X    if (city->size <= BASE) {
X	notify(city->side, "Bases can't produce anything!");
X	type = NOTHING;
X    } else if (human(city)) {
X	show_city(city);
X	type = read_unit_type("Production demands: ");
X	unshow_city(city);
X    } else {
X	type = machine_product(city);
X    }
X    return type;
X}
X
X/* Set up new product for a city.  There are two ways that the extra startup */
X/* cost will be incurred:  either it has been specifically directed via an */
X/* argument, or the type is different from the current type in production. */
X
Xset_product(city, type, renew)
XCity *city;
Xint type, renew;
X{
X    int sched = city->schedule;
X
X    if (type == NOTHING) {
X	/* machines should never decide to produce nothing */
X    } else if (type != city->product || renew) {
X	sched = utypes[type].schedule;
X	if (!renew) sched = utypes[type].startup;
X#ifdef SLOWTOWNS
X	if (city->size == TOWN) sched += sched/2;
X#endif SLOWTOWNS
X    }
X    city->product = type;
X    city->schedule = sched;
X}
X
X/* City repair proceeds each turn a little.  This is also a good time for */
X/* repair of any damaged units that are in town, and for checking on sieges! */
X/* These activities continue even if city changes hands, so siege check */
X/* never needs to exit early. */
X
Xmake_repairs(city)
XCity *city;
X{
X    int utype;
X    Unit *unit;
X    
X    if (surrounded(city->side, city->x, city->y)) {
X	check_siege(city);
X    }
X    if ((city->hp < ctypes[city->size].hp) &&
X	(gametime % ctypes[city->size].recovery == 0)) {
X	city->hp++;
X    }
X    for (unit = city->occupant; unit != NULL; unit = unit->nexthere) {
X	if (alive(unit)) {
X	    utype = unit->type;
X	    if (unit->hp < utypes[utype].hp) {
X		++(unit->hp);
X	    }
X	    if (unit->hp == utypes[utype].hp &&	utypes[utype].canhide) {
X		unit->hidden == TRUE;
X	    }
X	    unit->supply = utypes[utype].supply;
X	    unit->cautious = TRUE;
X	}
X    }
X}
X
X/* When city revolts, it reverts back to being neutral.  All units there are */
X/* lost, and the former owner's morale drops. */
X
Xcity_revolts(city)
XCity *city;
X{
X    Unit *unit;
X    Side *oldside = city->side;
X
X    notify(oldside, "%s revolts! (%s production lost)",
X	   city->name, unitnames[city->product]);
X    while (city->occupant != NULL) {
X	kill_unit(city->occupant, PRISONER);
X    }
X    city_changes_side(city, NULL, REVOLTED);
X    update_view(oldside, city->x, city->y);
X}
X
X/* Handle the general situation of a city changing allegiance from one side */
X/* to another.  The main effect is on the sides' territories. */
X
Xcity_changes_side(city, newside, reason)
XCity *city;
XSide *newside;
Xint reason;
X{
X    int value, newterr, newmorale;
X    Side *oldside = city->side;
X
X    value = city_value(city->size);
X    city->side = newside;
X    city->product = NOTHING;
X    adjust_territory(oldside, 0-value, TRUE);
X    adjust_territory(newside, value, (oldside != NULL));
X}
X
X/* This is a rather arbitrary scale - should be period-dependent. */
X
Xcity_value(csize)
Xint csize;
X{
X    int value = 1;
X
X    if (csize > BASE) value *= 5;
X    if (csize > TOWN) value *= 5;
X    return value;
X}
X
X/* City destruction is infrequent, but must be thorough, so we don't */
X/* get dangling pointers and weird behavior... */
X
Xremove_city(city)
XCity *city;
X{
X    int cx = city->x, cy = city->y;
X    City *head;
X    
X    while (city->occupant != NULL) {
X	kill_unit(city->occupant, UNITDEFEAT);
X    }
X    /* clean off the map */
X    set_square(cx, cy, terrain(cx, cy), RURAL);
X    set_city_at(cx, cy, NULL);
X    city->side = NULL;
X    city->size = RURAL;
X    /* unlink from the list of cities */
X    if (city == citylist) {
X	citylist = citylist->next;
X    } else {
X	for (head = citylist; head != NULL; head = head->next) {
X	    if (head->next == city) {
X		head->next = head->next->next;
X		break;
X	    }
X	}
X    }
X    city->next = NULL;
X}
X
X/* Display details about city.  This is long and ugly because we have to */
X/* assemble nicely formatted strings into buffers for display. */
X
Xshow_city(city)
XCity *city;
X{
X    bool flag;
X    int i, j, type, ucount[MAXUTYPES];
X    char tmp1[BUFSIZE], prodbuf[BUFSIZE], occbuf[BUFSIZE];
X    Unit *occ;
X
X    curx = city->x;  cury = city->y;
X
X    put_on_screen(curside, curx, cury);
X
X    if (city->side == curside || Debug) {
X	sprintf(spbuf2, "");
X	flag = FALSE;
X	for (i = 0; i < numutypes; ++i) ucount[i] = 0;
X	for (occ = city->occupant; occ != NULL; occ = occ->nexthere) {
X	    flag = TRUE;
X	    ucount[occ->type]++;
X	}
X	if (flag) {
X	    sprintf(occbuf, "Occ ");
X	    for (i = 0; i < numutypes; ++i) {
X		if (ucount[i] > 0) {
X		    sprintf(tmp1, "%d %c  ", ucount[i], unitchars[i]);
X		    strcat(occbuf, tmp1);
X		}
X	    }
X	    strcat(spbuf2, occbuf);
X	}
X	flag = FALSE;
X	for (type = 0; type < numutypes; ++type)
X	    if (city->standing[type].type != NONE) flag = TRUE;
X	if (flag) {
X	    sprintf(spbuf, "Orders: ");
X	    for (type = 0; type < numutypes; ++type) {
X		if (city->standing[type].type != NONE) {
X		    sprintf(prodbuf, "  %ss to %s",
X			    unitnames[type],
X			    order_desig(city->standing[type]));
X		    strcat(spbuf, prodbuf);
X		}
X	    }
X	    strcat(spbuf2, spbuf);
X	}
X	if (producing(city)) {
X	    sprintf(prodbuf, "  (New %s in %d turns)",
X		    unitnames[city->product], city->schedule);
X	} else if (city->size > BASE) {
X	    sprintf(prodbuf, "  (Idle for %d more turns)", city->schedule);
X	} else {
X	    sprintf(prodbuf, "");
X	}
X	sprintf(tmp1, "  Hp %d/%d", city->hp, ctypes[city->size].hp);
X	strcat(prodbuf, tmp1);
X    } else {
X	sprintf(spbuf2, "");
X	sprintf(prodbuf, "");
X    }
X    sprintf(spbuf, "%s", city_handle(curside, city));
X    if (curside->showcoords) strcat(spbuf, pos_desig(city->x, city->y));
X    strcat(spbuf, prodbuf);
X    /* spit it all onto the display */
X    set_current_info(curside, spbuf, spbuf2);
X    show_info(curside);
X    draw_cursor(curside, curx, cury);
X}
X
X/* Erase the cursor and do anything else to undo the city display. */
X
Xunshow_city(city)
XCity *city;
X{
X    erase_cursor(curside, curx, cury);
X}
X
X/* Generate the description by which a city is called */
X
Xchar citydesigbuf[BUFSIZE];
X
Xchar *
Xcity_handle(side, city)
XSide *side;
XCity *city;
X{
X    sprintf(citydesigbuf, "");
X    if (side == NULL) return citydesigbuf;
X    if (side == city->side)
X	strcat(citydesigbuf, "your ");
X    else
X	sprintf(citydesigbuf, "the %s ",
X		(city->side == NULL ? "neutral" : city->side->name));
X    strcat(citydesigbuf, citynames[city->size]);
X    strcat(citydesigbuf, " of ");
X    strcat(citydesigbuf, city->name);
X    return citydesigbuf;
X}
X
X/* Generate a name for a base, using best acrynomese.  Note that BSD will */
X/* produce garbage for second letter if not lowercase, but this isn't */
X/* exactly a critical piece of code. */
X
Xchar *
Xnew_base_name(side)
XSide *side;
X{
X    char *name;
X
X    name = malloc(10);
X    sprintf(name, "%c%c-%02d",
X	    side->name[0], toupper(side->name[1]), ++side->basenum);
X    return name;
X}
X
X/* City saving is complicated by the need to save all the standing orders */
X/* and their parameters. */
X
Xsave_city(city, fp, savecrud)
XCity *city;
XFILE *fp;
Xbool savecrud;
X{
X    int i, j;
X
X    fprintf(fp, "%s\n%d %d %d %d %d %d %d %d ",
X	    city->name, city->size, side_number(city->side),
X	    city->x, city->y, city->product, city->schedule,
X	    city->hp, savecrud);
X    fprintf(fp, "\n");
X    if (savecrud) {
X	for (i = 0; i < numutypes; ++i) {
X	    fprintf(fp, "%d %d %d ",
X		    city->produced[i],
X		    city->standing[i].type, city->standing[i].rept);
X	    for (j = 0; j < NUMORDERPARMS; ++j) {
X		fprintf(fp, "%d ", city->standing[i].parms[j]);
X	    }
X	    fprintf(fp, "\n");
X	}
X    }
X}
X
X/* City restoration has to read the matrix of standing orders back, but */
X/* only if the appropriate flag was written out (scenarios don't care about */
X/* the orders) */
X
Xrestore_city(fp)
XFILE *fp;
X{
X    char name[BUFSIZE];
X    int i, j, t1, t2, t3, t4, t5, t6, t7, savecrud;
X    City *city;
X
X    fgets(name, BUFSIZE-1, fp);  name[strlen(name)-1] = '\0';
X    fscanf(fp, "%d %d %d %d %d %d %d %d\n",
X	   &t1, &t2, &t3, &t4, &t5, &t6, &t7, &savecrud);
X    city = create_city(t1, t3, t4, name, NULL, t5, t6, t7, 0);
X    city->side = side_n(t2);
X    if (savecrud) {
X	for (i = 0; i < numutypes; ++i) {
X	    fscanf(fp, "%d %d %d", &t1, &t2, &t3);
X	    city->produced[i] = t1;
X	    city->standing[i].type = t2;
X	    city->standing[i].rept = t3;
X	    for (j = 0; j < NUMORDERPARMS; ++j) {
X		fscanf(fp, "%d", &t1);
X		city->standing[i].parms[j] = t1;
X	    }
X	}
X	fscanf(fp, "\n");
X    }
X    adjust_territory(city->side, city_value(city->size), FALSE);
X    if (Debug) printf("Restored city %s\n", city->name);
X}
END_OF_city.c
if test 12579 -ne `wc -c <city.c`; then
    echo shar: \"city.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f mplay.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"mplay.c\"
else
echo shar: Extracting \"mplay.c\" \(11237 characters\)
sed "s/^X//" >mplay.c <<'END_OF_mplay.c'
X/* Copyright (c) 1987  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: mplay.c,v 1.2 87/05/31 11:56:30 shebs Exp $ */
X
X#include "xconq.h"
X
Xextern int assaulter, ttransport;
X
X/* Machine algorithm for deciding what a city should produce. */
X/* Since armies are the only sort of units that can capture new */
X/* cities, it is important to make sure that they are being produced */
X/* regularly. */
X/* This routine must return the type of unit decided upon. */
X
X/* Rules: only produce six of aircraft or land units, or three ships, before */
X/* switching to something else. */
X
Xmachine_product(city)
XCity *city;
X{
X    int allcities, armycities, seaports, type, apcities, tmp;
X    int armyunits, apunits;
X    City *city2;
X    Unit *unit2;
X
X    if (producing(city)) {
X	tmp = city->produced[city->product];
X	if (tmp > 0 &&
X	    ((watertype(city->product) && tmp % 3 == 0) || (tmp % 6 == 0))) {
X	    ++(city->produced[city->product]);
X	    city->product = NOTHING;
X	}
X    }
X    if (city->schedule >= 0 && producing(city)) return city->product;
X
X    allcities = armycities = apcities = seaports = 0;
X    armyunits = apunits = 0;
X    for (city2 = citylist; city2 != NULL; city2 = city2->next) {
X	if (city2->side == city->side) {
X	    ++allcities;
X	    if (city2->product == assaulter) ++armycities;
X	    if (city2->product == ttransport) ++apcities;
X	    if (adj_sea(city2->x, city2->y)) ++seaports;
X	}
X    }
X    for (unit2 = unitlist; unit2 != NULL; unit2 = unit2->next) {
X	if (unit2->side == city->side) {
X	    if (unit2->type == assaulter) ++armyunits;
X	    if (unit2->type == ttransport) ++apunits;
X	}
X    }
X    if (!adj_sea(city->x, city->y)) {
X	if (armycities < 3)
X	    type = assaulter;
X	else {
X	    tmp = 0;
X	    while (tmp++ < MAXTRIES) {
X		type = random(numutypes);
X		if (!watertype(type)) break;
X	    }
X	}
X    } else if (armycities > 0 && apunits < 4 && apcities < 1)
X	type = ttransport;
X    else if (apunits >= 4 && armycities >= 2) {
X        type = random(numutypes);
X	if (type == assaulter || type == ttransport)
X	    type = random(numutypes);
X    } else
X	type = assaulter;
X    if (type != city->product)
X	notify(city->side, "%s will now build %s units",
X	       city->name, unitnames[type]);
X    return type;
X}
X
X/* Used in a search_area below. */
X
Xcityp(x, y)
Xint x, y;
X{
X    City *city = city_at(x, y);
X
X    return (city != NULL && city->side == curside);
X}
X
X/* True only if some sort of enemy city has been seen. */
X
Xecityp(x, y)
Xint x, y;
X{
X    int view;
X    Side *side;
X
X    view = side_view(curside, x, y);
X    if (view != EMPTY) {
X	side = side_n(valign(view));
X	if (vthing(view) == VCITY && enemy_side(side, curside)) return TRUE;
X    }
X    return FALSE;
X}
X
X/* True only if some sort of defeatable enemy has been seen. */
X
Xeunitp(x, y)
Xint x, y;
X{
X    int view;
X    Side *side;
X
X    view = side_view(curside, x, y);
X
X    if (view == UNSEEN && flip_coin()) return TRUE;
X    if (view != EMPTY) {
X	side = side_n(valign(view));
X	if (vthing(view) == VUNIT &&
X	    enemy_side(side, curside) &&
X	    good_unit_odds(curunit->type, vtype(view)))
X	    return TRUE;
X    }
X    return FALSE;
X}
X
X/* True if we found a plausible transport. */
X
Xtransportp(x, y)
Xint x, y;
X{
X    Unit *unit = unit_at(x, y);
X
X    return (unit != NULL && !enemy_side(curunit->side, unit->side) &&
X	    can_carry(unit, curunit));
X}
X
X/* True if we found a plausible passenger. */
X
Xpassengerp(x, y)
Xint x, y;
X{
X    Unit *unit = unit_at(x, y);
X
X    return (unit != NULL && !enemy_side(curunit->side, unit->side) &&
X	    can_carry(curunit, unit));
X}
X
X/* True if given square has not been viewed. */
X
Xunseenp(x, y)
Xint x, y;
X{
X    return (side_view(curside, x, y) == UNSEEN);
X}
X
X/* Decide on and make a move or set orders for a machine player */
X
X/* Our general setup here is stupidly aggressive - if there's an enemy */
X/* seen in the vicinity, everything will eventually go after and attack it. */
X/* There are some exceptions; if a unit cannot capture or damage a city, */
X/* it will not attack, nor if the odds are stacked greatly against it. */
X/* This lead to bizarre behavior, because the unit will move towards until */
X/* it gets adjacent, then retreat (oh well!). */
X/* When not attacking, units generally move randomly or towards unexplored */
X/* areas (no, armies are not smart about trying to cross the ocean */
X
Xmachine_move(unit)
XUnit *unit;
X{
X    int utype = unit->type, ux = unit->x, uy = unit->y, ex, ey, etype;
X    City *ecity;
X    Unit *eunit;
X    Side *us = unit->side;
X
X    if (Debug)
X	printf("%s is deciding its move\n", unit_handle(side_n(0), unit));
X    if ((low_supplies(unit) || 2*unit->hp <= utypes[utype].hp) &&
X	probability(98)) {
X	if (Debug) notify(us, "%s needs to return,", unit_handle(us, unit));
X	if (search_area(ux, uy, unit->supply, cityp, &ex, &ey)) {
X	    order_moveto(unit, ex, ey);
X	} else {
X	    if (Debug) notify(us, "%s can't return!", unit_handle(us, unit));
X	    order_movedir(unit, random_dir(), unit->supply+3);
X	}
X    } else if (adj_enemy_city(us, ux, uy, &ex, &ey)) {
X	if (Debug) notify(us, "%s dealing with enemy city at %d,%d",
X			  unit_handle(us, unit), ex, ey);
X	ecity = city_at(ex, ey);
X	if (could_capture(utype, ecity->size) && probability(90)) {
X	    order_moveto(unit, ex, ey);
X	} else if (unit->cargo != NULL && probability(80)) {
X	    do_sit(1);
X	} else if (could_bombard(utype, ecity->size) && !neutral(ecity) &&
X		   good_bomb_odds(utype, ecity->size) && probability(80)) {
X	    order_moveto(unit, ex, ey);
X	} else {
X	    order_movedir(unit, random_dir(), 1);
X	}
X    } else if (adj_enemy_unit(us, ux, uy, &ex, &ey)) {
X	if (Debug) notify(us, "%s dealing with enemy unit at %d,%d",
X			  unit_handle(us, unit), ex, ey);
X	eunit = unit_at(ex, ey);
X	etype = eunit->type;
X	if (good_unit_odds(utype, etype) && probability(70) &&
X	    unit->cargo == NULL) {
X	    order_moveto(unit, ex, ey);
X	} else if (utypes[utype].hittab[etype] <
X		   utypes[etype].hittab[utype]) {
X	    order_movedir(unit, find_dir(wrap(ux - ex), uy - ey), 4);
X	} else {
X	    do_sit(1);
X	}
X    } else if (adj_transport(unit, &ex, &ey) && probability(90)) {
X	if (Debug) notify(us, "%s getting on transport,",
X			  unit_handle(us, unit));
X	order_moveto(unit, ex, ey);
X    } else if (utype == ttransport) {
X	if (Debug) notify(us, "%s loading up,", unit_handle(us, unit));
X	if (cargo_size(unit, assaulter) == 0)  unit->goal = LOADING;
X	if (cargo_size(unit, assaulter) < 4 && unit->goal == LOADING) {
X	    if (city_at(ux, uy) || probability(20)) {
X		order_movedir(unit, random_dir(), 1);
X	    } else if (probability(50) &&
X		       search_area(ux, uy, 5, passengerp, &ex, &ey)) {
X		order_moveto(unit, ex, ey);
X	    } else {
X		order_sentry(unit, 10);
X	    }
X	} else {
X	    unit->goal = NORMAL;
X	    look_for_trouble(unit, 80);
X	}
X    } else if (unit->transport != NULL) {
X	unit->orders.type = RANDOM;
X	unit->orders.rept = 2;
X    } else {
X	look_for_trouble(unit, 70);
X    }
X    if (Debug) 
X	notify(us, "%s will now %s",
X	       unit_handle(us, unit), order_desig(unit->orders));
X}
X
Xlook_for_trouble(unit, chance)
XUnit *unit;
Xint chance;
X{
X    int utype = unit->type, ux = unit->x, uy = unit->y, ex, ey, etype;
X    City *ecity;
X    Unit *eunit;
X    Side *us = unit->side;
X
X    if (probability(60) &&
X	search_area(ux, uy, random(20+gametime/10), ecityp, &ex, &ey)) {
X	if (Debug) notify(us, "%s looking for enemy,", unit_handle(us, unit));
X	order_moveto(unit, ex, ey);
X    } else if (probability(50) &&
X	       search_area(ux, uy, random(20+gametime/10), eunitp, &ex, &ey)) {
X	if (Debug) notify(us, "%s looking for enemy,", unit_handle(us, unit));
X	order_moveto(unit, ex, ey);
X    } else if (probability(30) &&
X	       search_area(ux, uy, 10, transportp, &ex, &ey)) {
X	if (Debug) notify(us, "%s found a transport,", unit_handle(us, unit));
X	order_moveto(unit, ex, ey);
X    } else if (probability(30)) {
X	if (Debug) notify(us, "%s being random,", unit_handle(us, unit));
X	unit->orders.type = RANDOM;
X	unit->orders.rept = 8;
X    } else if (unit->transport != NULL || probability(10)) {
X	if (Debug) notify(us, "%s sitting a spell,", unit_handle(us, unit));
X	order_sentry(unit, (unit->transport != NULL ? 40 : 2));
X    } else if (probability(50) &&
X	       search_area(ux, uy, random(10+gametime/10), unseenp, &ex, &ey)) {
X	if (Debug) notify(us, "%s exploring,", unit_handle(us, unit));
X	order_moveto(unit, ex, ey);
X    } else {
X	if (Debug) notify(us, "%s just moving,", unit_handle(us, unit));
X	order_movedir(unit, random_dir(), random(12)+3);
X    }
X}
X
Xcould_bombard(utype, csize)
Xint utype, csize;
X{
X    return (utypes[utype].vscity[csize] == BOMBARD);
X}
X
X/* This is really only plausible for ww2 period file. */
X
Xgood_bomb_odds(utype, csize)
Xint utype, csize;
X{
X    return (utypes[utype].cityvs[csize] < 50 ||
X	    utypes[utype].powervscity[csize] > 3);
X}
X
X/* This should only be true if it's generally advisable for a unit to make */
X/* an attack */
X
Xgood_unit_odds(utype, etype)
Xint utype, etype;
X{
X    return ((utypes[utype].hittab[etype] * utypes[utype].power >=
X	     utypes[etype].hittab[utype] * utypes[etype].power) &&
X	    utypes[utype].hittab[etype] >= 30);
X}
X
Xadj_sea(x, y)
Xint x, y;
X{
X    int x1, y1;
X    
X    for (x1 = x-1; x1 <= x+1; ++x1)
X	for (y1 = y-1; y1 <= y+1; ++y1)
X	    if (terrain(wrap(x1), y1) == SEA) return TRUE;
X    return FALSE;
X}
X
Xadj_land(x, y)
Xint x, y;
X{
X    int x1, y1;
X    
X    for (x1 = x-1; x1 <= x+1; ++x1)
X	for (y1 = y-1; y1 <= y+1; ++y1)
X	    if (terrain(wrap(x1), y1) != SEA) return TRUE;
X    return FALSE;
X}
X
Xadj_transport(unit, exp, eyp)
XUnit *unit;
Xint *exp, *eyp;
X{
X    int d, ix, iy;
X    Unit *other;
X    
X    for (d = 0; d < 8; ++d) {
X	ix = unit->x + dirx[d];  iy = unit->y + diry[d];
X	other = unit_at(wrap(ix), iy);
X	if (other != NULL && other->side == unit->side &&
X	    could_carry(other, unit)) {
X	    *exp = wrap(ix);  *eyp = iy;
X	    return TRUE;
X	}
X    }
X    return FALSE;
X}
X
X/* look for anything unfriendly */
X
Xadj_enemy_city(side, x, y, exp, eyp)
Xint x, y, *exp, *eyp;
XSide *side;
X{
X    int d, ix, iy;
X    City *city;
X
X    for (d = 0; d < 8; ++d) {
X	ix = wrap(x + dirx[d]);  iy = y + diry[d];
X	if ((city = city_at(ix, iy)) != NULL && enemy_side(side, city->side)) {
X	    *exp = ix;  *eyp = iy;
X	    return TRUE;
X	}
X    }
X    return FALSE;
X}
X
X/* look for anything unfriendly */
X
Xadj_enemy_unit(side, x, y, exp, eyp)
Xint x, y, *exp, *eyp;
XSide *side;
X{
X    int d, ix, iy;
X    Unit *unit;
X
X    for (d = 0; d < 8; ++d) {
X	ix = wrap(x + dirx[d]);  iy = y + diry[d];
X	if ((unit = unit_at(ix, iy)) != NULL && enemy_side(side, unit->side)) {
X	    *exp = ix;  *eyp = iy;
X	    return TRUE;
X	}
X    }
X    return FALSE;
X}
X
X/* for a given location, see if enemy-held */
X
Xenemy_in(side, x, y)
Xint x, y;
XSide *side;
X{
X    char view;
X    
X    view = side_view(side, x, y);
X
X    return (view != UNSEEN &&
X	    view != EMPTY &&
X	    enemy_side(side, side_n(valign(view))));
X}
X
X/* this routine is keyed to the intelligence of the side */
X/* smart enemies ally with each other and go after the player */
X
Xenemy_side(s1, s2)
XSide *s1, *s2;
X{
X    if (s1 == s2) return FALSE;
X    return (s1 == NULL || s2 == NULL ||
X	    !(s1->host == NULL && s2->host == NULL));
X}
END_OF_mplay.c
if test 11237 -ne `wc -c <mplay.c`; then
    echo shar: \"mplay.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 4 \(of 7\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 6 7 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 7 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0