/* The main simulation-running code in Xconq, turn start and end calcs.
   Copyright (C) 1986-1989, 1991-1998 Stanley T. Shebs.

Xconq is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.  See the file COPYING.  */

/* This code calculates what happens at the beginning and end of each turn. */

#include "conq.h"
#include "kernel.h"
extern int stop_apply;

/* Advanced unit support. */
extern void run_advanced_units(void);

// --- OBSOLETE ---------------------------------

//	extern void compute_acp_advanced_unit(Unit *unit);

// --------------------------------------------

extern void run_turn_start PARAMS ((void));
extern void run_restored_turn_start PARAMS ((void));
extern void run_turn_end PARAMS ((void));

extern int gamestatesafe;

static void add_new_sides_to_game PARAMS ((void));
static void run_tech_leakage PARAMS ((void));
static void cache_init_tech_levels PARAMS ((void));
static void run_tooling_attrition PARAMS ((void));
static void run_cp_attrition PARAMS ((void));
static void reset_all_reserves PARAMS ((void));
static void compute_moves PARAMS ((void));
static void finish_movement PARAMS ((void));
static void test_agreements PARAMS ((void));
static void compute_sun PARAMS ((void));
static void run_sun PARAMS ((void));
static void compute_season PARAMS ((void));
static void run_environment PARAMS ((void));
static void mix_winds PARAMS ((void));
static void all_see_cell_weather PARAMS ((int x, int y));
static void run_spies PARAMS ((void));
static void run_accidents PARAMS ((void));
static void run_attrition PARAMS ((void));
static void run_revolts PARAMS ((void));
static void unit_revolt PARAMS ((Unit *unit, int force));
static void run_surrenders PARAMS ((void));
static void unit_surrender PARAMS ((Unit *unit));
static void maybe_surrender_to PARAMS ((Unit *unit, Unit *unit2));
static int excess_left PARAMS ((int x, int y));
static void try_transfer_to_cell PARAMS ((int x, int y));
static int sharable_left PARAMS ((int x, int y));
static void try_sharing_with_cell PARAMS ((int x, int y));
static void run_economy PARAMS ((void));
static int base_production PARAMS ((Unit *unit, int m));
static void try_transfer PARAMS ((Unit *from, Unit *to, int r));
static void try_transfer_aux PARAMS ((Unit *from, Unit *to, int r));
static int can_satisfy_need PARAMS ((Unit *unit, int r, int need));
static void run_people_consumption PARAMS ((void));
static void run_cell_consumption PARAMS ((void));
static void run_unit_base_consumption PARAMS ((void));
static void run_unit_starvation PARAMS ((void));
static void unit_consumes PARAMS ((Unit *unit));
static int in_supply PARAMS ((Unit *unit, int m));
static void run_self_builds PARAMS ((void));
static void run_environment_effects PARAMS ((void));
static void damage_unit_with_temperature PARAMS ((Unit *unit, int n));
static void run_people_side_changes PARAMS ((void));
static void run_appearances PARAMS ((void));
static void run_disappearances PARAMS ((void));
static void run_hp_recovery PARAMS ((void));
static void run_auto_repair PARAMS ((void));
static void auto_repair_from_here PARAMS ((int x, int y));
static void auto_repair_unit PARAMS ((Unit *unit, Unit *unit2));
static void run_morale_recovery PARAMS ((void));
static int season_effect PARAMS ((int u));
static void run_unit_fates PARAMS ((void));
static void run_detonation_accidents PARAMS ((void));
static void run_people_limits PARAMS ((void));
static void spy_on_location PARAMS ((int x, int y));

/* The number of the current turn within a year. */

int curyearpart = -1;

/* The season name for the current turn. */

char *curseasonname;

char *curdatestr;

/* How often to do saves while playing. */

int checkpoint_interval;

/* The number of new sides that have been requested to be added. */

int new_sides_requested;

char **players_requested;

/* True whenever the game has both day and night. */

short daynight = FALSE;

/* The location of the sun, as a position relative to the area.  The
   actual values may be far outside the area. */

int sunx, suny;

/* The sun's previous location. */

int lastsunx = -1, lastsuny = -1;

/* Flags indicating whether various sets of calculations need to be done. */

/* Typically we set to -1 to indicate that the value is uncomputed, then detect
   when the value is first needed and compute a true/false value, which we then
   use thereafter. */

short any_tooling_attrition = -1;

short any_cp_attrition = -1;

short any_self_builds = -1;

short any_environment_effects = -1;

short any_appearances = -1;

short any_disappearances = -1;

short any_people_side_changes = -1;

short *any_people_surrenders = NULL;

short any_hp_recovery = -1;

short any_auto_repair = -1;

short any_morale_recovery = -1;

short any_tech_leakage = -1;

short any_detonation_accidents = -1;

short any_lost_vanish = -1;

short any_lost_wreck = -1;

short any_lost_surrender = -1;

short any_lost_revolt = -1;

short any_unit_production = -1;

short any_terrain_production = -1;

short any_people_production = -1;

short any_people_consumption = -1;

short any_cell_consumption = -1;

short any_unit_base_consumption = -1;

short any_unit_starvation = -1;

short any_people_max = -1;

short any_spying = -1;

short any_annual_temp_change = -1;

short any_accidents = -1;

short any_attrition = -1;

short any_revolts = -1;

short any_surrenders = -1;

static short *surrender_ranges;

static char *will_be_auto_repaired;

static short *auto_repair_range_max;

static char *any_lost_surrenders;

/* Do everything that would happen before movement in a turn. */

void
run_turn_start()
{
    int curturn;
    time_t turncalcstart, turncalcend;
    Side *side;

    /* Increment the turn number. */
    curturn = g_turn();
    ++curturn;
    set_g_turn(curturn);
    /* Add any sides that were requested to be added during the previous turn. */
    add_new_sides_to_game();
    /* Warn players if the game is nearly over. */
    if (curturn >= g_last_turn()) {
	if (g_extra_turn() > 0) {
	    notify_all("This may be the last turn in the game!");
	} else {
	    notify_all("This is the last turn in the game!");
	}
    }
    time(&turncalcstart);
    update_all_progress_displays("turn start calcs", -1);
    compute_season();
    Dprintf("##### TURN %d (%s) #####\n", curturn, curdatestr);
    /* Mark the change of turn even if only dumping AI and planning info. */
    if (!Debug)
      DMprintf("##### TURN %d (%s) #####\n", curturn, curdatestr);
    for_all_sides(side) {
	side->finishedturn = FALSE;
	update_turn_display(side, TRUE);
	if (realtime_game()) {
	    update_clock_display(side, TRUE);
	}
    }
    /* Do a variety of each-turn backdrop activities. */
    run_sun();
    run_environment();
    run_economy();

    /* The new supply system. */
    run_supply();
    
    /* Advanced unit support. */
    run_advanced_units();

    run_hp_recovery();
    run_auto_repair();
    run_self_builds();
    run_morale_recovery();
    run_appearances();
    run_accidents();
    run_attrition();
    run_revolts();
    run_surrenders();
    run_unit_fates();
    run_detonation_accidents();
    sort_units(TRUE);
    compute_moves();
    run_spies();
    run_tech_leakage();
    run_tooling_attrition();
    run_cp_attrition();
    cache_init_tech_levels();
    reset_all_reserves();
    gamestatesafe = FALSE;
    if ((checkpoint_interval > 0) && (curturn % checkpoint_interval == 0)) {
	write_entire_game_state(checkpoint_filename());
    }
    time(&turncalcend);
    Dprintf("%d seconds to calc at turn start\n",
	    idifftime(turncalcend, turncalcstart));
}

/* Do computations to start the first turn of a restored game. */

void
run_restored_turn_start()
{
    Side *side;

    compute_season();
    Dprintf("##### TURN %d (%s) #####\n", g_turn(), curdatestr);
    for_all_sides(side) {
	update_turn_display(side, TRUE);
	if (realtime_game()) {
	    update_clock_display(side, TRUE);
	}
    }
    compute_sun();
    sort_units(TRUE);
    /* We're done with restore-specific tweaks, turn the flag off. */
    midturnrestore = FALSE;
}

/* Do everything associated with the end of a turn. */

void
run_turn_end()
{
    update_all_progress_displays("turn end calcs", -1);
    finish_movement();
    run_people_consumption();
    run_cell_consumption();
    run_unit_base_consumption();
    run_unit_starvation();
    run_environment_effects();
    run_people_side_changes();
    /* This should come after other people-related computations,
       since this only constrains generic overcrowding. */
    run_people_limits();
    flush_dead_units();
    check_post_turn_scores();
    test_agreements();
    run_disappearances();
#ifdef DEBUGGING
    if (Debug)
      report_malloc();
#endif /* DEBUGGING */
    /* See if we've hit a preset end to the game. */
    if (g_turn() >= g_last_turn() && !probability(g_extra_turn())) {
	notify_all("This is the end of the last turn in the game!");
	end_the_game();
    }
}

/* Interfaces should call this to have another side added to the current
   game.  The actual addition happens during turn setup, so as not to
   risk confusing list traversals or AI calculations. */

void
request_additional_side(playerspec)
char *playerspec;
{
    if (numsides + new_sides_requested + 1 <= g_sides_max()) {
	if (players_requested == NULL)
	  players_requested = (char **) xmalloc(MAXSIDES * sizeof(char *));
	if (empty_string(playerspec))
	  playerspec = ",ai";
	players_requested[new_sides_requested++] = copy_string(playerspec);
	notify_all("Will add a new side (player \"%s\") at the start of the next turn",
		   playerspec);
    } else {
	notify_all("Additional side requested (player \"%s\"), but not possible to add",
		   playerspec);
    }
}

/* Add the requested number of new sides into an ongoing game. */

static void
add_new_sides_to_game()
{
    int i;
    Side *side, *side2;
    Player *player;
    Unit *unit;
    extern int need_ai_for_new_side;

    if (new_sides_requested > 0) {
	for (i = 0; i < new_sides_requested; ++i) {
	    if (numsides >= g_sides_max())
	      break;
	    /* Grow side-referencing objects. */
	    for_all_units(unit) {
		if (unit->opinions != NULL) {
		    init_unit_opinions(unit, numsides + 1);
		}
	    }
	    side = make_up_a_side();
	    player = add_player();
	    parse_player_spec(player, players_requested[i]);
	    player->side = side;
	    side->player = player;
	    /* Set the player's advantage to be the side's advantage, if not
	       already set. */
	    if (player->advantage == 0) {
		player->advantage = side->advantage;
	    }
	    run_synth_methods();
	    init_doctrine(side);
	    init_self_unit(side);
	    if (g_use_side_priority()) {
		int maxpri = 0;

		for_all_sides(side2) {
		    if (side2->priority > maxpri)
		      maxpri = side2->priority;
		}
		side->priority = maxpri + 1;
		/* If the indepside's priority was set automatically to be
		   one up from regular sides', bump it up to be past the new
		   side's priority also. */
		if (indepside->priority == side->priority)
		  ++(indepside->priority);
	    }
	    /* Count all of the new side's units as initial gain. */
	    for_all_side_units(side, unit) {
		count_gain(side, unit->type, initial_gain);
	    }
	    /* Maybe record that we need to create an AI for the new side later. */
	    if (side_wants_ai(side)) {
		need_ai_for_new_side = TRUE;
	    }
	    for_all_sides(side2) {
		/* Give all other AIs a chance to rework internal data structures. */
		if (side_has_ai(side2) && side2 != side) {
		    ai_react_to_new_side(side2, side);
		}
		/* Add the new side to displays. */
		update_side_display(side2, side, TRUE);
		notify(side2, "A new side %s (played by %s) is in the game.",
		       side_desig(side), player_desig(player));
	    }
	}
	if (i > 0) {
	    /* Recalculate all view info for all sides; simpler than
	       trying to figure out what's really changed. */
	    reset_coverage();
	    reset_all_views();
	    compute_all_feature_centroids();
	    /* Redraw everything that's displayed. */
	    update_everything();
	}
	if (i < new_sides_requested) {
	    notify_all("Cannot create %d of the requested new sides",
		       new_sides_requested - i);
	}
    }
    /* We've handled everything we're going to, reset the counter. */
    new_sides_requested = 0;
}

