DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T m

⟦5c540a4a6⟧ TextFile

    Length: 22132 (0x5674)
    Types: TextFile
    Names: »move.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/X/Xconq/move.c« 

TextFile

/* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
/* This program may be used, copied, modified, and redistributed freely */
/* for noncommercial purposes, so long as this notice remains intact. */

/* RCS $Header: move.c,v 1.1 88/06/21 12:30:23 shebs Exp $ */

/* Nothing happens in xconq without movement, and it involves some rather */
/* complicated control structures in order to get the details right. */
/* Units only actually move by following preset orders; when they appear */
/* to be moving under manual control, they are just following orders that */
/* only last for one move before new orders are requested from the player. */

/* Order of movement is sequential by sides, but non-moving sides are in */
/* survey mode and can do things. */

#include "config.h"
#include "misc.h"
#include "dir.h"
#include "period.h"
#include "side.h"
#include "unit.h"
#include "map.h"
#include "global.h"

/* Complaints about inability to do various things should only appear if */
/* a human player issues the order to an awake unit. */

#define human_order(u) ((u)->side->directorder && humanside((u)->side))

/* Unit can be awake permanently or temporarily. */

#define is_awake(u) ((u)->orders.type == AWAKE || (u)->awake)

bool delaymove;    /* true when unit move has been delayed */
bool loopagain;    /* flag to go with delaymove flag */
bool randomness;   /* true if some unit types move randomly */

int movetries;     /* Number of times unit tried to move (temp for mplay) */

Side *curside;     /* current side for when turns are sequential */

/* Cache useful details about units (for optimization only). */

init_movement()
{
    int u, t;

    randomness = FALSE;
    for_all_unit_types(u) {
	for_all_terrain_types(t) {
	    if (utypes[u].randommove[t] > 0) randomness = TRUE;
	}
    }
}

/* The movement phases starts by precomputing theoretical maxima for moves, */
/* then does each side one-by-one.  Before iterating, each human side must */
/* be wedged (to ignore input). */

movement_phase()
{
    Unit *unit;
    Side *side, *side2;

    if (Debug) printf("Entering movement phase\n");
    compute_moves();
    for_all_sides(side) {
	side->directorder = FALSE;
	side->movunit = NULL;
	side->curunit = NULL;
	if (humanside(side)) {
	} else {
	    init_machine_turn(side);
	}
    }
    for_all_sides(side) {
	if (Debug) printf("--- %s ---\n", side->name);
	if (!side->lost) {
	    curside = side;
	    for_all_sides(side2) {
		if (!humanside(side) && humanside(side2) && !side2->lost)
		    wedge_mode(side2);
		update_sides(side2);
	    }
	    loopagain = TRUE;
	    while (loopagain && !side->lost) {
		loopagain = FALSE;
		for_all_units(unit) {
		    if (unit->side == side && !side->lost) {
			if (!humanside(side) && change_machine_product(unit)) {
			    request_new_product(unit);
			} else {
			    make_current(side, unit);
			    cancel_request(side);
			    move_mode(side);
			    move_1(side, unit);
			}
		    }
		}
	    }	    
	}
    }
    /* For display hacking, also to find misc bugs :-) */
    curside = NULL;
    for_all_sides(side)  {
	if (humanside(side) && !side->lost) {
	    wedge_mode(side);
	    update_sides(side);
	}
    }
}

/* Compute moves for all the units at once. */

compute_moves()
{
    Unit *unit;

    for_all_units(unit) {
	unit->movesleft = compute_move(unit);
	unit->actualmoves = 0;
    }
}

/* Compute number of moves available to the units.  This is complicated by */
/* reduction of movement due to damage and the effect of occupants on */
/* mobility.  Also, we never let a moving unit have a movement of zero, */
/* since this causes endless difficulties, despite the apparent improvement */
/* in realism.  (or allow 0 but round up when possible?) */

/* Moves should be recomputed and possibly adjusted downward after a hit. */

compute_move(unit)
Unit *unit;
{
    int u = unit->type, moves = 0;
    Unit *occ;

    if (!neutral(unit) && (moves = utypes[u].speed) > 0) {
	if (cripple(unit)) {
	    moves = (moves * unit->hp) / (utypes[u].crippled + 1);
	}
	for_all_occupants(unit, occ) {
	    if (utypes[u].mobility[occ->type] != 100) {
		moves = (moves * utypes[u].mobility[occ->type]) / 100;
		break;
	    }
	}
	moves = max(1, moves);
    }
    if (idled(unit) && global.setproduct) moves++;
    return moves;
}

