/* Unit plan handling for Xconq.
   Copyright (C) 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.  */

#include "conq.h"
extern int target_visible(Unit *unit, Task *task);
#include "kernel.h"
extern int repair_if_damaged(Unit *unit);

extern short any_auto_repair;

static void plan_passive(Unit *unit, int try);
static void plan_offense(Unit *unit);
static void plan_offense_support(Unit *unit);
static void plan_defense(Unit *unit);
static void plan_exploration(Unit *unit);
static void plan_explorer_support(Unit *unit);
static void plan_improve(Unit *unit);
static void plan_colonize(Unit *unit);
static void plan_colonizer_support(Unit *unit);
static int good_cell_to_colonize(int u, Unit *unit);
static void plan_random(Unit *unit);
static void wake_at(int x, int y);

static int alternate_target_here(int x, int y);

static int side_planning_to_capture(Side *side, int u, int x, int y);

#define CLEAR_AGENDA 99

/* (should have a generic struct for all plan type attrs) */

char *plantypenames[] = {

#undef  DEF_PLAN
#define DEF_PLAN(NAME,code) NAME,

#include "plan.def"

    NULL
};

/* Every unit that can act needs a plan object, types that can't act
   should have it cleared out.  Note that incomplete units are expected
   to be able to act in the future, so it's acceptable to run this for
   incomplete units to give them a plan. */

void
init_unit_plan(unit)
Unit *unit;
{
    if (u_acp(unit->type) > 0) {
	/* Might already have a plan, so don't always realloc. */
	if (unit->plan == NULL) {
	    unit->plan = create_plan();
	}
	/* Put the plan into a default state, side will work it up later. */
	/* (should release goals also) */
	clear_task_agenda(unit->plan);
	/* Zero the plan just as xmalloc would. */
	memset(unit->plan, 0, sizeof(Plan));
	unit->plan->type = PLAN_PASSIVE;
	unit->plan->creation_turn = g_turn();
	/* Allow AIs to make this unit do things. */
	unit->plan->aicontrol = TRUE;
	/* Enable supply alarms by default. */
	unit->plan->supply_alarm = TRUE;
	/* Clear the task outcome. */
	unit->plan->last_task_outcome = TASK_UNKNOWN;
    } else {
	/* Brainless units don't need anything, can free up plan. */
	if (unit->plan != NULL) {
	    free_plan(unit->plan);
	}
	unit->plan = NULL;
    }
}

void
set_unit_plan_type(side, unit, type)
Side *side;
Unit *unit;
int type;
{
    int oldtype;

    if (unit->plan) {
	oldtype = unit->plan->type;
	if (type != oldtype) {
	    if (type == PLAN_NONE) {
		type = PLAN_PASSIVE;
		force_replan(side, unit, FALSE);
	    }
	    unit->plan->type = type;
	    if (side != NULL)
	      update_unit_display(side, unit, TRUE);
	}
    }
}

void
set_unit_asleep(side, unit, flag, recurse)
Side *side;
Unit *unit;
int flag, recurse;
{
    int oldflag;
    Unit *occ;

    if (unit->plan) {
	oldflag = unit->plan->asleep;
	if (flag != oldflag) {
	    unit->plan->asleep = flag;
	    if (side != NULL)
	      update_unit_display(side, unit, TRUE);
	}
    }
    if (recurse) {
    	for_all_occupants(unit, occ) {
	    set_unit_asleep(side, occ, flag, recurse);
    	}
    }
}

void
set_unit_reserve(side, unit, flag, recurse)
Side *side;
Unit *unit;
int flag, recurse;
{
    int oldflag;
    Unit *occ;

    if (unit->plan) {
	oldflag = unit->plan->reserve;
	if (flag != oldflag) {
	    unit->plan->reserve = flag;
	    if (side != NULL)
	      update_unit_display(side, unit, TRUE);
	}
    }
    if (recurse) {
    	for_all_occupants(unit, occ) {
	    set_unit_reserve(side, occ, flag, recurse);
    	}
    }
}

void
set_unit_ai_control(side, unit, flag, recurse)
Side *side;
Unit *unit;
int flag, recurse;
{
    int oldflag;
    Unit *occ;

    if (unit->plan) {
	oldflag = unit->plan->aicontrol;
	if (flag != oldflag) {
	    unit->plan->aicontrol = flag;
	    if (side != NULL)
	      update_unit_display(side, unit, TRUE);
	}
    }
    if (recurse) {
    	for_all_occupants(unit, occ) {
	    set_unit_ai_control(side, occ, flag, recurse);
    	}
    }
}

void
set_unit_main_goal(side, unit, goal)
Side *side;
Unit *unit;
Goal *goal;
{
    if (unit->plan) {
	unit->plan->maingoal = goal;
    }
}

void
set_unit_waiting_for_transport(side, unit, flag)
Side *side;
Unit *unit;
int flag;
{
    if (unit->plan) {
	unit->plan->waitingfortransport = flag;
    }
}

/* Execute the plan. */

void
execute_plan(unit, try)
Unit *unit;
int try;
{
    Plan *plan = unit->plan;
    extern int planexecs;

    if (!in_play(unit) || !completed(unit)) {
	DMprintf("%s shouldn't be planning yet\n", unit_desig(unit));
	return; 
    }
    DMprintf("%s using plan %s", unit_desig(unit), plan_desig(plan));
    if (try > 1)
      DMprintf(" (try #%d)", try);
    DMprintf("\n");
    /* Units that are asleep or in reserve do nothing. */
    if (plan->asleep || plan->reserve)
      return;
    ++planexecs;
    if (try > 5) {
	DMprintf("%s redoing plan too often, going into reserve\n",
		 unit_desig(unit));
	plan->reserve = TRUE;
	return;
    }
    if (plan->execs_this_turn > 1000) {
	DMprintf("%s executed plan 1000 times this turn, going into reserve\n",
		 unit_desig(unit));
	plan->reserve = TRUE;
	return;
    }
    /* Unit actually has a plan, dispatch on its type. */
    switch (plan->type) {
      case PLAN_NONE:
	/* Unit has not gotten a plan yet, leave it alone. */
	break;
      case PLAN_PASSIVE:
	plan_passive(unit, try);
	break;
      case PLAN_OFFENSIVE:
	plan_offense(unit);
	break;
      case PLAN_DEFENSIVE:
	plan_defense(unit);
	break;
      case PLAN_EXPLORATORY:
	plan_exploration(unit);
	break;
      case PLAN_COLONIZING:
	plan_colonize(unit);
	break;
      case PLAN_IMPROVING:
	plan_improve(unit);
	break;
      case PLAN_RANDOM:
	plan_random(unit);
	break;
      default:
	case_panic("plan type", plan->type);
	break;
    }
    ++(plan->execs_this_turn);
}

/* See if we're too far away from an assigned position, set a task
   to move back if so. */

int
move_into_formation(unit)
Unit *unit;
{
    int nx, ny, dist; 
    Plan *plan = unit->plan;
    Goal *goal;
    Unit *leader;

    leader = plan->funit;
    if (leader != NULL) {
	goal = plan->formation;
	/* Ensure that the leader is still someone we want to follow. */
	if (!in_play(leader)
	    || !unit_trusts_unit(unit, leader)
	    || goal->args[0] != leader->id) {
	    notify(unit->side, "%s leader is gone, cancelling formation",
		   unit_handle(unit->side, unit));
	    /* (should free goal object?) */
	    plan->formation = NULL;
	    plan->funit = NULL;
	    /* Unit is available to do something else. */
	    return FALSE;
	}
	nx = leader->x + goal->args[1];  ny = leader->y + goal->args[2];
	dist = goal->args[3];
	if (distance(unit->x, unit->y, nx, ny) > dist) {
	    /* (should perhaps insert after current task?) */
	    set_move_to_task(unit, nx, ny, dist);
	    return TRUE;
	}
    }
    return FALSE;
}

int task_is_in_agenda(Plan *plan, Task *task);

/* See if there are any standing orders that currently apply to the given unit,
   and schedule a task if so.  Return TRUE if a task was added. */

int
execute_standing_order(unit, addtask)
Unit *unit;
int addtask;
{
    Unit *transport;
    Side *side;
    StandingOrder *sorder;

    side = unit->side;
    if (side == NULL)
      return FALSE;
    for (sorder = side->orders; sorder != NULL; sorder = sorder->next) {
	if (sorder->types[unit->type] && unit->plan) {
	    switch (sorder->condtype) {
	      case sorder_at:
		if (unit->x == sorder->a1 && unit->y == sorder->a2) {
		    /* If the task is already in the plan, don't do
		       anything. */
		    if (task_is_in_agenda(unit->plan, sorder->task))
		      return FALSE;
		    if (addtask)
		      add_task(unit, 0, clone_task(sorder->task));
		    return TRUE;
		}
		break;
	      case sorder_in:
		transport = unit->transport;
		if (transport != NULL && transport->id == sorder->a1) {
		    /* If the task is already in the plan, don't do
		       anything. */
		    if (task_is_in_agenda(unit->plan, sorder->task))
		      return FALSE;
		    if (addtask)
		      add_task(unit, 0, clone_task(sorder->task));
		    return TRUE;
		}
		break;
	      case sorder_near:
		if (distance(unit->x, unit->y, sorder->a1, sorder->a2) <= sorder->a3) {
		    /* If the task is already in the plan, don't do
		       anything. */
		    if (task_is_in_agenda(unit->plan, sorder->task))
		      return FALSE;
		    if (addtask)
		      add_task(unit, 0, clone_task(sorder->task));
		    return TRUE;
		}
		break;
	      default:
		run_warning("Unknown order condition type");
		break;
	    }
	}
    }
    return FALSE;
}

int tasks_match(Task *task1, Task *task2);

int
task_is_in_agenda(plan, task)
Plan *plan;
Task *task;
{
    Task *task2;

    for (task2 = plan->tasks; task2 != NULL; task2 = task2->next) {
	if (tasks_match(task, task2))
	  return TRUE;
    }
    return FALSE;
}

int
tasks_match(task1, task2)
Task *task1, *task2;
{
    int i;

    if (task1->type != task2->type)
      return FALSE;
    for (i = 0; i < MAXTASKARGS; ++i)
      if (task1->args[i] != task2->args[i])
	return FALSE;
    return TRUE;
}

/* Passive units just work from the task queue or else wait to be told
   what to do. */