/* Parse the syntax "[username][,ai][/config][@display][+advantage]".  The
   input string spec may be modified in place. */
/* (should move to side.c?) */
void
parse_player_spec(player, spec)
Player *player;
char *spec;
{
    int commapos, slashpos, atpos, pluspos;

    if (spec != NULL && strcmp(spec, "*") != 0) {
	/* Extract (destructively) a trailing advantage specification. */
	pluspos = iindex('+', spec);
	if (pluspos >= 0) {
	    player->advantage = max(1, atoi(&(spec[pluspos + 1])));
	    spec[pluspos] = '\0';
	}
	/* Extract a trailing displayname if given. */
	atpos = iindex('@', spec);
	if (atpos >= 0) {
	    player->displayname = copy_string(spec + atpos + 1);
	    spec[atpos] = '\0';
	}
	/* Extract a trailing configuration name if given. */
	slashpos = iindex('/', spec);
	if (slashpos >= 0) {
	    player->configname = copy_string(spec + slashpos + 1);
	    spec[slashpos] = '\0';
	}
	/* Extract a trailing AI type if given. */
	commapos = iindex(',', spec);
	if (commapos >= 0) {
	    player->aitypename = copy_string(spec + commapos + 1);
	    spec[commapos] = '\0';
	}
	/* Just a plain old string left. */
	if (strlen(spec) > 0) {
	    if (atpos >= 0) {
		/* Display given separately, so this is a name. */
		player->name = copy_string(spec);
	    } else {
		player->displayname = copy_string(spec);
	    }
	}
    }
    canonicalize_player(player);
}

/* Compute the leakage of technology from one side to another. */

static void
run_tech_leakage()
{
    int u;
    Side *side, *side2;

    if (any_tech_leakage < 0) {
	any_tech_leakage = FALSE;
	for_all_unit_types(u) {
	    if (u_tech_leakage(u) > 0) {
		any_tech_leakage = TRUE;
		break;
	    }
	}
	Dprintf("Any tech leakage: %d\n", any_tech_leakage);
    }
    if (!any_tech_leakage)
      return;
    Dprintf("Running tech leakage\n");
    for_all_sides(side) {
	for_all_sides(side2) {
	    if (side != side2 /* (should) and some contact between sides */) {
		for_all_unit_types(u) {
		    if (side->tech[u] < side2->tech[u]
			&& u_tech_leakage(u) > 0) {
			side->tech[u] += prob_fraction(u_tech_leakage(u));
		    }
		}
	    }
	}
    }
}

/* Remember each side's tech levels before it does any research actions
   during the turn.  This can be used to keep tech level from going up too
   fast if the player has lots of units doing research. */

static void
cache_init_tech_levels()
{
    int u;
    Side *side;

    if (using_tech_levels()) {
	for_all_sides(side) {
	    for_all_unit_types(u) {
		side->inittech[u] = side->tech[u];
	    }
	}
    }
}

/* Reduce some units' construction tooling randomly. */

static void
run_tooling_attrition()
{
    int u, u2, att;
    Unit *unit;

    /* Test whether tooling attrition is ever possible. */
    if (any_tooling_attrition < 0) {
	any_tooling_attrition = FALSE;
	for_all_unit_types(u) {
	    for_all_unit_types(u2) {
		if (uu_tp_attrition(u, u2) > 0) {
		    any_tooling_attrition = TRUE;
		    break;
		}
	    }
	    if (any_tooling_attrition)
	      break;
	}
	Dprintf("Any tooling attrition: %d\n", any_tooling_attrition);
    }
    if (!any_tooling_attrition)
      return;
    for_all_units(unit) {
	if (is_active(unit) && unit->tooling != NULL) {
	    for_all_unit_types(u2) {
		att = uu_tp_attrition(unit->type, u2);
		if (att > 0) {
		    unit->tooling[u2] -= prob_fraction(att);
		}
		if (unit->tooling[u2] < 0) unit->tooling[u2] = 0;
	    }
	}
    }
}

/* Reduce some incomplete units' cp. */

static void
run_cp_attrition()
{
    int u, att;
    Unit *unit;

    /* Test whether tooling attrition is ever possible. */
    if (any_cp_attrition < 0) {
	any_cp_attrition = FALSE;
	for_all_unit_types(u) {
	    if (u_cp_attrition(u) > 0) {
		any_cp_attrition = TRUE;
		break;
	    }
	}
	Dprintf("Any cp attrition: %d\n", any_cp_attrition);
    }
    if (!any_cp_attrition)
      return;
    Dprintf("Running cp attrition\n");
    for_all_units(unit) {
	if (alive(unit) && !completed(unit)) {
	    att = u_cp_attrition(unit->type);
	    if (att > 0 && xrandom(10000) < att) {
		--(unit->cp);
		if (unit->cp <= 0) {
		    kill_unit(unit, -1 /* for now */);
		}
	    }
	}
    }
}

static void
reset_all_reserves()
{
    Unit *unit;

    for_all_units(unit) {
	if (unit->plan != NULL) {
	    unit->plan->reserve = FALSE;
	}
    }
}

/* Compute moves and actions for all the units at once, put everybody that
   can do anything into a list. */

static void
compute_moves()
{
    int curturn = g_turn();
    Unit *unit;
    Side *side;

    for_all_sides_plus_indep(side) {
	side->numacting = 0;
	side->numfinished = 0;
	for_all_side_units(side, unit) {
	    if (unit->act) {
		/* Unit acp is set to -1 to indicate uninitialization,
		   but acp is computed by adding to the previous acp,
		   so when starting a new game (as opposed to
		   restoring an old one), acp should be inited to
		   zero.  (This could maybe be done better.) */
		if (curturn == 1)
		  unit->act->acp = 0;

		/* Advanced unit support. */

// --- USE NEW UNIT PROPERTY TO TURN OFF STANDARD ACP ------------

/* This keeps the option of letting advanced units do the standard acp calculation
followed by action(s) in the action loop, in addition to the building and research 
they do during run_advanced_units. However, disable_standard_acp should be set 
to TRUE for advanced units in order to avoid a number of complications. Setting it
to FALSE will in effect let advanced units act twice each turn, once in advanced
mode and once in the standard mode. */

		if (u_disable_standard_acp(unit->type))
			unit->act->initacp = unit->act->acp = 0;

//			compute_acp_advanced_unit(unit);

// ------------------------------------------------------

		else	
		  compute_acp(unit);

		update_unit_acp_display(side, unit, FALSE);
	    }
	}
    }
}

/* Compute the action points available to the unit this turn. */

void
compute_acp(unit)
Unit *unit;
{
    int u = unit->type, t, acp, mor, moreff, temp, tempeff, maxacp, minacp, err;
    Unit *occ;

    /* Units still under construction or off-area can't do anything. */
    if (!completed(unit) || !inside_area(unit->x, unit->y)) {
	unit->act->initacp = unit->act->acp = 0;
	return;
    }
    /* First compute how many action points are available. */
    /* Start with basic acp, normal or damaged as appropriate. */
    if (u_acp_damage_effect(u) != lispnil
	&& unit->hp < (u_hp_max(u) / u_parts(u))) {
	acp = damaged_acp(unit, u_acp_damage_effect(u));
    } else {
	acp = u_acp(u);
    }
    /* Adjust for occupants. */
    for_all_occupants(unit, occ) {
	if (is_active(occ)) {
	    acp = (acp * uu_acp_occ_effect(occ->type, u)) / 100;
	}
    }
    /* Adjust for night time. */
    if (night_at(unit->x, unit->y)) {
    	/* (should account for unit being on a road at night, etc) */
	t = terrain_at(unit->x, unit->y);
    	acp = (acp * ut_acp_night_effect(u, t)) / 100;
    }
    /* Adjust for morale. */
    if (u_acp_morale_effect(u) != lispnil) {
	mor = unit->morale;
	err = interpolate_in_list(mor, u_acp_morale_effect(u), &moreff);
	if (err == 0) {
	    acp = (acp * moreff) / 100;
	} else {
	    run_warning("Morale %d out of bounds for acp-morale-effect", mor);
	}
    }
    /* Adjust for temperature. */
    if (temperatures_defined() && u_acp_temp_effect(u) != lispnil) {
	temp = temperature_at(unit->x, unit->y);
	err = interpolate_in_list(temp, u_acp_temp_effect(u), &tempeff);
	if (err == 0) {
	    acp = (acp * tempeff) / 100;
	} else {
	    run_warning("Temperature %d out of bounds for acp-temperature-effect", temp);
	}
    }
    /* Adjust for season. */
    if (u_acp_season_effect(u) != lispnil) {
    	acp = (acp * season_effect(u)) / 100;
    }
    /* Clip to upper and lower acp-per-turn limits. */
    acp = max(acp, u_acp_turn_min(u));
    if (u_acp_turn_max(u) >= 0)
      acp = min(acp, u_acp_turn_max(u));
    /* Increment the unit's available acp by the acp we get for this turn. */
    unit->act->initacp = unit->act->acp + acp;
    /* Now clip the unit's accumulated acp to its limits. */
    minacp = u_acp_min(u);
    unit->act->initacp = max(unit->act->initacp, minacp);
    maxacp = (u_acp_max(u) < 0 ? acp : u_acp_max(u));
    unit->act->initacp = min(unit->act->initacp, maxacp);
    /* Current acp is now the initial acp. */
    unit->act->acp = unit->act->initacp;
    /* Zero the counts of what actually got done. */
    unit->act->actualmoves = 0;
}

/* Compute and return the acp of a damaged unit, using a list of (hp acp) pairs
   and interpolating between them. */

int
damaged_acp(unit, effect)
Unit *unit;
Obj *effect;
{
    int u, err, rslt;

    u = unit->type;
    err = interpolate_in_list_ext(unit->hp, effect, 0, 0, 0, 0, u_hp(u), u_acp(u), &rslt);
    if (err != 0) {
	run_warning("cannot get damaged acp for %s at hp %d, using %d",
		    u_type_name(u), u_acp(u));
	rslt = u_acp(u);
    }
    return rslt;
}

static void
compute_season()
{
    Obj *names, *rest, *elt;

    curseasonname = NULL;
    if (world.yearlength > 1) {
	curyearpart = (g_turn() + g_initial_year_part()) % world.yearlength;
	/* Determine the name of the season, if defined. */
	names = g_season_names();
	if (names != NULL && names != lispnil && consp(names)) {
	    for (rest = names; rest != lispnil; rest = cdr(rest)) {
		elt = car(rest);
		if (consp(elt)
		    && numberp(car(elt))
		    && numberp(cadr(elt))
		    && between(c_number(car(elt)), curyearpart, c_number(cadr(elt)))
		    && stringp(car(cddr(elt))))
		  curseasonname = c_string(car(cddr(elt)));
	    }
	}
    } else {
	curyearpart = 0;
    }
    /* Update the preformatted date/season string. */
    strcpy(curdatestr, absolute_date_string(g_turn()));
    if (curseasonname != NULL) {
	strcat(curdatestr, " (");
	strcat(curdatestr, curseasonname);
	strcat(curdatestr, ")");
    }
}

static int
season_effect(u)
int u;
{
    int err, rslt;

    if (curyearpart < 0)
      compute_season();
    err = interpolate_in_list(curyearpart, u_acp_season_effect(u), &rslt);
    if (err != 0) {
	rslt = 100;
    }
    return rslt;
}

/* See how any agreements' terms are holding up. */

static void
test_agreements()
{
    Agreement *ag;

    for_all_agreements(ag) {
	if (ag->state == in_force) {
		/* what? */
	}
    }
}

/* Compute sun-related data. */