/* To move a single unit, keep iterating until all its moves are used up, */
/* or it dies, or its movement has been postponed until the other units */
/* have been done.  A single move is either under preset orders, or must be */
/* supplied by a human player, or computed by a machine player. */

move_1(side, unit)
Side *side;
Unit *unit;
{
    Side *side2;

    if (Debug) printf("%s going to move\n", unit_handle(NULL, unit));
    movetries = 0;
    while (alive(unit) && unit->movesleft > 0 && !delaymove && !side->lost) {
	if (side->mode == MOVE) make_current(side, unit);
	if (idled(unit) && global.setproduct) {
	    request_new_product(unit);
	    for_all_sides(side2) {
		if (humanside(side2) && !side2->lost) {
		    if (side2 != curside) survey_mode(side2);
		    request_command(side2);
		}
	    }
	    handle_requests();
	} else if (!is_awake(unit)) {
	    follow_order(unit);
	} else if (humanside(side) && under_control(unit)) {
	    side->directorder = TRUE;
	    for_all_sides(side2) {
		if (humanside(side2) && !side2->lost) {
		    if (side2 != curside) {
			if (side2->mode != SURVEY) goto_empty_hex(side2);
			survey_mode(side2);
		    }
		    request_command(side2);
		}
	    }
	    handle_requests();
	} else {
	    show_info(side);
	    machine_move(unit);
	}
    }
    if (delaymove) {
	delaymove = FALSE;
	loopagain = TRUE;
    }
}

/* The display and interaction should be mostly shut down. */

wedge_mode(side)
Side *side;
{
    int oldmode = side->mode;

    if (Debug) printf("%s side in wedge mode\n", side->name);
    side->mode = WEDGED;
    cancel_request(side);
    clear_info(side);
    if (side->mode != oldmode) show_timemode(side);
}

/* Switching to move mode involves shifting from wherever the cursor is, */
/* back to the unit that was being moved earlier. */

move_mode(side)
Side *side;
{
    int oldmode = side->mode;

    if (Debug) printf("%s side in move mode\n", side->name);
    side->mode = MOVE;
    if (side->movunit != NULL) {
	make_current(side, side->movunit);
	put_on_screen(side, side->curx, side->cury);
	side->movunit = NULL;
    }
    if (side->mode != oldmode) show_timemode(side);
}

/* Switching to survey mode */

survey_mode(side)
Side *side;
{
    int oldmode = side->mode;

    if (Debug) printf("%s side in survey mode\n", side->name);
    side->mode = SURVEY;
    if (side->movunit == NULL) side->movunit = side->curunit;
    make_current(side, unit_at(side->curx, side->cury));
    if (side->curunit != side->movunit) show_info(side);
    if (side->mode != oldmode) show_timemode(side);
}

/* Test if a unit (on a human side) is actually under manual control. */

under_control(unit)
Unit *unit;
{
    return probability(utypes[unit->type].control);
}

/* Set the "current" unit of a side - the one being displayed, moved, etc. */

make_current(side, unit)
Side *side;
Unit *unit;
{
    if (unit != NULL && alive(unit) &&
	(unit->side == side || Debug || Build)) {
	side->curunit = unit;
	side->curx = unit->x;  side->cury = unit->y;
    } else {
	side->curunit = NULL;
    }
}

unowned_p(x, y)
int x, y;
{
    Unit *unit = unit_at(x, y);

    return (unit == NULL || unit->side != tmpside);
}

goto_empty_hex(side)
Side *side;
{
    int x, y;

    tmpside = side;
    if (search_area(side->curx, side->cury, 20, unowned_p, &x, &y)) {
	side->curx = x;  side->cury = y;
	side->curunit = NULL;
    }
}

/* Do single move of a single order for a given unit. */