static void
plan_passive(unit, try)
Unit *unit;
int try;
{
    Plan *plan = unit->plan;

    /* Special-case human cities in the intro game to automatically
       start producing infantry initially. */
    /* (would be more efficient to put in a once-per-turn location,
       should look for one) */
    if (g_turn() <= 1
	&& mainmodule != NULL
	&& ((mainmodule->name != NULL
	     && strcmp(mainmodule->name, INTRO_GAME) == 0)
	    || (mainmodule->origmodulename != NULL
		&& strcmp(mainmodule->origmodulename, INTRO_GAME) == 0))
	&& strcmp(u_type_name(unit->type), "city") == 0) {
	push_build_task(unit, 0, 99);
    }
    if (plan->supply_is_low && plan->supply_alarm) {
	plan->supply_alarm = FALSE;
	if (0 /* auto resupply */) {
	    set_resupply_task(unit, NONMTYPE);
	} else if (plan->tasks
		   && (plan->tasks->type == TASK_RESUPPLY
		       || (plan->tasks->type == TASK_MOVE_TO
			   && plan->tasks->next
			   && plan->tasks->next->type == TASK_RESUPPLY))) {
	    /* do nothing */
	} else {           
	    clear_task_agenda(plan);
	    set_waiting_for_tasks(unit, TRUE);
	}
    }
    if (plan->tasks) {
	/* (should check that doctrine being followed correctly) */
	execute_task(unit);
    } else if (unit->side
	       && unit->side->orders
	       && execute_standing_order(unit, TRUE)) {
	execute_task(unit);
    } else if (plan->formation && move_into_formation(unit)) {
	execute_task(unit);
    } else {
	/* Our goal is now to get guidance from the side. */
	set_waiting_for_tasks(unit, TRUE);
    }
}

/* A unit operating offensively advances and attacks when possible. */

int find_alternate_hit_target(Unit *unit, Task *task, int *xp, int *yp);

static void
plan_offense(unit)
Unit *unit;
{
    int u = unit->type;
    int x, y, w, h, range, x1, y1, nx, ny;
    Plan *plan = unit->plan;
    Task *lasttask;

    if (resupply_if_low(unit)) {
	if (plan->tasks)
	  execute_task(unit);
	return;
    }
    if (rearm_if_low(unit)) {
	if (plan->tasks)
	  execute_task(unit);
	return;
    }
    if (repair_if_damaged(unit)) {
	if (plan->tasks)
	  execute_task(unit);
	return;
    }
    if (plan->tasks) {
    	execute_task(unit);
    	if (plan->last_task_outcome == TASK_FAILED) {
	    lasttask = &(plan->last_task);
	    if (lasttask->type == TASK_HIT_UNIT
		&& lasttask->args[2] != NONUTYPE
		&& !target_visible(unit, lasttask)) {
		/* Target seems to have disappeared, look around for it. */
		DMprintf("%s hit target has disappeared, looking for it; ",
			 unit_desig(unit));
		if (find_alternate_hit_target(unit, lasttask, &nx, &ny)) {
		    if (plan->tasks
			&& plan->tasks->type == lasttask->type
			&& plan->tasks->args[0] == lasttask->args[0]
			&& plan->tasks->args[1] == lasttask->args[1]
			&& plan->tasks->args[2] == lasttask->args[2]
			&& plan->tasks->args[3] == lasttask->args[3]
			) {
			pop_task(plan);
		    }
		    push_hit_unit_task(unit, nx, ny, lasttask->args[2], lasttask->args[3]);
		    DMprintf(" found at %d,%d\n", nx, ny);
		} else {
		    DMprintf(" not found\n");
		}
	    }
	}
	/* Irrespective of what happened, we don't want to step on the task
	   yet. */
	return;
    }
    if (plan->maingoal && mobile(u)) {
	switch (plan->maingoal->type) {
	  case GOAL_VICINITY_HELD:
	    x = plan->maingoal->args[0];  y = plan->maingoal->args[1];
	    w = plan->maingoal->args[2];  h = plan->maingoal->args[3];
	    if (distance(x, y, unit->x, unit->y) > max(w, h)) {
		/* Outside the goal area - move in towards it. */
	    	if (random_point_near(x, y, w / 2, &x1, &y1)) {
		    x = x1;  y = y1;
	    	}
		DMprintf("%s to go on offensive to %d,%d\n",
			 unit_desig(unit), x, y);
		set_move_to_task(unit, x, y, max(w, h) / 2);
		if (unit->transport
		    && mobile(unit->transport->type)
		    && unit->transport->plan) {
		    set_move_to_task(unit->transport, x, y, max(w, h) / 2);
		}
	    } else {
		range = max(w, h);
		/* No special goal, look for something to fight with. */
		/* Sometimes be willing to look a little farther out. */
		if (probability(50))
		  range *= 2;
		if (do_for_occupants(unit)) {
		    /* Occupants have decided for us, fall through. */
		} else if (go_after_victim(unit, range)) {
		    /* Found a victim to go after, fall through. */
		} else if (probability(20) && self_build_base_for_self(unit)) {
		} else if (!all_see_all) {
		    DMprintf("%s will explore instead\n", unit_desig(unit));
		    plan_exploration(unit); /* or patrol */
		    /* Running under exploration rules now. */
		    return;
		}
	    }
	    break;
	  default:
	    DMprintf("offensive unit has some goal\n");
	    break;
	}
    } else if (mobile(u)) {
	range = operating_range_best(u);
	if (probability(50))
	  range = min(range, 2 * u_acp(u));
	if (do_for_occupants(unit)) {
	} else if (go_after_victim(unit, range)) {
	    /* No special goal, but found something to fight with. */
	} else if (!all_see_all) {
	    DMprintf("%s will explore instead\n", unit_desig(unit));
	    plan_exploration(unit); /* or patrol */
	    /* Running under exploration rules now. */
	    return;
	} else {
	    /* should go to a "best location" if possible. */
	    /* (should do a sentry task) */
	}
    } else if (can_fire(unit) && fire_at_opportunity(unit)) {
    } else {
	plan_offense_support(unit);
    }
    if (plan->tasks) {
    	execute_task(unit);
    } else {
    	DMprintf("%s found nothing to do offensively", unit_desig(unit));
    	if (flip_coin()) {
    	    DMprintf("- going into reserve");
    	    plan->reserve = TRUE;
    	} else if (probability(5)) {
    	    DMprintf("- going to sleep");
    	    plan->asleep = TRUE;
    	}
    	DMprintf("\n");
    }
}

/* Look through list of occupants to see if an occupant needs the transport to do
   something. */

int
do_for_occupants(unit)
Unit *unit;
{
    Unit *occ;
    Goal *goal;
    Task *task;

    for_all_occupants(unit, occ) {
	if (occ->plan) {
	    /* Get the unit towards its goal, if it has one. */
	    goal = occ->plan->maingoal;
	    if (goal != NULL
		&& goal->type == GOAL_VICINITY_HELD
		&& distance(goal->args[0], goal->args[1], unit->x, unit->y)
		> goal->args[2]) {
		set_move_to_task(unit, goal->args[0], goal->args[1],
				  max(goal->args[2] / 2, 1));
		DMprintf("%s will go where occupant %s wants to go (goal %s)\n",
			 unit_desig(unit), unit_desig(occ), goal_desig(goal));
		return TRUE;
	    }
	    /* If the unit does not have a goal, see if it has a task. */
	    for_all_tasks(occ->plan, task) {
		if ((task->type == TASK_MOVE_TO
		     || task->type == TASK_HIT_UNIT)
		    && (task->args[0] != unit->x
			|| task->args[1] != unit->y)
			&& distance(task->args[0], task->args[1], unit->x, unit->y) > 1
			) {
		    /* Note that we assume the transport is mobile, which is OK currently
		       because of where this routine is called from. */
		    set_move_to_task(unit, task->args[0], task->args[1], 1);
		    DMprintf("%s will go where occupant %s wants to go (task %s)\n",
			     unit_desig(unit), unit_desig(occ), task_desig(task));
		    return TRUE;
		}
	    }
	}
    }
    return FALSE;
}

int
self_build_base_for_self(unit)
Unit *unit;
{
    int u = unit->type, u2, cando = FALSE;

    for_all_unit_types(u2) {
	if (uu_acp_to_create(u, u2) > 0
	    && (uu_creation_cp(u, u2) >= u_cp(u2)
	        || uu_acp_to_build(u, u2) > 0)
	    /* (should check if any advantage to building) */
	   ) {
	   cando = TRUE;
	   break;
	}
    }
    if (cando) {
	DMprintf("%s building %s as a base for itself\n",
		     unit_desig(unit), u_type_name(u2));
	set_build_task(unit, u2, 1);
	return TRUE;
    }
    return FALSE;
}

static void
plan_offense_support(unit)
Unit *unit;
{
    int u = unit->type, u2, u3 = NONUTYPE, backup = NONUTYPE;
    Task *task;

    for_all_unit_types(u2) {
	    if (mobile(u2)
		&& (type_can_attack(u2) || type_can_fire(u2))
		&& uu_acp_to_create(u, u2) > 0) {
		backup = u2;
		if (flip_coin()) {
		    u3 = u2;
		    break;
		}
	    }
    }
    if (u3 == NONUTYPE)
      u3 = backup;
    if (is_unit_type(u3)) {
	task = unit->plan->tasks;
    	if (task == NULL || task->type != TASK_BUILD) {
	    DMprintf("%s supporting offense by building %s\n",
		     unit_desig(unit), u_type_name(u3));
	    set_build_task(unit, u3, 2);
	} else {
	    DMprintf("%s already building, leaving alone\n",
		     unit_desig(unit));
	}
    } else {
    	DMprintf("%s has no way to support an offensive\n", unit_desig(unit));
    }
}

int
find_alternate_hit_target(unit, task, xp, yp)
Unit *unit;
Task *task;
int *xp, *yp;
{
    int range;

    tmpunit = unit;
    tmputype = task->args[2];
    tmpside = side_n(task->args[3]);
    /* (should adjust search radius for speed?) */
    range = u_acp(tmputype) + 1;
    return search_around(task->args[0], task->args[1], range,
			 alternate_target_here, xp, yp, 1);
}