static void
compute_sun()
{
    int curtime, highest;

    switch (world.daylength) {
      case 0:
	/* Sun is at a fixed position. */
	daynight = TRUE;
	sunx = area.sunx;  suny = area.suny;
	break;
      case 1:
	/* No sun effects at all, every place uniformly lit. */
      	daynight = FALSE;
	break;
      default:
	/* Normal days and nights. */
	daynight = TRUE;
	/* If world has a appropriate circumference, the sun moves over
	   it at a regular pace. */
	if (world.circumference >= area.width) {
	    lastsunx = sunx;  lastsuny = suny;
	    curtime = ((g_turn() * 100 + g_initial_day_part())
		       % (world.daylength * 100));
	    sunx = ((curtime * world.circumference) / (world.daylength * 100)
		    + area.sunx);
	    /* Adjust for the world's axialtilt.  The approximation is
	       basically a triangle wave instead of a more correct
	       sinusoid. */
	    highest = ((world.circumference / 4) * world.axial_tilt) / 90;
	    if (curtime < ((world.daylength * 100) / 2))
	      suny = area.suny - highest
		+ (curtime * 2 * highest) / ((world.daylength * 100) / 2);
	    else
	      suny = area.suny + highest
		- (((curtime - ((world.daylength * 100) / 2)) * 2 * highest)
		   / ((world.daylength * 100) / 2));
	    suny -= area.latitude;
	}
	break;
    }
    if (daynight) {
	Dprintf("Sun is now at %d,%d\n", sunx, suny);
    }
}
/* Compute the position of the sun for this turn. */

static void
run_sun()
{
    int x, y;
    Side *side;

    compute_sun();
    if (world.daylength <= 1 || world.circumference < area.width)
      return;
    /* Find a cell whose lighting has changed; if so, update the
       whole area at once. */
    for_all_cells(x, y) {
	if (lighting(x, y, sunx, suny) != lighting(x, y, lastsunx, lastsuny)) {
	    for_all_sides(side) {
		update_area_display(side);
	    }
	    /* (Note that "break" won't work here, because for_all_cells
	       is actually a pair of nested loops.) */
	    return;
	}
    }
}

static int num_key_points;

struct a_key_point {
    int x, y;
    int temp;
} *key_points;

static void calc_key_point_temps PARAMS ((int yearpart));
static int interpolate_temperature PARAMS ((int x, int y));

static void
calc_key_point_temps(yearpart)
int yearpart;
{
    int i, err, rslt;
    Obj *lis, *item, *loc;

    num_key_points = length(area.temp_year);
    if (num_key_points > 0 && key_points == NULL) {
	key_points =
	  (struct a_key_point *) xmalloc(num_key_points * sizeof(struct a_key_point));
    }
    i = 0;
    for_all_list(area.temp_year, lis) {
	item = car(lis);
	loc = car(item);
	if (consp(loc)) {
	    key_points[i].x = c_number(car(loc));
	    key_points[i].y = c_number(cadr(loc));
	}
	err = interpolate_in_list(yearpart, cdr(item), &rslt);
	if (err != 0) {
	    run_warning("Year part %d not within range of temp_year list", yearpart);
	    rslt = 0;
	}
	key_points[i].temp = rslt;
	++i;
    }
}

static int
interpolate_temperature(x, y)
int x, y;
{
    int d0, d1, t0, t1, i, besti, nextbesti, d;

    if (num_key_points == 1) {
	return key_points[0].temp;
    } else if (num_key_points == 2) {
	d0 = distance(x, y, key_points[0].x, key_points[0].y);
	t0 = key_points[0].temp;
	d1 = distance(x, y, key_points[1].x, key_points[1].y);
	t1 = key_points[1].temp;
	return ((t0 * d1 + t1 * d0) / (d0 + d1));
    } else if (num_key_points > 2) {
	besti = 0;
	nextbesti = -1;
	d0 = distance(x, y, key_points[besti].x, key_points[besti].y);
	for (i = 2; i < num_key_points; ++i) {
	    d = distance(x, y, key_points[i].x, key_points[i].y);
	    if (d < d0) {
		nextbesti = besti;
		d1 = d0;
		besti = i;
		d0 = distance(x, y, key_points[besti].x, key_points[besti].y);
	    } else if ((nextbesti < 0 || d < d1) && i != besti) {
		nextbesti = i;
		d1 = distance(x, y, key_points[nextbesti].x, key_points[nextbesti].y);
	    }
	}
	d1 = distance(x, y, key_points[nextbesti].x, key_points[nextbesti].y);
	t0 = key_points[besti].temp;
	t1 = key_points[nextbesti].temp;
	return ((t0 * d1 + t1 * d0) / (d0 + d1));
    } else {
	run_error("???");
	return 0;
    }
}

/* Compute environment changes. */

static void
run_environment()
{
    int yrlen = world.yearlength, x, y, dir, t, celltemp;
    int anychanges = FALSE;

    if (mintemp == maxtemp && !any_wind_variation_in_layer)
	return;
    if (mintemp != maxtemp && !temperatures_defined()) {
        allocate_area_temperatures();
        allocate_area_scratch(3);
    }
    if (any_annual_temp_change < 0) {
	any_annual_temp_change = FALSE;
	if (yrlen > 0 && area.temp_year != lispnil) {
	    any_annual_temp_change = TRUE;
	}
    }
    if (any_annual_temp_change) {
	calc_key_point_temps(curyearpart);
    }
    if (any_clouds && !clouds_defined()) {
        allocate_area_clouds();
        allocate_area_scratch(3);
    }
    if (any_wind_variation_in_layer && !winds_defined()) {
        allocate_area_winds();
        allocate_area_scratch(2);
    }
    /* The tmp1 record will record where any weather changes occur. */
    for_all_cells(x, y) {
	set_tmp1_at(x, y, FALSE);
    }
    if (mintemp != maxtemp /* and any temperature changes */) {
	/* Compute the average temperature at each point in the world. */
	for_all_cells(x, y) {
	    /* Save the prev temp. */
	    set_tmp2_at(x, y, temperature_at(x, y));
	    t = terrain_at(x, y);
	    if (any_annual_temp_change)
		celltemp = interpolate_temperature(x, y);
	    else
		celltemp = t_temp_avg(t);
	    /* Add in a random variation if specified. */
	    if (t_temp_variability(t) > 0) {
		celltemp += (xrandom(t_temp_variability(t))
			     - t_temp_variability(t)/2);
	    }
	    /* Higher elevations can be much colder. */
	    /* (In this pos, will influence lowlands via moderation -
	       realistic?) */
	    if (elevations_defined()
		&& g_temp_floor_elev() != 0
		&& elev_at(x, y) < g_temp_floor_elev()) {
		celltemp -=
		    ((celltemp - g_temp_floor()) * elev_at(x, y))
		    / g_temp_floor_elev();
	    }
	    /* Clip to terrain type's limits. */
	    if (celltemp < t_temp_min(t))
		celltemp = t_temp_min(t);
	    if (celltemp > t_temp_max(t))
		celltemp = t_temp_max(t);
	    /* Record the (unmoderated) temperature of the cell. */
	    set_temperature_at(x, y, celltemp);
	}
	/* Sometimes the scale of the world is such that neighboring cells
	   influence each other's temperatures. */
	if (g_temp_mod_range() > 0) {
	    /* only doing a range of 1... */
	    for_all_interior_cells(x, y) {
		set_tmp3_at(x, y, temperature_at(x, y));
		for_all_directions(dir) {
		    set_tmp3_at(x, y,
				(tmp3_at(x, y)
				 + temperature_at(x+dirx[dir], y+diry[dir])));
		}
	    }
	    for_all_interior_cells(x, y) {
		celltemp = tmp3_at(x, y) / (NUMDIRS + 1);
		t = terrain_at(x, y);
		/* Clip to terrain type's limits. */
		if (celltemp < t_temp_min(t))
		    celltemp = t_temp_min(t);
		if (celltemp > t_temp_max(t))
		    celltemp = t_temp_max(t);
		set_temperature_at(x, y, celltemp);
	    }
	}
	/* Set a changed bit at any changed cells. */
 	for_all_cells(x, y) {
	    if (temperature_at(x, y) != tmp2_at(x, y))
		set_tmp1_at(x, y, TRUE);
	    anychanges = TRUE;
	}
    }
    if (any_clouds /* and any cloud changes */) {
	/* Save the previous state. */
	for_all_interior_cells(x, y) {
	    set_tmp2_at(x, y, raw_cloud_at(x, y));
	}
	/* Do completely random changes to clouds. */
	if (1 /* any random changes */) {
	    for_all_cells(x, y) {
		int cloud = raw_cloud_at(x, y), newcloud;
		int t = terrain_at(x, y);
		int anychange;

		anychange = FALSE;
		if (probability(10)) {
		    newcloud = cloud + (flip_coin() ? 1 : -1);
		    newcloud = max(newcloud, t_clouds_min(t));
		    newcloud = min(newcloud, t_clouds_max(t));
		    if (newcloud != cloud) {
			cloud = newcloud;
			anychange = TRUE;
		    }
		}
		if (anychange) {
		    set_raw_cloud_at(x, y, cloud);
		}
	    }
	}
	/* Let winds move clouds around. */
	if (winds_defined() /* and wind affects clouds */) {
	    int x1, y1, winddir, sum, count, newcloud, t, raw;

	    for_all_cells(x, y) {
		/* Existing cloud accounts for bulk of cloud present. */
		sum = 6 * 100 * raw_cloud_at(x, y);
		count = 6;
		for_all_directions(dir) {
		    if (point_in_dir(x, y, dir, &x1, &y1)) {
			if (wind_force_at(x1, y1) > 0) {
			    winddir = wind_dir_at(x1, y1);
			    if (winddir == opposite_dir(dir)) {
				sum += 100 * raw_cloud_at(x1, y1);
				++count;
			    }
			}
		    }
		}
		raw = sum / count;
		newcloud = raw / 100;
		/* Round off the results.  Otherwise, all clouds will gradually
		   disappear as the effect of truncation accumulates. */
		if (raw % 100 > 50)
		    ++newcloud;
		t = terrain_at(x, y);
		newcloud = max(newcloud, t_clouds_min(t));
		newcloud = min(newcloud, t_clouds_max(t));
		set_tmp3_at(x, y, newcloud);
	    }
	    /* Copy over the buffered-up changes. */
	    for_all_interior_cells(x, y) {
		set_raw_cloud_at(x, y, tmp3_at(x, y));
	    }
	}
	/* Set a changed bit at any changed cells. */
	for_all_cells(x, y) {
	    if (raw_cloud_at(x, y) != tmp2_at(x, y))
		set_tmp1_at(x, y, TRUE);
	    anychanges = TRUE;
	}
    }
    /* Do wind changes. */
    if (any_wind_variation_in_layer) {
	/* Save the previous state. */
	for_all_interior_cells(x, y) {
	    set_tmp2_at(x, y, raw_wind_at(x, y));
	}
	vary_winds();
	if (g_wind_mix_range() > 0) {
	    mix_winds();
	}
	/* Set a changed bit at any changed cells. */
	for_all_cells(x, y) {
	    if (raw_wind_at(x, y) != tmp2_at(x, y))
		set_tmp1_at(x, y, TRUE);
	    anychanges = TRUE;
	}
    }
    /* See if any displays should change and report if so. */
    if (anychanges) {
	for_all_cells(x, y) {
	    if (tmp1_at(x, y)) {
		all_see_cell_weather(x, y);
	    }
	}
    }
}

vary_winds()
{
    int x, y, t, wdir, wforce, newwforce, anychange;

    for_all_interior_cells(x, y) {
	wdir = wind_dir_at(x, y);
	wforce = wind_force_at(x, y);
	t = terrain_at(x, y);
	anychange = FALSE;
	if (probability(t_wind_variability(t))) {
	    wdir = (flip_coin() ? right_dir(wdir) : left_dir(wdir));
	    anychange = TRUE;
	}
	if (probability(t_wind_force_variability(t))) {	
	    newwforce = wforce + (flip_coin() ? 1 : -1);
	    newwforce = max(newwforce, t_wind_force_min(t));
	    newwforce = min(newwforce, t_wind_force_max(t));
	    if (newwforce != wforce) {
		wforce = newwforce;
		anychange = TRUE;
	    }
	}
	if (anychange) {
	    set_wind_at(x, y, wdir, wforce);
	}
    }
}