follow_order(unit)
Unit *unit;
{
    bool success = FALSE;
    int u = unit->type;
    Side *us = unit->side;

    if (Debug) printf("%s doing %s with %d moves left\n",
		      unit_handle(NULL, unit), order_desig(&(unit->orders)),
		      unit->movesleft);
/* somewhere this has a right home */
/*    unit->awake = FALSE;  */
    switch (unit->orders.type) {
    case SENTRY:
	if (unit->orders.rept-- > 0) {
	    unit->movesleft = 0;
	    success = TRUE;
	}
	break;
    case MOVEDIR:
	success = move_dir(unit, unit->orders.p.dir);
	break;
    case MOVETO:
	success = move_to_dest(unit);
	break;
    case EDGE:
	success = follow_coast(unit);
	break;
    case FOLLOW:
	success = follow_leader(unit);
	break;
    case PATROL:
	success = move_patrol(unit);
	break;
    case AWAKE:
    case NONE:
    default:
        case_panic("order type", unit->orders.type);
    }
    if (alive(unit)) {
	if (success || !humanside(us)) {
	    unit->movesleft -=
		1 + utypes[u].moves[terrain_at(unit->x, unit->y)];
	}
	if (!us->directorder || success) {
	    maybe_wakeup(unit);
	}
    }
    us->directorder = FALSE;
    if (!success) movetries++;
}

/* Force unit to try to move in given direction. */

move_dir(unit, dir)
Unit *unit;
int dir;
{
    if (unit->orders.rept-- > 0) {
	return move_to_next(unit, dirx[dir], diry[dir], TRUE, TRUE);
    }
    return FALSE;
}

/* Have unit try to move to its ordered position. */

move_to_dest(unit)
Unit *unit;
{
    return move_to(unit, unit->orders.p.pt[0].x, unit->orders.p.pt[0].y,
		   (unit->orders.flags & SHORTESTPATH));
}

/* Follow a coast line. This is a version of contour-following, but flaky */
/* because our terrain is discrete rather than continuous.  The algorithm */
/* looks backward then goes around in a circle looks for an edge; a hex */
/* the unit can move into, which has an obstacle hex adjacent. */

/* The direction-munging things should be abstracted. */

follow_coast(unit)
Unit *unit;
{
    int cw = 1, olddir = unit->orders.p.dir;
    int testdir, k;
    
    /* try to go in straight line first (why?) */
    if (flip_coin() && direction_works(unit, olddir)) return olddir;
    testdir = (olddir - 2 * cw + 6) % 6;
    for_all_directions(k) {
	if (direction_works(unit, testdir)) {
	    unit->orders.p.dir = testdir;
	    return TRUE;
	}
	testdir = (testdir + cw + 6) % 6;
    }
    /* If none of the directions work out... */
    wake_unit(unit, FALSE);
    notify(unit->side, "%s can't figure out where to move to!",
	   unit_handle(unit->side, unit));
    return FALSE;
}

direction_works(unit, dir)
Unit *unit;
int dir;
{
    int nx, ny;

    nx = wrap(unit->x + dirx[dir]);  ny = limit(unit->y + diry[dir]);
    if (could_move(unit->type, terrain_at(nx, ny)) &&
	adj_obstacle(unit->type, nx, ny)) {
	move_dir(unit, dir);
	return TRUE;
    } else {
	return FALSE;
    }
}

adj_obstacle(type, x, y)
int type, x, y;
{
    int d, x1, y1;

    for_all_directions(d) {
	x1 = wrap(x + dirx[d]);  y1 = limit(y + diry[d]);
	if (!could_move(type, terrain_at(x1, y1))) return TRUE;
    }
    return FALSE;
}

/* Unit attempts to follow its leader around, but not too closely. */

follow_leader(unit)
Unit *unit;
{
    Unit *leader = unit->orders.p.leader;

    if (!alive(leader)) {
	wake_unit(unit, FALSE);
	return FALSE;
    } else if (abs(unit->x - leader->x) < 2 && abs(unit->y - leader->y) < 2) {
	unit->movesleft = 0;
	return TRUE;
    } else {
	return move_to(unit, leader->x, leader->y, FALSE);
    }
}

/* Patrol just does move_to, but cycling waypoints around when the first */
/* one has been reached. */