static int
alternate_target_here(x, y)
int x, y;
{
    int uview;
    Unit *unit2;
    Side *side = tmpunit->side;

    if (units_visible(side, x, y)) {
	if ((unit2 = unit_at(x, y)) != NULL
	    && unit2->type == tmputype
	    && unit2->side == tmpside)
	  return TRUE;
    } else if (terrain_view(side, x, y) != UNSEEN
	       && (uview = unit_view(side, x, y)) != EMPTY
	       && vtype(uview) == tmputype
	       && side_n(vside(uview)) == tmpside)
      return TRUE;
    return FALSE;
}

/* Defensive units don't go out looking for trouble, but they should
   react strongly to threats. */

static void
plan_defense(unit)
Unit *unit;
{
    int u = unit->type, range, x, y, w, h, x1, y1;
    Plan *plan = unit->plan;

    if (resupply_if_low(unit)) {
	if (plan->tasks)
	  execute_task(unit);
	return;
    }
    if (rearm_if_low(unit)) {
	if (plan->tasks)
	  execute_task(unit);
	return;
    }
    if (repair_if_damaged(unit)) {
	if (plan->tasks)
	  execute_task(unit);
	return;
    }
    if (plan->tasks) {
    	/* (should analyze and maybe decide to change task) */
    	execute_task(unit);
    	return;
    }
    if (plan->maingoal) {
	switch (plan->maingoal->type) {
	  case GOAL_VICINITY_HELD:
	    x = plan->maingoal->args[0];  y = plan->maingoal->args[1];
	    w = plan->maingoal->args[2];  h = plan->maingoal->args[3];
	    if (distance(x, y, unit->x, unit->y) > max(w, h)) {
		/* Outside the goal area - move in towards it. */
	    	if (random_point_near(x, y, w / 2, &x1, &y1)) {
		    x = x1;  y = y1;
	    	}
		DMprintf("%s to go on defensive to %d,%d\n",
			 unit_desig(unit), x, y);
		set_move_to_task(unit, x, y, max(w, h) / 2);
		if (unit->transport
		    && mobile(unit->transport->type)
		    && unit->transport->plan) {
		    set_move_to_task(unit->transport, x, y, max(w, h) / 2);
		}
	    } else {
		range = max(w, h);
		/* No special goal, look for something to fight with. */
		/* Sometimes be willing to look a little farther out. */
		if (probability(50))
		  range *= 2;
		if (do_for_occupants(unit)) {
		    /* Occupants have decided for us, fall through. */
		} else if (go_after_victim(unit, range)) {
		    /* Found a victim to go after, fall through. */
		} else if (probability(20) && self_build_base_for_self(unit)) {
		} else if (!all_see_all) {
		    DMprintf("%s will explore instead\n", unit_desig(unit));
		    plan_exploration(unit); /* or patrol */
		    /* Running under exploration rules now. */
		    return;
		}
	    }
	    break;
	  default:
	    DMprintf("defensive unit has some goal\n");
	    break;
	}
	/* (might be able to defend by interposing self?) */
	return;
    }
    /* Generally useful to capture things, so even units on defense
       should take opportunities if they can. */
    if (capture_indep_if_nearby(unit))
      return;
    if (capture_useful_if_nearby(unit))
      return;
    if (can_fire(unit)) {
	/* No special goal, look for something to shoot at. */
	if (fire_at_opportunity(unit)) {
	    execute_task(unit);
	    return;
	}
	/* Nothing to shoot at, so just hang out. */
	/* (should consider moving to better shooting position if possible) */
    } else if (can_attack(unit)) {
	/* Consider moving a short ways to attack an interloper. */
	range = 3 * u_acp(u);
	if (go_after_victim(unit, range)) {
	    execute_task(unit);
	    return;
	}
	/* Nobody close by, just hang out, shifting around a bit occasionally. */
	if (mobile(unit->type) && probability(10)) {
	    if (random_point_near(unit->x, unit->y, u_acp(u), &x1, &y1)) {
		DMprintf("%s to shift defensive position to %d,%d\n",
			 unit_desig(unit), x1, y1);
		set_move_to_task(unit, x1, y1, 0);
		execute_task(unit);
		return;
	    }
	}
    } else {
	/* No specific goal, no combat ability - so nothing to do! */
	/* (but should test for ability to detonate or capture first) */
    }
    if (plan->tasks) {
	execute_task(unit);
    } else if (!plan->reserve) {
	/* Just stay in reserve for now. */
	DMprintf("%s going into defensive reserve\n", unit_desig(unit));
	plan->reserve = TRUE;
    } else {
	/* can never get here? */
    }
}

static void
plan_colonize(Unit *unit)
{
    Task *task = unit->plan->tasks;
    Plan *plan = unit->plan;
    int 	type[MAXUTYPES];
    int 	numtypes = 0;
    int 	u = unit->type;
    int 	u2;

    /* Maybe set building task for colonizers. */
    if (u_colonizer(u)) {
	/* Pick an advanced unit type to build. */
	for_all_unit_types(u2) {
	    if (u_advanced(u2) 
		&& could_create(u, u2)
		&& type_allowed_on_side(u2, unit->side)) {
		type[numtypes] = u2;
		numtypes++;
	    }
	}
	if (numtypes) {
	    u2 = type[xrandom(numtypes)];
	    if (task == NULL || task->type != TASK_BUILD) {
		/* First test if we have found a good spot to settle. */
		if(good_cell_to_colonize(u2, unit)) {
		    /* Initiate construction if spot is OK. */
		    set_build_task(unit, u2, 1);
		    DMprintf("%s colonizing by building %s\n", 
			     unit_desig(unit), u_type_name(u2));
		}
	    } else DMprintf("%s already building, leaving alone\n", 
			    unit_desig(unit));
	} else DMprintf("%s has no way to colonize\n", 
			unit_desig(unit));
    }
    /* Then execute task. */
    if (unit->plan->tasks) {
	execute_task(unit);
	return;
	/* No pending tasks. */
    } else if (mobile(u)) {
	/* Let colonizer move in a random dir along straight line. */
	set_move_dir_task(unit, random_dir(), xrandom(20));
	return;
	/* Set a build task for non-colonizers. */
    } else {
	plan_colonizer_support(unit);
	return;
    }
    /* Check again if we now have a task to execute. */
    if (unit->plan->tasks) {
	execute_task(unit);
	/* Let colonizer move in a random dir along straight line if task failed. */
	if (mobile(u) && unit->plan->last_task_outcome == TASK_FAILED)
	  set_move_dir_task(unit, random_dir(), xrandom(20));

	/* Else maybe go to sleep. */
    } else if (probability(10)) {
	DMprintf("no tasks, going to sleep (dunno why)\n");
	plan->asleep = TRUE;
    }
}

static void
plan_colonizer_support(Unit *unit)
{
    int 	type[MAXUTYPES];
    int 	numtypes = 0;
    int 	u;

	/* Load all colonizing types into type vector. */
    for_all_unit_types(u) {
	if (type_allowed_on_side(u, unit->side) 
	    && could_create(unit->type, u)
	    && u_colonizer(u)) {
	    type[numtypes] = u;
	    numtypes++;
	} 
    }
    /* Then pick one of them at random. */
    if (numtypes) {
	u = type[xrandom(numtypes)];
	DMprintf("%s supporting colonization by building %s\n",
		 unit_desig(unit), u_type_name(u));
	push_build_task(unit, u, 1);
	return;
	/* Load all possible types instead if we failed. */
    } else for_all_unit_types(u) {
	if (type_allowed_on_side(u, unit->side) 
	    && could_create(unit->type, u)) {
	    type[numtypes] = u;
	    numtypes++;
	} 
    }
    /* Then pick one of them at random. */
    if (numtypes) {
	u = type[xrandom(numtypes)];
	DMprintf("%s can't build colonizers, building %s instead\n",
		 unit_desig(unit), u_type_name(u));
	push_build_task(unit, u, 1);
	return;
	/* Nothing can be built. */
    } else	DMprintf("Sorry, %s can't build anything!\n", unit_desig(unit));
}

static void
plan_improve(Unit *unit)
{
    int 	type[MAXUTYPES];
    int 	numtypes = 0;
    int 	u;

	/* Load all immobile types (facilities) into type vector. */
    for_all_unit_types(u) {
	if (type_allowed_on_side(u, unit->side) 
	    && could_create(unit->type, u)
	    && !mobile(u)) {
	    type[numtypes] = u;
	    numtypes++;
	} 
    }
    /* Then pick one of them at random. */
    if (numtypes) {
	u = type[xrandom(numtypes)];
	DMprintf("%s improving itself by building %s\n",
		 unit_desig(unit), u_type_name(u));
	push_build_task(unit, u, 1);
	return;
	/* Load all possible types instead if we failed. */
    } else for_all_unit_types(u) {
	if (type_allowed_on_side(u, unit->side) 
	    && could_create(unit->type, u)) {
	    type[numtypes] = u;
	    numtypes++;
	} 
    }
    /* Then pick one of them at random. */
    if (numtypes) {
	u = type[xrandom(numtypes)];
	DMprintf("%s can't build facility, building %s instead\n",
		 unit_desig(unit), u_type_name(u));
	push_build_task(unit, u, 1);
	return;
	/* Nothing can be built. */
    } else	DMprintf("Sorry, %s can't build anything!\n", unit_desig(unit));
}

static int 
good_cell_to_colonize(int u, Unit *unit)
{
    int 	x1, y1, m, supply[MAXMTYPES], maxsize = PROPHI;
    Unit	*unit2;
			
    /* First zero the supply vector. */
    for_all_material_types (m)
      supply[m] = 0;
    /* Go through all cells within reach and compute supply of materials. */
    for_all_cells_within_n_steps(unit->x, unit->y, u_reach(u), x1, y1) {
	if (!inside_area(x1, y1))
	  continue;
	/* Return FALSE if other city or enemy unit is nearby. */
	for_all_stack(x1, y1, unit2) {
	    if (u_advanced(unit2->type) 
		|| !trusted_side(unit2->side, unit->side))
	      return FALSE; 
	}
	/* Don't count supply in cells already used by others. */
	if (user_at(x1, y1) != NOUSER)
	  continue;
	/* Add the supply of materials from this cell. */
	for_all_material_types (m)
	  supply[m] += tm_production(terrain_at(x1, y1), m); 
    }
    /* Calculate max sustainable city size at this location. */
    for_all_material_types (m) {
	if (um_consumption_per_size(u, m) > 0)
	  maxsize =
	    min(maxsize, (float) supply[m] / um_consumption_per_size(u, m));
    }
    /* Return FALSE if there is not enough food around 
	to sustain the minimal size goal for this unit type. */
    if (maxsize < u_minimal_size_goal(u))
      return FALSE;
    return TRUE;
}

