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