static void
mix_winds()
{
    int num, i, x, y, dir, x1, y1, wdir, wforce, sumx, sumy, n, t;

    num = (area.width * area.height) / 6;

    for (i = 0; i < num; ++i) {
	random_point(&x, &y);
	wdir = wind_dir_at(x, y);
	wforce = wind_force_at(x, y);
	sumx = dirx[wdir] * wforce;  sumy = diry[wdir] * wforce;
	n = 1;
	for_all_directions(dir) {
	    if (point_in_dir(x, y, dir, &x1, &y1)) {
		wdir = wind_dir_at(x1, y1);
		wforce = wind_force_at(x1, y1);
		sumx += dirx[wdir] * wforce;  sumy += diry[wdir] * wforce;
		++n;
	    }
	}
	/* Over time, the rounding-down effect of division would
	   causes all winds to diminish to zero.  Counteract by
	   incrementing the sum. */
	sumx += n - 1;  sumy += n - 1;
	sumx = sumx / n;  sumy = sumy / n;
	if (sumx == 0 && sumy == 0) {
	    wdir = random_dir();
	    wforce = 0;
	} else {
	    wdir = approx_dir(sumx, sumy);
	    wforce = distance(0, 0, sumx, sumy);
	}
	t = terrain_at(x, y);
	wforce = max(wforce, t_wind_force_min(t));
	wforce = min(wforce, t_wind_force_max(t));
	set_wind_at(x, y, wdir, wforce);
    }
}

/* Update all sides with all weather changes that have happened at
   the given location. */

static void
all_see_cell_weather(x, y)
int x, y;
{
    int oldview;
    Side *side;

    /* (should build layer mask and call cell updating once) */
    for_all_sides(side) {
	if (all_see_all) {
	    update_cell_display(side, x, y,
				UPDATE_TEMP|UPDATE_CLOUDS|UPDATE_WINDS);
	} else if (g_see_weather_always()
	    ? (terrain_view(side, x, y) != UNSEEN)
	    : (cover(side, x, y) > 0)) {
	    if (temperatures_defined()) {
		oldview = temperature_view(side, x, y);
		if (oldview != temperature_at(x, y)) {
		    set_temperature_view(side, x, y, temperature_at(x, y));
		    update_cell_display(side, x, y, UPDATE_TEMP);
		}
	    }
	    if (clouds_defined()) {
		oldview = cloud_view(side, x, y);
		if (oldview != raw_cloud_at(x, y)) {
		    set_cloud_view(side, x, y, raw_cloud_at(x, y));
		    update_cell_display(side, x, y, UPDATE_CLOUDS);
		}
	    }
	    if (winds_defined()) {
		oldview = wind_view(side, x, y);
		if (oldview != raw_wind_at(x, y)) {
		    set_wind_view(side, x, y, raw_wind_at(x, y));
		    update_cell_display(side, x, y, UPDATE_WINDS);
		}
	    }
	}
    }
}

/* Given that the spying unit is going to get info about other units at this
   location, figure out just what it is going to see. */

static void
spy_on_location(x, y)
int x, y;
{
    int qual;
    Unit *unit2, *occ;

    for_all_stack(x, y, unit2) {
	if (unit2->side != tmpunit->side) {
	    qual = uu_spy_quality(tmpunit->type, unit2->type);
	    if (probability(qual)) {
		/* Spy got something, report it. */
		/* (should be more worked-out, dunno exactly how) */
		see_exact(tmpunit->side, x, y);
		for_all_occupants(unit2, occ) {
		    /* (should get info about occupants?) */
		}
		/* Might also be able to track the unit. */
		if (xrandom(10000) < uu_spy_track(tmpunit->type, unit2->type)) {
		    /* (should inform side of tracking?) */
		    add_side_to_set(tmpunit->side, unit2->tracking);
		}
	    }
	}
    }
}

/* Certain kinds of units can do spying for the side they're on. */

static void
run_spies()
{
    int u, chance;
    Unit *unit;

    if (any_spying < 0) {
	any_spying = FALSE;
	for_all_unit_types(u) {
	    if (u_spy_chance(u) > 0) {
		any_spying = TRUE;
		break;
	    }
	}
	/* But spying is pointless if everybody can see everything anyway. */
	if (all_see_all)
	  any_spying = FALSE;
	Dprintf("Any spying: %d\n", any_spying);
    }
    if (!any_spying)
      return;
    Dprintf("Running spies\n");
    for_all_units(unit) {
	if (is_active(unit)) {
	    u = unit->type;
	    chance = u_spy_chance(u);
	    if (chance > 0 && xrandom(10000) < chance) {
		/* Spying is successful, decide how much was seen. */
		tmpunit = unit;
		apply_to_area(unit->x, unit->y, u_spy_range(u), spy_on_location);
	    }
	}
    }
}

/* Test each unit that is out in the open to see if a terrain-related
   accident happens to it.  Accidents can either kill the unit instantly or
   just damage it. */

static void
run_accidents()
{
    int u, t, chance, hit;
    Unit *unit;

    if (any_accidents < 0) {
	any_accidents = FALSE;
	for_all_unit_types(u) {
	    for_all_terrain_types(t) {
		if (ut_accident_vanish(u, t) > 0
		    || ut_accident_hit(u, t) > 0) {
		    any_accidents = TRUE;
		    break;
		}
	    }
	    if (any_accidents)
	      break;
    	}
    }
    if (!any_accidents)
      return;
    Dprintf("Running accidents\n");
    for_all_units(unit) {
	if (in_play(unit) && unit->transport == NULL) {
	    u = unit->type;
	    t = terrain_at(unit->x, unit->y);
	    chance = ut_accident_vanish(u, t);
	    if (chance > 0 && xrandom(10000) < chance) {
	    	/* Kill the unit outright. */
		kill_unit(unit, H_UNIT_VANISHED);
		/* (should make a hevt) */
	    } else if ((chance = ut_accident_hit(u, t)) > 0 && xrandom(10000) < chance) {
		/* Damage the unit. */
		hit = roll_dice(ut_accident_damage(u, t));
		if (hit > 0) {
		    unit->hp2 -= hit;
		    damage_unit(unit, accident_dmg, NULL);
		}
	    }
	}
    }
}

/* Attrition only takes out a few hp at a time, but can be deadly...
   Note that attrition does not affect incomplete units - use cp-attrition
   for those. */

static void
run_attrition()
{
    int u, t, dmg;
    Unit *unit;

    if (any_attrition < 0) {
	any_attrition = FALSE;
	for_all_unit_types(u) {
	    for_all_terrain_types(t) {
		if (ut_attrition(u, t) > 0) {
		    any_attrition = TRUE;
		    break;
		}
	    }
	    if (any_attrition)
	      break;
    	}
    }
    if (!any_attrition)
      return;
    Dprintf("Running attrition\n");
    for_all_units(unit) {
	if (is_active(unit)) {
	    u = unit->type;
	    dmg = prob_fraction(ut_attrition(u, terrain_at(unit->x, unit->y)));
	    /* This is like hit_unit but doesn't have other effects. */
	    unit->hp2 -= dmg;
	    damage_unit(unit, attrition_dmg, NULL);
	}
    }
}

/* Check each unit to see whether it revolts spontaneously.  While
   surrender is influenced by nearby units, revolt takes only the
   overall state of the world into account. */

static void
run_revolts()
{
    int u;
    Unit *unit;

    if (any_revolts < 0) {
	any_revolts = FALSE;
	for_all_unit_types(u) {
	    if (u_revolt(u) > 0 || u_revolt_opinion_min(u) > 0) {
		any_revolts = TRUE;
		break;
	    }
    	}
    }
    if (!any_revolts)
      return;
    Dprintf("Running revolts\n");
    for_all_units(unit) {
	if (in_play(unit)
	    && (u_revolt(unit->type) > 0
		|| (unit->opinions != NULL
		    && u_revolt_opinion_min(unit->type) > 0))) {
	    unit_revolt(unit, FALSE);
	}
    }
}

/* Test for and run a single revolt. */

static void
unit_revolt(unit, force)
Unit *unit;
int force;
{
    int u = unit->type, ux = unit->x, uy = unit->y, chance, lo, opin;
    int sideweights[MAXSIDES+1], sn2, sum, n, i, psum;
    Side *oldside = unit->side, *newside, *side2;
    Unit *unit2;

    chance = u_revolt(u);
    if (unit->opinions != NULL
	&& u_opinion_min(u) < 0
	&& u_revolt_opinion_min(u) > 0) {
	lo = u_revolt_opinion_min(u);
	opin = unit_opinion(unit, unit->side);
	if (opin < 0)
	  chance += ((lo - chance) * (0 - opin)) / (0 - u_opinion_min(u));
    }
    if (force || xrandom(10000) < chance) {
	newside = oldside;
	if (oldside != unit->origside
	    && (unit->origside == NULL
		|| unit->origside == indepside
		|| unit->origside->ingame)
	    && !trusted_side(oldside, unit->origside)
	    && unit_allowed_on_side(unit, unit->origside)
	    ) {
	    newside = unit->origside;
	} else if (unit->opinions != NULL) {
	    /* Find all the non-allied sides in the game and weight
	       each according to the unit's opinion of the side. */
	    sum = 0;
	    for_all_sides_plus_indep(side2) {
		sn2 = side_number(side2);
		sideweights[sn2] = 0;
		if ((side2 == NULL || side2 == indepside || side2->ingame)
		    && side2 != oldside
		    && !trusted_side(oldside, side2)
		    && unit_allowed_on_side(unit, side2)
		    /* Don't go over to a side that doesn't even know
                       we're there. */
		    && (all_see_all
			|| side2 == indepside
			|| (unit_view(side2, ux, uy) != EMPTY
			    && vtype(unit_view(side2, ux, uy)) == unit->type))
		    ) {
		    sideweights[sn2] =
		      unit_opinion(unit, side2) + u_opinion_min(u);
		}
		sum += sideweights[sn2];
	    }
	    /* Select one of the sides. */
	    if (sum > 0) {
		n = xrandom(sum);
		psum = 0;
		for (i = 0; i <= numsides; ++i) {
		    psum += sideweights[i];
		    if (n <= psum) {
			newside = side_n(i);
			break;
		    }
		}
	    }
	} else {
	    /* Find all the non-allied sides in the game and weight
	       each according to the number of units of the same type
	       present. */
	    sum = 0;
	    for_all_sides_plus_indep(side2) {
		sn2 = side_number(side2);
		sideweights[sn2] = 0;
		if ((side2 == NULL || side2 == indepside || side2->ingame)
		    && side2 != oldside
		    && !trusted_side(oldside, side2)
		    && unit_allowed_on_side(unit, side2)
		    /* Don't go over to a side that doesn't even know
                       we're there. */
		    && (all_see_all
			|| side2 == indepside
			|| (unit_view(side2, ux, uy) != EMPTY
			    && vtype(unit_view(side2, ux, uy)) == unit->type))
		    ) {
		    sideweights[sn2] = 1;
		    for_all_side_units(side2, unit2) {
			/* OK if not ideal to count real units, this
                           is just a weighting */
			if (in_play(unit2) && unit2->type == unit->type)
			  ++(sideweights[sn2]);
		    }
		}
		sum += sideweights[sn2];
	    }
	    /* Select one of the sides. */
	    if (sum > 0) {
		n = xrandom(sum);
		psum = 0;
		for (i = 0; i <= numsides; ++i) {
		    psum += sideweights[i];
		    if (n <= psum) {
			newside = side_n(i);
			break;
		    }
		}
	    }
	}
	/* Might not have been much of a revolt. */
	if (newside == oldside)
	  return;
	/* Tell the players what happened. */
	/* (should notify other players, if they're observing the unit?) */
	if (newside == NULL) {
	    notify(oldside, "%s revolts, becomes independent!",
		   unit_handle(oldside, unit));
	} else {
	    notify(oldside, "%s revolts, goes over to %s!",
		   unit_handle(oldside, unit), short_side_title(newside));
	    notify(newside, "%s revolts, comes over to your side!",
		   unit_handle(newside, unit));
	}
	change_unit_side(unit, newside, H_UNIT_REVOLTED, NULL);
	/* (should set new opinions and maybe morale) */
	/* Give the previous side a last view of the situation. */
	see_exact(oldside, ux, uy);
	update_cell_display(oldside, ux, uy, TRUE);
	all_see_cell(ux, uy);
    }
}

/* Test whether surrenders can happen in this game. */