static void
plan_exploration(unit)
Unit *unit;
{
    Plan *plan = unit->plan;
    int u = unit->type;
    int x, y, w, h, range, x1, y1;

    /* If the world has no secrets, exploration is sort of pointless. */
    if (all_see_all) {
    	plan->reserve = TRUE;
    	return;
    }
    if (resupply_if_low(unit)) {
	if (plan->tasks)
	  execute_task(unit);
	return;
    }
    if (repair_if_damaged(unit)) {
	if (plan->tasks)
	  execute_task(unit);
	return;
    }
    if (capture_indep_if_nearby(unit))
      return;
    if (capture_useful_if_nearby(unit))
      return;
    if (plan->tasks) {
    	/* (should see if a change of task is worthwhile) */
    	execute_task(unit);
    	return;
    }
    if (plan->maingoal) {
	switch (plan->maingoal->type) {
	  case GOAL_VICINITY_KNOWN:
	  case GOAL_VICINITY_HELD:
	    if (mobile(u)) {
		x = plan->maingoal->args[0];  y = plan->maingoal->args[1];
		w = plan->maingoal->args[2];  h = plan->maingoal->args[3];
		if (distance(x, y, unit->x, unit->y) > max(w, h)) {
		    /* Out of the area, move into it. */
	    	    if (random_point_near(x, y, max(w, h) / 2, &x1, &y1)) {
	    		x = x1;  y = y1;
	    	    }
		    DMprintf("%s to explore towards %d,%d\n",
			     unit_desig(unit), x, y);
		    set_move_to_task(unit, x, y, max(w, h) / 2);
		} else {
		    if (explore_reachable_cell(unit, max(w, h) + 2)) {
			/* Found a cell to explore. */
		    } else {
		    	if (flip_coin()) {
			    DMprintf("%s clearing goal\n", unit_desig(unit));
			    plan->maingoal = NULL;
		    	}
			DMprintf("%s to walk randomly\n", unit_desig(unit));
			random_walk(unit);
		    }
		}
	    } else {
		plan_explorer_support(unit);
	    }
	    break;
	  case GOAL_WORLD_KNOWN:
	    if (mobile(u)) {
		if (explore_reachable_cell(unit, area.maxdim)) {
		    /* Found a cell to explore. */
		} else {
		    DMprintf("%s to walk randomly\n", unit_desig(unit));
		    random_walk(unit);
		}
	    } else {
		plan_explorer_support(unit);
	    }
	    break;
	  default:
	    DMprintf("%s goal %s?\n",
		     unit_desig(unit), goal_desig(unit->plan->maingoal));
	    break;
	}
    } else {
	/* No specific goal, just poke around. */
	if (mobile(u)) {
	    range = area.maxdim / 2;
	    if (explore_reachable_cell(unit, range)) {
	    } else if (flip_coin()) {
		/* (should call a plan eraser) */
		unit->plan->type = PLAN_PASSIVE;
	    } else {
		DMprintf("%s to walk randomly\n", unit_desig(unit));
		random_walk(unit);
	    }
	} else {
	    plan_explorer_support(unit);
	}
    }
    if (plan->tasks) {
        execute_task(unit);
    } else {
        if (probability(10)) {
    	    DMprintf("no tasks, going to sleep (dunno why)\n");
    	    plan->asleep = TRUE;
        }
    }
}

static void
plan_explorer_support(unit)
Unit *unit;
{
    int u = unit->type, u2, u3, backup;
    Task *task;

    for_all_unit_types(u2) {
	    if (mobile(u2)
		&& 1 /* better on more kinds of terrain? */
		&& uu_acp_to_create(u, u2) > 0) {
		backup = u2;
		if (flip_coin()) {
		    u3 = u2;
		    break;
		}
	    }
    }
    if (u3 == NONUTYPE)
      u3 = backup;
    if (u3 != NONUTYPE) {
	task = unit->plan->tasks;
    	if (task == NULL || task->type != TASK_BUILD) {
	    DMprintf("%s supporting exploration by building %s\n",
		     unit_desig(unit), u_type_name(u3));
	    push_build_task(unit, u3, 2);
	} else {
	    DMprintf("%s already building, leaving alone\n",
		     unit_desig(unit));
	}
    } else {
    	DMprintf("%s has no way to support exploration\n", unit_desig(unit));
    }
}

static int victimx, victimy, victimrating, victimutype, victimsidenum;

int
victim_here(x, y)
int x, y;
{
    int u2 = NONUTYPE, uview, rating, dist;
    Unit *unit2;
    Side *side = tmpunit->side, *oside = NULL;

    if (units_visible(side, x, y)) {
	if ((unit2 = unit_at(x, y)) != NULL) {
	    u2 = unit2->type;
	    oside = unit2->side;
	} else {
	    return FALSE;
	}
    } else if (terrain_view(side, x, y) != UNSEEN
	       && (uview = unit_view(side, x, y)) != EMPTY) {
	u2 = vtype(uview);
	oside = side_n(vside(uview));
    } else {
	return FALSE;
    }
    if (is_unit_type(u2)
	&& enemy_side(side, oside)
	&& ((could_hit(tmpunit->type, u2)
	     && uu_damage(tmpunit->type, u2) > 0
	     && (!worth_capturing(side, u2, oside, x, y)
	         || capture_chance(tmpunit->type, u2, oside) > 0))
	    || capture_chance(tmpunit->type, u2, oside) > 0)
	) {
	/* If our unit can't capture the prospective victim, but somebody
	   else is planning to do so, leave it alone and keep looking for
	   a victim. */
	if ((uu_acp_to_attack(tmpunit->type, u2) < 1
	     || capture_chance(tmpunit->type, u2, oside) <= 0)
	    && side_planning_to_capture(side, u2, x, y)) {
	    return FALSE;
	}
	if (capture_chance(tmpunit->type, u2, oside) > 0) {
	    victimutype = u2;
	    victimsidenum = side_number(oside);
	    return TRUE;
	}
	if (tmpunit->occupant != NULL
	    && could_hit(u2, tmpunit->type)
	    && uu_damage(u2, tmpunit->type) > 0
	    /* and valuable occupants not protected... */
	    ) {
	    return FALSE;
	}
	rating = uu_zz_bhw(tmpunit->type, u2);
	/* Further-away units are less interesting than closer ones. */
	dist = distance(tmpunit->x, tmpunit->y, x, y);
	if (dist > u_acp(tmpunit->type)) {
	    rating /= max(1, isqrt(dist - u_acp(tmpunit->type)));
	}
	if (rating > victimrating || (rating == victimrating && flip_coin())) {
	    victimx = x;  victimy = y;
	    victimrating = rating;
	    victimutype = u2;
	    victimsidenum = side_number(oside);
	}
    }
    return FALSE;
}

/* This decides whether a given unit type seen at a given location is worth
   trying to capture. */

int
worth_capturing(side, u2, side2, x, y)
Side *side, *side2;
int u2, x, y;
{
    int u, bestchance = 0;

    /* See how likely we are to be able to capture the type. */
    for_all_unit_types(u) {
	bestchance = max(capture_chance(u, u2, side2), bestchance);
    }
    if (bestchance == 0)
      return FALSE;
    /* (should account for other considerations too, like which types of units we have) */
    return TRUE;
}

/* This routine looks for somebody, anybody to attack. */

int
go_after_victim(unit, range)
Unit *unit;
int range;
{
    int x, y, rslt;

    tmpunit = unit;
    DMprintf("%s seeking victim within %d; found ",
	     unit_desig(unit), range);
    victimrating = -9999;
    rslt = search_around(unit->x, unit->y, range, victim_here, &x, &y, 1);
    if (rslt) {
	DMprintf("s%d %s at %d,%d\n", victimsidenum, u_type_name(victimutype), x, y);
	/* Set up a task to go after the unit found. */
	/* (should be able to set capture task if better) */
	set_hit_unit_task(unit, x, y, victimutype, victimsidenum);
	if (unit->transport != NULL
	    && mobile(unit->transport->type)
	    && unit->transport->plan) {
	    set_move_to_task(unit->transport, x, y, 1);
	}
    } else if (victimrating > -9999) {
	DMprintf("s%d %s (rated %d) at %d,%d\n",
		 victimsidenum, u_type_name(victimutype), victimrating, victimx, victimy);
	/* Set up a task to go after the unit found. */
	/* (should be able to set capture task if better) */
	set_hit_unit_task(unit, victimx, victimy, victimutype, victimsidenum);
	if (unit->transport != NULL
	    && mobile(unit->transport->type)
	    && unit->transport->plan) {
	    set_move_to_task(unit->transport, victimx, victimy, 1);
	}
	/* We succeeded after all. */
	rslt = TRUE;
    } else {
	DMprintf("nothing\n");
    }
    return rslt;
}

static int targetx, targety, targetrating, targetutype, targetsidenum;

int
target_here(x, y)
int x, y;
{
    int u2 = NONUTYPE, uview, rating, dist;
    Unit *unit2;
    Side *side = tmpunit->side, *oside = NULL;

    if (units_visible(side, x, y)) {
	if ((unit2 = unit_at(x, y)) != NULL) {
	    u2 = unit2->type;
	    oside = unit2->side;
	} else {
	    /* Nothing to shoot at here. */
	    return FALSE;
	}
    } else if (terrain_view(side, x, y) != UNSEEN
	       && (uview = unit_view(side, x, y)) != EMPTY) {
	u2 = vtype(uview);
	oside = side_n(vside(uview));
    } else {
	/* Nothing (or at least nothing visible) to shoot at here. */
	return FALSE;
    }
    if (is_unit_type(u2)
	&& enemy_side(side, oside)
	&& could_hit(tmpunit->type, u2)
	&& uu_damage(tmpunit->type, u2) > 0
	/* and have correct ammo */
	&& !side_planning_to_capture(side, u2, x, y)
	) {
        rating = uu_zz_bfw(tmpunit->type, u2);
	/* Further-away units are less interesting than closer ones. */
	dist = distance(tmpunit->x, tmpunit->y, x, y);
	if (dist > 0)
	  rating /= dist;
	if (rating > targetrating || (rating == targetrating && flip_coin())) {
	    targetx = x;  targety = y;
	    targetrating = rating;
	    targetutype = u2;
	    targetsidenum = side_number(oside);
	}
    }
    return FALSE;
}