move_patrol(unit)
Unit *unit;
{
    int tx, ty;

    if (unit->orders.rept-- > 0) {
	if (unit->x == unit->orders.p.pt[0].x &&
	    unit->y == unit->orders.p.pt[0].y) {
	    tx = unit->orders.p.pt[0].x;
	    ty = unit->orders.p.pt[0].y;
	    unit->orders.p.pt[0].x = unit->orders.p.pt[1].x;
	    unit->orders.p.pt[0].y = unit->orders.p.pt[1].y;
	    unit->orders.p.pt[1].x = tx;
	    unit->orders.p.pt[1].y = ty;
	}
	return move_to(unit, unit->orders.p.pt[0].x, unit->orders.p.pt[0].y,
		       (unit->orders.flags & SHORTESTPATH));
    }
    return TRUE;
}

/* Retreat is a special kind of movement. */
/* Veterans should get several tries at retreating to a good place, perhaps */
/* one try per point of "veteranness"? */

retreat_unit(unit)
Unit *unit;
{
    int dir;
    bool success;

    dir = random_dir();
    success = move_to_next(unit, dirx[dir], diry[dir], FALSE, FALSE);
    return success;
}

/* This weird-looking routine computes next directions for moving to a */
/* given spot.  The basic strategy is to prefer to go in the x or y distance */
/* that is the greatest difference, so as to even the two displacements out. */
/* (This leaves more options open if blockage several hexes away.)  Make it */
/* probabilistic, so repeated travel will eventually trace the envelope of */
/* possible moves.  The number of directions ranges from 1 to 4, depending */
/* on whether there is a straight-line path to the dest, and whether we are */
/* required to take a direct path or are allowed to move in dirs that don't */
/* the unit any closer (we never increase our distance though). */
/* Some trickinesses:  world is cylindrical, so must resolve ambiguity about */
/* getting to the same place going either direction (we pick shortest). */

#define left_of(d) (((d) + 5) % 6)
#define rite_of(d) (((d) + 1) % 6)

move_to(unit, tx, ty, shortest)
Unit *unit;
int tx, ty;
bool shortest;
{
    bool closer, success;
    int dx, dxa, dy, dist, d1, d2, d3, d4, axis = -1, hextant = -1, tmp;

    dist = distance(unit->x, unit->y, tx, ty);
    dx = tx - unit->x;  dy = ty - unit->y;

    dxa = (tx + world.width) - unit->x;
    if (abs(dx) > abs(dxa)) dx = dxa;
    dxa = (tx - world.width) - unit->x;
    if (abs(dx) > abs(dxa)) dx = dxa;
    if (dx == 0 && dy == 0) {
	wake_unit(unit, FALSE);
	return FALSE;
    }
    axis = hextant = -1;
    if (dx == 0) {
	axis = (dy > 0 ? NE : SW);
    } else if (dy == 0) {
	axis = (dx > 0 ? EAST : WEST);
    } else if (dx == (0 - dy)) {
	axis = (dy > 0 ? NW : SE);
    } else if (dx > 0) {
	hextant = (dy > 0 ? EAST : (abs(dx) > abs(dy) ? SE : SW));
    } else {
	hextant = (dy < 0 ? WEST : (abs(dx) > abs(dy) ? NW : NE));
    }
    if (axis >= 0) {
	d1 = d2 = axis;
    }
    if (hextant >= 0) {
	d1 = left_of(hextant);
	d2 = hextant;
    }
    d3 = left_of(d1);
    d4 = rite_of(d2);
    closer = (shortest || dist == 1);
    if (flip_coin()) {
        tmp = d1;  d1 = d2; d2 = tmp;
    }
    success = move_to_next(unit, dirx[d1], diry[d1], FALSE, 1);
    if (!success)
	success = move_to_next(unit, dirx[d2], diry[d2], closer, 1);
    if (!success && !closer) {
	if (opposite_dir(unit->lastdir) == d3) {
	    success = move_to_next(unit, dirx[d4], diry[d4], TRUE, 1);
	} else if (opposite_dir(unit->lastdir) == d4) {
	    success = move_to_next(unit, dirx[d3], diry[d3], TRUE, 1);
	} else {
	    success = move_to_next(unit, dirx[d3], diry[d3], FALSE, 1);
	    if (!success)
		success = move_to_next(unit, dirx[d4], diry[d4], TRUE, 1);
	}
    }
    return success;
}