static void
run_surrenders()
{
    int u1, u2, u3, range;
    Unit *unit;

    if (any_surrenders < 0) {
	any_surrenders = FALSE;    
	for_all_unit_types(u1) {
	    for_all_unit_types(u2) {
		if (uu_surrender_chance(u1, u2) > 0) {
		    any_surrenders = TRUE;
		    if (surrender_ranges == NULL) {
			surrender_ranges =
			  (short *) xmalloc(numutypes * sizeof(short));
			for_all_unit_types(u3)
			  surrender_ranges[u3] = -1;
		    }
		    range = uu_surrender_range(u1, u2);
		    surrender_ranges[u1] = max(range, surrender_ranges[u1]);
		}
    	    }
    	}
    }
    if (!any_surrenders)
      return;
    Dprintf("Running surrenders\n");
    /* For each unit, look for units nearby that might surrender to it. */
    for_all_units(unit) {
	if (in_play(unit)
	    /* For now anyway, nobody surrenders to independents. */
	    && !indep(unit)) {
	    unit_surrender(unit);
	}
    }
}

/* Units may surrender to enemy units that are visible nearby.
   Independents have to be treated specially, since they don't have a
   view to work from.  We sort of compute the view "on the fly". */

static void
unit_surrender(unit)
Unit *unit;
{
    int u = unit->type, dir, x1, y1, range /*, surrounded = TRUE */;
    Unit *unit2;

    range = surrender_ranges[u];
    if (range < 0) {
	/* This unit won't surrender, nothing to do. */
    } else if (range > 1) {
	run_warning("Surrender range of %d not supported, ignoring", range);
    } else {
	/* Range is 0 or 1; check other units in this cell. */
	for_all_stack(unit->x, unit->y, unit2) {
	    if (in_play(unit2)
		&& unit2->side != unit->side
		&& uu_surrender_chance(u, unit2->type) > 0
		&& visible_to(unit, unit2)) {
		maybe_surrender_to(unit, unit2);
	    }
	}
	/* Check on adjacent units. */
        if (range == 1) {
	    for_all_directions(dir) {
		if (interior_point_in_dir(unit->x, unit->y, dir, &x1, &y1)) {
		    for_all_stack(x1, y1, unit2) {
			if (in_play(unit2)
			    && unit2->side != unit->side
			    && uu_surrender_chance(u, unit2->type) > 0
			    && visible_to(unit, unit2)) {
			    maybe_surrender_to(unit, unit2);
			}
		    }
		}
	    }
	}
    }
}

/* Calculate whether one unit is visible to another, even if the other
   is independent. */

int
visible_to(unit, unit2)
Unit *unit, *unit2;
{
    int uview;

    if (all_see_all) {
	return TRUE;
    } else if (unit->side != NULL) {
    	uview = unit_view(unit->side, unit2->x, unit2->y);
	return (uview != EMPTY && vtype(uview) == unit2->type);
    } else {
	/* (should be more careful to check see-chances) */
    	if (distance(unit->x, unit->y, unit2->x, unit2->y)
	    <= u_vision_range(unit->type))
    	  return TRUE;
    	else
    	  return FALSE;
    }
}

static void
maybe_surrender_to(unit, unit2)
Unit *unit, *unit2;
{
    int chance;

    chance = uu_surrender_chance(unit->type, unit2->type);
    if (xrandom(10000) < chance) {
	capture_unit(unit, unit2, H_UNIT_SURRENDERED);
    }
}

int tmpexcess;

/* We've "found what we were searching for" when the excess to distribute
   is gone. */

static int
excess_left(x, y)
int x, y;
{
    return (tmpexcess > 0);
}

static void
try_transfer_to_cell(x, y)
int x, y;
{
    Unit *unit2, *occ;

    if (tmpexcess <= 0)
      return;
    for_all_stack(x, y, unit2) {
	if (in_play(unit2) && unit2->side == tmpunit->side) {
	    try_transfer(tmpunit, unit2, tmpmtype);
	}
    }
    for_all_stack(x, y, unit2) {
	if (in_play(unit2) && unit2->side == tmpunit->side) {
	    for_all_occupants(unit2, occ) {
		if (in_play(occ) && occ->side == tmpunit->side) {
		    try_transfer(tmpunit, occ, tmpmtype);
		}
	    }
	}
    }
}

static int
sharable_left(x, y)
int x, y;
{
    return tmpunit->supply[tmpmtype] > (um_storage_x(tmpunit->type, tmpmtype) / 2);
}

static void
try_sharing_with_cell(x, y)
int x, y;
{
    Unit *unit2, *occ;

    if (!sharable_left(x, y))
      return;
    for_all_stack(x, y, unit2) {
	if (in_play(unit2) && unit2->side == tmpunit->side) {
	    try_sharing(tmpunit, unit2, tmpmtype);
	}
    }
    for_all_stack(x, y, unit2) {
	if (in_play(unit2) && unit2->side == tmpunit->side) {
	    for_all_occupants(unit2, occ) {
		if (in_play(occ) && occ->side == tmpunit->side) {
		    try_sharing(tmpunit, occ, tmpmtype);
		}
	    }
	}
    }
}

/* The main routine does production, distribution, and discarding in order. */

static void
run_economy()
{
    int u, m, t, amt, dist, x, y, x1, y1, m1, m2;
    int prod, ptivity, dir, ptivityadj, stor, oldamt, newamt;
    int ttotals[MAXMTYPES], utotals[MAXMTYPES];
    Unit *unit;
    
    if (nummtypes == 0)
      return;
    if (any_unit_production < 0) {
	any_unit_production = FALSE;
	for_all_unit_types(u) {
	    for_all_material_types(m) {
		if (max(um_base_production(u, m),
			um_occ_production(u, m)) > 0) {
		    any_unit_production = TRUE;
		    break;
		}
	    }
	}
	Dprintf("Any unit production: %d\n", any_unit_production);
    }
    if (any_terrain_production < 0) {
	any_terrain_production = FALSE;
	for_all_terrain_types(t) {
	    for_all_material_types(m) {
		if (tm_production(t, m) > 0) {
		    any_terrain_production = TRUE;
		    break;
		}
	    }
	}
	Dprintf("Any terrain production: %d\n", any_terrain_production);
    }
    if (any_people_production < 0) {
	any_people_production = FALSE;
	for_all_material_types(m1) {
	    if (m_people(m1) > 0) {
		for_all_material_types(m2) {
		    if (mm_people_production(m1, m2) > 0) {
			any_people_production = TRUE;
			break;
		    }
		}
	    }
	}
	Dprintf("Any people production: %d\n", any_people_production);
    }
    /* Note that the flags above only test for subphases; we have to run
       this overall, because redistribution may happen even if nothing
       is being produced or consumed automatically. */
    Dprintf("Running economy\n");
    if (Debug) {
	for_all_material_types(m)
	  ttotals[m] = utotals[m] = 0;
    }
    /* Make new materials but don't clip to storage capacities yet. */
    if ((any_terrain_production || any_people_production)
	&& any_cell_materials_defined()) {
	for_all_material_types(m) {
	    if (cell_material_defined(m)) {
		for_all_cells(x, y) {
		    if (any_terrain_production) {
			t = terrain_at(x, y);
			prod = tm_production(t, m);
			if (prod > 0) {
			    oldamt = material_at(x, y, m);
			    newamt = oldamt + prod;
			    set_material_at(x, y, m, newamt);
			    if (Debug) {
				stor = tm_storage_x(t, m);
				if (newamt > stor)
				  ttotals[m] += (newamt - stor);
			    }
			}
		    }
		    if (any_people_production) {
			for_all_material_types(m1) {
			    if (m_people(m1) > 0) {
				prod = mm_people_production(m1, m);
				if (prod > 0) {
				    oldamt = material_at(x, y, m);
				    newamt = oldamt + prod;
				    set_material_at(x, y, m, newamt);
				    if (Debug) {
					stor = tm_storage_x(t, m);
					if (newamt > stor)
					  ttotals[m] += (newamt - stor);
				    }
				}
			    }
			}
		    }
		}
	    }
	}
    }
    if (any_unit_production) {
	for_all_units(unit) {
	    if (in_play(unit) && completed(unit)) {
		u = unit->type;
		for_all_material_types(m) {
		    t = terrain_at(unit->x, unit->y);
		    prod = base_production(unit, m);
		    if (prod > 0) {
			ptivity = ut_productivity(u, t);
			/* Note that we've switched to hundredths. */
			ptivity = max(ptivity, um_productivity_min(u, m));
			ptivity = min(ptivity, um_productivity_max(u, m));
			amt = prob_fraction(prod * ptivity);
			if (1 /* any adj productivity */) {
			    ptivityadj = 0;
			    for_all_directions(dir) {
				if (point_in_dir(unit->x, unit->y, dir, &x1, &y1))
				  ptivityadj = max(ptivityadj, ut_productivity_adj(u, terrain_at(x1, y1)));
			    }
			    ptivityadj = max(ptivityadj, um_productivity_min(u, m));
			    ptivityadj = min(ptivityadj, um_productivity_max(u, m));
			    amt += prob_fraction(prod * ptivityadj);
			}
			unit->supply[m] += amt;
			if (Debug && unit->supply[m] > um_storage_x(u, m))
			  utotals[m] += (unit->supply[m] - um_storage_x(u, m));
		    }
		}
	    }
	}
    }
    Dprintf("Overflow is:");
    Dprintf("  (for terrain)");
    for_all_material_types(m)
      Dprintf(" %d", ttotals[m]);
    Dprintf("  (for units)");
    for_all_material_types(m)
      Dprintf(" %d", utotals[m]);
    Dprintf("\n");
    /* Move stuff around - try to get rid of any excess. */
    /* (should also do cell-cell, cell-unit, unit-cell xfers) */
    for_all_units(unit) {
	if (in_play(unit) && !indep(unit)) {
	    u = unit->type;
	    for_all_material_types(m) {
		stor = um_storage_x(u, m);
		if (unit->supply[m] > stor) {
		    dist = um_outlength(u, m);
		    if (dist >= 0) {
		    	tmpunit = unit;
		    	tmpmtype = m;
		    	tmpexcess = unit->supply[m] - stor;
			search_and_apply(unit->x, unit->y, dist, excess_left,
					 &x1, &y1, 1,
					 try_transfer_to_cell, 999999);
		    }
		}
	    }
	}
    }
    /* Throw away excess that can't be stored anywhere. */
    if (Debug) {
	for_all_material_types(m)
	  ttotals[m] = utotals[m] = 0;
    }
    if (any_cell_materials_defined()) {
	for_all_material_types(m) {
	    if (cell_material_defined(m)) {
		for_all_cells(x, y) {
		    t = terrain_at(x, y);
		    stor = tm_storage_x(t, m);
		    oldamt = material_at(x, y, m);
		    newamt = min(oldamt, stor);
		    set_material_at(x, y, m, newamt);
		    if (Debug && newamt < oldamt)
		      ttotals[m] += (oldamt - newamt);
		}
	    }
	}
    }
    for_all_units(unit) {
	u = unit->type;
	for_all_material_types(m) {
	    stor = um_storage_x(u, m);
	    oldamt = unit->supply[m];
	    newamt = min(oldamt, stor);
	    unit->supply[m] = newamt;
	    if (Debug && newamt < oldamt)
	      utotals[m] += (oldamt - newamt);
	}
    }
    Dprintf("Discarded ");
    Dprintf("  (for terrain)");
    for_all_material_types(m)
      Dprintf(" %d", ttotals[m]);
    Dprintf("  (for units)");
    for_all_material_types(m)
      Dprintf(" %d", utotals[m]);
    Dprintf("\n");
    /* This next phase is for sharing of scarcer supplies. */
    for_all_units(unit) {
	if (in_play(unit) && !indep(unit)) {
	    u = unit->type;
	    for_all_material_types(m) {
		dist = um_outlength(u, m);
		if (dist >= 0) {
		    tmpunit = unit;
		    tmpmtype = m;
		    search_and_apply(unit->x, unit->y, dist, sharable_left,
				     &x1, &y1, 1,
				     try_sharing_with_cell, 999999);
		}
	    }
	}
    }
    /* Finally, reset supply alarms. */
    for_all_units(unit) {
	if (in_play(unit) && unit->plan != NULL) {
	    if (unit->plan->supply_is_low
		&& !past_halfway_point(unit)) {
		unit->plan->supply_alarm = TRUE;
		unit->plan->supply_is_low = FALSE;
    		update_unit_display(unit->side, unit, TRUE); 
	    }
	}
    }
}