int
fire_at_opportunity(unit)
Unit *unit;
{
    int x, y, range, rslt;

    tmpunit = unit;
    range = u_range(unit->type);
    targetrating = -9999;
    DMprintf("%s seeking target within %d; found ",
             unit_desig(unit), range);
    rslt = search_around(unit->x, unit->y, range, target_here, &x, &y, 1);
    if (rslt) {
	DMprintf("s%d %s at %d,%d\n", targetsidenum, u_type_name(targetutype), x, y);
	/* Set up a task to shoot at the unit found. */
	set_hit_unit_task(unit, x, y, targetutype, targetsidenum);
    } else if (targetrating > -9999) {
	DMprintf("s%d %s (rated %d) at %d,%d\n",
		 targetsidenum, u_type_name(targetutype), targetrating, x, y);
	/* Set up a task to shoot at the unit found. */
	set_hit_unit_task(unit, targetx, targety, targetutype, targetsidenum);
    } else {
	DMprintf("nothing\n");
    }
    return rslt;
}

/* This is called by individual plans to see if a particular unit type at a
   particular location is scheduled to be captured. */

static int
side_planning_to_capture(side, u, x, y)
Side *side;
int u, x, y;
{
    Task *task;
    Unit *unit;

    for_all_side_units(side, unit) {
	if (in_play(unit)
	    && unit->plan) {
	    for_all_tasks(unit->plan, task) {
		if (task->type == TASK_CAPTURE
		    && task->args[0] == x && task->args[1] == y
		    && (task->args[2] == NONUTYPE || task->args[2] == u))
		  return TRUE;
		if (task->type == TASK_HIT_UNIT
		    && task->args[0] == x && task->args[1] == y
		    && (task->args[2] == NONUTYPE || task->args[2] == u)
		    && uu_capture(unit->type, u) > 0)
		  return TRUE;
	    }
	}
    }
    return FALSE;
}

/* Check to see if our grand plans are at risk of being sideswiped by lack of
   supply, and set up a resupply task if so. */

int
resupply_if_low(unit)
Unit *unit;
{
    int u = unit->type, lowm;
    Task *curtask = unit->plan->tasks;

    if (!mobile(u))
      return FALSE;
    lowm = low_on_supplies_one(unit);
    if (lowm != NONMTYPE) {
	/* See if we're already moving to a supply source. */
	if (curtask != NULL
	    && curtask->type == TASK_MOVE_TO /* or other types? */
	    && supplies_here(unit, curtask->args[0], curtask->args[1], lowm))
	  /* Let the movement task execute. */
	  return FALSE;
	/* See if we already have a resupply task for the same material. */
	if (curtask != NULL
	    && curtask->type == TASK_RESUPPLY
	    && curtask->args[0] == lowm)
	  return (execute_task(unit) != TASK_FAILED);
	/* Otherwise set up a task. */
	DMprintf("%s low on %s, looking for source\n",
		 unit_desig(unit), m_type_name(lowm));
	set_resupply_task(unit, lowm);
	return (execute_task(unit) != TASK_FAILED);
    }
    return FALSE;
}

/* Return a type of essential material that the unit is running out of. */

int
low_on_supplies_one(unit)
Unit *unit;
{
    int u = unit->type, m;

    for_all_material_types(m) {
	if ((um_base_consumption(u, m) > 0 || um_consumption_per_move(u, m) > 0)
	    && um_storage_x(u, m) > 0
	    && unit->supply[m] <= (unit_doctrine(unit)->resupply_percent * um_storage_x(u, m)) / 100
	    ) {
	    return m;
	}
    }
    return NONMTYPE;
}

int
rearm_if_low(unit)
Unit *unit;
{
    int u = unit->type, lowm;
    Task *curtask = unit->plan->tasks;

    if (!mobile(u))
      return FALSE;
    lowm = low_on_ammo_one(unit);
    if (lowm != NONMTYPE) {
	if (curtask != NULL
	    && curtask->type == TASK_MOVE_TO /* or other types? */
	    && supplies_here(unit, curtask->args[0], curtask->args[1], lowm))
	  /* Let the movement task execute. */
	  return FALSE;
	/* See if we already have a resupply task for the same material. */
	if (curtask != NULL
	    && curtask->type == TASK_RESUPPLY
	    && curtask->args[0] == lowm)
	  return (execute_task(unit) != TASK_FAILED);
	/* Otherwise set up a task. */
	DMprintf("%s low on %s, looking for source\n",
		 unit_desig(unit), m_type_name(lowm));
	set_resupply_task(unit, lowm);
	return (execute_task(unit) != TASK_FAILED);
    }
    return FALSE;
}

/* Return a type of material that we want to use in combat. */

int
low_on_ammo_one(unit)
Unit *unit;
{
    int u = unit->type, m;

    for_all_material_types(m) {
	if (um_consumption_per_attack(u, m) > 0
	    && um_storage_x(u, m) > 0
	    && unit->supply[m] <= (unit_doctrine(unit)->rearm_percent * um_storage_x(u, m)) / 100
	    ) {
	    return m;
	}
    }
    return NONMTYPE;
}

int
supplies_here(unit, x, y, m)
Unit *unit;
int x, y, m;
{
    Unit *unit2;

    for_all_stack(x, y, unit2) {
	if (unit2 != unit
	    && unit_trusts_unit(unit2, unit)
	    && unit2->supply[m] > 0
	    && um_outlength(unit2->type, m) >= 0) {
	    return TRUE;
	}
    }
    return FALSE;
}

extern int repair_if_damaged(Unit *unit);
extern int repairs_here(Unit *unit, int x, int y);

int
repair_if_damaged(unit)
Unit *unit;
{
    int u = unit->type;
    Task *curtask = unit->plan->tasks;

    if (unit->hp > (u_hp_max(u) * unit_doctrine(unit)->repair_percent) / 100)
      return FALSE;
    if (u_hp_recovery(u) <= 0 && !any_auto_repair)
      return FALSE;
    /* See if we already have a repair task. */
    if (curtask != NULL
	&& curtask->type == TASK_REPAIR)
      return (execute_task(unit) != TASK_FAILED);
    if (curtask != NULL
	&& curtask->type == TASK_MOVE_TO /* or other types? */
	&& repairs_here(unit, curtask->args[0], curtask->args[1]))
      /* Just let the movement task execute. */
      return FALSE;
    /* (should test whether any repairing units in existence) */
    /* Otherwise set up a task. */
    DMprintf("%s badly damaged, looking for repairs\n", unit_desig(unit));
    set_repair_task(unit);
    return (execute_task(unit) != TASK_FAILED);
}

int
repairs_here(unit, x, y)
Unit *unit;
int x, y;
{
    Unit *unit2;

    for_all_stack(x, y, unit2) {
	if (unit2 != unit
	    && unit_trusts_unit(unit2, unit)
	    && (uu_auto_repair(unit2->type, unit->type) > 0
		|| 0 /* (should allow for explicit repair actions) */)) {
	    return TRUE;
	}
    }
    return FALSE;
}

/* Look within a limited distance for any independent unit that could be
   captured, and set up tasks to go get it. */

int
capture_indep_if_nearby(unit)
Unit *unit;
{
    int u = unit->type, range = 5;
    int x, y, rslt;
    Task *curtask = unit->plan->tasks;

    if (!mobile(u))
      return FALSE;
    if (!could_capture_any(unit->type))
      return FALSE;
    tmpunit = unit;
    /* See if we're already doing such a task. */
    if (curtask != NULL
	&& ((curtask->type == TASK_MOVE_TO
	     && indep_captureable_here(curtask->args[0], curtask->args[1]))
	    || (curtask->type == TASK_CAPTURE
		&& indep_captureable_here(curtask->args[0], curtask->args[1]))))
      return FALSE;
    DMprintf("%s searching for easy capture within %d; found ",
	     unit_desig(unit), range);
    rslt = search_around(unit->x, unit->y, range, indep_captureable_here,
			 &x, &y, 1);
    if (rslt) {
	DMprintf("one at %d,%d\n", x, y);
	/* Set up a task to go after the unit found. */
	set_capture_task(unit, x, y);
	if (unit->transport
	    && mobile(unit->transport->type)
	    && unit->transport->plan) {
	    set_move_to_task(unit->transport, x, y, 1);
	}
	return (execute_task(unit) != TASK_FAILED);
    } else {
	DMprintf("nothing\n");
    }
    return FALSE;
}

int
indep_captureable_here(x, y)
int x, y;
{
    int u2 = NONUTYPE, uview;
    Unit *unit2;
    Side *side = tmpunit->side, *side2 = NULL;

    /* Collect a type and side of unit at the given location. */
    if (units_visible(side, x, y)) {
	if ((unit2 = unit_at(x, y)) != NULL) {
	    u2 = unit2->type;
	    side2 = unit2->side;
	} else {
	    return FALSE;
	}
    } else if (terrain_view(side, x, y) != UNSEEN
	       && (uview = unit_view(side, x, y)) != EMPTY) {
	u2 = vtype(uview);
	side2 = side_n(vside(uview));
    } else {
	return FALSE;
    }
    return (is_unit_type(u2)
    	    && side2 == NULL
	    && capture_chance(tmpunit->type, u2, side2) > 10);
}

/* Look within a limited distance for any type of unit that would be good to
   own, and set up tasks to go get it. */

int
capture_useful_if_nearby(unit)
Unit *unit;
{
    int u = unit->type, range = 2;
    int x, y, rslt;
    Task *curtask = unit->plan->tasks;

    if (!mobile(u))
      return FALSE;
    if (!could_capture_any(unit->type))
      return FALSE;
    tmpunit = unit;
    /* See if we're already doing such a task. */
    if (curtask != NULL
	&& ((curtask->type == TASK_MOVE_TO
	     && useful_captureable_here(curtask->args[0], curtask->args[1]))
	    || (curtask->type == TASK_CAPTURE
		&& useful_captureable_here(curtask->args[0], curtask->args[1]))))
      return FALSE;
    DMprintf("%s searching for useful capture within %d; found ",
	     unit_desig(unit), range);
    rslt = search_around(unit->x, unit->y, range, useful_captureable_here,
			 &x, &y, 1);
    if (rslt) {
	DMprintf("one at %d,%d\n", x, y);
	/* Set up a task to go after the unit found. */
	set_capture_task(unit, x, y);
	if (unit->transport
	    && mobile(unit->transport->type)
	    && unit->transport->plan) {
	    set_move_to_task(unit->transport, x, y, 1);
	}
	return (execute_task(unit) != TASK_FAILED);
    } else {
	DMprintf("nothing\n");
    }
    return FALSE;
}

