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

games@tekred.TEK.COM (06/29/88)

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



#! /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 5 (of 18)."
# Contents:  do.c lib/paccity.map unit.c
# Wrapped by billr@saab on Wed Jun 29 08:55:35 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f do.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"do.c\"
else
echo shar: Extracting \"do.c\" \(30185 characters\)
sed "s/^X//" >do.c <<'END_OF_do.c'
X/* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
X/* This program may be used, copied, modified, and redistributed freely */
X/* for noncommercial purposes, so long as this notice remains intact. */
X
X/* RCS $Header: do.c,v 1.1 88/06/21 12:30:06 shebs Exp $ */
X
X/* This file contains almost all command functions. */
X/* Help commands are in a separate file. */
X
X#include "config.h"
X#include "misc.h"
X#include "dir.h"
X#include "period.h"
X#include "side.h"
X#include "unit.h"
X#include "map.h"
X#include "global.h"
X
Xextern bool delaymove;
X
Xchar *grok_string();
X
Xbool reconfig;          /* true when an option needs screen reconfigured */
Xbool remap;             /* true when only main map needs redrawing */
Xbool reinfo;            /* true when only info display needs redrawing */
Xbool tmparea = TRUE;    /* true for area painting, false for bar painting */
X
X/* Things will be totally scrogged if two human players in build mode... */
X
Xint tmpterr = 0;	/* temporary terrain type for area operation */
Xint tmpdist = 0;        /* temporary argument for painting */
Xint tmpflag;		/* temporary int for area operation */
X
X/* Move in given direction a given distance - used for both single and */
X/* automatic multiple moves. */
X
Xdo_dir(side, dir, n)
XSide *side;
Xint dir, n;
X{
X    if (side->teach) {
X	cache_movedir(side, dir, n);
X    } else {
X	switch (side->mode) {
X	case MOVE:
X	    if (side->curunit != NULL) order_movedir(side->curunit, dir, n);
X	    break;
X	case SURVEY:
X	    move_survey(side,
X			wrap(side->curx + n*dirx[dir]),
X			side->cury + n*diry[dir]);
X	    break;
X	default:
X	    case_panic("mode", side->mode);
X	}
X    }
X}
X
X/* Wake *everything* (that's ours) within the given radius.  Two commands */
X/* actually; lowercase is transports only, uppercase is everybody. */
X
Xdo_wakeup(side, n)
XSide *side;
Xint n;
X{
X    if (side->teach) {
X	cache_awake(side, 1);
X    } else {
X	wakeup_area(side, n, TRUE);
X    }
X}
X
X/* Wake up only the main/current unit in a hex. */
X
Xdo_wakemain(side, n)
XSide *side;
Xint n;
X{
X    if (side->teach) {
X	cache_awake(side, 1);
X    } else {
X	wakeup_area(side, n, FALSE);
X    }
X}
X
X/* The area wakeup. */
X
Xwake_at(x, y)
Xint x, y;
X{
X    Unit *unit = unit_at(x, y);
X
X    if (unit != NULL && (unit->side == tmpside || Build))
X	wake_unit(unit, tmpflag);
X}
X
Xwakeup_area(side, n, occs)
XSide *side;
Xint n, occs;
X{
X    tmpside = side;
X    tmpflag = occs;
X    apply_to_area(side->curx, side->cury, n, wake_at);
X}
X
X/* Put unit to sleep for a while.  If we sleep it next to something that */
X/* might wake it up, then adjust flags so it won't wake up on next turn. */
X
Xdo_sentry(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    if (side->teach) {
X	cache_sentry(side, n);
X    } else {
X	order_sentry(unit, n);
X	if (n > 1 && adj_enemy(unit))
X	    unit->orders.flags &= ~(ENEMYWAKE|NEUTRALWAKE);
X    }
X}
X
X/* Don't move for remainder of turn, but be awake next turn.  This also */
X/* hooks into terrain painting, since the space bar is big and convenient. */
X
Xdo_sit(side, n)
XSide *side;
Xint n;
X{
X    if (side->mode == SURVEY && Build) {
X	paint_terrain(side);
X    } else if (side->curunit != NULL) {
X	do_sentry(side, side->curunit, 1);
X    } else {
X	cmd_error(side, "No unit to operate on here!");
X    }
X}
X
X/* Set unit to move to a given location.  */
X
Xx_moveto(side)
XSide *side;
X{
X    if (grok_position(side)) {
X	if (side->teach) {
X	    cache_moveto(side, side->reqposx, side->reqposy);
X	} else if (Build) {
X	    leave_hex(side->requnit);
X	    occupy_hex(side->requnit, side->reqposx, side->reqposy);
X	    make_current(side, side->requnit);
X	    all_see_hex(side->curx, side->cury);
X	} else {
X	    order_moveto(side->requnit, side->reqposx, side->reqposy);
X	}
X	restore_cur(side);
X    } else {
X	request_input(side, side->requnit, x_moveto);
X    }
X}
X
X/* The command proper. */
X
Xdo_moveto(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    ask_position(side, "Move to where?");
X    request_input(side, unit, x_moveto);
X    side->reqposx = side->curx;  side->reqposy = side->cury;
X}
X
X/* Auxiliary stuff used when searching for place to return to.  Note that */
X/* a good refueling spot will be woken up, so it won't get too far away */
X/* before unit has a chance to get there. */
X/* Won't find refueling places inside other units, sigh. */
X
Xrefuel_here(x, y)
Xint x, y;
X{
X    Unit *unit = unit_at(x, y);
X
X    if (unit != NULL && unit->side == tmpside && can_carry(unit, tmpunit)) {
X	wake_unit(unit, FALSE);
X	return TRUE;
X    }
X    return FALSE;
X}
X
X/* Search for a friendly refueler within range and set course for it.  */
X/* Warn player and refuse to move if nothing close enough. */
X
Xdo_return(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    int x, y, u = unit->type, r, range = max(world.width, world.height);
X
X    for_all_resource_types(r) {
X	if (utypes[u].tomove[r] > 0) {
X	    range = min(range, unit->supply[r] / utypes[u].tomove[r]);
X	}
X    }
X    tmpside = side;
X    tmpunit = unit;
X    if (search_area(unit->x, unit->y, range, refuel_here, &x, &y)) {
X	if (unit->x != x || unit->y != y) {
X	    order_moveto(unit, x, y);
X	    unit->orders.flags = SHORTESTPATH;
X	} else {
X	    cmd_error(side, "Already at a resupply point!");
X	}
X    } else {
X	cmd_error(side, "No resupply point in range!");
X    }
X}
X
X/* Set unit to attempt to follow a coast.  Needs a starting direction, */
X/* which can be computed from a position. */
X
Xx_coast(side)
XSide *side;
X{
X    int dir;
X
X    if (grok_position(side)) {
X	if (side->curx != side->reqposx || side->cury != side->reqposy) {
X	    dir = find_dir(side->reqposx - side->curx,
X			   side->reqposy - side->cury);
X	    if (side->teach) {
X		cache_edge(side, dir, side->reqvalue2);
X	    } else {
X		order_edge(side->requnit, dir, side->reqvalue2);
X	    }
X	    restore_cur(side);
X	} else {
X	    cmd_error(side, "No particular direction at own hex??");
X	}
X    } else {
X	request_input(side, side->requnit, x_coast);
X    }
X}
X
X/* The command proper just sets up the interaction. */
X
Xdo_coast(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    ask_position(side, "Move in which direction?");
X    request_input(side, unit, x_coast);
X    side->reqposx = side->curx;  side->reqposy = side->cury;
X    side->reqvalue2 = n;
X}
X
X/* Set orders to follow a leader unit. */
X
Xx_follow(side)
XSide *side;
X{
X    Unit *leader;
X
X    if (grok_position(side)) {
X	if ((leader = unit_at(side->reqposx, side->reqposy)) != NULL &&
X	    leader->side == side) {
X	    if (leader != side->requnit) {
X		if (side->teach) {
X		    cache_follow(side, leader, side->reqvalue2);
X		} else {
X		    order_follow(side->requnit, leader, side->reqvalue2);
X		}
X	    } else {
X		cmd_error(side, "Unit can't follow itself!");
X	    }
X	} else {
X	    cmd_error(side, "No unit to follow!");
X	}
X	restore_cur(side);
X    } else {
X	request_input(side, side->requnit, x_follow);
X    }
X}
X
X/* The command proper. */
X
Xdo_follow(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    ask_position(side, "Which unit to follow?");
X    request_input(side, unit, x_follow);
X    side->reqposx = side->curx;  side->reqposy = side->cury;
X    side->reqvalue2 = n;
X}
X
X/* Patrolling goes back and forth between two points.  First point is the */
X/* current position. */
X
Xx_patrol(side)
XSide *side;
X{
X    if (grok_position(side)) {
X	if (side->teach) {
X	    cache_patrol(side, side->sounit->x, side->sounit->y,
X			 side->reqposx, side->reqposy, side->reqvalue2);
X	} else {
X	    order_patrol(side->requnit, side->requnit->x, side->requnit->y,
X			 side->reqposx, side->reqposy, side->reqvalue2);
X	}
X	restore_cur(side);
X    } else {
X	request_input(side, side->requnit, x_patrol);
X    }
X}
X
X/* The command proper. */
X
Xdo_patrol(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    ask_position(side, "What other endpoint for patrol?");
X    request_input(side, unit, x_patrol);
X    side->reqposx = side->curx;  side->reqposy = side->cury;
X    side->reqvalue2 = n;
X}
X
X/* Delay a unit's move until a later time.  The set flag will be recognized */
X/* by the movement loops, when deciding which unit to move next. */
X
Xdo_delay(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    delaymove = TRUE;
X    notify(side, "Delaying moving %s.", unit_handle(side, unit));
X}
X
X/* Get rid of unit.  Some units cannot be disbanded, but if they can, the */
X/* resources go to a transport if one is there.  Disbanding a transport */
X/* also disbands all the occupants - oh well. */
X
Xdo_disband(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    int u = unit->type;
X
X    if (utypes[u].disband || Build) {
X	notify(side, "%s goes home.", unit_handle(side, unit));
X	if (unit->transport != NULL) recycle_unit(unit, unit->transport);
X	kill_unit(unit, DISBAND);
X	make_current(side, unit_at(side->curx, side->cury));
X    } else {
X	cmd_error(side, "You can't just get rid of the %s!", utypes[u].name);
X    }
X}
X
X/* Reclaim both the unit's supplies and anything used in its making, but */
X/* only let a maker of the unit reclaim its ingredients. */
X
Xrecycle_unit(unit, unit2)
XUnit *unit, *unit2;
X{
X    int u = unit->type, u2 = unit2->type, r, scrap;
X
X    for_all_resource_types(r) {
X	transfer_supply(unit, unit2, r, unit->supply[r]);
X	if (could_make(u2, u) > 0) {
X	    scrap = utypes[u].tomake[r];
X	    unit2->supply[r] += (scrap * period.efficiency) / 100;
X	}
X    }
X}
X
X/* Give a unit to another side (possibly to neutrals).  Units that won't */
X/* change their sides when captured won't change voluntarily either. */
X
Xdo_give_unit(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    int u = unit->type;
X
X    if (utypes[u].changeside || Build) {
X	unit_changes_side(unit, side_n(n), CAPTURE, PRISONER);
X	all_see_hex(unit->x, unit->y);
X    } else {
X	cmd_error(side, "You can't just give away the %s!", utypes[u].name);
X    }
X}
X
X/* Marking is for the purpose of rearranging units within a hex. */
X
Xdo_mark_unit(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    side->markunit = unit;
X    notify(side, "%s has been marked.", unit_handle(side, unit));
X}
X
X/* This is a clever (if I do say so myself) command to examine all occupants */
X/* and suboccupants, in a preorder fashion. */
X
Xdo_occupant(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    Unit *nextup;
X
X    switch (side->mode) {
X    case MOVE:
X	cmd_error(side, "Can only look at occupants when in survey mode!");
X	break;
X    case SURVEY:
X	if (unit->occupant != NULL) {
X	    make_current(side, unit->occupant);
X	} else if (unit->nexthere != NULL) {
X	    make_current(side, unit->nexthere);
X	} else {
X	    nextup = unit->transport;
X	    if (nextup != NULL) {
X		while (nextup->transport != NULL && nextup->nexthere == NULL) {
X		    nextup = nextup->transport;
X		}
X		if (nextup->nexthere != NULL)
X		    make_current(side, nextup->nexthere);
X		if (nextup->transport == NULL)
X		    make_current(side, nextup);
X	    }
X	}
X	break;
X    default:
X	case_panic(side->mode, "mode");
X	break;
X    }
X}
X
X/* This can actually do general rearrangement, but defaults to putting the */
X/* unit on the first available transport in the hex.  */
X/* What about trying to embark a unit on itself or on its previous transp? */
X
Xdo_embark(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    Unit *mainunit = unit_at(side->curx, side->cury);
X    Unit *transport = NULL;
X
X    if (mainunit != unit) {
X	if (side->markunit == NULL ||
X	    side->markunit->x != unit->x || side->markunit->y != unit->y) {
X	    for_all_occupants(mainunit, transport) {
X		if (can_carry(transport, unit)) break;
X	    }
X	} else {
X	    transport = side->markunit;
X	}
X	if (transport != NULL) {
X	    leave_hex(unit);
X	    occupy_unit(unit, transport);
X	} else {
X	    cmd_error(side, "No plausible transport!");
X	}
X    } else {
X	cmd_error(side, "Nothing for this unit to get into!");
X    }
X}
X
X/* Give supplies to a transport.  The argument tells how many to give. */
X
Xdo_give(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    bool something = FALSE;
X    int u = unit->type, m, r, gift, actual;
X    Unit *main = unit->transport;
X
X    if (main != NULL) {
X	sprintf(spbuf, "");
X	m = main->type;
X	for_all_resource_types(r) {
X	    gift = (n < 0 ? utypes[m].storage[r] - main->supply[r] : n);
X	    if (gift > 0) {
X		something = TRUE;
X		/* Be stingy if we're low */
X		if (2 * unit->supply[r] < utypes[u].storage[r])
X		    gift = max(1, gift/2);
X		actual = transfer_supply(unit, main, r, gift);
X		sprintf(tmpbuf, " %d %s", actual, rtypes[r].name);
X		strcat(spbuf, tmpbuf);
X	    }
X	}
X	if (something) {
X	    notify(side, "%s gave%s.", unit_handle(side, unit), spbuf);
X	} else {
X	    notify(side, "%s gave nothing.", unit_handle(side, unit));
X	}
X    } else {
X	cmd_error(side, "Can't transfer supplies here!");
X    }
X}
X
X/* Take supplies from transport.  Both the transport must have something */
X/* left. */
X
Xdo_take(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    bool something = FALSE;
X    int u = unit->type, m, r, need, actual;
X    Unit *main = unit->transport;
X
X    if (main != NULL) {
X	sprintf(spbuf, "");
X	m = main->type;
X	for_all_resource_types(r) {
X	    need = (n < 0 ? utypes[u].storage[r] - unit->supply[r] : n);
X	    if (need > 0) {
X		something = TRUE;
X		/* Be stingy if we're low */
X		if (2 * main->supply[r] < utypes[m].storage[r])
X		    need = max(1, need/2);
X		actual = transfer_supply(main, unit, r, need);
X		sprintf(tmpbuf, " %d %s", actual, rtypes[r].name);
X		strcat(spbuf, tmpbuf);
X	    }
X	}
X	if (something) {
X	    notify(side, "%s got%s.", unit_handle(side, unit), spbuf);
X	} else {
X	    notify(side, "%s needed nothing.", unit_handle(side, unit));
X	}
X    } else {
X	cmd_error(side, "Can't transfer supplies here!");
X    }
X}
X
X/* Take the current player out of the game while letting everybody else */
X/* continue on. */
X
Xx_resign(side)
XSide *side;
X{
X    if (grok_bool(side)) resign_game(side, side->reqoside);
X}
X
Xdo_resign(side, n)
XSide *side;
Xint n;
X{
X    ask_bool(side, "Do you really want to give up?", FALSE);
X    request_input(side, NULL, x_resign);
X    side->reqoside = side_n(n);
X}
X
X/* Unconditional resignation - usable by everybody. */
X
Xresign_game(side, side2)
XSide *side, *side2;
X{
X    notify_all("Those wimpy %s have given up!", plural_form(side->name));
X    if (side2 != NULL) {
X	notify_all("... and they gave all their stuff to the %s!",
X		   plural_form(side2->name));
X    }
X    side_loses(side, side2);
X}
X
X/* Leave quickly when the boss walks by.  One person can kill a multi-player */
X/* game, which isn't too great, but the alternatives are complicated. */
X/* The stats file will be left behind, to foment argument about who would */
X/* have won... This routine also includes a trapdoor for freezing/unfreezing */
X/* machine players when building - mode display will invert to confirm this. */
X
Xx_exit(side)
XSide *side;
X{
X    Unit *unit;
X
X    if (grok_bool(side)) {
X	close_displays();
X	printf("\nThe outcome remains undecided");
X	if (numhumans == 1 && side->humanp) {
X	    printf(", but you're probably the loser!\n\n", side->host);
X	} else {
X	    printf("...\n\n");
X	}
X	/* Need to kill off all units to finish up statistics */
X	for_all_units(unit) if (alive(unit)) kill_unit(unit, ENDOFWORLD);
X	print_statistics();
X	exit(0);
X    }
X    if (Build) {
X	Freeze = !Freeze;
X	show_timemode(side);
X    }
X}
X
Xdo_exit(side, n)
XSide *side;
Xint n;
X{
X    ask_bool(side, "Do you REALLY want to end the game for EVERYBODY?",
X	     FALSE);
X    request_input(side, NULL, x_exit);
X}
X
X/* Stuff game state into a file.  By default, it goes into the current */
X/* directory.  If building a scenario, will ask about each section, values */
X/* of globals, and dest file before actually writing anything. */
X/* No capability to write out period at present... */
X
Xx_save_1(side)
XSide *side;
X{
X    char *sects, *fname;
X    int sdetail = 1, udetail = 1;
X
X    if ((sects = grok_string(side)) != NULL) {
X	fname = "random.scn";
X	sprintf(spbuf, "------");
X	if (iindex('v', sects) >= 0) spbuf[0] = '+';
X	if (iindex('p', sects) >= 0) spbuf[1] = '+';
X	if (iindex('m', sects) >= 0) spbuf[2] = '+';
X	if (iindex('g', sects) >= 0) spbuf[3] = '+';
X	if (iindex('s', sects) >= 0) spbuf[4] = '+';
X	if (iindex('u', sects) >= 0) spbuf[5] = '+';
X	if (iindex('s', sects) >= 0) {
X	    sdetail = 1;
X	    if (isdigit(sects[iindex('s', sects)+1]))
X		sdetail = sects[iindex('s', sects)+1] - '0';
X	}
X	if (iindex('u', sects) >= 0) {
X	    udetail = 1;
X	    if (isdigit(sects[iindex('u', sects)+1]))
X		udetail = sects[iindex('u', sects)+1] - '0';
X	}
X	notify(side, "Mapfile with sections %s will be saved to \"%s\" ...",
X	       spbuf, fname);
X	if (write_scenario(fname, spbuf, sdetail, udetail)) {
X	    notify(side, "Done writing to \"%s\".", fname);
X	} else {
X	    cmd_error(side, "Can't open file \"%s\"!", fname);
X	}
X    } else {
X	request_input(side, NULL, x_save_1);
X    }
X}
X
X/* Make a header appropriate to a save file, write the file, and leave. */
X
Xx_save_2(side)
XSide *side;
X{
X    if (grok_bool(side)) {
X	notify_all("Game will be saved to \"%s\" ...", SAVEFILE);
X	if (write_savefile(SAVEFILE)) {
X	    close_displays();
X	    exit(0);
X	} else {
X	    cmd_error(side, "Can't open file \"%s\"!", SAVEFILE);
X	}
X    }
X}
X
X/* The command proper just sets up different handlers, depending on */
X/* whether we're building (and therefore saving a scenario/fragment), or */
X/* saving as much game state as possible, for resumption later. */
X
Xdo_save(side, n)
XSide *side;
Xint n;
X{
X    if (Build) {
X	ask_string(side, "Sections to write?", "ms1u1");
X	request_input(side, NULL, x_save_1);
X    } else {
X	ask_bool(side, "You really want to save?", FALSE);
X	request_input(side, NULL, x_save_2);
X    }
X}
X
X/* Redraw everything using the same code as when windows need a redraw. */
X
Xdo_redraw(side, n)
XSide *side;
Xint n;
X{
X    redraw(side);
X}
X
X/* Name or rename the current unit or a given side.  We make a copy of the */
X/* string after it's been successfully read, just in case. */
X
Xx_name(side)
XSide *side;
X{
X    char *name;
X    Side *side2;
X
X    if ((name = grok_string(side)) == NULL) {
X	request_input(side, side->requnit, x_name);
X    } else if (strlen(name) == 0) {
X	notify(side, "Name not changed.");
X    } else if (side->requnit != NULL) {
X	side->requnit->name = copy_string(name);
X    } else if (side->reqoside != NULL) {
X	side->reqoside->name = copy_string(name);
X	for_all_sides(side2) show_all_sides(side2);
X    } else {
X	cmd_error(side, "Nothing to name!");
X    }
X}
X
X/* The command proper decides between unit and side naming. */
X
Xdo_name(side, n)
XSide *side;
Xint n;
X{
X    if (side->curunit != NULL) {
X	ask_string(side, "New name for unit:", side->curunit->name);
X    } else {
X	ask_string(side, "New name for yourself:", side->name);
X	side->reqoside = side;
X    }
X    request_input(side, side->curunit, x_name);
X}
X
X/* Designate the current location as the center of action and sort all */
X/* of our own units relative to it. */
X
Xdo_center(side, n)
XSide *side;
Xint n;
X{
X    side->cx = side->curx;  side->cy = side->cury;
X    sort_units();
X    notify(side, "Units reordered.");
X}
X
X/* Hook command to set miscellaneous options.  Can't do from command line */
X/* because each display may want different behavior.  This routine can */
X/* change the display dramatically, but it should only redraw if a change */
X/* has actually been made. */
X
X/* Conversion to machine player is irreversible, so we confirm it first. */
X
Xx_options_2(side)
XSide *side;
X{
X    if (grok_bool(side)) {
X	side->humanp = !side->humanp;
X	numhumans--;
X	init_sighandlers();
X    }
X}
X
Xx_options(side)
XSide *side;
X{
X    int n = side->reqvalue2;
X    char opt;
X
X    if ((opt = grok_char(side)) != '\0') {
X	switch (opt) {
X	case '?':
X	    notify(side, "Change (D)isplay Mode, (G)raph, Win (H)eight,");
X	    notify(side, "(I)nverse Video, (M)onochrome, (N)otice Buffer");
X	    notify(side, "(R)obot, Win (W)idth");
X	    break;
X	case 'd':
X	    remap = TRUE;
X	    side->showmode = (side->showmode + 1) % 4;
X	    break;
X	case 'g':
X	    reinfo = TRUE;
X	    side->graphical = !side->graphical;
X	    break;
X	case 'h':
X	    if (n < 5 || n > world.height) {
X		cmd_error(side, "Bad height %d!", n);
X	    } else {
X		if (n != side->vh) reconfig = TRUE;
X		side->vh = n;
X	    }
X	    break;
X	case 'i':
X	    if (side->monochrome) {
X		reconfig = TRUE;
X		side->bonw = !side->bonw;
X	    } else {
X		cmd_error(side, "Inverse video is only for monochrome!");
X	    }
X	    break;
X	case 'm':
X	    reconfig = TRUE;
X	    side->monochrome = !side->monochrome;
X	    side->bonw = FALSE;
X	    break;
X	case 'n':
X	    if (n < 1 || n > MAXNOTES) {
X		cmd_error(side, "Bad number of notes %d!", n);
X	    } else {
X		if (n != side->nh) reconfig = TRUE;
X		side->nh = n;
X	    }
X	    break;
X	case 'r':	
X	    if (side->mode == MOVE) {
X		ask_bool(side,
X			 "Do you really want to become a machine?", FALSE);
X		request_input(side, NULL, x_options_2);
X		return;
X	    } else {
X		cmd_error(side, "Must be in move mode!");
X	    }
X	    break;
X	case 'w':
X	    if (n < 5 || n > world.width || n > BUFSIZE) {
X		cmd_error(side, "Bad width %d!", n);
X	    } else {
X		if (n != side->vw) reconfig = TRUE;
X		side->vw = n;
X	    }
X	    break;
X	default:
X	    cmd_error(side, "unrecognized option '%c'", opt);
X	    break;
X	}
X	if (remap) {
X	    undraw_box(side);
X	    show_map(side);
X	}
X	if (reinfo) show_info(side);
X	if (reconfig) reconfigure_display(side);
X    } else {
X	request_input(side, NULL, x_options);
X    }
X}
X
X/* The command proper. */
X
Xdo_options(side, n)
XSide *side;
Xint n;
X{
X    reinfo = remap = reconfig = FALSE;
X    ask_char(side, "Options:", "dghimnrw");
X    request_input(side, NULL, x_options);
X    side->reqvalue2 = n;
X}
X
X/* Set standing orders for a unit of a given type that enters a given city. */
X/* Space for standing orders is dynamically allocated the first time we */
X/* request some orders. */
X
Xx_standing_1(side)
XSide *side;
X{
X    int type;
X
X    if ((type = grok_unit_type(side)) >= 0) {
X	if (type != NOTHING) get_standing_order(side, type);
X    } else {
X	request_input(side, side->requnit, x_standing_1);
X    } 
X}
X
X/* The command proper starts the ball rolling by prompting for the type */
X/* of unit that will get standing orders.  Of course, if a unit is not of */
X/* a type that has occupants, standing orders are pretty useless.  Also, */
X/* if only one type of occupant is possible, then no need to ask. */
X
Xdo_standing(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    int u;
X
X    u = ask_unit_type(side, "Type of occupant to get standing orders",
X		      utypes[unit->type].capacity);
X    if (u < 0) {
X	show_standing_orders(side, unit);
X	request_input(side, unit, x_standing_1);
X	side->sounit = unit;
X    } else if (u == NOTHING) {
X	cmd_error(side, "This unit never has occupants to give orders to!");
X    } else {
X	show_standing_orders(side, unit);
X	get_standing_order(side, u);
X    }
X}
X
X/* A standing order is acquired by snarfing the next order and saving it */
X/* rather than applying it to some unit. */
X
Xget_standing_order(side, type)
XSide *side;
Xint type;
X{
X    if (side->requnit->standing == NULL) {
X	side->requnit->standing =
X	    (StandingOrder *) malloc(sizeof(StandingOrder));
X    }
X    side->teach = TRUE;
X    side->soutype = type;
X    side->tmporder = (Order *) malloc(sizeof(Order));
X    notify(side, "Next input order will become the standing order.");
X    show_timemode(side);
X    request_command(side);
X}
X
X/* Survey mode allows player to look around (and change things) by moving */
X/* cursor.  The same command toggles in and out, so need a case statement. */
X/* Players waiting their turn will be in survey mode, but can't get out. */
X
Xdo_survey_mode(side, n)
XSide *side;
Xint n;
X{
X    switch (side->mode) {
X    case MOVE:
X	survey_mode(side);
X	break;
X    case SURVEY:
X	if (side == curside) {
X	    move_mode(side);
X	} else {
X	    cmd_error(side, "Not your turn yet!");
X	}
X	break;
X    default:
X	case_panic("mode", side->mode);
X    }
X}
X
X/* Change what a unit is producing. */
X
Xdo_product(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    if (!global.setproduct) {
X	cmd_error(side, "No construction changes allowed in this game!");
X    } else {
X	if (!can_produce(unit)) {
X	    cmd_error(side, "This unit can't build anything!");
X	} else {
X	    if (!utypes[unit->type].maker) {
X		wake_unit(unit, FALSE);
X	    }
X	    request_new_product(unit);
X	}
X    }
X}
X
X/* Set a unit to not produce anything (yes, this really is useful). */
X
Xdo_idle(side, unit, n)
XSide *side;
XUnit *unit;
Xint n;
X{
X    if (!global.setproduct) {
X	cmd_error(side, "No production changes allowed in this scenario!");
X    } else {
X	set_product(unit, NOTHING);
X	unit->schedule = n;
X	show_info(side);
X    }
X}
X
X/* Send a short (1 line) message to another player.  Some messages are */
X/* recognized specially, causing various actions. */
X
Xx_message(side)
XSide *side;
X{
X    char *msg;
X    Side *side3;
X
X    if ((msg = grok_string(side)) != NULL) {
X	if (side->reqoside == NULL) {
X	    if (msg != NULL && strlen(msg) > 0) {
X		notify_all("The %s announce: %s",
X			   plural_form(side->name), msg);
X	    }
X	} else if (strcmp(msg, "briefing") == 0) {
X	    notify(side->reqoside, "Receiving a briefing from the %s...",
X		   plural_form(side->name));
X	    reveal_side(side, side->reqoside, 100);
X	    notify(side, "You just briefed the %s on your position.",
X		   plural_form(side->reqoside->name));
X	} else if (strcmp(msg, "alliance") == 0) {
X	    notify(side, "You propose a formal alliance with the %s.",
X		   plural_form(side->reqoside->name));
X	    side->attitude[side_number(side->reqoside)] = ALLY;
X	    if (side->reqoside->attitude[side_number(side)] >= ALLY) {
X		declare_alliance(side, side->reqoside);
X		for_all_sides(side3) redraw(side3);
X	    } else {
X		notify(side->reqoside, "The %s propose a formal alliance.",
X		       plural_form(side->name));
X	    }
X	} else if (strcmp(msg, "neutral") == 0) {
X	    notify(side, "You propose neutrality with the %s.",
X		   plural_form(side->reqoside->name));
X	    side->attitude[side_number(side->reqoside)] = NEUTRAL;
X	    if (side->reqoside->attitude[side_number(side)] == NEUTRAL) {
X		declare_neutrality(side, side->reqoside);
X		for_all_sides(side3) redraw(side3);
X	    } else {
X		notify(side->reqoside, "The %s propose neutrality.",
X		       plural_form(side->name));
X	    }
X	} else if (strcmp(msg, "war") == 0) {
X	    notify(side, "You declare war on the %s!",
X		   plural_form(side->reqoside->name));
X	    declare_war(side, side->reqoside);
X	    for_all_sides(side3) redraw(side3);
X	} else if (strlen(msg) > 0) {
X	    notify(side->reqoside, "The %s say to you: %s",
X		   plural_form(side->name), msg);
X	} else {
X	    notify(side, "You keep your mouth shut.");
X	}
X    } else {
X	request_input(side, NULL, x_message);
X    }
X}
X
X/* The command proper. */
X
Xdo_message(side, n)
XSide *side;
Xint n;
X{
X    char prompt[BUFSIZE];
X    Side *side2;
X
X    side2 = side_n(n);
X    side->reqoside = side2;
X    if (side != side2) {
X	if (side2) {
X	    sprintf(prompt, "Say to the %s: ", plural_form(side2->name));
X	} else {
X	    sprintf(prompt, "Broadcast: ");
X	}
X	ask_string(side, prompt, NULL);
X	request_input(side, NULL, x_message);
X    } else {
X	cmd_error(side, "You mumble to yourself.");
X    }
X}
X
X/* Add a new player to the game. */
X/* Should use arg to decide whether to convert machine player (or just */
X/* use if available?)  Also needs to decide if new player is human and */
X/* which host to open, then go through side's startup seq and open disp. */
X/* Issues of cached values (?) and war/alliance setup and uses of numsides. */
X
Xdo_add_player(side, n)
XSide *side;
Xint n;
X{
X    notify(side, "Sorry, can't add new players yet!");
X}
X
X/* Command to display the program version.  Looks wired in, but of course */
X/* this is not something that we want to be easily changeable! */
X/* This will also show data about other sides. */
X
Xdo_version(side, n)
XSide *side;
Xint n;
X{
X   notify(side, " ");
X   notify(side,
X	  "XCONQ version %s", version);
X   notify(side,
X	  "(c) Copyright 1987, 1988  Stanley T. Shebs, University of Utah");
X   notify(side, " ");
X   if (Debug || Build) reveal_side(NULL, side, 100);
X}
X
X/* Create any unit anywhere.  It gets the usual initial supply, and its */
X/* current side is also its true side (i.e. it will never revolt). */
X
Xx_unit(side)
XSide *side;
X{
X    int u;
X    Unit *unit;
X
X    if ((u = grok_unit_type(side)) >= 0) {
X	if (u != NOTHING) {
X	    unit = create_unit(u, NULL);
X	    occupy_hex(unit, side->curx, side->cury);
X	    init_supply(unit);
X	    unit_changes_side(unit, side->reqoside, -1, -1);
X	    unit->trueside = unit->side;
X	    make_current(side, unit);
X	    all_see_hex(side->curx, side->cury);
X	}
X    } else {
X	ask_unit_type(side, "Type of unit to create?", NULL);
X	request_input(side, NULL, x_unit);
X    }
X}
X
X/* The command function proper, which only works in Build mode. */
X
Xdo_unit(side, n)
XSide *side;
Xint n;
X{
X    if (Build) {
X	if (unit_at(side->curx, side->cury) == NULL) {
X	    ask_unit_type(side, "Type of unit to create?", NULL);
X	    side->reqoside = side_n(n);
X	    request_input(side, NULL, x_unit);
X	} else {
X	    cmd_error(side, "Unit already here!");
X	}
X    } else {
X	cmd_error(side, "Not building a mapfile!");
X    }
X}
X
X/* Terrain editing alters a hexagonal area of given radius.  If only one */
X/* hex changed (the default), just update that alone; otherwise, go ahead */
X/* and redraw everything. */
X
X/* The command itself just sets up what will be drawn. */
X
Xdo_terrain(side, terr, n)
XSide *side;
Xint terr, n;
X{
X    tmpterr = terr;
X    tmpdist = min(abs(n), world.width);
X    tmparea = (n >= 0);
X    notify(side, "Will now paint %d hex %s of %s.",
X	   tmpdist, (tmparea ? "radius areas" : "bars"), ttypes[tmpterr].name);
X}
X
X/* Function to change just one hex and to echo that change. */
X/* Don't need to make it show instantly, can wait. */
X
Xset_one_hex(x, y)
Xint x, y;
X{
X    set_terrain_at(x, y, tmpterr);
X    see_exact(tmpside, x, y);
X    draw_hex(tmpside, x, y, FALSE);
X}
X
X/* Painting operation is activated by the "sit" command. */
X
Xpaint_terrain(side)
XSide *side;
X{
X    int i;
X
X    tmpside = side;
X    if (tmparea) {
X	apply_to_area(side->curx, side->cury, tmpdist, set_one_hex);
X    } else {
X	for (i = 0; i < tmpdist; ++i)
X	    set_one_hex(wrap(side->curx + i), side->cury);
X    }
X}
X
X/* Generic command error routine - beeps display etc. */
X
X/*VARARGS*/
Xcmd_error(side, control, a1, a2, a3, a4, a5, a6)
XSide *side;
Xchar *control, *a1, *a2, *a3, *a4, *a5, *a6;
X{
X    notify(side, control, a1, a2, a3, a4, a5, a6);
X    if (active_display(side)) beep(side);
X}
END_OF_do.c
if test 30185 -ne `wc -c <do.c`; then
    echo shar: \"do.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f lib/paccity.map -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"lib/paccity.map\"