static int
base_production(unit, m)
Unit *unit;
int m;
{
    int u = unit->type, occprod;

    if (unit->transport) {
	occprod = um_occ_production(u, m);
	return (occprod >= 0 ? occprod : um_base_production(u, m));
    } else {
	return um_base_production(u, m);
    }
}

/* Give away supplies, but save enough to stay alive for a couple turns. */

static void
try_transfer(from, to, m)
Unit *from, *to;
int m;
{
    int oldsupply = from->supply[m];

    try_transfer_aux(from, to, m);
    tmpexcess -= (oldsupply - from->supply[m]);
}

/* Note that this may be called on newly-completed units during a turn. */

void
try_sharing(from, to, m)
Unit *from, *to;
int m;
{
    try_transfer_aux(from, to, m);
}

/* Material redistribution uses this routine to move supplies around
   between units far apart or on the same cell. Try to do reasonable
   things with the materials.  Net producers are much more willing to
   give away supplies than net consumers. */

static void
try_transfer_aux(from, to, m)
Unit *from, *to;
int m;
{
    int nd, u = from->type, u2 = to->type, fromrate, torate;

    if (from != to &&
	um_inlength(u2, m) >= distance(from->x, from->y, to->x, to->y)) {
	if (completed(to)) {
	    /* Try for the transfer only if we're below capacity. */
	    nd = um_storage_x(u2, m) - to->supply[m];
	    if (nd  > 0) {
		if ((um_base_production(u, m) > um_base_consumption(u, m))
		    || (survival_time(to) < 3)
		    || (um_storage_x(u, m) * 4 >= um_storage_x(u2, m))) {
		    if (can_satisfy_need(from, m, nd)) {
			transfer_supply(from, to, m, nd);
		    } else if (can_satisfy_need(from, m, max(1, nd/2))) {
			transfer_supply(from, to, m, max(1, nd/2));
		    } else if (from->supply[m] > um_storage_x(u, m)) {
			transfer_supply(from, to, m,
					(from->supply[m]
					 - um_storage_x(u, m)));
		    }
		} else {
		    fromrate = u_speed(u) * um_consumption_per_move(u, m) * 3;
		    fromrate = max(1, fromrate);
		    torate = u_speed(u2) * um_consumption_per_move(u2, m) * 3;
		    torate = max(1, torate);
		    if ((from->supply[m] / fromrate)
			> (to->supply[m] / torate)) {
			transfer_supply(from, to, m,
					min(nd, (8 + from->supply[m]) / 9));
		    }
		}
	    }
	} else {
	    /* Incomplete units don't need supply, but they are a
	       handy overflow repository. */
	    if (from->supply[m] > um_storage_x(u, m)
		&& to->supply[m] < um_storage_x(u2, m)) {
		/* Calculate the limit on how much we can transfer usefully. */
		nd = min(um_storage_x(u2, m) - to->supply[m],
			 from->supply[m] - um_storage_x(u, m));
		transfer_supply(from, to, m, nd);
	    }
	}
    }
}

/* This estimates if a need can be met.  Note that total transfer of
   requested amount is not a good idea, since the supplies might be
   essential to the unit that has them first.  If we're not worried
   about resupply, or the request is very small, then we can spare it. */

/* (should replace with doctrine/plan controls, check underlying terrain) */

static int
can_satisfy_need(unit, m, need)
Unit *unit;
int m, need;
{
    int supp = unit->supply[m];
    int stor = um_storage_x(unit->type, m);
    /* (should cache these) */
    int lim = (unit_doctrine(unit)->resupply_percent * stor) / 100;

    if (supp > lim)
      return (need < ((supp - lim) * 7) / 8);
    else
      return (need < lim / 8);
}

/* Some types of units can become completed and grow to full size
   automatically when they get to a certain point. */

static void
run_self_builds()
{
    int u, cpper;
    Unit *unit;

    /* Precompute whether any self-building ever happens. */
    if (any_self_builds < 0) {
	any_self_builds = FALSE;
	for_all_unit_types(u) {
	    if (u_cp_per_self_build(u) > 0) {
		any_self_builds = TRUE;
		break;
	    }
	}
	Dprintf("Any self builds: %d\n", any_self_builds);
    }
    if (!any_self_builds)
      return;
    Dprintf("Running self builds\n");
    for_all_units(unit) {
	u = unit->type;
	if (in_play(unit)
	    && !fullsized(unit)
	    && (cpper = u_cp_per_self_build(u)) > 0
	    && unit->cp >= u_cp_to_self_build(u)) {
	    unit->cp += cpper;
	    if (unit->cp > u_cp(u))
	      unit->cp = u_cp(u);
	    if (completed(unit)) {
		make_unit_complete(unit);
	    } else {
	    	/* Let the player know that progress was made. */
		update_unit_display(unit->side, unit, TRUE);
	    }
	}
    }
}

static void
run_environment_effects()
{
    int err, dmg;
    Unit *unit;
    Obj *attrition;

    /* Precompute whether any environment effects happen. */
    if (any_environment_effects < 0) {
	int u;

	any_environment_effects = FALSE;
	for_all_unit_types(u) {
	    if (u_temp_attrition(u) != lispnil) {
		any_environment_effects = TRUE;
		break;
	    }
	}
	Dprintf("Any environment effects: %d\n", any_environment_effects);
    }
    if (!any_environment_effects)
      return;
    if (!temperatures_defined())
      return;
    Dprintf("Running environmental effects\n");
    for_all_units(unit) {
	if (is_active(unit)) {
	    attrition = u_temp_attrition(unit->type);
	    if (attrition != lispnil) {
		err = interpolate_in_list(temperature_at(unit->x, unit->y), attrition, &dmg);
		if (err != 0) {
		    dmg = 0;
		}
		damage_unit_with_temperature(unit, dmg);
	    }
	    /* (should check for storm damage here?) */
	}
    }
}

static void
damage_unit_with_temperature(unit, dmg)
Unit *unit;
int dmg;
{
    int n;

    n = prob_fraction(dmg);
    if (n >= unit->hp) {
	rescue_occupants(unit);
	kill_unit(unit, H_UNIT_DIED_FROM_TEMPERATURE);
    } else if (n > 0) {
	notify(unit->side, "%s loses %d HP due to the temperature",
		unit_handle(unit->side, unit), n);
	unit->hp -= n;
	unit->hp2 -= n;
	update_unit_display(unit->side, unit, TRUE);
    }
}

static void
run_people_side_changes()
{
    int x, y, u, t;
    Unit *unit;

    /* Precompute whether any people side changes can happen. */
    if (any_people_side_changes < 0) {
	any_people_side_changes = FALSE;
	for_all_unit_types(u) {
	    for_all_terrain_types(t) {
		if (ut_people_surrender(u, t) > 0) {
		    any_people_side_changes = TRUE;
		    break;
		}
	    }
	    if (any_people_side_changes)
	      break;
	}
	Dprintf("Any people side changes: %d\n", any_people_side_changes);
    }
    if (!any_people_side_changes)
      return;
    if (!people_sides_defined())
      return;
    /* Precompute a per-unit-type flag. */
    if (any_people_surrenders == NULL) {
	any_people_surrenders = (short *) xmalloc(numutypes * sizeof(short));
	for_all_unit_types(u) {
	    for_all_terrain_types(t) {
		if (ut_people_surrender(u, t) > 0) {
		    any_people_surrenders[u] = TRUE;
		    break;
		}
	    }
	}
    }
    Dprintf("Running people side changes\n");
    for_all_cells(x, y) {
	if (unit_at(x, y) != NULL) {
	    for_all_stack(x, y, unit) {
		/* The people here may change sides. */
	        u = unit->type;
		if (any_people_surrenders[u]
		    && probability(people_surrender_chance(u, x, y))) {
		    change_people_side_around(x, y, u, unit->side);
		}
	    }
	} else {
	    /* Unoccupied cells might see population revert. */
	    /* (this would need multiple-loyalty pops) */
	}
    }
}

int
people_surrender_chance(u, x, y)
int u, x, y;
{
    int m, chance, peop;

    chance = ut_people_surrender(u, terrain_at(x, y));
    /* Modify the basic chance according to people types, if present. */
    if (any_cell_materials_defined()) {
	for_all_material_types(m) {
	    if (m_people(m) > 0
		&& cell_material_defined(m)) {
		peop = material_at(x, y, m);
		if (peop > 0) {
		    chance = (chance * um_people_surrender(u, m)) / 100;
		}
	    }
	}
    }
    return chance;
}

void
change_people_side_around(x, y, u, side)
int x, y, u;
Side *side;
{
    int pop = people_side_at(x, y), s = side_number(side), dir, x1, y1;
    Side *oldside, *side2;

    if (pop != NOBODY
        && pop != s
        && !trusted_side(side, side_n(pop))) {
        oldside = side_n(pop);
	set_people_side_at(x, y, s);
	if (side) {
	    for_all_sides(side2) {
		if (side == side2 || trusted_side(side, side2)) {
		    add_cover(side2, x, y, 1);
		}
	    }
	}
	update_cell_display_all_sides(x, y, TRUE);
	for_all_directions(dir) {
	    if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
		update_cell_display_all_sides(x1, y1, TRUE);
	    }
	}
	/* Previous side(s) lose free coverage. */
	for_all_sides(side2) {
	    if (!trusted_side(side, side2)
		&& (oldside == side2 || trusted_side(oldside, side2))) {
		add_cover(side2, x, y, -1);
		/* Update coverage display. */
		update_cell_display(side2, x, y, UPDATE_COVER);
	   }
	}
    }
    /* (should add ability to change adjacent cells also) */
}

/* See if the numbers of individuals in a cell exceeds the max, and migrate or
   remove so as to bring the numbers back in line. */

static void
run_people_limits()
{
    int m, t, x, y, num, ratio, amt, newamt;

    /* Precompute whether there are any people limits. */    
    if (any_people_max < 0) {
	any_people_max = FALSE;
	for_all_terrain_types(t) {
	    if (t_people_max(t) >= 0) {
		any_people_max = TRUE;
		break;
	    }
	}
	Dprintf("Any people max: %d\n", any_people_max);
    }
    if (!any_people_max)
      return;
    if (!any_cell_materials_defined())
      return;
    Dprintf("Running people limits\n");
    for_all_cells(x, y) {
	t = terrain_at(x, y);
	if (t_people_max(t) >= 0) {
	    num = num_people_at(x, y);
	    if (num > t_people_max(t)) {
		/* Too many people here, trim them down. */
		/* Compute the ratio of limit to actual number.
		   (Note that actual number is guaranteed to be nonzero.) */
		ratio = (t_people_max(t) * 100) / num;
		for_all_material_types(m) {
		    if (m_people(m) > 0
			&& cell_material_defined(m)) {
			amt = material_at(x, y, m);
			if (amt > 0) {
			    newamt = (amt * ratio) / 100;
			    set_material_at(x, y, m, newamt);
			    /* (should update sides?) */
			}
		    }
		}
	    }
	}
    }
}

void
update_cell_display_all_sides(x, y, rightnow)
int x, y, rightnow;
{
    Side *side;

    for_all_sides(side) {
	/* (Testing for unit visibility is not quite right, but close
	   enough for now.) */
	if (side->ingame && units_visible(side, x, y)) {
	    update_cell_display(side, x, y, rightnow);
	}
    }
}

/* See if it's time for any scheduled arrivals to appear. */