int
useful_captureable_here(x, y)
int x, y;
{
    int u2 = NONUTYPE, uview;
    Unit *unit2;
    Side *side = tmpunit->side, *oside = NULL;

    if (units_visible(side, x, y)) {
	for_all_stack(x, y, unit2) {
	    u2 = unit2->type;
	    oside = unit2->side;
            if (is_unit_type(u2)
		&& !trusted_side(side, oside)
		&& capture_chance(tmpunit->type, u2, oside) > 0
		&& useful_type(side, u2)
		) {
		tmputype = u2;
		return TRUE;
	    }
	}
    } else if (terrain_view(side, x, y) != UNSEEN
	       && (uview = unit_view(side, x, y)) != EMPTY) {
	u2 = vtype(uview);
	oside = side_n(vside(uview));
        if (is_unit_type(u2)
    	    && !trusted_side(side, oside)
	    && capture_chance(tmpunit->type, u2, oside) > 0
	    && useful_type(side, u2)
	    ) {
	    tmputype = u2;
	    return TRUE;
	}
    }
    return FALSE;
}

/* Return true if the given type of unit is useful in some way to the
   given side.  This is almost always true. */

int
useful_type(side, u)
Side *side;
int u;
{
    if (!type_allowed_on_side(u, side))
      return FALSE;
    return TRUE;
}

int
could_capture_any(u)
int u;
{
    int u2;

    for_all_unit_types(u2) {
	if (uu_capture(u, u2) > 0 || uu_indep_capture(u, u2) > 0)
	  return TRUE;
	/* also check if u2 in game, on other side, etc? */
    }
    return FALSE;
}

/* This is a semi-testing routine that basically picks something for
   the unit to do without worrying about its validity.  The rest of
   the system should function correctly no matter what this thing
   comes up with.  Piles of warnings are likely, but that's OK. */

/* (should actually have two modes for this - testing, which allows
   bad data, and normal usage with no garbage generated, which is used
   with unintelligent AIs) */

static void
plan_random(Unit *unit)
{
    int dir, x1, y1, n, randmtype;
    TaskType tasktype;
    Task *task;
    Action action;
    char *argtypestr;

    if (flip_coin()) {
	if (unit->plan->tasks) {
	    execute_task(unit);
	    return;
	}
	/* Pick a random task. */
	tasktype = xrandom((int) NUMTASKTYPES);
	switch (tasktype) {
	  case TASK_NONE:
	    task = create_task(tasktype);
	    add_task(unit, CLEAR_AGENDA, task);
	    break;
	  case TASK_BUILD:
	    task = create_build_task(unit, xrandom(numutypes), xrandom(99));
	    add_task(unit, CLEAR_AGENDA, task);
	    break;
	  case TASK_CAPTURE:
	    dir = random_dir();
	    point_in_dir(unit->x, unit->y, dir, &x1, &y1);
	    set_capture_task(unit, x1, y1);
	    break;
	  case TASK_COLLECT:
	    if (nummtypes > 0) {
		randmtype = (flip_coin() ? xrandom(nummtypes) : NONMTYPE);
	    	set_collect_task(unit, randmtype, unit->x, unit->y);
	    }
	    break;
	  case TASK_DISBAND:
	    set_disband_task(unit);
	    break;
	  case TASK_DO_ACTION:
	    task = create_task(tasktype);
	    add_task(unit, CLEAR_AGENDA, task);
	    break;
	  case TASK_HIT_POSITION:
	    task = create_task(tasktype);
	    add_task(unit, CLEAR_AGENDA, task);
	    break;
	  case TASK_HIT_UNIT:
	    task = create_task(tasktype);
	    add_task(unit, CLEAR_AGENDA, task);
	    break;
	  case TASK_MOVE_DIR:
	    dir = random_dir();
	    n = xrandom(10);
	    set_move_dir_task(unit, dir, n);
	    break;
	  case TASK_MOVE_TO:
	    dir = random_dir();
	    point_in_dir(unit->x, unit->y, dir, &x1, &y1);
	    set_move_to_task(unit, x1, y1, 0);
	    break;
	  case TASK_OCCUPY:
	    task = create_task(tasktype);
	    add_task(unit, CLEAR_AGENDA, task);
	    break;
	  case TASK_PICKUP:
	    task = create_task(tasktype);
	    add_task(unit, CLEAR_AGENDA, task);
	    break;
	  case TASK_PRODUCE:
	    if (nummtypes > 0) {
		randmtype = (flip_coin() ? xrandom(nummtypes) : NONMTYPE);
		task = create_produce_task(unit, randmtype, xrandom(100));
		add_task(unit, CLEAR_AGENDA, task);
	    }
	    break;
	  case TASK_REPAIR:
	    set_repair_task(unit);
	    break;
	  case TASK_RESEARCH:
	    task = create_task(tasktype);
	    add_task(unit, CLEAR_AGENDA, task);
	    break;
	  case TASK_RESUPPLY:
	    if (nummtypes > 0) {
		randmtype = (flip_coin() ? xrandom(nummtypes) : NONMTYPE);
		set_resupply_task(unit, randmtype);
	    }
	    break;
	  case TASK_SENTRY:
	    set_sentry_task(unit, xrandom(5));
	    break;
	  default:
	    case_panic("task type", tasktype);
	    break;
	}
    }
    if (unit->plan && unit->plan->tasks)
      return;
    /* Otherwise go for a random action. */
    memset(&action, 0, sizeof(Action));
    action.type = (ActionType) xrandom((int) NUMACTIONTYPES);
    argtypestr = actiondefns[(int) action.type].argtypes;
    make_plausible_random_args(argtypestr, 0, &(action.args[0]), unit);
    if (flip_coin()) {
	action.actee = unit->id;
    } else {
	while (find_unit(action.actee = xrandom(numunits)+1) == NULL
	       && probability(98));
    }
    unit->act->nextaction = action;
    DMprintf("%s will randomly try %s\n",
	     unit_desig(unit), action_desig(&action));
}

/* This attempts to make some vaguely plausible arguments for an action,
   using the types of each arg as a guide.  It also generates *invalid*
   arguments occasionally, which tests error checking in the actions' code. */

void
make_plausible_random_args(argtypestr, i, args, unit)
char *argtypestr;
int i;
short *args;
Unit *unit;
{
    char argch;
    int	slen, arg;

    slen = strlen(argtypestr);
    while (i < slen && i < 10) {
	argch = argtypestr[i];
	switch (argch) {
	  case 'n':
	    arg = (flip_coin() ? xrandom(10) :
		   (flip_coin() ? xrandom(100) :
		    (xrandom(20000) - 10000)));
	    break;
	  case 'u':
	    /* Go a little outside range, so as to get some invalid types. */
	    arg = xrandom(numutypes + 2) - 1;
	    break;
	  case 'm':
	    arg = xrandom(nummtypes + 2) - 1;
	    break;
	  case 't':
	    arg = xrandom(numttypes + 2) - 1;
	    break;
	  case 'a':
	    arg = xrandom(numatypes + 2) - 1;
	    break;
	  case 'x':
	    arg = (unit != NULL && flip_coin() ? (unit->x + xrandom(5) - 2) :
		   (xrandom(area.width + 4) - 2));
	    break;
	  case 'y':
	    arg = (unit != NULL && flip_coin() ? (unit->y + xrandom(5) - 2) :
		   (xrandom(area.height + 4) - 2));
	    break;
	  case 'z':
	    arg = (flip_coin() ? 0 : xrandom(10));
	    break;
	  case 'd':
	    arg = random_dir();
	    break;
	  case 'U':
	    /* Cast around for a valid unit. */
	    while (find_unit(arg = xrandom(numunits)+1) == NULL
		   && probability(98));
	    break;
	  case 'S':
	    arg = xrandom(numsides + 3) - 1;
	    break;
	  default:
	    run_warning("Garbled action arg type '%c'\n", argch);
	    arg = 0;
	    break;
	}
	args[i++] = arg;
    }
}

/* Random walking just attempts to move around. */

void
random_walk(unit)
Unit *unit;
{
    int dir = random_dir(), x1, y1, tries = 0;

    while (!interior_point_in_dir(unit->x, unit->y, dir, &x1, &y1)) {
    	if (++tries > 500) {
	    run_warning("something is wrong");
	    break;
	}
	dir = random_dir();
    }
    set_move_to_task(unit, x1, y1, 0);
}

/* Record the unit as waiting for orders about what to do. */

void
set_waiting_for_tasks(unit, flag)
Unit *unit;
int flag;
{
    unit->plan->waitingfortasks = flag;
    if (unit->side != NULL)
      unit->side->numwaiting += (unit->plan->waitingfortasks ? 1 : -1);
}

/* Put a unit in reserve. */

void
reserve_unit(side, unit)
Side *side;
Unit *unit;
{
    if (unit->plan) {
	unit->plan->reserve = TRUE;
	set_waiting_for_tasks(unit, FALSE);
	update_unit_display(side, unit, TRUE);
    }
}

/* General routine to wake a unit up (and maybe all its cargo). */

void
wake_unit(side, unit, wakeocc)
Side *side;
Unit *unit;
int wakeocc;
{
    Unit *occ;

    /* (should test that side is permitted to wake) */
    if (unit->plan) {
	unit->plan->asleep = FALSE;
	unit->plan->reserve = FALSE;
	update_unit_display(side, unit, TRUE);
    }
    if (wakeocc) {
	for_all_occupants(unit, occ)
	  wake_unit(side, occ, wakeocc);
    }
}

/* The area wakeup. */

static int tmpflag;

static void
wake_at(x, y)
int x, y;
{
    Unit *unit;

    for_all_stack(x, y, unit) {
	if (side_controls_unit(tmpside, unit)) {
	    wake_unit(tmpside, unit, tmpflag);
	}
    }
}

void
wake_area(side, x, y, n, occs)
Side *side;
int x, y, n, occs;
{
    tmpside = side;
    tmpflag = occs;
    apply_to_area(x, y, n, wake_at);
}

