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