static void
run_appearances()
{
    int curturn, nx, ny, nw, nh, nx0, ny0, nx1, ny1, tries;
    int need_rescan, scan_tries;
    Unit *unit, *transport;

    /* Precompute whether any units will appear at a given time. */
    if (any_appearances < 0) {
    	any_appearances = FALSE;
	for_all_units(unit) {
	    if (unit->cp < 0 && unit_appear_turn(unit) >= 0) {
		any_appearances = TRUE;
		break;
	    }
	}
	Dprintf("Any appearances: %d\n", any_appearances);
    }
    if (!any_appearances)
      return;
    Dprintf("Running appearances\n");
    curturn = g_turn();
    need_rescan = TRUE;
    scan_tries = 5;
    while (need_rescan && scan_tries-- > 0) {
	need_rescan = FALSE;
	for_all_units(unit) {
	    /* See if now time for a unit to appear. */
	    if (unit->cp < 0
		&& unit_appear_turn(unit) >= 0
		&& unit_appear_turn(unit) <= curturn) {
		/* Set the unit to its correct cp. */
		unit->cp = (- unit->cp);
		/* Get the base location at which it will appear. */
		nx = nx0 = (- unit->prevx);  ny = ny0 = (- unit->prevy);
		nw = unit_appear_var_x(unit);  nh = unit_appear_var_y(unit);
		tries = ((nw >= 0 && nh >= 0) ? 100 : 1);
		while (tries-- > 0) {
		    if (nw >= 0 && nh >= 0 && random_point_in_area(nx0, ny0, nw, nh, &nx1, &ny1)) {
			nx = nx1;  ny = ny1;
		    }
		    /* Do the usual steps to place the unit. */
		    /* (should add case for appearing on conn - share with patch_obj_refs code?) */
		    if (inside_area(nx, ny)) {
			if (unit->transport != NULL) {
			    /* nothing to do?? */
			} else if (can_occupy_cell(unit, nx, ny)) {
			    enter_cell(unit, nx, ny);
			    tries = 0;
			} else {
			    /* Search this cell for units to enter. */
			    for_all_stack(nx, ny, transport) {
			 	if (unit->side == transport->side
				    && can_occupy(unit, transport)) {
				    enter_transport(unit, transport);
				    tries = 0;
				    break;
				}
			    }
			}
		    }
		}
		if (inside_area(unit->x, unit->y)) {
		    init_unit_actorstate(unit, FALSE);
		    init_unit_plan(unit);
		} else {
		    /* We've got a problem, put the unit back. */
		    unit->cp = (- unit->cp);
		    /* Go around again, perhaps a plausible transport appeared during
		       this pass. */
		    need_rescan = TRUE;
		}
	    }
    	}
    }
}

/* Perform any prescheduled disappearances. */

static void
run_disappearances()
{
    int curturn;
    Unit *unit;

    /* Precompute whether any units will disappear at a given time. */
    /* Note that this won't be right if any units created during the game
       must disappear, but there is no during-the-game way to create a unit
       that is scheduled to disappear, so this is no problem. */
    if (any_disappearances < 0) {
    	any_disappearances = FALSE;
	for_all_units(unit) {
	    if (unit_disappear_turn(unit) >= 0) {
		any_disappearances = TRUE;
		break;
	    }
	}
	Dprintf("Any disappearances: %d\n", any_disappearances);
    }
    if (!any_disappearances)
      return;
    Dprintf("Running disappearances\n");
    curturn = g_turn();
    for_all_units(unit) {
    	/* See if now time for a unit to disappear. */
    	if (in_play(unit)
	    && unit_disappear_turn(unit) >= 0
	    && unit_disappear_turn(unit) <= curturn) {
	    /* (should eject occupants first if possible) */
	    kill_unit(unit, H_UNIT_LEFT_WORLD);
	}
    }
}

/* Some types of units recover lost hp spontaneously. */

static void
run_hp_recovery()
{
    int u, hprecovery, hpmax, oldhp;
    Unit *unit;

    /* Precompute whether any units ever recover hp. */
    if (any_hp_recovery < 0) {
	any_hp_recovery = FALSE;
    	for_all_unit_types(u) {
	    if (u_hp_recovery(u) > 0) {
		any_hp_recovery = TRUE;
		break;
	    }
	}
	Dprintf("Any hp recovery: %d\n", any_hp_recovery);
    }
    if (!any_hp_recovery)
      return;
    Dprintf("Running hp recovery\n");
    for_all_units(unit) {
	if (is_active(unit)) {
	    u = unit->type;
	    hprecovery = u_hp_recovery(u);
	    hpmax = u_hp(u);
	    /* (should only do for one part of multi-part unit?) */
	    if (hprecovery > 0
	        && unit->hp < hpmax
	        && unit->hp > u_hp_to_recover(u)) {
		oldhp = unit->hp;
		add_to_unit_hp(unit, prob_fraction(hprecovery));
		/* Inform the player if the unit's hp changed. */
		if (unit->hp != oldhp) {
		    update_unit_display(unit->side, unit, TRUE);
		}
	    }
	}
    }
}

/* Some types of units can repair others without doing actions. */

static void
run_auto_repair()
{
    int u1, u2, u, hpmax, oldhp;
    Unit *unit, *occ;

    /* Precompute whether any units will repair others automatically. */
    if (any_auto_repair < 0) {
	any_auto_repair = FALSE;
    	for_all_unit_types(u1) {
	    for_all_unit_types(u2) {
		if (uu_auto_repair(u2, u1) > 0) {
		    any_auto_repair = TRUE;
		    break;
		}
	    }
	}
	Dprintf("Any auto repair: %d\n", any_auto_repair);
    }
    if (!any_auto_repair)
      return;
    /* Precompute which types will repair other types, and the radius
       out to which this can happen. */
    if (will_be_auto_repaired == NULL) {
	will_be_auto_repaired = xmalloc(numutypes);
	auto_repair_range_max =
	  (short *) xmalloc(numutypes * sizeof(short));
    	for_all_unit_types(u1) {
	    will_be_auto_repaired[u1] = FALSE;
	    auto_repair_range_max[u1] = -1;
	    for_all_unit_types(u2) {
		if (uu_auto_repair(u2, u1) > 0) {
		    will_be_auto_repaired[u1] = TRUE;
		    auto_repair_range_max[u1] =
		      max(auto_repair_range_max[u1], uu_auto_repair_range(u2, u1));
		}
	    }
	}
    }
    Dprintf("Running auto repair\n");
    for_all_units(unit) {
	if (is_active(unit)) {
	    u = unit->type;
	    hpmax = u_hp(u);
	    /* (should only do for one part of multi-part unit?) */
	    if (unit->hp < u_hp(u) && will_be_auto_repaired[u]) {
		oldhp = unit->hp;
		if (auto_repair_range_max[u] < 0) {
		    if (unit->transport && uu_auto_repair(unit->transport->type, u) > 0) {
			auto_repair_unit(unit->transport, unit);
		    } else {
			for_all_occupants(unit, occ) {
			    if (is_active(occ) && uu_auto_repair(occ->type, u) > 0) {
				auto_repair_unit(occ, unit);
			    }
			}
		    }
		} else {
		    tmpunit = unit;
		    apply_to_area(unit->x, unit->y, auto_repair_range_max[u], auto_repair_from_here);
		}
		/* Inform the player if the unit's hp changed. */
		if (unit->hp != oldhp) {
		    update_unit_display(unit->side, unit, TRUE);
		}
	    }
	}
    }
}

/* Try to auto-repair using anything found at the given location. */

static void
auto_repair_from_here(x, y)
int x, y;
{
    int dist;
    Unit *unit2;

    /* Skip out if we're all repaired. */
    if (tmpunit->hp == u_hp(tmpunit->type)) {
	stop_apply = TRUE;
	return;
    }
    for_all_stack(x, y, unit2) {
	if (unit2 != tmpunit
	    && trusted_side(unit2->side, tmpunit->side)
	    && uu_auto_repair(unit2->type, tmpunit->type) > 0) {
	    dist = distance(tmpunit->x, tmpunit->y, unit2->x, unit2->y);
	    if (dist <= uu_auto_repair_range(unit2->type, tmpunit->type)) {
		auto_repair_unit(unit2, tmpunit);
	    }
	}
    }
}

/* Do the actual auto-repair. */

static void
auto_repair_unit(unit, unit2)
Unit *unit, *unit2;
{
    int u = unit->type, u2 = unit2->type, m, repair;

    /* Check the basic restrictions on repair. */
    if (unit->hp < uu_hp_to_repair(u, u2))
      return;
    for_all_material_types(m) {
	if (unit->supply[m] < um_to_repair(u, m))
	  return;
    }
    repair = uu_auto_repair(u, u2);
    add_to_unit_hp(unit2, prob_fraction(repair));
}

/* (should move to unit.c?) */

void
add_to_unit_hp(unit, hp)
Unit *unit;
int hp;
{
    int hpmax;

    unit->hp += hp;
    hpmax = u_hp(unit->type);
    if (unit->hp > hpmax)
      unit->hp = hpmax;
    unit->hp2 += hp;
    if (unit->hp2 > hpmax)
      unit->hp2 = hpmax;
}

/* Some types of units recover lost morale spontaneously. */

static void
run_morale_recovery()
{
    int u, moralerecovery, moralemax, oldmorale;
    Unit *unit;

    /* Precompute whether any units ever recover morale. */
    if (any_morale_recovery < 0) {
	any_morale_recovery = FALSE;
    	for_all_unit_types(u) {
	    if (u_morale_recovery(u) > 0) {
		any_morale_recovery = TRUE;
		break;
	    }
	}
	Dprintf("Any morale recovery: %d\n", any_morale_recovery);
    }
    if (!any_morale_recovery)
      return;
    Dprintf("Running morale recovery\n");
    for_all_units(unit) {
	if (is_active(unit)) {
	    u = unit->type;
	    moralerecovery = u_morale_recovery(u);
	    moralemax = u_morale_max(u);
	    if (moralerecovery > 0 && unit->morale < moralemax) {
		/* Change the morale (using a routine that does all the
		   notification). */
		change_morale(unit, 1, prob_fraction(moralerecovery));
	    }
	}
    }
}

/* Decide what happens to units belonging to sides that have lost.
   Not all units will necessarily disappear right away, so this runs
   at each turn. */

static void lost_unit_surrender PARAMS ((Unit *unit));
static void maybe_surrender_lost_to PARAMS ((Unit *unit, Unit *unit2));

static void
run_unit_fates()
{
    int u, u1, u2, chance, anylost;
    Side *side, *origside;
    Unit *unit;

    if (any_lost_vanish < 0) {
	any_lost_vanish = FALSE;
	for_all_unit_types(u1) {
	    if (u_lost_vanish(u1) > 0) {
		any_lost_vanish = TRUE;
		break;
	    }
	}
    }
    if (any_lost_wreck < 0) {
	any_lost_wreck = FALSE;
	for_all_unit_types(u1) {
	    if (u_lost_wreck(u1) > 0) {
		any_lost_wreck = TRUE;
		break;
	    }
	}
    }
    if (any_lost_surrender < 0) {
	any_lost_surrender = FALSE;
	for_all_unit_types(u1) {
	    for_all_unit_types(u2) {
		if (uu_lost_surrender(u1, u2) > 0) {
		    any_lost_surrender = TRUE;
		    if (any_lost_surrenders == NULL)
		      any_lost_surrenders = xmalloc(numutypes);
		    any_lost_surrenders[u1] = TRUE;
		    break;
		}
	    }
	}
    }
    if (any_lost_revolt < 0) {
	any_lost_revolt = FALSE;
	for_all_unit_types(u1) {
	    if (u_lost_revolt(u1) > 0) {
		any_lost_revolt = TRUE;
		break;
	    }
	}
    }
    if (!(any_lost_vanish
	  || any_lost_wreck
	  || any_lost_surrender
	  || any_lost_revolt))
      return;
    /* Don't kick in until at least one side has lost. */
    anylost = FALSE;
    for_all_sides(side) {
	if (side_lost(side)) {
	    anylost = TRUE;
	    break;
	}
    }
    if (!anylost)
      return;
    /* Although it would be more efficient to only scan units on sides
       that have lost, units may be changing sides, so per-side list
       scanning will lose. */
    for_all_units(unit) {
	if (side_lost(unit->side)) {
	    if (in_play(unit)) {
		u = unit->type;
		if (any_lost_wreck) {
		    chance = u_lost_wreck(u);
		    if (chance > 0 && xrandom(10000) < chance) {
			wreck_unit(unit);
		    }
		}
	    }
	    if (in_play(unit)) {
		u = unit->type;
		if (any_lost_vanish) {
		    chance = u_lost_vanish(u);
		    if (chance > 0 && xrandom(10000) < chance) {
			kill_unit(unit, H_UNIT_VANISHED);
		    }
		}
	    }
	    if (in_play(unit)) {
		origside = unit->side;
		if (any_lost_surrender && any_lost_surrenders[u]) {
		    lost_unit_surrender(unit);
		}
		if (any_lost_revolt && unit->side == origside) {
		    chance = u_lost_revolt(u);
		    if (chance > 0 && xrandom(10000) < chance) {
			unit_revolt(unit, TRUE);
		    }
		}
	    }
	}
    }
}