/* This function and a couple auxes encode most of the rules about how */
/* units can move.  Attempts to move onto other units are handled */
/* by other functions below.  Unit will also refuse to move onto the edge of */
/* the map or into the wrong kind of terrain.  Otherwise, it succeeds in its */
/* movement and we put it at the new spot.  If at any time, the unit */
/* could not move and yet was supposed to, it will wake up. */

move_to_next(unit, dx, dy, mustgo, atk)
Unit *unit;
int dx, dy;
bool mustgo, atk;
{
    bool offcourse = FALSE, success = FALSE;
    int nx, ny, newdir, utype = unit->type;
    Unit *unit2;
    Side *us = unit->side;

    if (randomness) {
	offcourse = (random(10000) <
		     utypes[utype].randommove[terrain_at(unit->x, unit->y)]);
	if (offcourse) {
	    newdir = random_dir();
	    dx = dirx[newdir];  dy = diry[newdir];
	    notify(us, "%s goes off course...", unit_handle(us, unit));
	}
    }

    nx = wrap(unit->x + dx);  ny = unit->y + dy;

    if ((unit2 = unit_at(nx, ny)) != NULL) {
	success = move_to_unit(unit, unit2, dx, dy, mustgo, atk, offcourse);
    } else if (!between(1, ny, world.height-2)) {
	if (global.leavemap) {
	    kill_unit(unit, DISBAND);
	} else if (offcourse) {
	    notify(us, "%s has fallen off the edge of the world!",
		   unit_handle(us, unit));
	    kill_unit(unit, DISASTER);
	} else if (human_order(unit) && mustgo) {
	    cmd_error(us, "%s can't leave this map!",
		      unit_handle(us, unit));
	}
    } else if (!could_move(utype, terrain_at(nx, ny))) {
	if (offcourse) {
	    notify(us, "%s has met with disaster!", unit_handle(us, unit));
	    kill_unit(unit, DISASTER);
	} else if (human_order(unit) && mustgo) {
	    cmd_error(us, "%s won't go into the %s!",
		      unit_handle(us, unit), ttypes[terrain_at(nx, ny)].name);
	}
    } else {
	move_unit(unit, nx, ny);
	unit->lastdir = find_dir(dx, dy);
	success = TRUE;
    }
    /* Units don't get dead by failing to move, so test not needed here. */
    if (!success && mustgo) {
/*	unit->awake = TRUE;  */
	wake_unit(unit, FALSE);
    }
    return success;
}

/* An enemy unit will be attacked, unless unit is on a transport *and* it */
/* cannot move to that hex anyway. */
/* Also will refuse if hit prob < 10% (can still defend tho) */
/* If the attackee is destroyed, then unit will attempt to move in again. */
/* A friendly transport will be boarded unless it is full. */
/* Blank refusal to move if any other unit. */

/* Allies treat each other's units as their own. */

move_to_unit(unit, unit2, dx, dy, mustgo, atk, offcourse)
Unit *unit, *unit2;
int dx, dy;
bool mustgo, atk, offcourse;
{
    int u = unit->type, u2 = unit2->type, u2x = unit2->x, u2y = unit2->y;
    Side *us = unit->side;

    if (!allied_side(us, unit2->side)) {
	if (unit->transport != NULL && impassable(unit, u2x, u2y)) {
	    if (human_order(unit) && mustgo) {
		cmd_error(us, "%s can't attack there!", unit_handle(us, unit));
	    }
	} else if (side_view(us, u2x, u2y) == EMPTY) {
	    notify(us, "%s spots something!", unit_handle(us, unit));
	    see_exact(us, u2x, u2y);
	    draw_hex(us, u2x, u2y, TRUE);
	} else if (atk && (unit->orders.flags & ATTACKUNIT)) {
	    if (!could_hit(u, u2) && !could_capture(u, u2)) {
		if (human_order(unit) && mustgo) {
		    cmd_error(us, "%s refuses to attack a %s!",
			      unit_handle(us, unit), utypes[u2].name);
		}
	    } else if (!enough_ammo(unit, unit2)) {
		if (human_order(unit) && mustgo) {
		    cmd_error(us, "%s is out of ammo!", unit_handle(us, unit));
		}
	    } else {
		if (attack(unit, unit2)) {
		    /* if battle won, can try moving again */
		    return move_to_next(unit, dx, dy, FALSE, FALSE);
		}
		return TRUE;
	    }
	} else {
	    /* here if aircraft blocked on return, etc - no action needed */
	}
    } else if (could_carry(u2, u)) {
	if (!can_carry(unit2, unit)) {
	    if (human_order(unit) && mustgo) {
		cmd_error(us, "%s is full already!", unit_handle(us, unit2));
	    }
	} else {
	    move_unit(unit, u2x, u2y);
	    unit->movesleft -= utypes[u].entertime[u2];
	    unit->lastdir = find_dir(dx, dy);
	    return TRUE;
 	}
    } else if (could_carry(u, u2)) {
	if (impassable(unit, u2x, u2y)) {
	    if (human_order(unit) && mustgo) {
		cmd_error(us, "%s can't pick up anybody there!",
			  unit_handle(us, unit));
	    }
	} else if (!can_carry(unit, unit2)) {
	    if (human_order(unit) && mustgo) {
		cmd_error(us, "%s is full already!", unit_handle(us, unit));
	    }
	} else if (unit->orders.flags == 0) {
	    /* blow out at the bottom */
	} else {
	    move_unit(unit, u2x, u2y);
	    unit->lastdir = find_dir(dx, dy);
	    return TRUE;
	}
    } else {
	if (offcourse) {
	    notify(us, "%s is involved in a wreck!", unit_handle(us, unit));
	    kill_unit(unit, DISASTER);
	    kill_unit(unit2, DISASTER);
	} else if (human_order(unit) && mustgo) {
	    cmd_error(us, "%s refuses to attack its friends!",
		      unit_handle(us, unit));
	}
    }
    return FALSE;
}