void
set_formation(unit, leader, x, y, dist, flex)
Unit *unit, *leader;
int x, y, dist, flex;
{
    Plan *plan = unit->plan;
    Goal *goal;

    if (plan == NULL)
      return;
    if (!in_play(unit))
      return;
    if (leader != NULL) {
	if (!in_play(leader))
	  return;
	goal = create_goal(GOAL_KEEP_FORMATION, unit->side, TRUE);
	goal->args[0] = leader->id;
    } else {
	if (!inside_area(x, y))
	  return;
	goal = create_goal(GOAL_KEEP_FORMATION, unit->side, TRUE);
	goal->args[0] = 0;
    }
    goal->args[1] = x;  goal->args[2] = y;
    goal->args[3] = dist;
    goal->args[4] = flex;
    plan->formation = goal;
    plan->funit = leader;
}

void
delay_unit(unit, flag)
Unit *unit;
int flag;
{
    if (in_play(unit) && unit->plan) {
	unit->plan->delayed = TRUE;
    }
}


/* Return the distance that we can go by shortest path before running out
   of important supplies.  Will return at least 1, since we can *always*
   move one cell to safety.  This is a worst-case routine, too complicated
   to worry about units getting refreshed by terrain or whatever. */

int
range_left(unit)
Unit *unit;
{
    int u = unit->type, m, least = 12345; /* bigger than any real value */
    
    for_all_material_types(m) {
	if (um_consumption_per_move(u, m) > 0) {
	    least = min(least, unit->supply[m] / um_consumption_per_move(u, m));
	}
#if 0
	/* This code is too pessimistic if no account taken of supply line or
	   production, so leave out for now. */
	if (um_base_consumption(u, m) > 0) {
	    tmp = (u_speed(u) * unit->supply[m]) / um_base_consumption(u, m);
	    least = min(least, tmp);
	}
#endif
    }
    return (least == 12345 ? 1 : least);
}

/* Estimate the goodness and badness of cells in the immediate vicinity. */

int
find_worths(range)
int range;
{
    return 0;
}

/* This is a heuristic estimation of the value of one unit type
   hitting on another.  Should take cost of production into account as
   well as the chance and significance of any effect. */

int
attack_worth(unit, e)
Unit *unit;
int e;
{
    int u = unit->type, worth;

    worth = uu_zz_bhw(u, e);
    /* Risk of death? */
/*    if (uu_damage(e, u) >= unit->hp)
	worth /= (could_capture(u, e) ? 1 : 4);
    if (could_capture(u, e)) worth *= 4; */
    return worth;
}

/* Support functions. */

/* Return true if the given position is threatened by the given unit type. */

int
threat(side, u, x0, y0)
Side *side;
int u, x0, y0;
{
    int d, x, y, thr = 0;
    Side *side2;
    int view;

    for_all_directions(d) {
    	point_in_dir(x0, y0, d, &x, &y);
	view = 0 /* side_view(side, x, y) */;
	if (view != UNSEEN && view != EMPTY) {
	    side2 = side_n(vside(view));
	    if (allied_side(side, side2)) {
		if (uu_capture(u, vtype(view)) > 0) thr += 1000;
		if (uu_zz_bhw(u, vtype(view)) > 0) thr += 100;
	    }
	}
    }
    return thr;
}

void
pop_task(plan)
Plan *plan;
{
    Task *oldtask;

    if (plan->tasks) {
	oldtask = plan->tasks;
	plan->tasks = plan->tasks->next;
	free_task(oldtask);
    }
}

int
react_to_enemies(unit)
Unit *unit;
{
    return (unit->plan ? TRUE : FALSE);
}

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

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

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

/* Basic routine to compute how long a unit will take to build something. */

int
build_time(unit, prod)
Unit *unit;
int prod;
{
    int schedule = 1 /* uu_make(unit->type, prod) */;
    int u, research_delay = 0;

    /* Add penalty (or unpenalty!) for first unit of a type. */
    /* is "counts" a reliable way to test? */
    if (unit->side->counts[prod] <= 1) {
/*	research_delay = ((schedule * u_research(prod)) / 100);  */
	for_all_unit_types(u) {
	    if (unit->side->counts[u] > 1) {
		research_delay -=
		  (1 /*uu_make(unit->type, u)*/ * 
		   uu_tech_crossover(prod, u)) / 100;
	    }
	    if (research_delay > 0) {
		schedule += research_delay;
	    }
	}
    }
    return schedule;
}

int
clear_task_agenda(plan)
Plan *plan;
{
    int numcleared;
    Task *oldtask;

    if (plan == NULL || plan->tasks == NULL)
      return 0;
    numcleared = 0;
    while (plan->tasks != NULL) {
    	oldtask = plan->tasks;
    	plan->tasks = plan->tasks->next;
    	free_task(oldtask);
    	++numcleared;
    }
    return numcleared;
}

Plan *
create_plan()
{
    Plan *plan = (Plan *) xmalloc(sizeof(Plan));
    return plan;
}

void 
free_plan(plan)
Plan *plan;
{
    if (plan == NULL)
      run_error("no plan here?");
    /* Make tasks available for reallocation. */
    clear_task_agenda(plan);
    free(plan);
}

/* Describe a plan succinctly.  This is primarily for debugging, not
   for normal user display. */

char *planbuf = NULL;

char *
plan_desig(plan)
Plan *plan;
{
    Task *task;
    int extra = 0;

    if (planbuf == NULL)
      planbuf = xmalloc(1000);
    if (plan == NULL) {
	sprintf(planbuf, "no plan");
    } else if (plan->type == PLAN_NONE) {
	sprintf(planbuf, "unformed plan");
    } else {
	if (plan->tasks) {
	    tmpbuf[0] = '\0';
	    for_all_tasks(plan, task) {
		if (strlen(tmpbuf) < 100) {
		    strcat(tmpbuf, " ");
		    strcat(tmpbuf, task_desig(task));
		} else {
		    ++extra;
		}
	    }
	    if (extra > 0) {
		tprintf(tmpbuf, " ... %d more ...", extra);
	    }
	} else {
	    sprintf(tmpbuf, " no tasks");
	}
	sprintf(planbuf, "type %s %s",
		plantypenames[plan->type], goal_desig(plan->maingoal));
	if (plan->formation) {
	    strcat(planbuf, " ");
	    strcat(planbuf, goal_desig(plan->formation));
	}
	if (plan->asleep)
	  strcat(planbuf, " asleep");
	if (plan->reserve)
	  strcat(planbuf, " reserve");
	if (plan->delayed)
	  strcat(planbuf, " delayed");
	if (plan->waitingfortasks)
	  strcat(planbuf, " waiting");
	if (plan->supply_alarm)
	  strcat(planbuf, " supply_alarm");
	if (plan->supply_is_low)
	  strcat(planbuf, " supply_is_low");
	strcat(planbuf, tmpbuf);
    }
    return planbuf;
}

/* True if unit is in immediate danger of being captured. */
/* Needs check on capturer transport being seen. */

int
might_be_captured(unit)
Unit *unit;
{
    int d, x, y;
    Unit *unit2;

    for_all_directions(d) {
      if (interior_point_in_dir(unit->x, unit->y, d, &x, &y)) {
	if (((unit2 = unit_at(x, y)) != NULL) &&
	    (enemy_side(unit->side, unit2->side)) &&
	    (uu_capture(unit2->type, unit->type) > 0))
	      return TRUE;
      }
    }
    return FALSE;
}

/* Clear a unit's plan out. */

void
force_replan(side, unit, passive_only)
Side *side;
Unit *unit;
int passive_only;
{
    extern int need_ai_planning;

    if (unit->plan == NULL)
      return;
    if ((passive_only ? unit->plan->type == PLAN_PASSIVE : TRUE)) {
	unit->plan->type = PLAN_PASSIVE;
	clear_task_agenda(unit->plan);
    }
    unit->plan->maingoal = NULL;
    unit->plan->formation = NULL;
    unit->plan->funit = NULL;
    unit->plan->asleep = FALSE;
    unit->plan->reserve = FALSE;
    set_waiting_for_tasks(unit, FALSE);
    unit->plan->delayed = FALSE;
    need_ai_planning = TRUE;
}

/* Auxiliary functions for unit planning in Xconq. */

/* router flags */

#define SAMEPATH 1
#define EXPLORE_PATH 2

/* These macros are a cache used for planning purposes by machines. */

#define markloc(x, y) (set_tmp1_at(x, y, mark))

#define markedloc(x, y) (tmp1_at(x, y) == mark)

#define get_fromdir(x, y) (tmp2_at(x, y))

#define set_fromdir(x, y, dir) (set_tmp2_at(x, y, dir))

#define get_dist(x, y) (tmp3_at(x, y))

#define set_dist(x, y, d) (set_tmp3_at(x, y, d))

int
occupant_could_capture(unit, u2)
Unit *unit;
int u2;
{
    Unit *occ;

    for_all_occupants(unit, occ)
      if (uu_capture(occ->type, u2) > 0)
	return TRUE;
    return FALSE;
}

/* Check to see if there is anyone around to capture. */

int
can_capture_neighbor(unit)
Unit *unit;
{
    int d, x, y;
    int view;
    Side *side2;

    for_all_directions(d) {
      if (interior_point_in_dir(unit->x, unit->y, d, &x, &y)) {
	view = unit_view(unit->side, x, y);
	if (view != UNSEEN && view != EMPTY) {
	    side2 = side_n(vside(view));
	    if (!allied_side(unit->side, side2)) {
		if (uu_capture(unit->type, vtype(view)) > 0) {
		    /* need some other way to change move order quickly */
		    return TRUE;
		}
	    }
	}
      }
    }
    return FALSE;
}

/* check if our first occupant can capture something.  Doesn't look at
   other occupants. */

int
occupant_can_capture_neighbor(unit)
Unit *unit;
{
    Unit *occ = unit->occupant;

    if (occ != NULL && occ->act && occ->act->acp > 0 && occ->side == unit->side) {
	if (can_capture_neighbor(occ)) {
	    return TRUE;
	}
    }
    return FALSE;
}

/* Find the closes unit, first prefering bases, and then transports. */