static void
lost_unit_surrender(unit)
Unit *unit;
{
    int u = unit->type, dir, x1, y1;
    Unit *unit2;

    for_all_stack(unit->x, unit->y, unit2) {
	if (in_play(unit2)
	    && unit2->side != unit->side
	    && uu_lost_surrender(u, unit2->type) > 0
	    && visible_to(unit, unit2)) {
	    maybe_surrender_lost_to(unit, unit2);
	}
    }
    /* Check on adjacent units. */
    for_all_directions(dir) {
	if (interior_point_in_dir(unit->x, unit->y, dir, &x1, &y1)) {
	    for_all_stack(x1, y1, unit2) {
		if (in_play(unit2)
		    && unit2->side != unit->side
		    && uu_surrender_chance(u, unit2->type) > 0
		    && visible_to(unit, unit2)) {
		    maybe_surrender_lost_to(unit, unit2);
		}
	    }
	}
    }
}

static void
maybe_surrender_lost_to(unit, unit2)
Unit *unit, *unit2;
{
    int chance;

    chance = uu_lost_surrender(unit->type, unit2->type);
    if (xrandom(10000) < chance) {
	capture_unit(unit, unit2, H_UNIT_SURRENDERED);
    }
}

static void
run_detonation_accidents()
{
    int u, t, x, y, z, chance;
    Unit *unit;

    /* Precompute whether accidental detonation can ever occur. */
    if (any_detonation_accidents < 0) {
	any_detonation_accidents = FALSE;
	for_all_unit_types(u) {
	    for_all_terrain_types(t) {
		if (ut_detonation_accident(u, t) > 0) {
		    any_detonation_accidents = TRUE;
		    break;
		}
	    }
	    if (any_detonation_accidents)
	      break;
	}
	Dprintf("Any detonation accidents: %d\n", any_detonation_accidents);
    }
    if (!any_detonation_accidents)
      return;
    Dprintf("Running detonation accidents\n");
    for_all_units(unit) {
	if (in_play(unit) && completed(unit)) {
	    x = unit->x;  y = unit->y;  z = unit->z;
	    t = terrain_at(x, y);
	    /* (should account for being an occupant?) */
	    chance = ut_detonation_accident(unit->type, t);
	    if (chance > 0 && xrandom(10000) < chance) {
		extern int max_u_detonate_effect_range;

		/* Detonate the unit right where it is. */
		detonate_unit(unit, x, y, z);
		reckon_damage_around(x, y, max_u_detonate_effect_range);
	    }
	}
    }
}

/* Take care of details that no longer require any interaction, at least
   none that can't wait until the next turn. */

static void
finish_movement()
{
    int lostacp;
    Unit *unit;
    Side *side, *side2;

    for_all_sides(side) {
	if (Debug) {
	    lostacp = 0;
	    for_all_side_units(side, unit) {
		if (is_active(unit) && unit->act && unit->act->acp > 0) {
		    lostacp += unit->act->acp;
		}
	    }
	    if (lostacp > 0) {
		Dprintf("%s forfeited %d acp overall.\n",
			side_desig(side), lostacp);
	    }
	}
    }
    for_all_sides(side)  {
	update_side_display_all_sides(side, TRUE);
    }
}

/* Handle consumption by people. */

static void
run_people_consumption()
{
    int mm1, mm2, x, y, m1, m2, t, consum, oldamt, newamt, newtype;

    /* Precompute whether any people consumption ever happens. */
    if (any_people_consumption < 0) {
	any_people_consumption = FALSE;
	for_all_material_types(mm1) {
	    for_all_material_types(mm2) {
	    	if (mm_people_consumption(mm1, mm2) > 0) {
		    any_people_consumption = TRUE;
		    break;
	    	}
	    }
	    if (any_people_consumption)
	      break;
	}
	Dprintf("Any consumption by people: %d\n", any_people_consumption);
    }
    if (!any_people_consumption)
      return;
    if (!any_cell_materials_defined())
      return;
    Dprintf("Running people consumption\n");
    for_all_material_types(m1) {
	if (cell_material_defined(m1)) {
	    for_all_material_types(m2) {
		if (cell_material_defined(m2)) {
		    consum = mm_people_consumption(m1, m2);
		    if (consum > 0) {
			for_all_cells(x, y) {
			    oldamt = material_at(x, y, m2);
			    newamt = oldamt - consum;
			    if (newamt < 0) {
				newamt = 0;
				/* Check for exhaustion. */
				/* (should share with cell consumption) */
				t = terrain_at(x, y);
				if (probability(tm_change_on_exhaust(t, m2)) &&
				    tm_exhaust_type(t, m2) != NONTTYPE) {
				    newtype = tm_exhaust_type(t, m2);
				    /* Change the terrain's type. */
				    change_terrain_type(x, y, newtype);
				}
			    }
			    set_material_at(x, y, m2, newamt);
			}
		    }
		}
	    }
	}
    }
}

/* Handle consumption by terrain. */

static void
run_cell_consumption()
{
    int x, y, t, m, t2, m2, consum, oldamt, newamt, willchange, newtype;

    /* Precompute whether any cell base consumption ever happens. */
    if (any_cell_consumption < 0) {
	any_cell_consumption = FALSE;
	for_all_terrain_types(t2) {
	    for_all_material_types(m2) {
	    	if (tm_consumption(t2, m2) > 0) {
		    any_cell_consumption = TRUE;
		    break;
	    	}
	    }
	    if (any_cell_consumption)
	      break;
	}
	Dprintf("Any consumption by cells: %d\n", any_cell_consumption);
    }
    if (!any_cell_consumption)
      return;
    if (!any_cell_materials_defined())
      return;
    Dprintf("Running cell consumption\n");
    for_all_cells(x, y) {
	t = terrain_at(x, y);
	willchange = FALSE;
	for_all_material_types(m) {
	    if (cell_material_defined(m)) {
		consum = tm_consumption(t, m);
		oldamt = material_at(x, y, m);
		newamt = oldamt - consum;
		if (newamt < 0) {
		    newamt = 0;
		    /* Check for exhaustion. */
		    if (!willchange &&
			probability(tm_change_on_exhaust(t, m)) &&
			tm_exhaust_type(t, m) != NONTTYPE) {
			willchange = TRUE;
			newtype = tm_exhaust_type(t, m);
		    }
		}
		set_material_at(x, y, m, newamt);
	    }
	}
	if (willchange) {
	    /* Change the terrain's type. */
	    change_terrain_type(x, y, newtype);
	}
    }
}

/* Handle base consumption by units. */

static void
run_unit_base_consumption()
{
    int u, m, usedup, consump, tempeff, checkstarve;
    Obj *effect;
    Unit *unit;

    /* Precompute whether any base consumption ever happens. */
    if (any_unit_base_consumption < 0) {
	any_unit_base_consumption = FALSE;
	for_all_unit_types(u) {
	    for_all_material_types(m) {
	    	if (um_base_consumption(u, m) > 0) {
		    any_unit_base_consumption = TRUE;
		    break;
	    	}
	    }
	    if (any_unit_base_consumption)
	      break;
	}
	Dprintf("Any unit base consumption: %d\n", any_unit_base_consumption);
    }
    if (!any_unit_base_consumption)
      return;
    Dprintf("Running unit base consumption\n");
    for_all_units(unit) {
	if (is_active(unit)) {
	    u = unit->type;
	    checkstarve = FALSE;
	    for_all_material_types(m) {
		if (um_base_consumption(u, m) > 0
		    && !(unit->transport != NULL
			 && um_consumption_as_occupant(u, m) == 0)) {
		    /* Calculate what was already consumed by movement. */
		    usedup = 0;
		    if (unit->act != NULL)
		      usedup = unit->act->actualmoves * um_consumption_per_move(u, m);
		    consump = um_base_consumption(u, m);
		    /* If being transported, modify the base consumption. */
		    if (unit->transport != NULL)
		      consump = (consump * um_consumption_as_occupant(u, m)) / 100;
#if 0 /* (should think about this code a bit more) */
		    /* Modify consumption if temperature effects. */
		    effect = u_consume_temp_effect(u);
		    if (temperatures_defined() && effect != lispnil) {
			tempeff = interpolate_in_list(temperature_at(unit->x, unit->y), effect, FALSE, 999);
			consump = (consump * tempeff) / 100;
		    }
#endif
		    /* Subtract consumption that was not already used up in movement. */
		    if (usedup < consump)
		      unit->supply[m] -= (consump - usedup);
		    /* Don't let supply go below zero. */
		    if (unit->supply[m] < 0)
		      unit->supply[m] = 0;
		    if (unit->supply[m] == 0)
		      checkstarve = TRUE;
		}
	    }
	    /* Consider triggering the low-supply flag. */
	    if (alive(unit)
		&& unit->plan
		&& !unit->plan->supply_is_low
		&& past_halfway_point(unit)
		) {
		unit->plan->supply_is_low = TRUE;
		update_unit_display(unit->side, unit, TRUE); 
	    }
	}
    }
}

/* Starvation is a separate computation that follows consumption, because it
   may be that units lose necessary material in other ways than consumption,
   and the loss may be slow enough that this has to run in each of several
   turns, and not just once at the point of supply exhaustion. */

static void
run_unit_starvation()
{
    int u, m;
    Unit *unit;

    /* Precompute whether any starvation ever happens. */
    if (any_unit_starvation < 0) {
	any_unit_starvation = FALSE;
	for_all_unit_types(u) {
	    for_all_material_types(m) {
	    	if (um_hp_per_starve(u, m) > 0) {
		    any_unit_starvation = TRUE;
		    break;
	    	}
	    }
	    if (any_unit_starvation)
	      break;
	}
	Dprintf("Any unit starvation: %d\n", any_unit_starvation);
    }
    if (!any_unit_starvation)
      return;
    Dprintf("Running unit starvation\n");
    for_all_units(unit) {
	if (is_active(unit)) {
	    maybe_starve(unit, TRUE);
	}
    }
}

/* What happens to a unit that runs out of supplies.  If it can survive
   on nothing, then there may be a few turns of grace, depending on
   how the dice roll... */

void
maybe_starve(unit, partial)
Unit *unit;
int partial;
{
    int u = unit->type, m, starv, oneloss, hploss = 0;

    for_all_material_types(m) {
	if (unit->supply[m] <= 0 && !in_supply(unit, m)) {
	    starv = um_hp_per_starve(u, m);
	    if (starv > 0) {
		oneloss = prob_fraction(starv);
		hploss = max(hploss, oneloss);
	    }
	}
    }
    if (hploss > 0) {
	if (hploss >= unit->hp) {
	    /* (should let occupants try to escape first) */
	    kill_unit(unit, H_UNIT_STARVED);
	} else if (partial) {
	    unit->hp -= hploss;
	    unit->hp2 -= hploss;
	    /* (should do other hp loss consequences) */
	    /* (use generic damage routine?) */
	    update_unit_display(unit->side, unit, TRUE);
	}
    }
}

/* Check if the unit has ready access to a source of supplies. */

/* (should be more sophisticated and account for supply lines etc) */

static int
in_supply(unit, m)
Unit *unit;
int m;
{
    Unit *unit2;

    if (unit->transport != NULL) {
    	if (unit->transport->supply[m] > 0)
	  return TRUE;
    }
#if 0 /* how to make this work right? */
    for_all_stack(unit->x, unit->y, unit2) {
    	if (unit2 != unit && unit2->supply[m] > 0)
	  return TRUE;
    }
#endif
    return FALSE;
}