/* Perform the act of moving proper. (very simple, never fails) */
/* This is also the right place to put in anything that happens if the *
/* unit actually changes its location. */

move_unit(unit, nx, ny)
Unit *unit;
int nx, ny;
{
    Side *us = unit->side;

    if (producing(unit) && !utypes[unit->type].maker) {
	notify(us, "%s moved, cancelling its build!", unit_handle(us, unit));
	if (active_display(us) && humanside(us)) beep(us);
	set_product(unit, NOTHING);
	unit->schedule = 0;
    }
    leave_hex(unit);
    occupy_hex(unit, nx, ny);
    consume_move_supplies(unit);
}

/* This routine is too strict, doesn't account for resupply at start of next */
/* turn.  Hacked to check on transport at least. */

consume_move_supplies(unit)
Unit *unit;
{
    int u = unit->type, r;
    
    unit->actualmoves++;
    for_all_resource_types(r) {
	if (utypes[u].tomove[r] > 0) {
	    unit->supply[r] -= utypes[u].tomove[r];
	    if (unit->supply[r] <= 0 && unit->transport == NULL) {
		exhaust_supply(unit);
		return;
	    }
	}
    }
}

/* When doing a survey, you can move the cursor anywhere and it will show */
/* what is at that hex, but not give away too much! */

move_survey(side, nx, ny)
Side *side;
int nx, ny;
{
    if (between(1, ny, world.height-2)) {
	side->curx = nx;  side->cury = ny;
	make_current(side, unit_at(side->curx, side->cury));
    } else {
	if (active_display(side)) beep(side);
    }
    put_on_screen(side, side->curx, side->cury);
    show_info(side);
}

/* This routine encodes nearly all of the conditions under which a unit */
/* following orders might wake up and request new instructions. */

maybe_wakeup(unit)
Unit *unit;
{
    if (unit->orders.rept <= 0) {
	wake_unit(unit, FALSE);
    } else if ((unit->orders.flags & SUPPLYWAKE) && low_supplies(unit)) {
	wake_unit(unit, TRUE);
	unit->orders.flags &= ~SUPPLYWAKE;
    } else if ((unit->orders.flags & ENEMYWAKE) && adj_enemy(unit)) {
	wake_unit(unit, FALSE);
    }
}

/* True if unit is adjacent to an unfriendly. */

adj_enemy(unit)
Unit *unit;
{
    int d, x, y, view;

    for_all_directions(d) {
	x = wrap(unit->x + dirx[d]);  y = unit->y + diry[d];
	if (!neutral(unit)) {
	    view = side_view(unit->side, x, y);
	    if (view != EMPTY && enemy_side(side_n(vside(view)), unit->side))
		return TRUE;
	}
    }
    return FALSE;
}