int
find_closest_unit(side, x0, y0, maxdist, pred, rxp, ryp)
Side *side;
int x0, y0, maxdist, (*pred)(), *rxp, *ryp;
{
#if 0    
    Unit *unit;
    int u, dist;
    int found = FALSE;

    for_all_unit_types(u) {
	if (u_is_base(u)) {
	    for (unit = NULL /* side_strategy(side)->unitlist[u]*/; unit != NULL; unit = unit->mlist) {
		if (alive(unit) &&
		    (dist = distance(x0, y0, unit->x, unit->y)) <= maxdist) {
		    if ((*pred)(unit->x, unit->y)) {
			maxdist = dist - 1;
			*rxp = unit->x;  *ryp = unit->y;
			found = TRUE;
		    }
		}
	    }
	}
    }
    if (found) {
	return TRUE;
    }
    for_all_unit_types(u) {
	if (!u_is_base(u) && u_is_transport(u)) {
	    for (unit = NULL /*side_strategy(side)->unitlist[u]*/; unit != NULL; unit = unit->mlist) {
		if (alive(unit)
		    && distance(x0, y0, unit->x, unit->y) <= maxdist) {
		    if ((*pred)(unit->x, unit->y)) {
			maxdist = dist - 1;
			*rxp = unit->x;  *ryp = unit->y;
			found = TRUE;
		    }
		}
	    }
	}
    }
    if (found) {
	return TRUE;
    }
    /* (What's the point of finding a non-base/non-transport?) */
    for_all_unit_types(u) {
	if (!u_is_base(u) && !u_is_transport(u)) {
	    for (unit = NULL/*side_strategy(side)->unitlist[u]*/; unit != NULL; unit = unit->mlist) {
		if (alive(unit)
		    && distance(x0, y0, unit->x, unit->y) <= maxdist) {
		    if ((*pred)(unit->x, unit->y)) {
			maxdist = dist - 1;
			*rxp = unit->x;  *ryp = unit->y;
			found = TRUE;
		    }
		}
	    }
	}
    }
    if (found) {
	return TRUE;
    }
#endif
    return FALSE;
}

/* True if the given unit is a sort that can build other units. */

int
can_build(unit)
Unit *unit;
{
    int u2;

    for_all_unit_types(u2) {
	if (could_create(unit->type, u2))
	  return TRUE;
    }
    return FALSE;
}

int
can_build_or_help(unit)
Unit *unit;
{
    int u2;

    for_all_unit_types(u2) {
	if (could_create(unit->type, u2)
	    || uu_acp_to_build(unit->type, u2) > 0
	    || uu_acp_to_research(unit->type, u2) > 0)
	  return TRUE;
    }
    return FALSE;
}

int
can_produce(unit)
Unit *unit;
{
    int m;

    for_all_material_types(m) {
	if (um_acp_to_produce(unit->type, m))
	  return TRUE;
    }
    return FALSE;
}


/* Test if unit can move out into adjacent cells. */

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

    for_all_directions(d) {
	if (interior_point_in_dir(unit->x, unit->y, d, &x, &y)) {
	    /* (should account for world-leaving options?) */
	    if (could_live_on(unit->type, terrain_at(x, y)))
	      return TRUE;
	}
    }
    return FALSE;
}

/* Returns the type of missing supplies. Not great routine if first */
/* material is a type of ammo. */

int
out_of_ammo(unit)
Unit *unit;
{
    int u = unit->type, r;

    for_all_material_types(r) {
	if (um_consumption_per_attack(u, r) > 0 && unit->supply[r] <= 0)
	    return r;
    }
    return (-1);
}

/* Check how long a unit can sit where it is */

int
survival_time(unit)
Unit *unit;
{
    int u = unit->type, m, least = 99999, rate, tmp;
    int t = terrain_at(unit->x, unit->y);

    for_all_material_types(m) {
	rate = (um_base_consumption(u, m)
		- (um_base_production(u, m) * ut_productivity(u, t)) / 100);
	if (rate > 0) {
	    tmp = unit->supply[m];
	    if (unit->act) {
		tmp += unit->act->actualmoves * um_consumption_per_move(u, m);
	    }
	    least = min(least, tmp / rate);
	}
    }
    return least;
}

int
usable_cell(unit, x, y)
Unit *unit;
int x, y;
{
    int u = unit->type;
    int view = unit_view(unit->side, x, y);
    
    return (((view == EMPTY) || view == UNSEEN ||
	    (allied_side(side_n(vside(view)),unit->side) &&
	     could_carry(vtype(view), u))) &&
	       could_live_on(u, terrain_at(x, y)));
}

Task *explorechain;

int
explorable_cell(x, y)
int x, y;
{
    return (terrain_view(tmpside, x, y) == UNSEEN);
}

int
reachable_unknown(x, y)
int x, y;
{
    if (!inside_area(x, y))
      return FALSE;
    if (terrain_view(tmpside, x, y) == UNSEEN) {
    	if (adj_known_ok_terrain(x, y, tmpside, tmpunit->type)) {
	    return TRUE;
	} else {
	    return FALSE;
	}
    } else {
	return FALSE;
    }
}

/* Test whether the given location has an adjacent cell that is ok for
   the given type to be out in the open. */

int
adj_known_ok_terrain(x, y, side, u)
int x, y, u;
Side *side;
{
    int dir, x1, y1, t;

    if (!inside_area(x, y) || side == NULL)
      return FALSE;
    for_all_directions(dir) {
	if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
	    if (terrain_view(side, x1, y1) == UNSEEN)
	      continue;
	    t = terrain_at(x1, y1);
	    if (!terrain_always_impassable(u, t))
	      return TRUE;
	}
    }
    return FALSE;
}

/* Go to the nearest cell that we can see how to get to. */

int
explore_reachable_cell(unit, range)
Unit *unit;
int range;
{
    int x, y;

    if (all_see_all || g_terrain_seen())
      return FALSE;
    tmpunit = unit;
    tmpside = unit->side;
    DMprintf("%s searching within %d for cell to explore -", unit_desig(unit), range);
    if (search_around(unit->x, unit->y, range, reachable_unknown, &x, &y, 1)) {
	set_move_to_task(unit, x, y, 1);
	DMprintf("found one at %d,%d\n", x, y);
	return TRUE;
    }
    DMprintf("found nothing\n");
    return FALSE;
}

/* Check for any makers this unit should be capturing. */

int
should_capture_maker(unit)
Unit *unit;
{
    return 0;
}

#if 0
/* Returns true if the given unit can't leave its cell for some reason. */

int
no_possible_moves(unit)
Unit *unit;
{
    int fx = unit->x, fy = unit->y, ut = unit->type;
    int d, x, y;
    int view;
    Side *side = unit->side;

    for_all_directions(d) {
	x = wrapx(fx + dirx[d]);  y = limity(fy + diry[d]);
	view = unit_view(side, x, y);
	if (view == EMPTY) {
	    if (could_move(ut, terrain_at(x, y)))
	      return FALSE;
	} else if (enemy_side(side_n(vside(view)) , side)
		   && could_hit(ut, vtype(view))) {
	    return FALSE;
	} else if (could_carry(vtype(view), ut) &&
		   allied_side(side_n(vside(view)), side))
	  return FALSE;
    }
    return TRUE;
}
#endif

/* Estimate the usual number of turns to finish construction. */

int
normal_completion_time(u, u2)
int u, u2;
{
    if (u_acp(u) == 0 || uu_cp_per_build(u, u2) == 0)
      return (-1);
    return (u_cp(u2) - uu_creation_cp(u, u2)) /
      (uu_cp_per_build(u, u2) * u_acp(u));
}


/* True if anybody at all is on any adjacent cell. */

int
adj_unit(x, y)
int x, y;
{
    int d, x1, y1;

    for_all_directions(d) {
	if (interior_point_in_dir(x, y, d, &x1, &y1)) {
	    if (unit_at(x1, y1))
	      return TRUE;
	}
    }
    return FALSE;
}

/* A unit runs low on supplies at the halfway point.  Formula is the same
   no matter how/if occupants eat transports' supplies. */

int
past_halfway_point(unit)
Unit *unit;
{
    int u = unit->type, m;

    for_all_material_types(m) {
	if (((um_base_consumption(u, m) > 0) || (um_consumption_per_move(u, m) > 0)) &&
	    /* should check that the transport is adequate for supplying the fuel */ 
	    (unit->transport == NULL)) {
	    if (2 * unit->supply[m] <= um_storage_x(u, m))
	      return TRUE;
	}
    }
    return FALSE;
}


/* This is the maximum distance from "home" that a unit can expect to get,
   travelling on its most hostile terrain type. */

int
operating_range_worst(u)
int u;
{
    int m, t, prod, range, worstrange = area.maxdim;

    for_all_material_types(m) {
    	if (um_base_consumption(u, m) > 0) {
	    for_all_terrain_types(t) {
	    	if (!terrain_always_impassable(u, t)) {
	    	    prod = (um_base_production(u, m) * ut_productivity(u, t)) / 100;
	    	    if (prod < um_base_consumption(u, m)) {
			range = um_storage_x(u, m) / (um_base_consumption(u, m) - prod);
			if (range < worstrange)
			  worstrange = range;
		    }
		}
	    }
	}
    }
    return worstrange;
}

/* Same, but for best terrain. */

int
operating_range_best(u)
int u;
{
    int m, t, prod, range, tbestrange, tbest = 0, bestrange = 0;
    int moves, consump;

    for_all_terrain_types(t) {
	if (!terrain_always_impassable(u, t)) {
	    tbestrange = area.maxdim;
	    for_all_material_types(m) {
		consump = 0;
		moves = (u_acp(u) * u_speed(u)) / 100;
		if (um_consumption_per_move(u, m) > 0) {
		    consump = moves * um_consumption_per_move(u, m);
		}
		if (moves <= 0)
		  moves = 1;
		if (um_base_consumption(u, m) > 0) {
		    consump = max(consump, um_base_consumption(u, m));
		}
	    	prod = (um_base_production(u, m) * ut_productivity(u, t)) / 100;
	    	if (prod > 0) {
		    consump = max(0, consump - prod);
		}
		if (consump > 0) {
		    range = (um_storage_x(u, m) * moves) / consump;
		    if (range < tbestrange)
		      tbestrange = range;
		}
	    }
	    if (tbestrange > bestrange) {
		bestrange = tbestrange;
		tbest = t;
	    }
	}
    }
    return bestrange;
}

void
dispose_of_plan(unit)
Unit *unit;
{
    /* Might not be anything to dispose of. */
    if (unit->plan == NULL)
      return;
    free_plan(unit->plan);
    unit->plan = NULL;
}

int
lookup_plan_type(name)
char *name;
{
    int i;
    extern char *plantypenames[];

    for (i = 0; plantypenames[i] != NULL; ++i)
      /* should get real enum */
      if (strcmp(name, plantypenames[i]) == 0)
	return i;
    return PLAN_NONE;
}