else
echo shar: Extracting \"lib/paccity.map\" \(1482 characters\)
sed "s/^X//" >lib/paccity.map <<'END_OF_lib/paccity.map'
XXconq 1 -----+
Xpacific.map
XUnits 72 1 0
X* Gauhati 30,132 -1
X* Naha 218,131 -1
X* Imphal 224,129 -1
X@ Kunming 241,129 -1
X* Dacca 217,127 -1
X* Gejin 242,127 -1
X* Wuzhou 258,127 -1
X* Nanning 253,126 -1
X@ Calcutta 214,124 -1
X* Chittagong 223,123 -1
X* Mandalay 231,123 -1
X* Zhanjiang 259,123 -1
X* Ha-noi 249,122 -1
X* Hai-phong 251,122 -1
X* Haikou 1,119 -1
X* Viangchan 246,116 -1
X* Pegu 236,115 -1
X* Bassein 234,113 -1
X@ Rangoon 236,113 -1
X* Hue 259,112 -1
X* Tarlac 25,112 -1
X* Da-nang 2,110 -1
X@ Manila 26,110 -1
X* Bangkok 248,107 -1
X* Batangas 29,107 -1
X* Legazpi 34,106 -1
X* Cam-ranh 6,104 -1
X* Sai-gon 2,102 -1
X* Iliolo 34,101 -1
X@ Cebu 37,100 -1
X* Davao 42,97 -1
X* George*Town 43,94 -1
X* Ipoh 256,90 -1
X* Medan 259,89 -1
X* Kuala*Lumpur 253,86 -1
X* Singapore 1,86 -1
X@ Pontianak 6,82 -1
X* Samarinda 19,79 -1
X* Padang 3,77 -1
X* Balikpapan 9,76 -1
X* Jambi 85,74 -1
X* Jayapura 13,73 -1
X* Palembang 33,73 -1
X* Banjarmasin 60,73 -1
X* Ambon 109,71 -1
X* Rabaul 44,70 -1
X* Ujung*Pendang 16,69 -1
X@ Telukbetung 20,67 -1
X@ Jakarta 22,66 -1
X* Bandung 28,66 -1
X* Semarang 100,66 -1
X@ Lae 33,65 -1
X* Surabaya 28,64 -1
X* Yogyakarta 34,63 -1
X* Malang 41,62 -1
X* Port 104,61 -1
X* Moresby 58,60 -1
X* Darwin 74,54 -1
X* Cairns 107,46 -1
X* Townsville 113,41 -1
X* Suva 177,39 -1
X* Noumea 155,35 -1
X* Rockhampton 124,33 -1
X* Brisbane 133,24 -1
X* Perth 65,15 -1
X@ Sydney 135,12 -1
X* Adelaide 113,10 -1
X* Whangerei 183,9 -1
X* Auckland 186,6 -1
X@ Melbourne 127,4 -1
X* Tauranga 190,4 -1
X* New*Plymouth 188,1 -1
END_OF_lib/paccity.map
if test 1482 -ne `wc -c <lib/paccity.map`; then
    echo shar: \"lib/paccity.map\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f unit.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"unit.c\"
else
echo shar: Extracting \"unit.c\" \(19503 characters\)
sed "s/^X//" >unit.c <<'END_OF_unit.c'
X/* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
X/* This program may be used, copied, modified, and redistributed freely */
X/* for noncommercial purposes, so long as this notice remains intact. */
X
X/* RCS $Header: unit.c,v 1.1 88/06/21 12:30:44 shebs Exp $ */
X
X/* This file contains all code relating specifically to units. */
X
X/* Since units appear and disappear with distressing regularity (so it seems */
X/* to the player trying to keep up with all of them!), we have a little */
X/* storage manager for them.  Since this sort of thing is tricky, there is a */
X/* two-level procedure for getting rid of units.  First the hit points are */
X/* reduced to zero, at which point the unit is considered "dead".  At the */
X/* end of a turn, we actually GC the dead ones.  At this point their type */
X/* slot becomes NOTHING, and they are available for allocation. */
X
X#include "config.h"
X#include "misc.h"
X#include "period.h"
X#include "side.h"
X#include "unit.h"
X#include "map.h"
X#include "global.h"
X
Xchar *ordinal();
X
XUnit *units;           /* array of all units */
XUnit *unitlist;        /* pointer to head of list of units */
XUnit *tmpunit;         /* global temporary used in several places */
X
Xchar unitbuf[BUFSIZE];
X
Xint numunits;          /* total number of units in existence */
Xint maxunits;          /* current size of array of units */
Xint nextid;            /* next number to be used for ids */
Xint occdeath[MAXUTYPES];  /* buffer for remembering occupant death */
X
X/* Init all the unit entries so we can find them when allocating. */
X
Xinit_units()
X{
X    int i;
X
X    maxunits = INITMAXUNITS;
X    units = (Unit *) malloc(maxunits * sizeof(Unit));
X    for (i = 0; i < maxunits; ++i) units[i].type = NOTHING;
X    unitlist = NULL;
X    numunits = 0;
X    nextid = 0;
X}
X
X/* Create a new unit of given type, with given name.  Default all the other */
X/* slots - routines that need to will fill them in properly.  This routine */
X/* will return a valid unit or NULL.  Note that unit morale starts out at */
X/* max, signifying hopeful but inexperienced recruits marching off to war. */
X
XUnit *
Xcreate_unit(type, name)
Xint type;
Xchar *name;
X{
X    int i, r;
X    Unit *newunit;
X
X    for (i = 0; i < maxunits; ++i) {
X	if (units[i].type == NOTHING) break;
X    }
X    if (i == maxunits) {
X	if (grow_unit_array()) {
X	    return create_unit(type, name);
X	} else {
X	    return NULL;
X	}
X    }
X    newunit = &units[i];
X    newunit->type = type;
X    if (name == NULL || strlen(name) == 0) {
X	newunit->name = NULL;
X    } else {
X	newunit->name = copy_string(name);
X    }
X    newunit->x = newunit->y = -1;
X    newunit->number = 0;
X    newunit->side = NULL;
X    newunit->id = nextid++;
X    newunit->trueside = NULL;
X    newunit->hp = utypes[type].hp;
X    newunit->quality = 0;
X    newunit->morale = utypes[type].maxmorale;
X    newunit->fatigue = 0;
X    newunit->product = NOTHING;
X    newunit->schedule = 0;
X    newunit->built = 0;
X    for_all_resource_types(r) newunit->supply[r] = 0;
X    newunit->transport = NULL;
X    newunit->group = 0;
X    newunit->goal = 0;
X    newunit->awake = FALSE;   /* i.e., is usually not *temporarily* awake */
X    newunit->standing = NULL;
X    newunit->occupant = NULL;
X    newunit->nexthere = NULL;
X    link_in_unit(newunit);
X    wake_unit(newunit, FALSE);
X    ++numunits;
X    return newunit;
X}
X
X/* Add unit to front of list.  It will be sorted into place shortly. */
X
Xlink_in_unit(unit)
XUnit *unit;
X{
X    unit->next = unitlist;
X    unitlist = unit;
X}
X
X/* Attempt to the number of units that can be played.  We do the obvious - */
X/* malloc a new array and copy everything over.  First copy byte-by-byte, */
X/* then adjust non-NULL pointers to other units.  Must also take care of */
X/* any globals or other variables.  (such as in side structs) */
X
X/* At the moment, this seems to cause weird changes in unit behavior which */
X/* I don't understand.  One missing thing here is to change all the ptrs */
X/* on the world map itself... Another solution would be to do in between */
X/* turns, perhaps after flushing dead units. */
X
Xgrow_unit_array()
X{
X#ifdef GROWABLE
X    char *base, *newbase;
X    int newmax, i;
X    Unit *newunits;
X
X    newmax = maxunits + maxunits / 2;
X    if (Debug) printf("Extending unit array to hold %d units.\n", newmax);
X    newunits = (Unit *) malloc(newmax * sizeof(Unit));
X    base = (char *) units;
X    newbase = (char *) newunits;
X    for (i = 0; i < maxunits * sizeof(Unit); ++i) newbase[i] = base[i];
X    for (i = 0; i < maxunits; ++i) {
X	if (newunits[i].next) 
X	    newunits[i].next =
X		(Unit *) ((char *) units[i].next + (newbase - base));
X	if (newunits[i].nexthere) 
X	    newunits[i].nexthere =
X		(Unit *) ((char *) units[i].nexthere + (newbase - base));
X	if (newunits[i].transport) 
X	    newunits[i].transport =
X		(Unit *) ((char *) units[i].transport + (newbase - base));
X	if (newunits[i].occupant) 
X	    newunits[i].occupant =
X		(Unit *) ((char *) units[i].occupant + (newbase - base));
X    }
X    /* Need to patch the map as well, maybe other things? */
X    for (i = maxunits; i < newmax; ++i) newunits[i].type = NOTHING;
X    free(units);
X    maxunits = newmax;
X    units = newunits;
X    if (unitlist) unitlist = (Unit *) ((char *) unitlist + (newbase - base));
X    notify_all("The unit array has been grown.");
X    return TRUE;
X#else
X    notify_all("Can't make any more units!");
X    return FALSE;
X#endif
X}
X
X/* Find a good initial unit for the given side.  Only requirement these */
X/* days is that it be of the type specified in the period file, and not */
X/* already used by somebody.  If enough failed tries to find one, */
X/* something may be wrong, but not necessarily. */
X
XUnit *
Xrandom_start_unit()
X{
X    int tries = numunits;
X    Unit *unit;
X
X    while (tries-- > 0) {
X	unit = &units[random(numunits)];
X	if (neutral(unit) && unit->type == period.firstutype) return unit;
X    }
X    for_all_units(unit) {
X	if (neutral(unit) && unit->type == period.firstutype) return unit;
X    }
X    return NULL;
X}
X
X/* A unit occupies a hex either by entering a unit on that hex or by having */
X/* the occupant pointer filled in.  If something goes wrong, return false. */
X/* This is heavily used. */
X
Xoccupy_hex(unit, x, y)
XUnit *unit;
Xint x, y;
X{
X    register int u = unit->type, o;
X    register Unit *other = unit_at(x, y);
X
X    if (other) {
X	o = other->type;
X	if (could_carry(o, u)) {
X	    occupy_unit(unit, other);
X	} else if (could_carry(u, o)) {
X	    leave_hex(other);
X	    occupy_hex(unit, x, y);
X	    occupy_hex(other, x, y);
X	} else {
X	    return FALSE;
X	}
X    } else {
X	set_unit_at(x, y, unit);
X	occupy_hex_aux(unit, x, y);
X	all_see_occupy(unit, x, y);
X	all_see_hex(x, y);
X    }
X    return TRUE;
X}
X
X/* Recursive helper to update everybody's position.  This should be one of */
X/* two routine that modify unit positions (leaving is the other). */
X/* *Every* occupant will increment viewing coverage - strains realism, */
X/* but prevents strange bugs. */
X
Xoccupy_hex_aux(unit, x, y)
XUnit *unit;
Xint x, y;
X{
X    register Unit *occ;
X
X    unit->x = x;  unit->y = y;
X    cover_area(unit, x, y, 1);
X    for_all_occupants(unit, occ) occupy_hex_aux(occ, x, y);
X}
X
X/* Decide whether transport has the capability to house the given unit. */
X/* Check both basic capacity and relative volumes. */
X
Xcan_carry(transport, unit)
XUnit *transport, *unit;
X{
X    int u = unit->type, u2 = transport->type, total = 0, volume = 0;
X    int hold = utypes[transport->type].holdvolume;
X    Unit *occ;
X    
X    for_all_occupants(transport, occ) {
X	if (occ->type == u) total++;
X	volume += utypes[occ->type].volume;
X    }
X    if (cripple(transport)) {
X	hold = (hold * transport->hp) / (utypes[u2].crippled + 1);
X    }
X    return ((total + 1 <= utypes[u2].capacity[u]) &&
X	    (volume + utypes[u].volume <= hold));
X}
X
X/* Units become passengers by linking into front of transport's passenger */
X/* list.  They only wake up if the transport is a moving type, but always */
X/* pick up standing orders if any defined.  A passenger will get sorted to */
X/* move after transport, at end of turn. */
X
Xoccupy_unit(unit, transport)
XUnit *unit, *transport;
X{
X    Order *neworders;
X
X    unit->nexthere = transport->occupant;
X    transport->occupant = unit;
X    unit->transport = transport;
X    if (mobile(transport->type)) wake_unit(unit, FALSE);
X    if (transport->standing != NULL) {
X	neworders = (transport->standing->orders)[unit->type];
X	if (neworders->type != NONE) {
X	    if (Debug) printf("%s getting orders %s",
X			      unit_handle(NULL, unit), order_desig(neworders));
X	    copy_orders(&(unit->orders), neworders);
X	}
X    }
X    occupy_hex_aux(unit, transport->x, transport->y);
X    all_see_occupy(unit, transport->x, transport->y);
X}
X
X/* Unit departs from a hex by zeroing out pointer if in hex or by being */
X/* removed from the list of transport occupants. */
X/* Dead units (hp = 0) may be run through here, so don't error out. */
X
Xleave_hex(unit)
XUnit *unit;
X{
X    register int ux = unit->x, uy = unit->y;
X
X    if (ux < 0 || uy < 0) {
X	/* Sometimes called twice */
X    } else if (unit->transport != NULL) {
X	leave_unit(unit, unit->transport);
X	leave_hex_aux(unit);
X	all_see_leave(unit, ux, uy);
X    } else {
X	set_unit_at(ux, uy, NULL);
X	see_exact(unit->side, ux, uy);
X	see_hex(unit->side, ux, uy);
X	leave_hex_aux(unit);
X	all_see_leave(unit, ux, uy);
X	all_see_hex(ux, uy);
X    }
X}
X
X/* Trash old coordinates (recursively) just in case.  Catches many bugs... */
X
Xleave_hex_aux(unit)
XUnit *unit;
X{
X    register Unit *occ;
X
X    cover_area(unit, unit->x, unit->y, -1);
X    unit->x = -1;  unit->y = -1;
X    for_all_occupants(unit, occ) leave_hex_aux(occ);
X}
X
X/* Disembarking unlinks from the list of passengers. */
X
Xleave_unit(unit, transport)
XUnit *unit, *transport;
X{
X    Unit *occ;
X
X    if (unit == transport->occupant) {
X	transport->occupant = unit->nexthere;
X    } else {
X	for_all_occupants(transport, occ) {
X	    if (unit == occ->nexthere) {
X		occ->nexthere = occ->nexthere->nexthere;
X		break;
X	    }
X	}
X    }
X    unit->transport = NULL;
X}
X
X/* Handle the general situation of a unit changing allegiance from one side */
X/* to another.  This is a common internal routine, so no messages here. */
X
Xunit_changes_side(unit, newside, reason1, reason2)
XUnit *unit;
XSide *newside;
Xint reason1, reason2;
X{
X    Side *oldside = unit->side;
X    Unit *occ;
X
X    if (oldside != NULL) {
X	oldside->units[unit->type]--;
X	if (producing(unit)) oldside->building[unit->product]--;
X	if (reason2 >= 0) oldside->balance[unit->type][reason2]++;
X	update_state(oldside, unit->type);
X    }
X    if (newside == NULL && !utypes[unit->type].isneutral) {
X	kill_unit(unit, -1);
X    } else {
X	if (newside != NULL) {
X	    newside->units[unit->type]++;
X	    if (producing(unit)) newside->building[unit->product]++;
X	    if (reason1 >= 0) newside->balance[unit->type][reason1]++;
X	    update_state(newside, unit->type);
X	}
X	for_all_occupants(unit, occ) {
X	    unit_changes_side(occ, newside, reason1, reason2);
X	}
X    }
X    if (alive(unit)) {
X	cover_area(unit, unit->x, unit->y, -1);
X	assign_unit_to_side(unit, newside);
X	cover_area(unit, unit->x, unit->y, 1);
X    }
X}
X
X/* Change the product of a unit.  Displays will need appropriate mods. */
X
Xset_product(unit, type)
XUnit *unit;
Xint type;
X{
X    if (!neutral(unit) && global.setproduct && type != unit->product) {
X	if (unit->product != NOTHING) {
X	    unit->side->building[unit->product]--;
X	    update_state(unit->side, unit->product);
X	}
X	if (type != NOTHING) {
X	    unit->side->building[type]++;
X	    update_state(unit->side, type);
X	}
X	unit->product = type;
X	unit->built = 0;
X	unit->movesleft--;
X    }
X}
X
X/* Set product completion time.  Startup development cost incurred only */
X/* if directed. */
X/* Technology development cost only incurred for very first unit that the */
X/* side constructs.  Both tech and startup are percent addons. */
X
Xset_schedule(unit)
XUnit *unit;
X{
X    int schedule, prod = unit->product;
X
X    if (producing(unit)) {
X	schedule = utypes[unit->type].make[prod];
X	if (unit->built == 0)
X	    schedule += ((schedule * utypes[prod].startup) / 100);
X	if (unit->side->counts[prod] <= 1)
X	    schedule += ((schedule * utypes[prod].research) / 100);
X	unit->schedule = schedule;
X    }
X}
X
X/* Remove a unit from play.  This is different from making it available for */
X/* reallocation - only the unit flusher can do that.  We remove all the */
X/* passengers too, recursively.  Sometimes units are "killed twice", so */
X/* be sure not to run all this twice.  Also count up occupant deaths, being */
X/* sure not to count the unit itself as an occupant. */
X
Xkill_unit(unit, reason)
XUnit *unit;
Xint reason;
X{
X    int u = unit->type, u2;
X
X    if (alive(unit)) {
X	for_all_unit_types(u2) occdeath[u2] = 0;
X	leave_hex(unit);
X	kill_unit_aux(unit, reason);
X	occdeath[u]--;
X    }
X}
X
X/* Trash it now - occupant doesn't need to leave_hex.  Also record a reason */
X/* for statistics, and update the apropriate display.  The unit here should */
X/* be known to be alive. */
X
Xkill_unit_aux(unit, reason)
XUnit *unit;
Xint reason;
X{
X    int u = unit->type;
X    Unit *occ;
X    
X    unit->hp = 0;
X    occdeath[u]++;
X    if (unit->side != NULL && reason >= 0) {
X	unit->side->balance[u][reason]++;
X	unit->side->units[u]--;
X	if (producing(unit)) unit->side->building[unit->product]--;
X	update_state(unit->side, u);
X    }
X    for_all_occupants(unit, occ) if (alive(occ)) kill_unit_aux(occ, reason);
X}
X
X/* Get rid of all dead units at once.  It's important to get rid of all */
X/* of the dead units, so they don't come back and haunt us. */
X/* (This routine is basically a garbage collector, and should not be called */
X/* during a unit list traversal.) The process starts by finding the first */
X/* live unit, making it the head, then linking around all in the middle. */
X
Xflush_dead_units()
X{
X    Unit *unit, *unitprev;
X
X    while (unitlist != NULL && !alive(unitlist)) {
X	unit = unitlist->next;
X	flush_one_unit(unitlist);
X	unitlist = unit;
X    }
X    for_all_units(unitprev) {
X	unit = unitprev->next;
X	if (unit != NULL && !alive(unit)) {
X	    unitprev->next = unit->next;
X	    flush_one_unit(unit);
X	}
X    }
X}
X
X/* Keep it clean - hit all links to other places.  Some might not be */
X/* strictly necessary, but this is not an area to take chances with. */
X
Xflush_one_unit(unit)
XUnit *unit;
X{
X    unit->type = NOTHING;
X    unit->occupant = NULL;
X    unit->transport = NULL;
X    unit->nexthere = NULL;
X    unit->next = NULL;
X    --numunits;
X}
X
X/* Do multiple passes of bubble sort.  This is intended to improve locality */
X/* among unit positions and reduce the amount of scrolling around. */
X/* Data is generally coherent, so bubble sort not too bad if we allow */
X/* early termination when everything in order. */
X/* If slowness objectionable, replace with something clever. */
X
Xsort_units()
X{
X    bool flips = TRUE;
X    int passes = 0;
X    register Unit *prevunit, *unit, *nextunit;
X
X    while (flips && passes < numunits / 10) {
X	flips = FALSE;
X	prevunit = NULL;
X	unit = unitlist;
X	while (unit != NULL) {
X	    if (unit->next != NULL && !in_order(unit, unit->next)) {
X		flips = TRUE;
X		nextunit = unit->next;
X		if (prevunit != NULL) prevunit->next = nextunit;
X		unit->next = nextunit->next;
X		nextunit->next = unit;
X		prevunit = nextunit;
X		if (unit == unitlist) unitlist = nextunit;
X	    } else {
X		prevunit = unit;
X		unit = unit->next;
X	    }
X	}
X	passes++;
X    }
X    if (Debug) printf("Sorting passes = %d\n", passes);
X}
X
X/* This can be pretty lax, with the exception that passengers *must* come */
X/* after their transports, or game saving will screw up. */
X
Xin_order(unit1, unit2)
XUnit *unit1, *unit2;
X{
X    int d1, d2;
X
X    if (side_number(unit1->side) < side_number(unit2->side)) return TRUE;
X    if (side_number(unit1->side) > side_number(unit2->side)) return FALSE;
X    if (!neutral(unit1)) {
X	d1 = distance(unit1->side->cx, unit1->side->cy, unit1->x, unit1->y);
X	d2 = distance(unit2->side->cx, unit2->side->cy, unit2->x, unit2->y);
X	if (d1 < d2) return TRUE;
X	if (d1 > d2) return FALSE;
X    }
X    if (unit1->y > unit2->y) return TRUE;
X    if (unit1->y < unit2->y) return FALSE;
X    if (unit1->x < unit2->x) return TRUE;
X    if (unit1->x > unit2->x) return FALSE;
X    if (unit1->transport == NULL && unit2->transport != NULL) return TRUE;
X    if (unit1->transport != NULL && unit2->transport == NULL) return FALSE;
X    if (unit1 == unit2->transport) return TRUE;
X    if (unit1->transport == unit2) return FALSE;
X    return TRUE;
X}
X
X/* A unit runs low on supplies at the halfway point, but only worries about */
X/* the essential items.  Formula is the same no matter how/if occupants eat */
X/* transports' supplies. */
X
Xlow_supplies(unit)
XUnit *unit;
X{
X    int u = unit->type, r;
X
X    for_all_resource_types(r) {
X	if ((utypes[u].consume[r] > 0) || (utypes[u].tomove[r] > 0)) {
X	    if (2 * unit->supply[r] <= utypes[u].storage[r]) return TRUE;
X	}
X    }
X    return FALSE;
X}
X
X/* Display the standing orders of the given unit. */
X
Xshow_standing_orders(side, unit)
XSide *side;
XUnit *unit;
X{
X    int u;
X
X    if (unit->standing != NULL) {
X	sprintf(spbuf, "Orders:  ");
X	for_all_unit_types(u) {
X	    if (unit->standing->orders[u]->type != NONE) {
X		sprintf(tmpbuf, "%s to %s;  ",
X			utypes[u].name,
X			order_desig(unit->standing->orders[u]));
X		strcat(spbuf, tmpbuf);
X	    }
X	}
X	notify(side, "%s", spbuf);
X    } else {
X	notify(side, "No standing orders defined yet.");
X    }
X}
X
X/* Build a short phrase describing a given unit to a given side. */
X/* First we supply identification of side, then of unit itself. */
X
Xchar *
Xunit_handle(side, unit)
XSide *side;
XUnit *unit;
X{
X    char *utypename = utypes[unit->type].name;
X
X    if (unit == NULL || !alive(unit)) return "???";
X    if (unit->side == NULL) {
X	sprintf(unitbuf, "the neutral ");
X    } else if (unit->side == side) {
X	sprintf(unitbuf, "your ");
X    } else {
X	sprintf(unitbuf, "the %s ", unit->side->name);
X    }
X    if (unit->name != NULL) {
X	sprintf(tmpbuf, "%s %s", utypename, unit->name);
X    } else if (unit->number > 0) {
X	sprintf(tmpbuf, "%s %s", ordinal(unit->number), utypename);
X    } else {
X	sprintf(tmpbuf, "%s", utypename);
X    }
X    strcat(unitbuf, tmpbuf);
X    return unitbuf;
X}
X
X/* Shorter unit description omits side name, but uses same buffer. */
X
Xchar *
Xshort_unit_handle(unit)
XUnit *unit;
X{
X    if (unit->name == NULL) {
X	sprintf(unitbuf, "%s %s",
X		ordinal(unit->number), utypes[unit->type].name);
X    } else {
X	sprintf(unitbuf, "%s", unit->name);
X    }
X    return unitbuf;
X}
X
X/* General-purpose routine to take an array of anonymous unit types and */
X/* summarize what's in it, using numbers and unit chars. */
X
Xchar *
Xsummarize_units(buf, ucnts)
Xchar *buf;
Xint *ucnts;
X{
X    char tmp[BUFSIZE];
X    int u;
X
X    sprintf(buf, "");
X    for_all_unit_types(u) {
X	if (ucnts[u] > 0) {
X	    sprintf(tmp, " %d %c", ucnts[u], utypes[u].uchar);
X	    strcat(buf, tmp);
X	}
X    }
X    return buf;
X}
X
X/* Search for a unit with the given id number. */
X
XUnit *
Xfind_unit(n)
Xint n;
X{
X    Unit *unit;
X
X    for_all_units(unit)	if (unit->id == n) return unit;
X    return NULL;
X}
X
X/* Given a unit character, find the type. */
X
Xfind_unit_char(ch)
Xchar ch;
X{
X    int u;
X
X    for_all_unit_types(u) if (utypes[u].uchar == ch) return u;
X    return NOTHING;
X}
X
X/* Generate a name for a unit, using best acrynomese.  This is invoked when */
X/* a new named unit has been created. */
X
Xchar *
Xmake_unit_name(unit)
XUnit *unit;
X{
X    sprintf(spbuf, "%c%c-%c-%02d",
X	    uppercase(unit->side->name[0]), uppercase(unit->side->name[1]),
X	    utypes[unit->type].uchar, unit->number);
X    return copy_string(spbuf);
X}
END_OF_unit.c
if test 19503 -ne `wc -c <unit.c`; then
    echo shar: \"unit.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 5 \(of 18\).
cp /dev/null ark5isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 18 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0