1 /* Unit plan handling for Xconq.
2    Copyright (C) 1991-2000 Stanley T. Shebs.
3    Copyright (C) 2005 Eric A. McDonald.
4 
5 Xconq is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.  See the file COPYING.  */
9 
10 #include "conq.h"
11 #include "kernel.h"
12 #include "aiutil.h"
13 #include "ai.h"
14 #include "aiscore.h"
15 #include "aiunit.h"
16 #include "aiunit2.h"
17 #include "aitact.h"
18 #include "aioprt.h"
19 
20 using namespace Xconq;
21 using namespace Xconq::AI;
22 
23 static void plan_passive(Unit *unit);
24 static void plan_offense(Unit *unit);
25 static void plan_defense(Unit *unit);
26 static void plan_exploration(Unit *unit);
27 static void plan_improve(Unit *unit);
28 static void wake_at(int x, int y);
29 static int resupply_if_low(Unit *unit);
30 static int rearm_if_low(Unit *unit);
31 static int plan_resupply(Unit *unit, int m);
32 static int repair_if_damaged(Unit *unit);
33 static int plan_repair(Unit *unit);
34 static int alternate_target_here(int x, int y);
35 static int do_for_occupants(Unit *unit);
36 static int ai_damage_ratio_vs_type(Unit *unit, int u2);
37 static int ai_score_potential_victim_occupants(
38     UnitView *uview, OccStatus occstatus, int victimflags, int dmgtypes);
39 static int ai_score_potential_victim(
40     UnitView *uview, OccStatus occstatus, int victimflags);
41 static int ai_type_choose_best_hit_method(int u, int u2);
42 static int ai_consider_capturing(
43     Unit *unit, UnitView *uview, int dmgthresh, int qthresh);
44 static int ai_consider_shaking(
45     Unit *unit, UnitView *uview, OccStatus occstatus);
46 extern int ai_go_after_victim(Unit *unit, int range, int broadcast);
47 static int fire_at_opportunity(Unit *unit);
48 static int explore_reachable_cell(Unit *unit, int range);
49 static int capture_useful_if_nearby(Unit *unit);
50 static int capture_indep_if_nearby(Unit *unit);
51 static void random_walk(Unit *unit);
52 static int worth_capturing(Side *side, int u2, Side *oside, int x, int y);
53 static int indep_captureable_here(int x, int y);
54 static int useful_type(Side *side, int u);
55 /* static int could_capture_any(int u); */
56 static Plan *create_plan(void);
57 static int might_be_captured(Unit *unit);
58 static int occupant_could_capture(Unit *unit, int etype);
59 static int can_capture_neighbor(Unit *unit);
60 static int occupant_can_capture_neighbor(Unit *unit);
61 static int find_closest_unit(Side *side, int x0, int y0, int maxdist,
62 			     int (*pred)(int x, int y), int *rxp, int *ryp);
63 static int reachable_unknown(int x, int y);
64 static int adj_known_ok_terrain(int x, int y, Side *side, int u);
65 static int normal_completion_time(int u, int u2);
66 static void maybe_set_materials_goal(Unit *unit, int u2);
67 
68 #if 0
69 static int go_after_captive(Unit *unit, int range);
70 static int range_left(Unit *unit);
71 static int find_worths(int range);
72 static int attack_worth(Unit *unit, int e);
73 static int threat(Side *side, int u, int x0, int y0);
74 static int move_patrol(Unit *unit);
75 static int build_time(Unit *unit, int prod);
76 static int out_of_ammo(Unit *unit);
77 static int explorable_cell(int x, int y);
78 static int should_capture_maker(Unit *unit);
79 extern int adj_unit(int x, int y);
80 #endif
81 
82 /* (should have a generic struct for all plan type attrs) */
83 
84 char *plantypenames[] = {
85 
86 #undef  DEF_PLAN
87 #define DEF_PLAN(NAME,code) NAME,
88 
89 #include "plan.def"
90 
91     NULL
92 };
93 
94 /* Every unit that can act needs a plan object, types that can't act
95    should have it cleared out.  Note that incomplete units are expected
96    to be able to act in the future, so it's acceptable to run this for
97    incomplete units to give them a plan. */
98 
99 void
init_unit_plan(Unit * unit)100 init_unit_plan(Unit *unit)
101 {
102     if (can_be_actor(unit)) {
103 	/* Might already have a plan, so don't always realloc. */
104 	if (unit->plan == NULL) {
105 	    unit->plan = create_plan();
106 	}
107 	/* Put the plan into a default state, side will work it up later. */
108 	/* (should release goals also) */
109 	clear_task_agenda(unit);
110 	/* Zero the plan just as xmalloc would. */
111 	memset(unit->plan, 0, sizeof(Plan));
112 	unit->plan->type = PLAN_PASSIVE;
113 	unit->plan->creation_turn = g_turn();
114 	/* Allow AIs to make this unit do things. */
115 	unit->plan->aicontrol = TRUE;
116 	/* Enable supply alarms by default. */
117 	unit->plan->supply_alarm = TRUE;
118 	/* Clear the task outcome. */
119 	unit->plan->last_task_outcome = TASK_UNKNOWN;
120     } else {
121 	/* Brainless units don't need anything, can free up plan. */
122 	if (unit->plan != NULL) {
123 	    free_plan(unit);
124 	}
125 	unit->plan = NULL;
126     }
127 }
128 
129 void
set_unit_plan_type(Side * side,Unit * unit,int type)130 set_unit_plan_type(Side *side, Unit *unit, int type)
131 {
132     int oldtype;
133 
134     if (unit->plan) {
135 	oldtype = unit->plan->type;
136 	if (type != oldtype) {
137 	    if (type == PLAN_NONE) {
138 		type = PLAN_PASSIVE;
139 		DMprintf( "Forced replan: %s lacks plan.\n", unit_desig(unit));
140 		force_replan(unit);
141 	    }
142 	    unit->plan->type = (PlanType)type;
143 	    clear_task_agenda(unit);
144 	    if (side != NULL)
145 	      update_unit_display(side, unit, TRUE);
146 	}
147     }
148 }
149 
150 void
set_unit_asleep(Side * side,Unit * unit,int flag,int recurse)151 set_unit_asleep(Side *side, Unit *unit, int flag, int recurse)
152 {
153     int oldflag;
154     Unit *occ;
155 
156     if (unit->plan) {
157 	oldflag = unit->plan->asleep;
158 	if (flag != oldflag) {
159 	    unit->plan->asleep = flag;
160 	    if (side != NULL)
161 	      update_unit_display(side, unit, TRUE);
162 	}
163     }
164     if (recurse) {
165     	for_all_occupants(unit, occ) {
166 	    set_unit_asleep(side, occ, flag, recurse);
167     	}
168     }
169 }
170 
171 void
set_unit_reserve(Side * side,Unit * unit,int flag,int recurse)172 set_unit_reserve(Side *side, Unit *unit, int flag, int recurse)
173 {
174     int oldflag;
175     Unit *occ;
176 
177     if (unit->plan) {
178 	oldflag = unit->plan->reserve;
179 	if (flag != oldflag) {
180 	    unit->plan->reserve = flag;
181 	    if (side != NULL)
182 	      update_unit_display(side, unit, TRUE);
183 	}
184     }
185     if (recurse) {
186     	for_all_occupants(unit, occ) {
187 	    set_unit_reserve(side, occ, flag, recurse);
188     	}
189     }
190 }
191 
192 void
set_unit_ai_control(Side * side,Unit * unit,int flag,int recurse)193 set_unit_ai_control(Side *side, Unit *unit, int flag, int recurse)
194 {
195     int oldflag;
196     Unit *occ;
197 
198     if (unit->plan) {
199 	oldflag = unit->plan->aicontrol;
200 	if (flag != oldflag) {
201 	    unit->plan->aicontrol = flag;
202 	    if (side != NULL)
203 	      update_unit_display(side, unit, TRUE);
204 	}
205     }
206     if (recurse) {
207     	for_all_occupants(unit, occ) {
208 	    set_unit_ai_control(side, occ, flag, recurse);
209     	}
210     }
211 }
212 
213 void
set_unit_curadvance(Side * side,Unit * unit,int a)214 set_unit_curadvance(Side *side, Unit *unit, int a)
215 {
216 	unit->curadvance = a;
217 }
218 
219 void
set_unit_researchdone(Side * side,Unit * unit,int flag)220 set_unit_researchdone(Side *side, Unit *unit, int flag)
221 {
222 	unit->researchdone = flag;
223 }
224 
225 void
set_unit_main_goal(Side * side,Unit * unit,Goal * goal)226 set_unit_main_goal(Side *side, Unit *unit, Goal *goal)
227 {
228     if (unit->plan) {
229 	unit->plan->maingoal = goal;
230     }
231 }
232 
233 void
set_unit_waiting_for_transport(Side * side,Unit * unit,int flag)234 set_unit_waiting_for_transport(Side *side, Unit *unit, int flag)
235 {
236     if (unit->plan) {
237 	unit->plan->waitingfortransport = flag;
238     }
239 }
240 
241 /* Execute the plan. */
242 
243 int
execute_plan(Unit * unit)244 execute_plan(Unit *unit)
245 {
246     Plan *plan = unit->plan;
247 
248     if (!in_play(unit) || !completed(unit)) {
249 	DMprintf("%s shouldn't be planning yet\n", unit_desig(unit));
250 	return 0;
251     }
252     DMprintf("%s using plan %s\n", unit_desig(unit), plan_desig(plan));
253     /* Units that are asleep or in reserve do nothing. */
254     /* (This never happens according to debugging). */
255     if (plan->asleep || plan->reserve) {
256       return 0;
257     }
258     if (plan->type == PLAN_PASSIVE && plan->execs_this_turn > 10) {
259 	DMprintf(" not found\n");
260     }
261     if (plan->execs_this_turn > 100) {
262 	DMprintf("%s executed plan 100 times this turn, going into reserve\n",
263 		 unit_desig(unit));
264 	plan->reserve = TRUE;
265 	return 1;
266     }
267     /* Unit actually has a plan, dispatch on its type. */
268     switch (plan->type) {
269       case PLAN_NONE:
270 	/* Unit has not gotten a plan yet, leave it alone. */
271 	break;
272       case PLAN_PASSIVE:
273 	plan_passive(unit);
274 	break;
275       case PLAN_OFFENSIVE:
276 	plan_offense(unit);
277 	break;
278       case PLAN_DEFENSIVE:
279 	plan_defense(unit);
280 	break;
281       case PLAN_EXPLORATORY:
282 	plan_exploration(unit);
283 	break;
284       case PLAN_IMPROVING:
285 	plan_improve(unit);
286 	break;
287       default:
288 	case_panic("plan type", plan->type);
289 	break;
290     }
291     ++(plan->execs_this_turn);
292     run_ui_idler();
293     return 1;
294 }
295 
296 /* Check if the unit is in formation or not. */
297 
298 int
is_in_formation(Unit * unit)299 is_in_formation(Unit *unit)
300 {
301     int nx, ny, dist;
302     Plan *plan = unit->plan;
303     Goal *goal;
304     Unit *leader;
305 
306     goal = plan->formation;
307     leader = plan->funit;
308     if (!leader)
309       return FALSE;
310     if (!in_play(leader)
311 	|| !unit_trusts_unit(unit, leader)
312 	|| goal->args[0] != leader->id)
313       return FALSE;
314     nx = leader->x + goal->args[1];  ny = leader->y + goal->args[2];
315     dist = goal->args[3];
316     if (distance(unit->x, unit->y, nx, ny) > dist)
317       return FALSE;
318     return TRUE;
319 }
320 
321 /* See if we're too far away from an assigned position, set a task
322    to move back if so. */
323 
324 int
move_into_formation(Unit * unit)325 move_into_formation(Unit *unit)
326 {
327     int nx, ny, dist;
328     Plan *plan = unit->plan;
329     Goal *goal;
330     Unit *leader;
331 
332     leader = plan->funit;
333     if (leader != NULL) {
334 	goal = plan->formation;
335 	/* Ensure that the leader is still someone we want to follow. */
336 	if (!in_play(leader)
337 	    || !unit_trusts_unit(unit, leader)
338 	    || goal->args[0] != leader->id) {
339 	    notify(unit->side, "%s leader is gone, cancelling formation",
340 		   unit_handle(unit->side, unit));
341 	    free(goal);
342 	    plan->formation = NULL;
343 	    plan->funit = NULL;
344 	    /* Unit is available to do something else. */
345 	    return FALSE;
346 	}
347 	nx = leader->x + goal->args[1];  ny = leader->y + goal->args[2];
348 	dist = goal->args[3];
349 	if (distance(unit->x, unit->y, nx, ny) > dist) {
350 	    /* (should perhaps insert after current task?) */
351 	    set_move_to_task(unit, nx, ny, dist);
352 	    return TRUE;
353 	}
354     }
355     return FALSE;
356 }
357 
358 int task_is_in_agenda(Plan *plan, Task *task);
359 
360 /* See if there are any standing orders that currently apply to the given unit,
361    and schedule a task if so.  Return TRUE if a task was added. */
362 
363 int
execute_standing_order(Unit * unit,int addtask)364 execute_standing_order(Unit *unit, int addtask)
365 {
366     Unit *transport;
367     Side *side = unit->side;
368     StandingOrder *sorder;
369 
370     for (sorder = side->orders; sorder != NULL; sorder = sorder->next) {
371 	if (sorder->types[unit->type] && unit->plan) {
372 	    switch (sorder->condtype) {
373 	      case sorder_at:
374 		if (unit->x == sorder->a1 && unit->y == sorder->a2) {
375 		    /* If the task is already in the plan, don't do
376 		       anything. */
377 		    if (task_is_in_agenda(unit->plan, sorder->task))
378 		      return FALSE;
379 		    if (addtask)
380 		      add_task(unit, 0, clone_task(sorder->task));
381 		    return TRUE;
382 		}
383 		break;
384 	      case sorder_in:
385 		transport = unit->transport;
386 		if (transport != NULL && transport->id == sorder->a1) {
387 		    /* If the task is already in the plan, don't do
388 		       anything. */
389 		    if (task_is_in_agenda(unit->plan, sorder->task))
390 		      return FALSE;
391 		    if (addtask)
392 		      add_task(unit, 0, clone_task(sorder->task));
393 		    return TRUE;
394 		}
395 		break;
396 	      case sorder_near:
397 		if (distance(unit->x, unit->y, sorder->a1, sorder->a2) <= sorder->a3) {
398 		    /* If the task is already in the plan, don't do
399 		       anything. */
400 		    if (task_is_in_agenda(unit->plan, sorder->task))
401 		      return FALSE;
402 		    if (addtask)
403 		      add_task(unit, 0, clone_task(sorder->task));
404 		    return TRUE;
405 		}
406 		break;
407 	      default:
408 		run_warning("Unknown order condition type");
409 		break;
410 	    }
411 	}
412     }
413     return FALSE;
414 }
415 
416 int tasks_match(Task *task1, Task *task2);
417 
418 int
task_is_in_agenda(Plan * plan,Task * task)419 task_is_in_agenda(Plan *plan, Task *task)
420 {
421     Task *task2;
422 
423     for (task2 = plan->tasks; task2 != NULL; task2 = task2->next) {
424 	if (tasks_match(task, task2))
425 	  return TRUE;
426     }
427     return FALSE;
428 }
429 
430 int
tasks_match(Task * task1,Task * task2)431 tasks_match(Task *task1, Task *task2)
432 {
433     int i;
434 
435     if (task1->type != task2->type)
436       return FALSE;
437     for (i = 0; i < MAXTASKARGS; ++i)
438       if (task1->args[i] != task2->args[i])
439 	return FALSE;
440     return TRUE;
441 }
442 
443 /* Passive units just work from the task queue or else wait to be told
444    what to do. */
445 
446 static void
plan_passive(Unit * unit)447 plan_passive(Unit *unit)
448 {
449     Plan *plan = unit->plan;
450 
451     /* Handle AI-controlled passive plans. */
452     if (ai_controlled(unit)) {
453 	if (resupply_if_low(unit)) {
454 	    return;
455 	}
456 	if (rearm_if_low(unit)) {
457 	    return;
458 	}
459 	if (repair_if_damaged(unit)) {
460 	    return;
461 	}
462 	/* Execute any old tasks associated with this plan. Return. */
463 	if (unit->plan->tasks) {
464 	    execute_task(unit);
465 	    return;
466 	}
467 	/* Else mention that we are replanning. */
468 	else {
469 	    DMprintf("Forced replan: %s is ai-controlled & passive.\n",
470 		     unit_desig(unit));
471 	    force_replan(unit);
472 	    return;
473 	}
474     }
475     /* Special-case human cities/towns in the intro game to automatically
476        start producing infantry initially. */
477     /* (would be more efficient to put in a once-per-turn location,
478        should look for one) */
479     if (g_turn() <= 1
480 	&& mainmodule != NULL
481 	&& ((mainmodule->name != NULL
482 	     && strcmp(mainmodule->name, INTRO_GAME) == 0)
483 	    || (mainmodule->origmodulename != NULL
484 		&& strcmp(mainmodule->origmodulename, INTRO_GAME) == 0))
485 	&& ((strcmp(u_type_name(unit->type), "city") == 0)
486 	    || (strcmp(u_type_name(unit->type), "town") == 0))) {
487 	push_construct_task(unit, 0, 99, unit->id, -1, -1);
488     }
489     if (plan->supply_is_low && plan->supply_alarm) {
490 	plan->supply_alarm = FALSE;
491 	if (0 /* auto resupply */) {
492 	    set_resupply_task(unit, NONMTYPE);
493 	} else if (plan->tasks
494 		   && (plan->tasks->type == TASK_RESUPPLY
495 		       || (plan->tasks->type == TASK_MOVE_TO
496 			   && plan->tasks->next
497 			   && plan->tasks->next->type == TASK_RESUPPLY))) {
498 	    /* do nothing */
499 	} else {
500 	    clear_task_agenda(unit);
501 	    set_waiting_for_tasks(unit, TRUE);
502 	}
503     }
504     if (plan->tasks) {
505 	/* (should check that doctrine being followed correctly) */
506 	execute_task(unit);
507     } else if (unit->side
508 	       && unit->side->orders
509 	       && execute_standing_order(unit, TRUE)) {
510 	execute_task(unit);
511     } else if (plan->formation && move_into_formation(unit)) {
512 	execute_task(unit);
513     } else {
514 	/* Our goal is now to get guidance from the side. */
515 	set_waiting_for_tasks(unit, TRUE);
516     }
517 }
518 
519 /* A unit operating offensively advances and attacks when possible. */
520 
521 int find_alternate_hit_target(Unit *unit, Task *task, int *xp, int *yp);
522 
523 static void
plan_offense(Unit * unit)524 plan_offense(Unit *unit)
525 {
526     int u = unit->type;
527     int x, y, w, h, range, x1, y1, nx, ny;
528     Plan *plan = unit->plan;
529     Task *lasttask;
530     Unit *unit2;
531 
532     if (resupply_if_low(unit)) {
533 	return;
534     }
535     if (rearm_if_low(unit)) {
536 	return;
537     }
538     if (repair_if_damaged(unit)) {
539 	return;
540     }
541     /* Run any 'hit-unit' tasks. */
542     if (plan->tasks) {
543     	execute_task(unit);
544     	if (plan->last_task_outcome == TASK_FAILED) {
545 	    lasttask = &(plan->last_task);
546 	    if (lasttask->type == TASK_HIT_UNIT
547 		&& lasttask->args[2] != NONUTYPE
548 		&& !target_visible(unit, lasttask)) {
549 		/* Target seems to have disappeared, look around for it. */
550 		DMprintf("%s hit target has disappeared, looking for it; ",
551 			 unit_desig(unit));
552 		if (find_alternate_hit_target(unit, lasttask, &nx, &ny)) {
553 		    if (plan->tasks
554 			&& plan->tasks->type == lasttask->type
555 			&& plan->tasks->args[0] == lasttask->args[0]
556 			&& plan->tasks->args[1] == lasttask->args[1]
557 			&& plan->tasks->args[2] == lasttask->args[2]
558 			&& plan->tasks->args[3] == lasttask->args[3]
559 			) {
560 			pop_task(plan);
561 		    }
562 		    push_hit_unit_task(unit, nx, ny,
563 				       lasttask->args[2], lasttask->args[3]);
564 		    DMprintf(" found at %d,%d\n", nx, ny);
565 		} else {
566 		    DMprintf(" not found\n");
567 		}
568 	    }
569 	}
570 	/* Irrespective of what happened,
571 	    we don't want to step on the task yet. */
572 	return;
573     }
574     /* Follow through with other details of plan. */
575     if (plan->maingoal && mobile(u)) {
576 	switch (plan->maingoal->type) {
577 	  case GOAL_UNIT_OCCUPIED:
578 	    /* Move to occupy our goal if necessary. */
579 	    unit2 = find_unit(plan->maingoal->args[0]);
580 	    range = u_ai_tactical_range(unit->type);
581 	    if (ai_go_after_victim(unit, range, FALSE));
582 	    else if (in_play(unit2) && unit->transport != unit2)
583 	      set_occupy_task(unit, unit2);
584 	    else {
585 		free(plan->maingoal);
586 		plan->maingoal = NULL;
587 	    }
588 	    break;
589 	  case GOAL_CELL_OCCUPIED:
590 	    /* Move to occupy our goal if necessary. */
591 	    x = plan->maingoal->args[0];
592 	    y = plan->maingoal->args[1];
593 	    range = u_ai_tactical_range(unit->type);
594 	    if (ai_go_after_victim(unit, range, FALSE));
595 	    else if (unit->x != x || unit->y != y)
596 	      set_move_to_task(unit, x, y, 0);
597 	    else {
598 		free(plan->maingoal);
599 		plan->maingoal = NULL;
600 	    }
601 	    break;
602 	  case GOAL_VICINITY_HELD:
603 	    x = plan->maingoal->args[0];  y = plan->maingoal->args[1];
604 	    w = plan->maingoal->args[2];  h = plan->maingoal->args[3];
605 	    if (distance(x, y, unit->x, unit->y) > max(w, h)) {
606 		/* Outside the goal area - move in towards it. */
607 	    	if (random_point_near(x, y, w / 2, &x1, &y1)) {
608 		    x = x1;  y = y1;
609 	    	}
610 		DMprintf("%s to go on offensive to %d,%d\n",
611 			 unit_desig(unit), x, y);
612 		set_move_to_task(unit, x, y, max(w, h) / 2);
613 #if (0)
614 		if (unit->transport
615 		    && mobile(unit->transport->type)
616 		    && unit->transport->plan) {
617 		    set_move_to_task(unit->transport, x, y, max(w, h) / 2);
618 		}
619 #endif
620 	    } else {
621 		range = max(w, h);
622 		range = min(u_ai_tactical_range(unit->type), range);
623 #if (0)
624 		range = min(real_operating_range_best(unit), range);
625 		/* No special goal, look for something to fight with. */
626 		/* Sometimes be willing to look a little farther out. */
627 		if (probability(50))
628 		  range *= 2;
629 #endif
630 		/* Try to let occs decide for us. */
631 		if (mobile(u) && do_for_occupants(unit)) {
632 		/* Found a victim to go after, fall through. */
633 		} else if (ai_go_after_victim(unit, range, FALSE)) {
634 		/*! \todo Should consider transferring to another theater. */
635 		} else {
636 		    /* Do a random walk instead of just sitting there. */
637 		    DMprintf("%s to walk randomly\n", unit_desig(unit));
638 		    random_walk(unit);
639 	    	}
640 	    }
641 	    break;
642 	  default:
643 	    DMprintf("offensive unit has some goal\n");
644 	    break;
645 	}
646     } else if (mobile(u)) {
647 	/* Play it safe. Search every cell within the tactical range.
648 	    But don't search the whole world! */
649 	range = u_ai_tactical_range(u);
650 #if (0)
651 	if (probability(50))
652 	  range = min(range, 2 * type_max_acp(u));
653 #endif
654 	if (do_for_occupants(unit));
655 	/* No special goal, but found something to fight with. */
656 	else if (ai_go_after_victim(unit, range, FALSE));
657 	/*! \todo Should consider transferring to another theater. */
658 	/* Else go into reserve. */
659 	else {
660 	    if (mobile(unit->type) && probability(50)
661 		&& ((0 < u_defensive_worth(unit->type))
662 		    || (0 < u_ai_explorer_worth(unit->type))))
663 	      force_replan(unit);
664 	    else
665 	      set_unit_reserve(unit->side, unit, TRUE, FALSE);
666 	    return;
667 	}
668 	/* should go to a "best location" if possible. */
669 	/* (should do a sentry task) */
670     } else if (ai_go_after_victim(unit, u_ai_tactical_range(u), FALSE));
671     if (plan->tasks)
672     	execute_task(unit);
673     else {
674 	/* If we cannot find anything sensible to do, we force a replan.
675            This will force the AI to consider if this unit should be moved
676            to another theater of operations. */
677 	if (probability(20)) {
678 	    force_replan(unit);
679 	    DMprintf("%s found nothing to do offensively, replanning.\n",
680 		     unit_desig(unit));
681 	}
682 	else
683 	  set_unit_reserve(unit->side, unit, TRUE, FALSE);
684     }
685 }
686 
687 /* Look through list of occupants to see if an occupant needs the
688    transport to do something. */
689 
690 int
do_for_occupants(Unit * unit)691 do_for_occupants(Unit *unit)
692 {
693     Unit *occ = NULL;
694     Goal *goal = NULL;
695     Task *task = NULL;
696 
697     /* If transport is not AI-controlled, then do not muck with it. */
698     if (!ai_controlled(unit))
699       return FALSE;
700     /* If transport is immobile, then don't try moving for occs. */
701     if (!mobile(unit->type))
702       return FALSE;
703     /* Do whatever the occs are asking us to do. */
704     for_all_occupants(unit, occ) {
705 	if (occ->plan) {
706 	    /* Get the occ towards its goal, if it has one. */
707 	    goal = occ->plan->maingoal;
708 	    if (goal != NULL
709 		&& goal->type == GOAL_VICINITY_HELD
710 		&& (distance(goal->args[0], goal->args[1], unit->x, unit->y)
711 		    > goal->args[2])) {
712 		set_move_to_task(unit, goal->args[0], goal->args[1],
713 				  max(goal->args[2] / 2, 1));
714 		DMprintf("%s will go where occupant %s wants to go (goal %s)\n",
715 			 unit_desig(unit), unit_desig(occ), goal_desig(goal));
716 		return TRUE;
717 	    }
718 	    /* If the occ does not have a goal, see if it has a task. */
719 	    for_all_tasks(occ->plan, task) {
720 		if ((task->type == TASK_MOVE_TO
721 		     || task->type == TASK_HIT_UNIT)
722 		    && (task->args[0] != unit->x
723 			|| task->args[1] != unit->y)
724 			&& distance(task->args[0], task->args[1],
725 				    unit->x, unit->y) > 1) {
726 		    /* Note that we assume the transport is mobile,
727 		       which is OK currently because of where this
728 		       routine is called from. */
729 		    set_move_to_task(unit, task->args[0], task->args[1], 1);
730 		    DMprintf(
731 "%s will go where occupant %s wants to go (task %s)\n",
732 			     unit_desig(unit), unit_desig(occ),
733 			     task_desig(task));
734 		    return TRUE;
735 		}
736 	    }
737 	}
738     }
739     return FALSE;
740 }
741 
742 int
find_alternate_hit_target(Unit * unit,Task * task,int * xp,int * yp)743 find_alternate_hit_target(Unit *unit, Task *task, int *xp, int *yp)
744 {
745     int range;
746 
747     tmpunit = unit;
748     tmputype = task->args[2];
749     tmpside = side_n(task->args[3]);
750     /* (should adjust search radius for speed?) */
751     range = type_max_acp(tmputype) + 1;
752     return search_around(task->args[0], task->args[1], range,
753 			 alternate_target_here, xp, yp, 1);
754 }
755 
756 static int
alternate_target_here(int x,int y)757 alternate_target_here(int x, int y)
758 {
759     UnitView *uview;
760 
761     for_all_view_stack_with_occs(tmpunit->side, x, y, uview) {
762 	if (uview->type == tmputype
763 	    && uview->siden == tmpside->id)
764 	  return TRUE;
765     }
766     return FALSE;
767 }
768 
769 /* Defensive units don't go out looking for trouble, but they should
770    react strongly to threats. */
771 
772 static void
plan_defense(Unit * unit)773 plan_defense(Unit *unit)
774 {
775     int u = unit->type, range, x, y, w, h, x1, y1;
776     Plan *plan = unit->plan;
777     Unit *unit2;
778 
779     if (resupply_if_low(unit)) {
780 	return;
781     }
782     if (rearm_if_low(unit)) {
783 	return;
784     }
785     if (repair_if_damaged(unit)) {
786 	return;
787     }
788     if (plan->tasks) {
789     	/* (should analyze and maybe decide to change task) */
790     	execute_task(unit);
791     	return;
792     }
793     /* Listen to what our occs are telling us. */
794     if (mobile(unit->type) && do_for_occupants(unit)) {
795 	if (plan->tasks) {
796 	    execute_task(unit);
797 	    return;
798 	}
799     }
800     /* Proceed with normal planning. */
801     if (plan->maingoal) {
802 	switch (plan->maingoal->type) {
803   	  case GOAL_UNIT_OCCUPIED:
804 	    /* Move to occupy our goal if necessary. */
805 	    unit2 = find_unit(plan->maingoal->args[0]);
806 	    if (in_play(unit2) && unit->transport != unit2)
807 	      set_occupy_task(unit, unit2);
808 	    else if ((unit->transport == unit2)
809 		     && ai_go_after_victim(unit, 1, FALSE));
810 	    else {
811 		free(plan->maingoal);
812 		plan->maingoal = NULL;
813 	    }
814 	    break;
815   	  case GOAL_CELL_OCCUPIED:
816 	    /* Move to occupy our goal if necessary. */
817 	    x = plan->maingoal->args[0];  y = plan->maingoal->args[1];
818 	    if (unit->x != x || unit->y != y)
819 	      set_move_to_task(unit, x, y, 0);
820 	    else if ((unit->x == x) && (unit->y == y)
821 		     && ai_go_after_victim(unit,
822 					   u_ai_tactical_range(unit->type),
823 					   FALSE));
824 	    else {
825 		free(plan->maingoal);
826 		plan->maingoal = NULL;
827 	    }
828 	    break;
829 	  case GOAL_VICINITY_HELD:
830 	    x = plan->maingoal->args[0];  y = plan->maingoal->args[1];
831 	    w = plan->maingoal->args[2];  h = plan->maingoal->args[3];
832 	    if (distance(x, y, unit->x, unit->y) > max(w, h)) {
833 		/* Outside the goal area - move in towards it. */
834 	    	if (random_point_near(x, y, w / 2, &x1, &y1)) {
835 		    x = x1;  y = y1;
836 	    	}
837 		DMprintf("%s to go on defensive to %d,%d\n",
838 			 unit_desig(unit), x, y);
839 		set_move_to_task(unit, x, y, max(w, h) / 2);
840 		if (unit->transport
841 		    && mobile(unit->transport->type)
842 		    && unit->transport->plan) {
843 		    set_move_to_task(unit->transport, x, y, max(w, h) / 2);
844 		}
845 	    } else {
846 		range = max(w, h);
847 		range = min(u_ai_tactical_range(unit->type), range);
848 		/* No special goal, look for something to fight with. */
849 		/* Sometimes be willing to look a little farther out. */
850 		if (probability(50))
851 		  range *= 2;
852 		/* Occupants have decided for us, fall through. */
853 		if (do_for_occupants(unit));
854 		/* Found a victim to go after, fall through. */
855 		else if (ai_go_after_victim(unit, range, FALSE));
856 		/* Else go into reserve. */
857 		else
858 		  set_unit_reserve(unit->side, unit, TRUE, FALSE);
859 		/*! \todo Should consider transferring to another theater. */
860 	    }
861 	    break;
862 	  default:
863 	    DMprintf("defensive unit has some goal\n");
864 	    break;
865 	}
866 	/* (might be able to defend by interposing self?) */
867 	return;
868     }
869     if (can_attack_any(unit, unit) || can_fire_at_any(unit, unit)
870 	|| could_capture_any(unit->type)) {
871 	/* Use the tactical range. */
872 	if (ai_go_after_victim(unit, u_ai_tactical_range(unit->type), FALSE)) {
873 	    execute_task(unit);
874 	    return;
875 	}
876 	/* Nobody close by, just hang out, shifting around a bit
877            occasionally. */
878 	if (mobile(unit->type) && probability(10)) {
879 	    if (random_point_near(unit->x, unit->y, type_max_acp(u),
880 				  &x1, &y1)) {
881 		DMprintf("%s to shift defensive position to %d,%d\n",
882 			 unit_desig(unit), x1, y1);
883 		set_move_to_task(unit, x1, y1, 0);
884 		execute_task(unit);
885 		return;
886 	    }
887 	}
888 	/* Else go into reserve or replan. */
889 	else {
890 	    if (mobile(unit->type) && probability(50)
891 		&& ((0 < u_offensive_worth(unit->type))
892 		    || (0 < u_siege_worth(unit->type))
893 		    || (0 < u_ai_explorer_worth(unit->type))))
894 	      force_replan(unit);
895 	    else
896 	      set_unit_reserve(unit->side, unit, TRUE, FALSE);
897 	    return;
898 	}
899     } else {
900 	using namespace Xconq::AI;
901 	acquire_oprole(unit->side, unit->id, OR_CONSTRUCTOR);
902     }
903     if (plan->tasks) {
904 	execute_task(unit);
905     } else {
906 	    /* If we cannot find anything sensible to do, we force a replan.
907 	       This will force the AI to consider
908 		if this unit should be moved to another theater of
909 		operations. */
910 	if (probability(20)) {
911 	    force_replan(unit);
912 	    DMprintf("%s found nothing to do defensively, replanning.\n",
913 		     unit_desig(unit));
914 	}
915 	else
916 	  set_unit_reserve(unit->side, unit, TRUE, FALSE);
917     }
918 }
919 
920 #if (0) // HACKING NOTE: Saving a snippet we are still interested in.
921 static void
plan_colonize(Unit * unit)922 plan_colonize(Unit *unit)
923 {
924 	  case GOAL_COLONIZE:
925 	    x1 = -1;  y1 = -1;
926 	    u2 = plan->maingoal->args[0];
927 	    /* Try finding a good, unused spot to colonize. */
928 	    /* (TODO: Ensure that other side units don't have same idea.) */
929 	    search_around(unit->x, unit->y, u_ai_tactical_range(u),
930 			  good_cell_to_colonize, &x1, &y1, 1);
931 	    if (inside_area(x1, y1)
932 		&& valid(can_construct(unit, unit, u2))
933                 && (NODIR !=
934 			choose_move_direction(unit, x1, y1,
935 					      distance(x1, y1,
936 						       unit->x, unit->y)))) {
937 		set_construct_task(unit, u2, 1, -1, x1, y1);
938 		push_move_to_task(unit, x1, y1, 0);
939 		DMprintf(
940 			 "%s moving to (%d, %d) to colonize by building %s\n",
941 			 unit_desig(unit), x1, y1, u_type_name(u2));
942 	    }
943 	    /* If we have AI planning and need explorers,
944 		then try exploring to a reachable unknown cell. */
945 	    else if (ai_controlled(unit) && need_explorers(unit->side)
946 		     && (0 < u_ai_explorer_worth(u))
947 		     && explore_reachable_cell(unit, u_ai_tactical_range(u)));
948 	    /* Else, move in a random dir along a straight line. */
949 	    else
950 	      set_move_dir_task(unit, random_dir(),
951 				u_ai_tactical_range(u) +
952 				xrandom(u_ai_tactical_range(u) / 2));
953 	    break;
954 #endif
955 
956 #if (0) // HACKING NOTE: Saving a snippet we are still interested in.
957 static void
958 plan_colonize_support(Unit *unit)
959 {
960 	if (ai_controlled(unit))
961 	  maybe_set_materials_goal(unit, u2);
962 }
963 #endif
964 
965 //! Plan: Improve
966 /*!
967     This plan is currently a catch-all for a number of things:
968     (1) Preparing to change utype. [TODO]
969     (2) Changing utype.
970     (3) Building productivity-enhancing units. [TODO]
971     (4) Developing new tech for the side. [TODO]
972 
973     \todo Move this code to a new 'aiplan.c' file. Replace with hook invocation.
974     \todo Improve heuristics for selecting a new utype to change into.
975     \todo Allow designer-provided weights for selecting utype to change into.
976     \todo Write change-type prep analysis code.
977 */
978 
979 static void
980 plan_improve(Unit *unit)
981 {
982     static int *p_uimprove;
983 
984     int u = NONUTYPE, u2 = NONUTYPE;
985     Side *side = NULL;
986     int totchance = 0, luckynum = 0, uval = 0;
987     UnitView *uview = NULL, *uvstack = NULL;
988     Unit *transport = NULL;
989 
990     assert_error(in_play(unit),
991 		 "Attempting to execute plan for out-of-play unit");
992     assert_error(unit->plan, "Trying to run NULL plan");
993     u = unit->type;
994     side = unit->side;
995     /* Always take care of the basics before trying other stuff. */
996     if (resupply_if_low(unit)) {
997 	return;
998     }
999     if (rearm_if_low(unit)) {
1000 	return;
1001     }
1002     if (repair_if_damaged(unit)) {
1003 	return;
1004     }
1005     /* Execute any old tasks associated with this plan. Return. */
1006     if (unit->plan->tasks) {
1007     	execute_task(unit);
1008     	return;
1009     }
1010     // Initialize the improvement utypes array, if necessary.
1011     if (!p_uimprove)
1012 	p_uimprove = (int *)xmalloc(numutypes * sizeof(int));
1013     /* Could unit change type ever? */
1014     if (could_change_type(u)) {
1015 	/* Sum the various worths of u. */
1016 	uval = total_worth(u);
1017 	uval = max(0, uval);
1018 	/* Examine possible utypes to change into. */
1019 	for_all_unit_types(u2) {
1020 	    p_uimprove[u2] = 0;
1021 	    if (!valid(can_change_type_to(u, u2, side)))
1022 	      continue;
1023 	    /* Sum the various worths of u2. */
1024 	    p_uimprove[u2] = total_worth(u2);
1025 	    p_uimprove[u2] = max(0, p_uimprove[u2]);
1026 	    /* (TODO: Give bonus for utypes that are needed for victory.) */
1027 	    /* (TODO: If u2 could change back to u,
1028 			then we may need to think more about it.) */
1029 	}
1030 	/* Zero out entries that are probably not worthwhile. */
1031 	for_all_unit_types(u2) {
1032 	    if (uval >= p_uimprove[u2])
1033 	      p_uimprove[u2] = 0;
1034 	}
1035 	/* Sum chances. */
1036 	totchance = 0;
1037 	for_all_unit_types(u2)
1038 	  totchance += p_uimprove[u2];
1039 	/* Run the lottery, if we should. */
1040 	if (0 < totchance) {
1041 	    luckynum = xrandom(totchance);
1042 	    totchance = 0;
1043 	    for_all_unit_types(u2) {
1044 		totchance += p_uimprove[u2];
1045 		if (luckynum < totchance)
1046 		  break;
1047 	    }
1048 	    assert_warning_return(is_unit_type(u2),
1049 				  "Picked an invalid utype to change to",);
1050 	    /* Could unit change type now? */
1051 	    if (valid(can_change_type_to(unit, unit, u2))) {
1052 		if (prep_change_type_action(unit, unit, u2))
1053 		  return;
1054 	    }
1055 	    /* Else, find out what we can do to prepare for a change-type,
1056 		if anything. Also determine whether the cost of
1057 		changing type is too high in relation to what we could
1058 		construct, etc... in the same estimated timeframe. */
1059 	    else {
1060 		/* (TODO: Implement change-type prep analysis.) */
1061 	    }
1062 	} /* run lottery */
1063     } /* could change-type */
1064     /* Can unit construct anything useful inside itself? */
1065     else if (can_construct_any(unit, unit)) {
1066 	/* Pick out possible utypes to construct. */
1067 	for_all_unit_types(u2) {
1068 	    p_uimprove[u2] = 0;
1069 	    if (!type_can_occupy(u2, unit))
1070 	      continue;
1071 	    if (!can_construct(unit, unit, u2))
1072 	      continue;
1073 	    if (0 < u_ai_prod_enhancer_worth(u2))
1074 	      p_uimprove[u2] += u_ai_prod_enhancer_worth(u2);
1075 	    /* (TODO: Handle other cases, such as protection enhancement.) */
1076 	    totchance += p_uimprove[u2];
1077 	}
1078 	/* Run the lottery, if we should. */
1079 	if (totchance) {
1080 	    luckynum = xrandom(totchance);
1081 	    totchance = 0;
1082 	    for_all_unit_types(u2) {
1083 		totchance += p_uimprove[u2];
1084 		if (luckynum < totchance)
1085 		  break;
1086 	    }
1087 	    assert_warning_return(is_unit_type(u2),
1088 				  "Picked an invalid utype to construct",);
1089 	    // Rummage through potential transports in cell.
1090 	    // Note: Even if u2 cannot see cell and cannot survive on it,
1091 	    //  we may be able to construct it inside something we have there.
1092 	    uvstack = query_uvstack_at(unit->x, unit->y);
1093 	    for_all_uvstack(uvstack, uview) {
1094 		transport =
1095 		    choose_transport_to_construct_in(u2, unit->side, uview);
1096 		// TODO: Score all potential transports.
1097 		if (transport)
1098 		    break;
1099 	    }
1100 	    // Can survive on cell without transport?
1101 	    // Note: Rejects cells that we cannot see.
1102 	    // Note: Rejects cells that are too full.
1103 	    if (!transport
1104 		&& !valid(can_survive_on_known(
1105 			    u2, unit->side, unit->x, unit->y))) {
1106 		if (probability(20))
1107 		    force_replan(unit);
1108 		else
1109 		    set_unit_reserve(unit->side, unit, TRUE, FALSE);
1110 		return;
1111 	    }
1112 	    if (transport)
1113 		set_construct_task(unit, u2, 1, transport->id, -1, -1);
1114 	    else
1115 		set_construct_task(unit, u2, 1, -1, unit->x, unit->y);
1116 	} /* lottery */
1117     } /* can construct any */
1118     /* Else, no known improvements can be made. */
1119     else {
1120 	if (probability(20)) {
1121 	    force_replan(unit);
1122 	    DMprintf("%s could not find a way to improve, replanning.\n",
1123 		     unit_desig(unit));
1124 	}
1125 	else
1126 	  set_unit_reserve(unit->side, unit, TRUE, FALSE);
1127     }
1128     /* Execute any new tasks associated with this plan. Return. */
1129     if (unit->plan->tasks) {
1130     	execute_task(unit);
1131     	return;
1132     }
1133 }
1134 
1135 static void
1136 plan_exploration(Unit *unit)
1137 {
1138     Unit *unit2;
1139     Plan *plan = unit->plan;
1140     int u = unit->type;
1141     int x, y, w, h, range, x1, y1;
1142     int u2 = NONUTYPE;
1143 
1144     /* If the world has no secrets, exploration is sort of pointless. */
1145     if (g_see_all()) {
1146     	force_replan(unit);
1147     	return;
1148     }
1149     if (resupply_if_low(unit)) {
1150 	return;
1151     }
1152     if (rearm_if_low(unit)) {
1153 	return;
1154     }
1155     if (repair_if_damaged(unit)) {
1156 	return;
1157     }
1158     if (plan->tasks) {
1159     	/* (should see if a change of task is worthwhile) */
1160     	execute_task(unit);
1161     	return;
1162     }
1163     /* Listen to what our occs are telling us. */
1164     if (mobile(unit->type) && do_for_occupants(unit)) {
1165 	if (plan->tasks) {
1166 	    execute_task(unit);
1167 	    return;
1168 	}
1169     }
1170 #if (0)
1171     /* If we are a colonizer, we might also want to try colonizing. */
1172     if (0 < u_colonizer_worth(u) && good_cell_to_colonize(unit->x, unit->y)) {
1173 	for_all_unit_types(u2) {
1174 	    if (valid(can_construct(unit, unit, u2)) && !unit->nexthere) {
1175 		set_construct_task(unit, u2, 1, -1, unit->x, unit->y);
1176 		DMprintf(
1177 "%s decided to colonize while exploring; will build %s\n",
1178 			 unit_desig(unit), u_type_name(u2));
1179 		if (plan->tasks) {
1180 		    /* (should see if a change of task is worthwhile) */
1181 		    execute_task(unit);
1182 		    return;
1183 		}
1184 	    }
1185 	}
1186     }
1187 #endif
1188     /* Else, get down to exploration business. */
1189     if (plan->maingoal) {
1190 	switch (plan->maingoal->type) {
1191 	  case GOAL_UNIT_OCCUPIED:
1192 	    /* Move to occupy our goal if necessary. */
1193 	    unit2 = find_unit(plan->maingoal->args[0]);
1194 	    if (in_play(unit2) && unit->transport != unit2)
1195 	      set_occupy_task(unit, unit2);
1196 	    else {
1197 		free(plan->maingoal);
1198 		plan->maingoal = NULL;
1199 	    }
1200 	    break;
1201 	  case GOAL_CELL_OCCUPIED:
1202 	    /* Move to our goal if necessary. */
1203 	    x = plan->maingoal->args[0];
1204 	    y = plan->maingoal->args[1];
1205 	    if (unit->x != x || unit->y != y)
1206 	      set_move_to_task(unit, x, y, 0);
1207 	    else {
1208 		free(plan->maingoal);
1209 		plan->maingoal = NULL;
1210 	    }
1211 	    break;
1212 	  case GOAL_VICINITY_KNOWN:
1213 	  case GOAL_VICINITY_HELD:
1214 	    if (mobile(u)) {
1215 		x = plan->maingoal->args[0];  y = plan->maingoal->args[1];
1216 		w = plan->maingoal->args[2];  h = plan->maingoal->args[3];
1217 		if (distance(x, y, unit->x, unit->y) > max(w, h)) {
1218 		    /* Out of the area, move into it. */
1219 	    	    if (random_point_near(x, y, max(w, h) / 2, &x1, &y1)) {
1220 	    		x = x1;  y = y1;
1221 	    	    }
1222 		    DMprintf("%s to explore towards %d,%d\n",
1223 			     unit_desig(unit), x, y);
1224 		    set_move_to_task(unit, x, y, max(w, h) / 2);
1225 		} else {
1226 		    /* Found a cell to explore. */
1227 		    if (explore_reachable_cell(unit, max(w, h) + 2)) {
1228 		    } else {
1229 		    	if (flip_coin()) {
1230 			    DMprintf("%s clearing goal\n", unit_desig(unit));
1231                             free(plan->maingoal);
1232 			    plan->maingoal = NULL;
1233 		    	}
1234 			DMprintf("%s to walk randomly\n", unit_desig(unit));
1235 			random_walk(unit);
1236 		    }
1237 		}
1238 	    }
1239 	    else
1240 		random_walk(unit);
1241 	    break;
1242 	  default:
1243 	    DMprintf("%s goal %s?\n",
1244 		     unit_desig(unit), goal_desig(unit->plan->maingoal));
1245 	    break;
1246 	}
1247     } else {
1248 	/* No specific goal, just poke around. */
1249 	if (mobile(u)) {
1250 	    range = area.maxdim / 2;
1251 	    /* Found a cell to explore. */
1252 	    if (explore_reachable_cell(unit, range)) {
1253 	    } else {
1254 		DMprintf("%s to walk randomly\n", unit_desig(unit));
1255 		random_walk(unit);
1256 	    }
1257 	}
1258 	else
1259 	    random_walk(unit);
1260     }
1261     if (plan->tasks) {
1262         execute_task(unit);
1263     } else {
1264 	/* If we cannot find anything sensible to do, we force a replan.
1265            This will force the AI to consider if this unit should be moved
1266            to another theater of operations. */
1267 	if (probability(20)) {
1268 	    force_replan(unit);
1269 	    DMprintf("%s found nothing to do exploring, replanning.\n",
1270 		     unit_desig(unit));
1271 	}
1272 	else
1273 	  set_unit_reserve(unit->side, unit, TRUE, FALSE);
1274     }
1275 }
1276 
1277 /* These are used by AIs as well. */
1278 /* (Should be transplanted to 'ai.c'?) */
1279 
1280 int victim_x, victim_y, victim_rating, victim_utype, victim_sidenum;
1281 OccStatus victim_occstatus;
1282 int victim_flags;
1283 int victim_dmgtypes;
1284 
1285 int
1286 ai_type_choose_best_hit_method(int u1, int u2)
1287 {
1288     int dmgtypes = DAMAGE_TYPE_NONE;
1289     int atkdmgmax1 = -1, firedmgmax1 = -1;
1290     int atkrngidl1 = -1, firerngidl1 = -1, hitrngmax2 = -1;
1291     int atkrngdelta = 0, firerngdelta = 0;
1292     int dmgdelta1 = 0;
1293 
1294     dmgtypes = type_possible_damage_methods(u1, u2);
1295     if ((dmgtypes & DAMAGE_TYPE_ATTACK) && !(dmgtypes & DAMAGE_TYPE_FIRE))
1296       return DAMAGE_TYPE_ATTACK;
1297     if (!(dmgtypes & DAMAGE_TYPE_ATTACK) && (dmgtypes & DAMAGE_TYPE_FIRE))
1298       return DAMAGE_TYPE_FIRE;
1299     if (!(dmgtypes & DAMAGE_TYPE_ATTACK) && !(dmgtypes & DAMAGE_TYPE_FIRE))
1300       return DAMAGE_TYPE_NONE;
1301     atkdmgmax1 = type_attack_damage_max(u1, u2);
1302     firedmgmax1 = type_fire_damage_max(u1, u2);
1303     atkrngidl1 = type_ideal_attack_range_max(u1, u2);
1304     firerngidl1 = type_ideal_fire_range_max(u1, u2);
1305     hitrngmax2 = type_hit_range_max(u2, u1);
1306     atkrngdelta = atkrngidl1 - hitrngmax2;
1307     firerngdelta = firerngidl1 - hitrngmax2;
1308     dmgdelta1 = atkdmgmax1 - firedmgmax1;
1309     if (atkrngdelta == firerngdelta) {
1310         return ((dmgdelta1 >= 0) ? DAMAGE_TYPE_ATTACK : DAMAGE_TYPE_FIRE);
1311     }
1312     else if (atkrngdelta > firerngdelta) {
1313         if (dmgdelta1 >= 0)
1314           return DAMAGE_TYPE_ATTACK;
1315         else {
1316             if ((dmgdelta1 / (atkrngdelta - firerngdelta)) <= -2)
1317               return DAMAGE_TYPE_FIRE;
1318             else
1319               return DAMAGE_TYPE_ATTACK;
1320         }
1321     }
1322     else {
1323         if (dmgdelta1 <= 0)
1324           return DAMAGE_TYPE_FIRE;
1325         else {
1326             if ((dmgdelta1 / (atkrngdelta - firerngdelta)) <= -2)
1327               return DAMAGE_TYPE_ATTACK;
1328             else
1329               return DAMAGE_TYPE_FIRE;
1330         }
1331     }
1332     return dmgtypes;
1333 }
1334 
1335 int
1336 ai_score_potential_victim_occupants(UnitView *uview, OccStatus occstatus,
1337                                     int victimflags,
1338                                     int dmgtypes)
1339 {
1340     int rating = 0, occrating = 0;
1341     int u1 = NONUTYPE, u3 = NONUTYPE;
1342     int x = -1, y = -1;
1343     UnitView *uvocc = NULL;
1344     Side *side = NULL, *oside = NULL;
1345 
1346     u1 = tmpunit->type;
1347     side = tmpunit->side;
1348     x = uview->x; y = uview->y;
1349     /* Iterate through all known occupants. */
1350     for_all_occupant_views_with_occs(uview, uvocc) {
1351         occrating = 0;
1352         oside = side_n(uvocc->siden);
1353         u3 = uvocc->type;
1354         /* Penalty for thinking about enemy transport that contains
1355            friendlies. */
1356         if (!enemy_side(side, oside)) {
1357             if ((dmgtypes & DAMAGE_TYPE_ATTACK)
1358                 && could_damage_by_attacks(u1, u3))
1359               rating -= uu_hit(u1, u3);
1360             else if ((dmgtypes & DAMAGE_TYPE_FIRE)
1361                 && could_damage_by_fire(u1, u3))
1362               rating -= fire_hit_chance(u1, u3);
1363             continue;
1364         }
1365         /* Add in attack worth, if occ can be attacked. */
1366         if ((dmgtypes & DAMAGE_TYPE_ATTACK)
1367 	    && could_damage_by_attacks(u1, u3)) {
1368             occrating += uu_hit(u1, u3);
1369         }
1370         /* Add in fire-at worth, if occ can be fired at. */
1371         else if ((dmgtypes & DAMAGE_TYPE_FIRE)
1372                  && could_damage_by_fire(u1, u3)) {
1373             occrating += fire_hit_chance(u1, u3);
1374         }
1375         /* Add in capture worth, if occ can be captured. */
1376         if (victimflags & VICTIM_CAPTURABLE)
1377           occrating += capture_chance(u1, u3, oside);
1378         /* Adjust occrating if less than or equal to 0. */
1379         if (occrating <= 0) {
1380             rating += occrating;
1381             occrating = 2;
1382         }
1383         /* Add a bonus, if occ is a builder. */
1384         if (type_can_build(u3, oside))
1385           occrating += ((occrating * 50) / 100);
1386         /* Add a huge bonus, if occ might be self unit. */
1387         if (u_can_be_self(u3))
1388           occrating *= 3;
1389         /* Add occ rating to aggregate rating. */
1390         rating += occrating;
1391     }
1392     return rating;
1393 }
1394 
1395 int
1396 ai_score_potential_victim(UnitView *uview, OccStatus occstatus,
1397                           int victimflags)
1398 {
1399     int rating = 0, baserating = 0, isqrating;
1400     int u1 = NONUTYPE, u2 = NONUTYPE, u3 = NONUTYPE;
1401     int x = -1, y = -1;
1402     Side *side = NULL, *oside = NULL;
1403     int dmgtypes = DAMAGE_TYPE_NONE;
1404     Unit *unit3 = NULL;
1405     int dmgratio = -1;
1406     int popularity = 0;
1407     int dist = INT_MAX, dist2 = INT_MAX;
1408     int strikedist = -1, oprange = -1, strikedist2 = -1;
1409     int costratio = 0, costratiomax = 0;
1410     int numbuildees = 0;
1411 
1412     u1 = tmpunit->type;
1413     side = tmpunit->side;
1414     u2 = uview->type;
1415     x = uview->x; y = uview->y;
1416     oside = side_n(uview->siden);
1417     dist = distance(tmpunit->x, tmpunit->y, x, y);
1418     strikedist = type_hit_range_max(u1, u2);
1419     oprange = (new_acp_for_turn(tmpunit) * type_max_speed(u1)) / 100;
1420     dmgtypes = type_possible_damage_methods(u1, u2);
1421     /* Choose better method for u1 to hit u2, if necessary. */
1422     if ((dmgtypes & DAMAGE_TYPE_ATTACK) && (dmgtypes & DAMAGE_TYPE_FIRE))
1423       dmgtypes = ai_type_choose_best_hit_method(u1, u2);
1424     /* Add attack worth. */
1425     rating += max(0, uu_zz_bhw(u1, u2));
1426     /* Add fire worth. */
1427     rating += max(0, uu_zz_bfw(u1, u2));
1428     /* Add capture worth, if we can capture enemy. */
1429     if (victimflags & VICTIM_CAPTURABLE)
1430       rating += uu_zz_bcw(u1, u2);
1431     /* Add other worths that boost our estimation of the enemy. */
1432     rating += u_colonization_support_worth(u2);
1433     rating += u_exploration_support_worth(u2);
1434     rating += u_offensive_support_worth(u2);
1435     rating += u_defensive_support_worth(u2);
1436     /* Add encounter worth, if we can encounter enemy. */
1437     if (victimflags & VICTIM_ENCOUNTERABLE)
1438       rating += 10000;
1439 #if (0)
1440     /* Add a bonus, for each utype that an enemy can build. */
1441     for_all_unit_types(u3) {
1442         if (type_can_build_type(u2, oside, u3))
1443           ++numbuildees;
1444     }
1445     rating += ((numbuildees * 100) / numutypes);
1446 #endif
1447     /* If score is initially <= 1, still give it a chance to build up. */
1448     if (rating <= 1)
1449       baserating = 2;
1450     else
1451       baserating = rating;
1452     /* Add huge bonus if enemy unit might that side's self unit. */
1453     if (u_can_be_self(u2)) {
1454         if (rating <= 0)
1455           rating += (baserating * 3);
1456         else
1457           rating *= 3;
1458     }
1459     /* Update baserating. */
1460     if (rating > baserating)
1461       baserating = rating;
1462     /* Add in total transport worth, if enemy can transport. */
1463     if (CANNOT_HAVE_OCCS != occstatus) {
1464         for_all_unit_types(u3) {
1465             rating += uu_zz_btw(u2, u3);
1466         }
1467     }
1468 #if (0)
1469     /* Add in occupant ratings, if enemy does or may have occupants. */
1470     switch (occstatus) {
1471       case MAYBE_HAS_OCCS:
1472         rating += (baserating * 2);
1473         break;
1474       case DEFINITELY_HAS_OCCS:
1475         rating +=
1476 	    ai_score_potential_victim_occupants(uview, occstatus,
1477                                                 victimflags, dmgtypes);
1478         break;
1479       case DEFINITELY_HAS_NO_OCCS:
1480       case CANNOT_HAVE_OCCS:
1481       default:
1482         break;
1483     }
1484 #endif
1485     /* Add urgency bonus if enemy unit potentially threatens one of our
1486        builders. */
1487     for_all_side_units(side, unit3) {
1488         u3 = unit3->type;
1489         if (type_can_build(u3, side)) {
1490             dist2 = distance(unit3->x, unit3->y, x, y);
1491             strikedist2 = (could_damage(u2, u3))
1492                            ? type_hit_range_max(u2, u3) : -1;
1493             if (dist2 <= strikedist2) {
1494                 rating += ((baserating * 25) / 100);
1495                 strikedist2 = (could_damage(u2, u3))
1496                                ? type_ideal_hit_range_max(u2, u3) : -1;
1497                 if (dist2 <= strikedist2)
1498                     rating += ((baserating * 50) / 100);
1499             }
1500             /* Urgent. Attempt to stop possible capture or lift siege. */
1501             if ((dist2 <= 1) && could_capture(u2, u3, side))
1502               rating *= 2;
1503         }
1504     }
1505     /* Penalize, if other units on our side are already planning to mess
1506        with the enemy. */
1507     if (side_planning_to_capture_type_at(side, u2, x, y))
1508       popularity = n_planning_to_capture_type_at(side, u2, x, y);
1509     if (side_planning_to_hit_type_at(side, u2, x, y))
1510       popularity += n_planning_to_hit_type_at(side, u2, x, y);
1511     if (planning_to_capture_type_at(tmpunit, u2, x, y))
1512       --popularity;
1513     if (planning_to_hit_type_at(tmpunit, u2, x, y))
1514       --popularity;
1515     popularity = isqrt(popularity);
1516     if (popularity && (rating > 0))
1517       rating /= popularity;
1518     /* Add a bonus if it is more costly than our unit. */
1519     if (u_cp(u1)) {
1520         /* Increase score according to cost ratio. */
1521         if (u_cp(u2))
1522           rating += ((baserating * (u_cp(u2) / u_cp(u1)) * 50) / 100);
1523     }
1524     /* Could add many other score modifiers, such as whether the potential
1525         victim provides a critical material for a side, whether it is
1526         guaranteed to be a side's self unit, or if it has a greater value to
1527         the scorekeeper than our unit. */
1528     /* Also need to consider stack protection and cellwide protection, and
1529         the strengths of any enemy units that may be supporting our victim. */
1530 #if (0)
1531     dmgratio = ai_damage_ratio_vs_type(tmpunit, u2);
1532     /* Add a bonus, if enemy is outgunned by us. */
1533     if (!dmgratio)
1534       rating += ((baserating * 50) / 100);
1535     /* Update baserating. */
1536     if (rating > baserating)
1537       baserating = rating;
1538     if (baserating <= 0)
1539       baserating = 2;
1540     /* Add urgency bonus, if enemy can potentially hit or may actually be
1541        hitting us, and we are equal or superior to it. */
1542     if ((dmgratio <= 1) && could_damage(u2, u1)) {
1543         /* If it can hit us at all, we should be concerned. */
1544         if (type_hit_range_max(u2, u1) >= dist)
1545           rating += ((baserating * 50) / 100);
1546         /* If it can hit us with full effect, then be extra concerned. */
1547         if (type_ideal_hit_range_max(u2, u1) >= dist)
1548           rating += ((baserating * 75) / 100);
1549         /* If it is mobile, there is a greater chance that it moved in to
1550            attack us or that it can pester us in the future. */
1551         /* (Should actually examine the unit's movement rate.) */
1552         if (mobile(u2))
1553           rating += ((baserating * 125) / 100);
1554         /* More likely to be hitting us if it is less vulnerable. */
1555         if (!(victimflags & (VICTIM_CAPTURABLE | VICTIM_ENCOUNTERABLE)))
1556           rating += ((baserating * 50) / 100);
1557         if (!u_advanced(u2) && !type_can_build(u2, oside))
1558           rating += ((baserating * 50) / 100);
1559         if (!u_can_be_self(u2))
1560           rating += ((baserating * 50) / 100);
1561     }
1562     /* Heavily penalize score, if enemy can seriously outgun us. */
1563     if ((dmgratio > 1) && !(victimflags & VICTIM_ENCOUNTERABLE)) {
1564         if (type_ideal_hit_range_max(u1, u2) <=
1565             type_ideal_hit_range_max(u2, u1))
1566             rating -= (baserating - (baserating / dmgratio));
1567         else if (type_ideal_hit_range_max(u1, u2) <=
1568                  type_hit_range_max(u2, u1))
1569           rating -= (baserating - (baserating / (dmgratio / 2)));
1570         else if (victimflags & VICTIM_CAPTURABLE)
1571           rating -= (baserating - (baserating / dmgratio));
1572     }
1573     /* Update baserating. */
1574     if (rating > baserating)
1575       baserating = rating;
1576     if (baserating <= 0)
1577       baserating = 2;
1578 #endif
1579     isqrating = rating / (isqrt(dist) + 1);
1580     /* Penalize score according to turns from unit. */
1581     /* Note that this code is _different_ in purpose than the general
1582        distance penalizer later on. It penalizes according to a rough
1583        estimate of turns from the enemy unit rather than distance from
1584        the enemy unit. */
1585     if (oprange && (dist > strikedist) && ((dist - strikedist) > oprange))
1586       rating -= (baserating -
1587 		 (baserating / (((dist - strikedist) / oprange) + 1)));
1588     /* Add a 50% bonus if enemy is within striking distance. */
1589     if (dist <= strikedist) {
1590 	rating += (rating * 50) / 100;
1591     }
1592     /* Cap penalty to inverse square root of distance. */
1593     if (rating < isqrating)
1594       rating = isqrating;
1595     /* Penalize if uview is old. */
1596     if (!g_see_all() && !u_see_always(u2) && (1 < (g_turn() - uview->date)))
1597       rating /= (g_turn() - uview->date);
1598     return rating;
1599 }
1600 
1601 int
1602 ai_victim_here(int x, int y, int *numvictims)
1603 {
1604     int u1 = tmpunit->type, u2 = NONUTYPE, rating = 0, dist = 0;
1605     Side *side = tmpunit->side, *oside = NULL;
1606     UnitView *uview = NULL;
1607     Task *hittask = NULL;
1608     int strikedist = -1, moves = 0, omoves = 0;
1609 
1610     /* Should iterate also over visible occs? However, the code explicitly
1611 	handles occs. */
1612     for_all_view_stack(side, x, y, uview) {
1613         int victimflags = VICTIM_NOTHING_SPECIAL;
1614         OccStatus occstatus = CANNOT_HAVE_OCCS;
1615 
1616 	run_ui_idler(); /* Lengthy computations may be involved. */
1617 	u2 = uview->type;
1618 	oside = side_n(uview->siden);
1619         rating = 0;
1620         /* If it's a friendly, then obviously skip it. */
1621         if (!enemy_side(side, oside))
1622           continue;
1623         /* If there is an encounter result associated with it,
1624            then treat it like a capture. */
1625         /* We can be smarter than this, but this helps us out in games
1626             such as Civ2, where many of the encounter results for
1627             Villages are capture-like. */
1628         if (u_encounter_result(u2) != lispnil)
1629           victimflags |= VICTIM_ENCOUNTERABLE;
1630         /* If we cannot hit it, then skip it. */
1631         /* Currently model 1 does not allow for even the capture of
1632            undefended units by attack == 0 units. If this changes, then
1633            we will need to reassess this part of the code. */
1634         if (!could_hit(u1, u2) && !(victimflags & VICTIM_ENCOUNTERABLE))
1635           continue;
1636         /* If we are immobile and it is not in striking range, then skip it. */
1637         strikedist = type_hit_range_max(u1, u2);
1638         dist = distance(tmpunit->x, tmpunit->y, x, y);
1639         if (!mobile(u1) && (dist > strikedist))
1640           continue;
1641 	/* If we are slower than it, and cannot reach it this turn. */
1642 	if (tmpunit->act) {
1643 	    omoves = (type_max_acp(u2) * type_max_speed(u2)) / 100;
1644 	    moves = (tmpunit->act->acp * type_max_speed(u1)) / 100;
1645 	    /* (Assumes move-range == 1.) */
1646 	    if (dist > moves) {
1647 		moves = (type_max_acp(u1) * type_max_speed(u1)) / 100;
1648 		if (moves < omoves)
1649 		  continue;
1650 	    }
1651 	}
1652         if (capture_chance(u1, u2, oside))
1653           victimflags |= VICTIM_CAPTURABLE;
1654         /* Record the occupancy status. */
1655         occstatus = occ_status(uview);
1656         /* If it cannot be captured or encountered, and... */
1657         if (!victimflags) {
1658 #if (0)
1659             /* ...if it can hurt us much worse than we can hurt it,
1660                then skip it. */
1661             if (ai_damage_ratio_vs_type(tmpunit, u2) >= 2)
1662                 continue;
1663 #endif
1664             /* ...if it cannot have occs, and... */
1665             if (CANNOT_HAVE_OCCS == occstatus) {
1666                 /* ...if we cannot destroy it, then skip it. */
1667                 /* Though in theory, it might be useful to drain the
1668                    enemy's ACP with supressing fire. */
1669                 if (!could_destroy(u1, u2))
1670                   continue;
1671             } /* CANNOT_HAVE_OCCS */
1672         } /* !victimflags */
1673 	/* If it cannot be captured or encountered, and... */
1674 	if (!victimflags) {
1675              /* ...if it might hurt our occupants, then skip. */
1676              /* (This test could be improved.) */
1677 	    if ((tmpunit->occupant != NULL) && could_damage(u2, u1))
1678 		continue;
1679 	    /* ...if it is worth capturing by our side, and... */
1680 	    if (worth_capturing(side, u2, oside, x, y)
1681                 && (u_advanced(u2) || type_can_build(u2, oside))) {
1682 		/* ...if it belongs to indepside, and indepside has no player
1683 		and cannot build, then skip it. */
1684 		if (oside == indepside
1685 		    && !g_indepside_can_build()
1686 		    && !g_indepside_has_ai()) {
1687 			continue;
1688 		}
1689 #if (0)
1690 		/* Let 2 attackers pound a unit with known occs. */
1691 		if (occstatus == DEFINITELY_HAS_OCCS
1692 		    && (n_planning_to_hit_type_at(side, u2, x, y)
1693 		    	- planning_to_hit_type_at(tmpunit, u2, x, y)) > 1) {
1694 			continue;
1695 		/* Let 1 attacker pound a unit with possible occs. */
1696 		} else if (occstatus == MAYBE_HAS_OCCS
1697 		    && (n_planning_to_hit_type_at(side, u2, x, y)
1698 		    	- planning_to_hit_type_at(tmpunit, u2, x, y)) > 0) {
1699 			continue;
1700 		/* Don't go for units without occs. */
1701 		} else if (occstatus == DEFINITELY_HAS_NO_OCCS) {
1702 			continue;
1703 		}
1704 #endif
1705 		/* ...if it is too fragile to be shaken down, then skip it. */
1706 		if (!ai_consider_shaking(tmpunit, uview, occstatus)) {
1707 			continue;
1708 		} else {
1709 			victimflags |= VICTIM_SHAKEABLE;
1710             	}
1711             } else { /* not worth_capturing */
1712                 /* ...if we cannot destroy it, then skip it. */
1713                 /* Though in theory, it might be useful to drain the
1714                    enemy's ACP with supressing fire. */
1715 		if (!could_destroy(u1, u2))
1716                       continue;
1717 	    } /* worth_capturing */
1718          } /* !victimflags */
1719         /* Else, can capture it. */
1720         /* Score the potential victim. */
1721         rating = ai_score_potential_victim(uview, occstatus, victimflags);
1722         if (rating > victim_rating
1723             || (rating == victim_rating && flip_coin())) {
1724             /* See if we can get within striking distance. */
1725             if (mobile(u1)
1726                 && (dist > strikedist)
1727                 && (NODIR ==
1728                     choose_move_direction(tmpunit, x, y, max(1, strikedist))))
1729               continue;
1730             /* Tag the new victim. */
1731             DMprintf("\n\t...considering %s at %d,%d; assigned rating %d",
1732                      u_type_name(u2), x, y, rating);
1733             if (victimflags)
1734               DMprintf("\n\t\tflags = { %s %s }",
1735                        (victimflags & VICTIM_CAPTURABLE) ? "capturable" : "",
1736                        (victimflags & VICTIM_SHAKEABLE) ? "shakeable" : "");
1737             if (occstatus)
1738               DMprintf("\n\t\toccupancy status = %s",
1739                        (occstatus == MAYBE_HAS_OCCS) ? "maybe_has_occs" :
1740                        ((occstatus == DEFINITELY_HAS_OCCS) ?
1741                             "definitely_has_occs" : "definitely_has_no_occs"));
1742             victim_x = x;  victim_y = y;
1743             victim_rating = rating;
1744             victim_utype = u2;
1745             victim_sidenum = oside->id;
1746             victim_occstatus = occstatus;
1747             victim_flags = victimflags;
1748             victim_dmgtypes = type_possible_damage_methods(u1, u2);
1749             /* Increment the victim counter. */
1750             if (numvictims)
1751               ++(*numvictims);
1752         }
1753     } /* for_all_view_stack */
1754     return FALSE;
1755 }
1756 
1757 /* Evaluates the feasibility of shaking up a transport. */
1758 /* (A fancier version of this might take into account
1759    protection a transport provides its occs and vice versa.) */
1760 
1761 int
1762 ai_consider_shaking(Unit *unit, UnitView *uview, OccStatus occstatus)
1763 {
1764     int u1 = NONUTYPE, u2 = NONUTYPE, u3 = NONUTYPE;
1765     Side *side = NULL, *oside = NULL, *occside = NULL;
1766     int dmgmax1 = 0, occknocks = 0, occknocksbest = 0, tsptknocks = 0;
1767     UnitView *uvocc = NULL;
1768 
1769     u1 = unit->type;
1770     side = unit->side;
1771     u2 = uview->type;
1772     oside = side_n(uview->siden);
1773     dmgmax1 = type_damage_max(u1, u2);
1774     /* If we can damage it. */
1775     if (dmgmax1) {
1776         tsptknocks = u_hp_max(u2) / dmgmax1;
1777         occknocks = 0; /* No occ-knock jokes please. */
1778         /* I'm not occustomed to having my work knocked. */
1779         if (occstatus == DEFINITELY_HAS_OCCS) {
1780             for_all_occupant_views(uview, uvocc) {
1781                 u3 = uvocc->type;
1782                 occside = side_n(uvocc->siden);
1783                 dmgmax1 = type_damage_max(u1, uvocc->type);
1784                 if (dmgmax1) {
1785                     if (enemy_side(side, occside))
1786                       occknocks += u_hp_max(uvocc->type) / dmgmax1;
1787                     else
1788                       occknocks += 2 * (u_hp_max(uvocc->type) / dmgmax1);
1789                 }
1790                 if (occknocks >= tsptknocks)
1791                   break;
1792             }
1793             occknocksbest = occknocks;
1794         }
1795         else if (occstatus == MAYBE_HAS_OCCS) {
1796             /* This is a fairly expensive test. The results
1797                remain the same throughout the game, and
1798                hence should be precomputed. */
1799             for_all_unit_types(u3) {
1800                 if (type_can_occupy_empty_type(u3, u2)) {
1801                     dmgmax1 = type_damage_max(u1, u3);
1802                     occknocks = 0;
1803                     if (dmgmax1)
1804                       occknocks = u_hp_max(u3) / dmgmax1;
1805                     occknocksbest = max(occknocks, occknocksbest);
1806                     if (occknocksbest >= tsptknocks)
1807                       break;
1808                 }
1809             }
1810         }
1811         /* If transport it too fragile, then don't shake it. */
1812         if (((tsptknocks * (oside->udoctrine)[u2]->repair_percent) / 100)
1813             <= occknocksbest)
1814           return FALSE;
1815         /* If transport is tough enough,
1816            and if it maybe has occs, then maybe shake it. */
1817         else {
1818             if ((occstatus == MAYBE_HAS_OCCS) && probability(50))
1819               return FALSE;
1820         }
1821     } /* dmgmax1 */
1822     /* If we cannot damage the transport we always attack
1823     it on the assumption that it is building occs that we can
1824     damage (the calling code checks that type_can_build or
1825     u_advanced is true). */
1826     return TRUE;
1827 }
1828 
1829 /* Evaluates the feasibility of attempting to capture an unit. */
1830 
1831 int
1832 ai_consider_capturing(Unit *unit, UnitView *uview, int dmgthresh,
1833                            int qthresh)
1834 {
1835     int u1 = NONUTYPE, u2 = NONUTYPE, u3 = NONUTYPE;
1836     Side *side = NULL, *oside = NULL;
1837     int dmgmax1 = 0, ourknocks = 0;
1838     int qprob = 0;
1839     int i = 0;
1840     int ncaptors = 0;
1841 
1842     u1 = unit->type;
1843     side = unit->side;
1844     u2 = uview->type;
1845     oside = side_n(uview->siden);
1846     /* Sanity check. Can we even capture it? */
1847     if (!could_capture(u1, u2, oside))
1848       return FALSE;
1849     /* Can we even own the potential captive? */
1850     if (!type_allowed_on_side(u2, side))
1851       return FALSE;
1852     /* Can we take the potential captive without being scathed? */
1853     if ((could_capture_by_attacks(u1, u2, oside)
1854         || could_capture_by_fire(u1, u2, oside))
1855         && (type_hit_range_max(u2, u1) < 1))
1856       return TRUE;
1857     /* If it is a self-unit, can any other unit on our side capture it, or
1858        MUST we perform the task, no matter how unsavory? */
1859     /* (Should consider that destruction might be more palatable.) */
1860     if (u_can_be_self(u2)) {
1861         dmgthresh += 5;
1862         for_all_unit_types(u3) {
1863             if (u3 == u1)
1864               continue;
1865             if (!type_allowed_on_side(u3, side))
1866               continue;
1867             if (could_capture(u3, u2, oside)) {
1868                 ++ncaptors;
1869                 break;
1870             }
1871         }
1872         if (!ncaptors)
1873           return TRUE;
1874     }
1875     /* If it is a builder, then be willing to take greater risks to
1876        seize it. */
1877     if (type_can_build(u2, oside) || u_advanced(u2))
1878       ++dmgthresh;
1879     if (ai_damage_ratio_vs_type(unit, u2) >= dmgthresh) {
1880         dmgmax1 = type_damage_max(u2, u1);
1881         /* If it can damage us... */
1882         if (dmgmax1) {
1883             ourknocks = unit->hp2 / dmgmax1;
1884             qprob = 100 - capture_chance(u1, u2, oside);
1885             if (qprob > 0) {
1886                 for (i = 0; i < ourknocks; ++i) {
1887                     qprob = (qprob * qprob) / 100;
1888                     if (qprob <= qthresh)
1889                         break;
1890                 }
1891                 /* (Should factor in the value of the target,
1892                    and whether we could lose occs in a
1893                    shootout/capture attempt.) */
1894                 if (qprob > qthresh)
1895                   return FALSE;
1896             }
1897         } /* dmgmax1 */
1898         /* Else it cannot damage us, then we must be invincible to one
1899            another. */
1900         else {
1901             if (!could_capture_by_capture(u1, u2, oside))
1902               return FALSE;
1903         } /* !dmgmax1 */
1904     } /* ai_damage_ratio_vs_type(unit, u2) */
1905     return TRUE;
1906 }
1907 
1908 /* A generic damage-ratio evaluator. Assumes maximal damage. */
1909 /* (This function should be in 'ai.c'?) */
1910 
1911 int
1912 ai_damage_ratio_vs_type(Unit *unit, int u2)
1913 {
1914     int atkval1 = 0, atkval2 = 0;
1915     int u1 = NONUTYPE;
1916 
1917     u1 = unit->type;
1918     /* Can it even hit or damage us?
1919        And do we look like Freddy Kreuger to it? */
1920     if (could_destroy(u2, u1)) {
1921         /* (The difference in fire ranges and attack ranges should be
1922            accounted for. Which is to ask: can it hit us before we can
1923            hit it, or vice versa? And how hard are the hits?) */
1924         atkval1 = unit->hp2 * type_damage_max(u1, u2);
1925         if (atkval1) {
1926             atkval2 = u_hp_max(u2) * type_damage_max(u2, u1);
1927             if (!atkval2)
1928                 return 0; /* u2 cannot hurt u1 by conventional means. */
1929             /* How much more can u2 hurt u1 than u1 can hurt u2?
1930                If 0, then u1 is superior.
1931                If 1, then roughly equal.
1932                If >1, then u2 is superior. */
1933             return atkval2 / atkval1;
1934         }
1935         else
1936             return INT_MAX; /* u1 cannot hurt u2 by conventional means. */
1937     }
1938     return 0; /* u2 cannot destroy u1 by conventional means. */
1939 }
1940 
1941 /* This decides whether a given unit type seen at a given location is worth
1942    trying to capture. */
1943 
1944 int
1945 worth_capturing(Side *side, int u2, Side *side2, int x, int y)
1946 {
1947     int u, bestchance = 0;
1948 
1949     /* See how likely we are to be able to capture the type. */
1950     for_all_unit_types(u) {
1951 	bestchance = max(capture_chance(u, u2, side2), bestchance);
1952     }
1953     return bestchance;
1954     /* (should account for other considerations too, like which types of
1955        units we have) */
1956 }
1957 
1958 /* Note: go_after_victim can be used for both attacking and firing units since
1959 ai_victim_here only tests for could_hit.  */
1960 
1961 /* This routine looks for somebody, anybody to attack. */
1962 
1963 #if (0)
1964 int
1965 go_after_victim(Unit *unit, int range)
1966 {
1967     int x, y, rslt;
1968 
1969     tmpunit = unit;
1970     DMprintf("%s seeking victim within %d; found ",
1971 	     unit_desig(unit), range);
1972     victim_rating = -9999;
1973     rslt = limited_search_around(unit->x, unit->y, range, ai_victim_here,
1974                                  &x, &y, 1, 10);
1975     if (rslt) {
1976 	DMprintf("s%d %s at %d,%d\n",
1977                  victim_sidenum, u_type_name(victim_utype), x, y);
1978 	/* Set up a task to go after the unit found. */
1979 	/* (should be able to set capture task if better) */
1980 	set_hit_unit_task(unit, x, y, victim_utype, victim_sidenum);
1981 	if (unit->transport != NULL
1982 	    && mobile(unit->transport->type)
1983 	    && unit->transport->plan) {
1984 	    set_move_to_task(unit->transport, x, y, 1);
1985 	}
1986     } else if (victim_rating > -9999) {
1987 	DMprintf("s%d %s (rated %d) at %d,%d\n",
1988 		 victim_sidenum, u_type_name(victim_utype), victim_rating,
1989                  victim_x, victim_y);
1990 	/* Set up a task to go after the unit found. */
1991 	/* (should be able to set capture task if better) */
1992 	set_hit_unit_task(unit, victim_x, victim_y, victim_utype,
1993                           victim_sidenum);
1994 	if (unit->transport != NULL
1995 	    && mobile(unit->transport->type)
1996 	    && unit->transport->plan) {
1997 	    set_move_to_task(unit->transport, victim_x, victim_y, 1);
1998 	}
1999 	/* We succeeded after all. */
2000 	rslt = TRUE;
2001     } else {
2002 	DMprintf("nothing\n");
2003     }
2004     return rslt;
2005 }
2006 #endif
2007 
2008 #if 0		/* Unused. */
2009 
2010 /* The point of this new function is to limit the search for captives
2011    to a given range. */
2012 
2013 int
2014 go_after_captive(Unit *unit, int range)
2015 {
2016     int x, y, rslt;
2017 
2018     tmpunit = unit;
2019 
2020     DMprintf("%s searching for useful capture within %d; found ",
2021 	     unit_desig(unit), range);
2022     rslt = search_around(unit->x, unit->y, range,
2023 			 useful_captureable_here, &x, &y, 1);
2024     if (rslt) {
2025 	DMprintf("one at %d,%d\n", x, y);
2026 	/* Set up a task to go after the unit found. */
2027 	set_capture_task(unit, x, y);
2028 	if (unit->transport
2029 	    && mobile(unit->transport->type)
2030 	    && unit->transport->plan) {
2031 	    set_move_to_task(unit->transport, x, y, 1);
2032 	}
2033 	return (execute_task(unit) != TASK_FAILED);
2034     } else {
2035 	DMprintf("nothing\n");
2036 	return FALSE;
2037     }
2038 }
2039 
2040 #endif
2041 
2042 /* Given a location and a unit (in tmpunit), try to identify a target. */
2043 /* (should move to place to share with AIs) */
2044 
2045 int target_x, target_y, target_rating, target_utype, target_sidenum;
2046 
2047 int
2048 target_here(int x, int y)
2049 {
2050     int u2 = NONUTYPE, rating, dist;
2051     Side *side = tmpunit->side, *oside = NULL;
2052     Unit *unit3;
2053     UnitView *uview;
2054 
2055     for_all_view_stack_with_occs(side, x, y, uview) {
2056 	u2 = uview->type;
2057 	oside = side_n(uview->siden);
2058 	/* (should move all tests inside loop) */
2059 	if (is_unit_type(u2)
2060 	    && enemy_side(side, oside)
2061 	    && could_hit(tmpunit->type, u2)
2062 	    /* Also consider damage by fire. Moreover, if the unit can
2063 	       carry occupants they may be vulnerable even though the
2064 	       unit itself is not. */
2065          	&& (uu_damage(tmpunit->type, u2) > 0
2066 		    || fire_damage(tmpunit->type, u2) > 0
2067 		    || type_can_have_occs(u2))
2068 	    /* and have correct ammo */
2069 	    && !side_planning_to_capture_type_at(side, u2, x, y)
2070 	    ) {
2071 	    rating = uu_zz_bfw(tmpunit->type, u2);
2072 	    /* Further-away units are less interesting than closer ones. */
2073 	    dist = distance(tmpunit->x, tmpunit->y, x, y);
2074 	    if (dist > 0)
2075 	      rating /= dist;
2076 	    /* A larger city is more worth capturing. */
2077 	    rating *= uview->size;
2078 	    /* Real enemies are more important targets. */
2079 	    if (oside != NULL)
2080 	      rating *= 2;
2081 	    /* Always attack units that threaten one of our own cities. */
2082 	    for_all_side_units(side, unit3) {
2083 		if (u_advanced(unit3->type)
2084 		    && distance(unit3->x, unit3->y, x, y) < 5)
2085 		  rating *= 5 - distance(unit3->x, unit3->y, x, y);
2086 	    }
2087 	    if (rating > target_rating
2088 		|| (rating == target_rating && flip_coin())) {
2089 		target_x = x;  target_y = y;
2090 		target_rating = rating;
2091 		target_utype = u2;
2092 		target_sidenum = side_number(oside);
2093 	    }
2094 	}
2095     }
2096     return FALSE;
2097 }
2098 
2099 int
2100 fire_at_opportunity(Unit *unit)
2101 {
2102     int x, y, range, rslt;
2103 
2104     tmpunit = unit;
2105     range = u_range(unit->type);
2106     /* Look further for targets if we are mobile. */
2107     if (mobile(unit->type)) {
2108     	range += u_ai_tactical_range(unit->type);
2109     }
2110     target_rating = -9999;
2111     DMprintf("%s seeking target within %d; found ",
2112              unit_desig(unit), range);
2113     rslt = search_around(unit->x, unit->y, range, target_here, &x, &y, 1);
2114     if (rslt) {
2115 	DMprintf("s%d %s at %d,%d\n",
2116 		 target_sidenum, u_type_name(target_utype), x, y);
2117 	/* Set up a task to shoot at the unit found. */
2118 	set_hit_unit_task(unit, x, y, target_utype, target_sidenum);
2119     } else if (target_rating > -9999) {
2120 	DMprintf("s%d %s (rated %d) at %d,%d\n",
2121 		 target_sidenum, u_type_name(target_utype), target_rating,
2122 		 x, y);
2123 	/* Set up a task to shoot at the unit found. */
2124 	set_hit_unit_task(unit, target_x, target_y, target_utype, target_sidenum);
2125     } else {
2126 	DMprintf("nothing\n");
2127     }
2128     return rslt;
2129 }
2130 
2131 /* Find a unit task by task type. */
2132 
2133 Task *
2134 find_unit_task_by_type(Unit *unit, TaskType tt)
2135 {
2136     Task *task = NULL;
2137 
2138     if (in_play(unit) && unit->plan) {
2139         for_all_tasks(unit->plan, task) {
2140             if (task->type == tt)
2141               return task;
2142         }
2143     }
2144     return NULL;
2145 }
2146 
2147 /* Check to see if our grand plans are at risk of being sideswiped by lack of
2148    supply, and set up a resupply task if so. */
2149 
2150 int
2151 resupply_if_low(Unit *unit)
2152 {
2153     int m;
2154 
2155     /* Check if any supplies are below the trigger level. */
2156     m = low_on_supplies_one(unit);
2157     if (m != NONMTYPE) {
2158 	return plan_resupply(unit, m);
2159     }
2160     return FALSE;
2161 }
2162 
2163 /* Return a type of essential material that the unit is running out of. */
2164 
2165 int
2166 low_on_supplies_one(Unit *unit)
2167 {
2168     int u = unit->type, m;
2169 
2170     for_all_material_types(m) {
2171 	if ((um_base_consumption(u, m) > 0
2172 		|| um_consumption_per_move(u, m) > 0)
2173 	    && um_storage_x(u, m) > 0
2174 	    && unit->supply[m] <=
2175                (unit_doctrine(unit)->resupply_percent * um_storage_x(u, m))
2176                 / 100) {
2177 	    return m;
2178 	}
2179     }
2180     return NONMTYPE;
2181 }
2182 
2183 int
2184 rearm_if_low(Unit *unit)
2185 {
2186 	int m;
2187 
2188 	/* Check if any ammo is below the trigger level. */
2189 	m = low_on_ammo_one(unit);
2190     	if (m != NONMTYPE) {
2191 		return plan_resupply(unit, m);
2192 	}
2193 	return FALSE;
2194 }
2195 
2196 /* Return a type of material that we want to use to attack or fire. */
2197 
2198 int
2199 low_on_ammo_one(Unit *unit)
2200 {
2201     int u = unit->type, m, trigger;
2202 
2203     for_all_material_types(m) {
2204     	/* Skip materials that we cannot store. */
2205     	if (um_storage_x(u, m) <= 0) {
2206 		continue;
2207     	}
2208     	/* Compute the rearming trigger level. */
2209     	trigger =
2210 	    (unit_doctrine(unit)->rearm_percent * um_storage_x(u, m)) / 100;
2211 	if (could_fire_at_any(u)) {
2212 		/* First consider consumables (ammo). */
2213 		if ((um_consumption_per_fire(u, m) > 0
2214 		    /* Used incorrectly instead of um_consumption_per_fire in
2215 		    	some games (should fix this). */
2216 		    || um_consumption_per_attack(u, m) > 0)
2217 		    /* We are low either if we are below the trigger,
2218 			 or if we are unable to fire one more round. */
2219 	    	    && unit->supply[m] <=
2220 		       max(
2221 			um_consumption_per_fire(u, m) > 0 ?
2222 	    	    	um_consumption_per_fire(u, m) :
2223 	    	    	um_consumption_per_attack(u, m), trigger)) {
2224 			return m;
2225 		}
2226 		/* Then consider special materials (tools & weapons). */
2227 		if (um_to_fire(u, m) > 0
2228 		    /* We are low either if we are below the trigger,
2229 		        or if we are unable to fire at all. */
2230 		    && unit->supply[m] <= max(um_to_fire(u, m), trigger)) {
2231 			return m;
2232 		}
2233 	}
2234 	if (could_attack_any(u)) {
2235 		/* First consider consumables (ammo). */
2236 		if (um_consumption_per_attack(u, m) > 0
2237 		    /* We are low either if we are below the trigger,
2238 			 or if we are unable to fire one more round. */
2239 	    	    && unit->supply[m] <=
2240 		       max(um_consumption_per_attack(u, m), trigger)) {
2241 			return m;
2242 		}
2243 		/* Then consider special materials (tools & weapons). */
2244 		if (um_to_attack(u, m) > 0
2245 		    /* We are low either if we are below the trigger,
2246 			 or if we are unable to fire at all. */
2247 		    && unit->supply[m] <= max(um_to_attack(u, m), trigger)) {
2248 			return m;
2249 		}
2250 	}
2251     }
2252     return NONMTYPE;
2253 }
2254 
2255 /* Now common code called by resupply_if_low and rearm_if_low. This is
2256 really a plan since it sets various tasks. */
2257 
2258 int
2259 plan_resupply(Unit *unit, int m)
2260 {
2261     int u = unit->type, x = unit->x, y = unit->y, x1, y1, outcome, range;
2262     Task *curtask = unit->plan->tasks;
2263 
2264     /* Set up a resupply task if extraction of material from the
2265 	terrains is possible. */
2266     if (valid(check_extract_action(unit, unit, x, y, m, 1))) {
2267 	set_resupply_task(unit, m);
2268 	outcome = execute_task(unit);
2269 	/* Clear the task outcome if we failed so that we do not block
2270 	repair tasks or normal plan execution. Also clear the agenda
2271 	so that we do not execute the failed task again. */
2272 	if (outcome == TASK_FAILED) {
2273 	    clear_task_agenda(unit);
2274 	    clear_task_outcome(unit);
2275 	    return FALSE;
2276 	} else {
2277 	    return TRUE;
2278 	}
2279     }
2280     /* Setting up other types of resupply tasks makes sense only for mobile
2281 	units. A non-mobile unit cannot move to a supply source, and if supplies
2282 	are available in the same cell, it will benefit from this even if it is
2283 	doing something else such as building. */
2284     if (!mobile(u)) {
2285 	return FALSE;
2286     }
2287     /* Now set up things to look for resupply of firstlowm. */
2288     range = real_operating_range_best(unit);
2289     tmpside = unit->side;
2290     tmpunit = unit;
2291     if (lowm == NULL) {
2292 	lowm = (int *) xmalloc(nummtypes * sizeof(int));
2293     }
2294     lowm[0] = m;
2295     numlow = 1;
2296     /* Try resupplying in place.
2297 	This is preferable to moving to a resupply point. */
2298     if (can_resupply_from_here(unit->x, unit->y)
2299 	|| (can_survive_on_known(unit->type, unit->side, unit->x, unit->y)
2300 	    && can_auto_resupply_self(unit, lowm, numlow))) {
2301 	set_resupply_task(unit, m);
2302 	/* Execute the new resupply task. */
2303 	outcome = execute_task(unit);
2304     }
2305     /* See if we are already moving to a supply source. */
2306     else if (curtask != NULL
2307 	&& curtask->type == TASK_MOVE_TO
2308 	&& can_resupply_from_here(curtask->args[0], curtask->args[1])) {
2309 	/* Proceed with task execution. */
2310 	outcome = execute_task(unit);
2311     /* See if we already have a resupply task for the same material. */
2312     } else if (curtask != NULL
2313 	&& curtask->type == TASK_RESUPPLY
2314 	&& curtask->args[0] == m) {
2315 	/* Proceed with task execution. */
2316 	outcome = execute_task(unit);
2317     /* Otherwise set up a task. */
2318     } else if (search_around(x, y, range, can_resupply_from_here,
2319 			     &x1, &y1, 1)) {
2320 	DMprintf("%s low on %s, found resupply point at %d,%d.\n",
2321 		 unit_desig(unit), m_type_name(m), x1, y1);
2322 	set_resupply_task(unit, m);
2323 	/* Execute the new resupply task. */
2324 	outcome = execute_task(unit);
2325     /* Return, but don't clear the task outcome or the agenda if a
2326 	resupply task could not be set, so that execution of any existing
2327 	task in the queue may proceed. */
2328     } else {
2329 	DMprintf("%s low on %s, found no resupply point within range %d.\n",
2330 		 unit_desig(unit), m_type_name(m), range);
2331 	return FALSE;
2332     }
2333     /* Clear the task outcome if we failed so that we do not block
2334     repair tasks or normal plan execution. Also clear the agenda
2335     so that we do not execute the failed task again. */
2336     if (outcome == TASK_FAILED) {
2337 	clear_task_agenda(unit);
2338 	clear_task_outcome(unit);
2339 	return FALSE;
2340     } else {
2341 	return TRUE;
2342     }
2343 }
2344 
2345 int
2346 repair_if_damaged(Unit *unit)
2347 {
2348     int u = NONUTYPE;
2349     Task *tasks = NULL;
2350     int hpgoal = -1;
2351 
2352     assert_error(in_play(unit),
2353 		 "AI: Attempted to assess damage to out-of-play unit");
2354     u = unit->type;
2355     hpgoal = (u_hp(u) * unit_doctrine(unit)->repair_complete) / 100;
2356     // Is any repair needed?
2357     if (unit->hp
2358 	>= (u_hp_max(u) * unit_doctrine(unit)->repair_percent) / 100)
2359 	return FALSE;
2360     // Check if we are already working on repair or resupply,
2361     //	and do not interfere if so.
2362     tasks = (unit->plan ? unit->plan->tasks : NULL);
2363     if (tasks) {
2364 	if ((TASK_MOVE_TO == tasks->type) || (TASK_OCCUPY == tasks->type))
2365 	    tasks = tasks->next;
2366 	if (tasks) {
2367 	    if (TASK_RESUPPLY == tasks->type)
2368 		return FALSE;
2369 	    if ((TASK_REPAIR == tasks->type) && (unit->id != tasks->args[0]))
2370 		return FALSE;
2371 	}
2372     }
2373     // Set repair task on self, if one does not exist already.
2374     if (!tasks || (TASK_REPAIR != tasks->type))
2375 	set_repair_task(unit, unit->id, hpgoal);
2376     // Execute the new or existing task.
2377     if (TASK_FAILED != execute_task(unit))
2378 	return TRUE;
2379     return FALSE;
2380 }
2381 
2382 #if (0)
2383 int
2384 plan_repair(Unit *unit)
2385 {
2386     int u = unit->type, x = unit->x, y = unit->y, x1, y1, outcome, range;
2387     Task *curtask = unit->plan->tasks;
2388 
2389     /* Set up a repair task if explicit self-repair if possible.
2390     The task code will then set up a self-repair action, but we
2391     will in addition to this benefit from any auto-repair that is
2392     available in the cell. */
2393     if (valid(check_repair_action(unit, unit, unit))) {
2394 	set_repair_task(unit);
2395 	outcome = execute_task(unit);
2396 	/* Clear the task outcome if we failed so that we do not block
2397 	    normal plan execution. Also clear the agenda so that we do not
2398 	    execute the failed task again. */
2399 	if (outcome == TASK_FAILED) {
2400 	    clear_task_agenda(unit);
2401 	    clear_task_outcome(unit);
2402 	    return FALSE;
2403 	} else {
2404 	    return TRUE;
2405 	}
2406     }
2407     /* Setting up other types of repair tasks makes sense only for mobile
2408 	units. A non-mobile unit cannot move to a repair point, and if recovery
2409 	or auto-repair is available in its cell,
2410 	it will benefit from this,
2411 	 even if it is doing something else such as building. */
2412     if (!mobile(u)) {
2413 	return FALSE;
2414     }
2415     range = real_operating_range_best(unit);
2416     tmpunit = unit;
2417     /* Also set up a repair task if we have self recovery. */
2418     if (u_hp_recovery(u) > 0) {
2419 	set_repair_task(unit);
2420 	outcome = execute_task(unit);
2421     /* Then test if we already have a repair task. */
2422     } else if (curtask != NULL
2423 	       && curtask->type == TASK_REPAIR) {
2424 	/* Proceed with task execution. */
2425 	outcome = execute_task(unit);
2426     /* See if we are already moving to a repair point. */
2427     } else if (curtask != NULL
2428 	&& curtask->type == TASK_MOVE_TO
2429 	&& can_repair_from_here(curtask->args[0], curtask->args[1])) {
2430 	/* Proceed with task execution. */
2431 	outcome = execute_task(unit);
2432     /* Otherwise set up a repair task. */
2433     } else if (search_around(x, y, range, can_repair_from_here, &x1, &y1, 1)) {
2434 	DMprintf("%s damaged, found repair point at %d,%d.\n",
2435 		 unit_desig(unit), x1, y1);
2436 	set_repair_task(unit);
2437 	/* Execute the new resupply task. */
2438 	    outcome = execute_task(unit);
2439     /* Return, but don't clear the task outcome or the agenda if a
2440     repair task could not be set, so that execution of any existing
2441     task in the queue may proceed. */
2442     } else {
2443 	DMprintf("%s damaged, found no repair point within range %d.\n",
2444 		 unit_desig(unit), range);
2445 	return FALSE;
2446     }
2447     /* Clear the task outcome if we failed so that we do not block
2448     normal plan execution. Also clear the agenda so that we do not
2449     execute the failed task again. */
2450     if (outcome == TASK_FAILED) {
2451 	clear_task_agenda(unit);
2452 	clear_task_outcome(unit);
2453 	return FALSE;
2454     } else {
2455 	return TRUE;
2456     }
2457 }
2458 #endif
2459 
2460 /* Look within a limited distance for any independent unit that could be
2461    captured, and set up tasks to go get it. */
2462 
2463 int
2464 capture_indep_if_nearby(Unit *unit)
2465 {
2466     /* Use the unit's tactical range instead. */
2467     int	u = unit->type;
2468     int	range = u_ai_tactical_range(u);
2469     int x, y, rslt;
2470     Task *curtask = unit->plan->tasks;
2471 
2472     if (!mobile(u))
2473       return FALSE;
2474     if (!could_capture_any(unit->type))
2475       return FALSE;
2476     tmpunit = unit;
2477     /* See if we're already doing such a task. */
2478     if (curtask != NULL
2479 	&& ((curtask->type == TASK_MOVE_TO
2480 	     && indep_captureable_here(curtask->args[0], curtask->args[1]))
2481 	    || (curtask->type == TASK_CAPTURE
2482 		&& indep_captureable_here(curtask->args[0], curtask->args[1]))))
2483       return FALSE;
2484     DMprintf("%s searching for easy capture within %d; found ",
2485 	     unit_desig(unit), range);
2486     rslt = search_around(unit->x, unit->y, range, indep_captureable_here,
2487 			 &x, &y, 1);
2488     if (rslt) {
2489 	DMprintf("one at %d,%d\n", x, y);
2490 	/* Set up a task to go after the unit found. */
2491 	set_capture_task(unit, x, y, tmputype, tmpside->id);
2492 	if (unit->transport
2493 	    && mobile(unit->transport->type)
2494 	    && unit->transport->plan) {
2495 	    set_move_to_task(unit->transport, x, y, 1);
2496 	}
2497 	return (execute_task(unit) != TASK_FAILED);
2498     } else {
2499 	DMprintf("nothing\n");
2500     }
2501     return FALSE;
2502 }
2503 
2504 int
2505 indep_captureable_here(int x, int y)
2506 {
2507     int u2;
2508     Side *side = tmpunit->side, *side2;
2509     UnitView *uview;
2510 
2511     /* Should perhaps iterate over occs also, so that they can be
2512     captured even if the transport is not? */
2513     for_all_view_stack(side, x, y, uview) {
2514 	u2 = uview->type;
2515 	side2 = side_n(uview->siden);
2516 	if (side2 == indepside
2517 	    && side != indepside
2518 	    && capture_chance(tmpunit->type, u2, side2) > 10) {
2519 	  	tmputype = u2;
2520 		tmpside = side2;
2521 		return TRUE;
2522 	}
2523     }
2524     return FALSE;
2525 }
2526 
2527 /* Look within a limited distance for any type of unit that would be
2528    good to own, and set up tasks to go get it. */
2529 
2530 int
2531 capture_useful_if_nearby(Unit *unit)
2532 {
2533     /* Use the unit's tactical range instead. */
2534     int u = unit->type;
2535     int	range = u_ai_tactical_range(u);
2536     int x, y, rslt;
2537     Task *curtask = unit->plan->tasks;
2538 
2539     if (!mobile(u))
2540       return FALSE;
2541     if (!could_capture_any(unit->type))
2542       return FALSE;
2543     tmpunit = unit;
2544     /* See if we're already doing such a task. */
2545     if (curtask != NULL
2546 	&& ((curtask->type == TASK_MOVE_TO
2547 	     && useful_captureable_here(curtask->args[0], curtask->args[1]))
2548 	    || (curtask->type == TASK_CAPTURE
2549 		&& useful_captureable_here(curtask->args[0], curtask->args[1]))))
2550       return FALSE;
2551     DMprintf("%s searching for useful capture within %d; found ",
2552 	     unit_desig(unit), range);
2553     rslt = search_around(unit->x, unit->y, range, useful_captureable_here,
2554 			 &x, &y, 1);
2555     if (rslt) {
2556 	DMprintf("one at %d,%d\n", x, y);
2557 	/* Set up a task to go after the unit found. */
2558         if (g_combat_model() == 0) {
2559             if (!could_capture_by_capture(unit->type, tmputype, tmpside))
2560               set_hit_unit_task(unit, x, y, tmputype, tmpside->id);
2561             else
2562 	      set_capture_task(unit, x, y, tmputype, tmpside->id);
2563         }
2564         else if (g_combat_model() == 1) {
2565             set_hit_unit_task(unit, x, y, tmputype, tmpside->id);
2566         }
2567 	if (unit->transport
2568 	    && mobile(unit->transport->type)
2569 	    && unit->transport->plan) {
2570 	    set_move_to_task(unit->transport, x, y, 1);
2571 	}
2572 	return (execute_task(unit) != TASK_FAILED);
2573     } else {
2574 	DMprintf("nothing\n");
2575     }
2576     return FALSE;
2577 }
2578 
2579 int
2580 useful_captureable_here(int x, int y)
2581 {
2582     int u2;
2583     Side *side = tmpunit->side, *side2;
2584     UnitView *uview;
2585 
2586     /* Should perhaps iterate over occs also, so that they can be
2587     captured even if the transport is not? */
2588     for_all_view_stack(side, x, y, uview) {
2589 	u2 = uview->type;
2590 	side2 = side_n(uview->siden);
2591 	if (!trusted_side(side, side2)
2592 	    && capture_chance(tmpunit->type, u2, side2) > 0
2593 	    && useful_type(side, u2)
2594 	    ) {
2595 	    tmputype = u2;
2596 	    tmpside = side2;
2597 	    return TRUE;
2598 	}
2599     }
2600     return FALSE;
2601 }
2602 
2603 /* Return true if the given type of unit is useful in some way to the
2604    given side.  This is almost always true. */
2605 
2606 int
2607 useful_type(Side *side, int u)
2608 {
2609     if (!type_allowed_on_side(u, side))
2610       return FALSE;
2611     return TRUE;
2612 }
2613 
2614 #if (0)
2615 int
2616 could_capture_any(int u)
2617 {
2618     int u2;
2619 
2620     for_all_unit_types(u2) {
2621 	if (uu_capture(u, u2) > 0 || uu_indep_capture(u, u2) > 0)
2622 	  return TRUE;
2623 	/* also check if u2 in game, on other side, etc? */
2624     }
2625     return FALSE;
2626 }
2627 #endif
2628 
2629 /* This attempts to make some vaguely plausible arguments for an
2630    action, using the types of each arg as a guide.  It also generates
2631    *invalid* arguments occasionally, which tests error checking in the
2632    actions' code.  This is mainly useful for testing. */
2633 
2634 void
2635 make_plausible_random_args(char *argtypestr, int i, int *args, Unit *unit)
2636 {
2637     char argch;
2638     int	slen, arg;
2639 
2640     slen = strlen(argtypestr);
2641     while (i < slen && i < 10) {
2642 	argch = argtypestr[i];
2643 	switch (argch) {
2644 	  case 'n':
2645 	    arg = (flip_coin() ? xrandom(10) :
2646 		   (flip_coin() ? xrandom(100) :
2647 		    (xrandom(20000) - 10000)));
2648 	    break;
2649 	  case 'u':
2650 	    /* Go a little outside range, so as to get some invalid types. */
2651 	    arg = xrandom(numutypes + 2) - 1;
2652 	    break;
2653 	  case 'm':
2654 	    arg = xrandom(nummtypes + 2) - 1;
2655 	    break;
2656 	  case 't':
2657 	    arg = xrandom(numttypes + 2) - 1;
2658 	    break;
2659 	  case 'a':
2660 	    arg = xrandom(numatypes + 2) - 1;
2661 	    break;
2662 	  case 'x':
2663 	    arg = (unit != NULL && flip_coin() ? (unit->x + xrandom(5) - 2) :
2664 		   (xrandom(area.width + 4) - 2));
2665 	    break;
2666 	  case 'y':
2667 	    arg = (unit != NULL && flip_coin() ? (unit->y + xrandom(5) - 2) :
2668 		   (xrandom(area.height + 4) - 2));
2669 	    break;
2670 	  case 'z':
2671 	    arg = (flip_coin() ? 0 : xrandom(10));
2672 	    break;
2673 	  case 'd':
2674 	    arg = random_dir();
2675 	    break;
2676 	  case 'U':
2677 	    /* Cast around for a valid unit. */
2678 	    while (find_unit(arg = xrandom(numunits)+1) == NULL
2679 		   && probability(98)) {
2680 		   ;
2681 	    }
2682 	    break;
2683 	  case 'S':
2684 	    arg = xrandom(numsides + 3) - 1;
2685 	    break;
2686 	  default:
2687 	    run_warning("Garbled action arg type '%c'\n", argch);
2688 	    arg = 0;
2689 	    break;
2690 	}
2691 	args[i++] = arg;
2692     }
2693 }
2694 
2695 /* Random walking just attempts to move around. */
2696 
2697 void
2698 random_walk(Unit *unit)
2699 {
2700     int dir = random_dir(), x1, y1, tries = 0;
2701 
2702     while (!interior_point_in_dir(unit->x, unit->y, dir, &x1, &y1)) {
2703     	if (++tries > 500) {
2704 	    run_warning("something is wrong");
2705 	    break;
2706 	}
2707 	dir = random_dir();
2708     }
2709     set_move_to_task(unit, x1, y1, 0);
2710 }
2711 
2712 /* Record the unit as waiting for orders about what to do. */
2713 
2714 void
2715 set_waiting_for_tasks(Unit *unit, int flag)
2716 {
2717     if (unit->plan->waitingfortasks == flag) {
2718         return;
2719     }
2720     unit->plan->waitingfortasks = flag;
2721     if (unit->side != NULL) {
2722         unit->side->numwaiting += (unit->plan->waitingfortasks ? 1 : -1);
2723         update_unit_display(unit->side, unit, FALSE);
2724     }
2725 }
2726 
2727 /* General routine to wake a unit up (and maybe all its cargo). */
2728 
2729 void
2730 wake_unit(Side *side, Unit *unit, int forcewakeoccs)
2731 {
2732     Unit *occ;
2733 
2734     assert_warning_return(in_play(unit),
2735 			  "Attempted to wake an invalid unit.", );
2736     /* (should test that side is permitted to wake) */
2737     /* Wake the unit. */
2738     if (unit->plan) {
2739 	unit->plan->asleep = FALSE;
2740 	unit->plan->reserve = FALSE;
2741 	update_unit_display(side, unit, TRUE);
2742     }
2743     /* Try waking any occupants. */
2744     if (forcewakeoccs) {
2745 	for_all_occupants(unit, occ)
2746 	  wake_unit(side, occ, forcewakeoccs);
2747     }
2748 }
2749 
2750 /* Conditionally attempt to wake up a unit based on restrictions on recursive
2751    waking. */
2752 
2753 void
2754 selectively_wake_unit(Side *side, Unit *unit, int wakeoccs, int forcewakeoccs)
2755 {
2756     Unit *occ = NULL;
2757 
2758     assert_warning_return(in_play(unit),
2759 			  "Attempted to wake an invalid unit.", );
2760     /* If on a transport, then probably transport already
2761        has awakened us, but just to cover all the cases... */
2762     if ((unit->transport)
2763 	&& uu_can_recursively_wake(unit->transport->type,
2764 				   unit->type))
2765       wake_unit(unit->side, unit, forcewakeoccs);
2766     /* Else not on a transport. */
2767     else if (!(unit->transport))
2768       wake_unit(unit->side, unit, forcewakeoccs);
2769     /* Now wake any occs, if so desired. */
2770     /* Note that if 'forcewakeoccs' is set, then 'wake_unit' will
2771        recursively wake _all_ occs, and so the following code would be
2772        somewhat redundant. */
2773     /* (Need valid test case for this, so that it can be tested and
2774 	enabled.) */
2775 #if (0)
2776     if (!forcewakeoccs && wakeoccs) {
2777 	for_all_occupants(unit, occ) {
2778 	    if ((unit->side == occ->side)
2779 		&& uu_can_recursively_wake(unit->type, occ->type))
2780 	      selectively_wake_unit(unit->side, occ, TRUE, FALSE);
2781 	}
2782     }
2783 #endif
2784 }
2785 
2786 /* The area wakeup. */
2787 
2788 static int tmpflag;
2789 
2790 static void
2791 wake_at(int x, int y)
2792 {
2793     Unit *unit;
2794 
2795     for_all_stack(x, y, unit) {
2796 	if (side_controls_unit(tmpside, unit)) {
2797 	    wake_unit(tmpside, unit, tmpflag);
2798 	}
2799     }
2800 }
2801 
2802 void
2803 wake_area(Side *side, int x, int y, int n, int occs)
2804 {
2805     tmpside = side;
2806     tmpflag = occs;
2807     apply_to_area(x, y, n, wake_at);
2808 }
2809 
2810 void
2811 set_formation(Unit *unit, Unit *leader, int x, int y, int dist, int flex)
2812 {
2813     Plan *plan = unit->plan;
2814     Goal *goal;
2815 
2816     if (plan == NULL)
2817       return;
2818     if (!in_play(unit))
2819       return;
2820     if (leader != NULL) {
2821 	if (!in_play(leader))
2822 	  return;
2823 	goal = create_goal(GOAL_KEEP_FORMATION, unit->side, TRUE);
2824 	goal->args[0] = leader->id;
2825 	goal->args[1] = x;  goal->args[2] = y;
2826 	goal->args[3] = dist;
2827 	goal->args[4] = flex;
2828 	plan->formation = goal;
2829 	plan->funit = leader;
2830     } else {
2831 	/* A NULL leader means to clear the formation goal. */
2832 	plan->formation = NULL;
2833 	plan->funit = NULL;
2834     }
2835 }
2836 
2837 void
2838 delay_unit(Unit *unit, int flag)
2839 {
2840     if (in_play(unit) && unit->plan) {
2841 	unit->plan->delayed = flag;
2842     }
2843 }
2844 
2845 #if 0 	/* The four functions below are not used anywhere. */
2846 
2847 /* Return the distance that we can go by shortest path before running out
2848    of important supplies.  Will return at least 1, since we can *always*
2849    move one cell to safety.  This is a worst-case routine, too complicated
2850    to worry about units getting refreshed by terrain or whatever. */
2851 
2852 int
2853 range_left(Unit *unit)
2854 {
2855     int u = unit->type, m, least = 12345; /* bigger than any real value */
2856 
2857     for_all_material_types(m) {
2858 	if (um_consumption_per_move(u, m) > 0) {
2859 	    least = min(least, unit->supply[m] / um_consumption_per_move(u, m));
2860 	}
2861 #if 0
2862 	/* This code is too pessimistic if no account taken of supply line or
2863 	   production, so leave out for now. */
2864 	if (um_base_consumption(u, m) > 0) {
2865 	    tmp = (type_max_speed(u) * unit->supply[m]) /
2866 		  um_base_consumption(u, m);
2867 	    least = min(least, tmp);
2868 	}
2869 #endif
2870     }
2871     return (least == 12345 ? 1 : least);
2872 }
2873 
2874 /* Estimate the goodness and badness of cells in the immediate vicinity. */
2875 
2876 int
2877 find_worths(range)
2878 int range;
2879 {
2880     return 0;
2881 }
2882 
2883 /* This is a heuristic estimation of the value of one unit type
2884    hitting on another.  Should take cost of production into account as
2885    well as the chance and significance of any effect. */
2886 
2887 int
2888 attack_worth(unit, e)
2889 Unit *unit;
2890 int e;
2891 {
2892     int u = unit->type, worth;
2893 
2894     worth = uu_zz_bhw(u, e);
2895     /* Risk of death? */
2896 /*    if (uu_damage(e, u) >= unit->hp)
2897 	worth /= (could_capture(u, e) ? 1 : 4);
2898     if (could_capture(u, e)) worth *= 4; */
2899     return worth;
2900 }
2901 
2902 /* Support functions. */
2903 
2904 /* Return true if the given position is threatened by the given unit type. */
2905 
2906 int
2907 threat(Side *side, int u, int x0, int y0)
2908 {
2909 #if 0
2910     int d, x, y, thr = 0;
2911     Side *side2;
2912     int view;
2913 
2914     for_all_directions(d) {
2915     	point_in_dir(x0, y0, d, &x, &y);
2916 	view = 0 /* side_view(side, x, y) */;
2917 	if (view != UNSEEN && view != EMPTY) {
2918 	    side2 = side_n(vside(view));
2919 	    if (allied_side(side, side2)) {
2920 		if (uu_capture(u, vtype(view)) > 0) thr += 1000;
2921 		if (uu_zz_bhw(u, vtype(view)) > 0) thr += 100;
2922 	    }
2923 	}
2924     }
2925     return thr;
2926 #endif
2927     return 0;
2928 }
2929 
2930 #endif
2931 
2932 void
2933 pop_task(Plan *plan)
2934 {
2935     Task *oldtask;
2936 
2937     if (plan->tasks) {
2938 	oldtask = plan->tasks;
2939 	plan->tasks = plan->tasks->next;
2940 	free_task(oldtask);
2941     }
2942 }
2943 
2944 #if 0 	/* The two functions below are unused. */
2945 
2946 /* Patrol just does move_to, but cycling waypoints around when the first */
2947 /* one has been reached. */
2948 
2949 int
2950 move_patrol(Unit *unit)
2951 {
2952 #if 0
2953     int tx, ty;
2954 
2955     if (unit->plan->orders.rept-- > 0) {
2956 	if (unit->x == unit->plan->orders.p.pt[0].x &&
2957 	    unit->y == unit->plan->orders.p.pt[0].y) {
2958 	    tx = unit->plan->orders.p.pt[0].x;
2959 	    ty = unit->plan->orders.p.pt[0].y;
2960 	    unit->plan->orders.p.pt[0].x = unit->plan->orders.p.pt[1].x;
2961 	    unit->plan->orders.p.pt[0].y = unit->plan->orders.p.pt[1].y;
2962 	    unit->plan->orders.p.pt[1].x = tx;
2963 	    unit->plan->orders.p.pt[1].y = ty;
2964 	}
2965 	return move_to(unit, unit->plan->orders.p.pt[0].x, unit->plan->orders.p.pt[0].y,
2966 		       (unit->plan->orders.flags & SHORTESTPATH));
2967     }
2968 #endif
2969     return TRUE;
2970 }
2971 
2972 /* Basic routine to compute how long a unit will take to build something. */
2973 
2974 int
2975 build_time(Unit *unit, int prod)
2976 {
2977     int schedule = 1 /* uu_make(unit->type, prod) */;
2978     int u, develop_delay = 0;
2979 
2980     /* Add penalty (or unpenalty!) for first unit of a type. */
2981     /* is "counts" a reliable way to test? */
2982     if (unit->side->counts[prod] <= 1) {
2983 /*	develop_delay = ((schedule * u_develop(prod)) / 100);  */
2984 	for_all_unit_types(u) {
2985 	    if (unit->side->counts[u] > 1) {
2986 		develop_delay -=
2987 		  (1 /*uu_make(unit->type, u)*/ *
2988 		   uu_tech_crossover(prod, u)) / 100;
2989 	    }
2990 	    if (develop_delay > 0) {
2991 		schedule += develop_delay;
2992 	    }
2993 	}
2994     }
2995     return schedule;
2996 }
2997 
2998 #endif
2999 
3000 int
3001 clear_task_agenda(Unit *unit)
3002 {
3003     Plan	*plan = unit->plan;
3004     int		numcleared;
3005     Task	*oldtask;
3006 
3007     if (plan == NULL || plan->tasks == NULL)
3008 	return 0;
3009     numcleared = 0;
3010     while (plan->tasks != NULL) {
3011     	oldtask = plan->tasks;
3012     	plan->tasks = plan->tasks->next;
3013     	free_task(oldtask);
3014     	++numcleared;
3015     }
3016     return numcleared;
3017 }
3018 
3019 void
3020 clear_task_outcome(Unit *unit)
3021 {
3022 	if (unit->plan == NULL)
3023 	    run_error("no plan here?");
3024 	unit->plan->last_task_outcome = TASK_UNKNOWN;
3025 }
3026 
3027 Plan *
3028 create_plan(void)
3029 {
3030     Plan *plan = (Plan *) xmalloc(sizeof(Plan));
3031     return plan;
3032 }
3033 
3034 void
3035 free_plan(Unit *unit)
3036 {
3037     Plan *plan = unit->plan;
3038 
3039     if (plan == NULL)
3040       run_error("no plan here?");
3041     /* Make tasks available for reallocation. */
3042     clear_task_agenda(unit);
3043     free(plan);
3044 }
3045 
3046 /* Describe a plan succinctly.  This is primarily for debugging, not
3047    for normal user display. */
3048 
3049 char *planbuf = NULL;
3050 
3051 char *
3052 plan_desig(Plan *plan)
3053 {
3054     Task *task;
3055     int extra = 0;
3056 
3057     if (planbuf == NULL)
3058       planbuf = (char *)xmalloc(1000);
3059     if (plan == NULL) {
3060 	sprintf(planbuf, "no plan");
3061     } else if (plan->type == PLAN_NONE) {
3062 	sprintf(planbuf, "unformed plan");
3063     } else {
3064 	if (plan->tasks) {
3065 	    tmpbuf[0] = '\0';
3066 	    for_all_tasks(plan, task) {
3067 		if (strlen(tmpbuf) < 100) {
3068 		    strcat(tmpbuf, " ");
3069 		    strcat(tmpbuf, task_desig(task));
3070 		} else {
3071 		    ++extra;
3072 		}
3073 	    }
3074 	    if (extra > 0) {
3075 		tprintf(tmpbuf, " ... %d more ...", extra);
3076 	    }
3077 	} else {
3078 	    sprintf(tmpbuf, " no tasks");
3079 	}
3080 	sprintf(planbuf, "type %s %s",
3081 		plantypenames[plan->type], goal_desig(plan->maingoal));
3082 	if (plan->formation) {
3083 	    strcat(planbuf, " ");
3084 	    strcat(planbuf, goal_desig(plan->formation));
3085 	}
3086 	if (plan->asleep)
3087 	  strcat(planbuf, " asleep");
3088 	if (plan->reserve)
3089 	  strcat(planbuf, " reserve");
3090 	if (plan->delayed)
3091 	  strcat(planbuf, " delayed");
3092 	if (plan->waitingfortasks)
3093 	  strcat(planbuf, " waiting");
3094 	if (plan->supply_alarm)
3095 	  strcat(planbuf, " supply_alarm");
3096 	if (plan->supply_is_low)
3097 	  strcat(planbuf, " supply_is_low");
3098 	strcat(planbuf, tmpbuf);
3099     }
3100     return planbuf;
3101 }
3102 
3103 /* True if unit is in immediate danger of being captured. */
3104 /* Needs check on capturer transport being seen. */
3105 
3106 int
3107 might_be_captured(Unit *unit)
3108 {
3109     int d, x, y;
3110     Unit *unit2;
3111 
3112     for_all_directions(d) {
3113       if (interior_point_in_dir(unit->x, unit->y, d, &x, &y)) {
3114 	if (((unit2 = unit_at(x, y)) != NULL) &&
3115 	    (enemy_side(unit->side, unit2->side)) &&
3116 	    (uu_capture(unit2->type, unit->type) > 0))
3117 	      return TRUE;
3118       }
3119     }
3120     return FALSE;
3121 }
3122 
3123 /* Clear a unit's plan out. */
3124 
3125 void
3126 force_replan(Unit *unit)
3127 {
3128     extern int need_ai_planning;
3129 
3130     if (unit->plan == NULL)
3131       return;
3132     unit->plan->type = PLAN_PASSIVE;
3133     clear_task_agenda(unit);
3134     unit->plan->maingoal = NULL;
3135     unit->plan->formation = NULL;
3136     unit->plan->funit = NULL;
3137     unit->plan->asleep = FALSE;
3138     unit->plan->reserve = FALSE;
3139     set_waiting_for_tasks(unit, FALSE);
3140     unit->plan->delayed = FALSE;
3141     unit->plan->last_task_outcome = TASK_UNKNOWN;
3142     need_ai_planning = TRUE;
3143 }
3144 
3145 /* Auxiliary functions for unit planning in Xconq. */
3146 
3147 /* router flags */
3148 
3149 #define SAMEPATH 1
3150 #define EXPLORE_PATH 2
3151 
3152 /* These macros are a cache used for planning purposes by machines. */
3153 
3154 #define markloc(x, y) (set_tmp1_at(x, y, mark))
3155 
3156 #define markedloc(x, y) (tmp1_at(x, y) == mark)
3157 
3158 #define get_fromdir(x, y) (tmp2_at(x, y))
3159 
3160 #define set_fromdir(x, y, dir) (set_tmp2_at(x, y, dir))
3161 
3162 #define get_dist(x, y) (tmp3_at(x, y))
3163 
3164 #define set_dist(x, y, d) (set_tmp3_at(x, y, d))
3165 
3166 int
3167 occupant_could_capture(Unit *unit, int u2)
3168 {
3169     Unit *occ;
3170 
3171     for_all_occupants(unit, occ)
3172       if (uu_capture(occ->type, u2) > 0)
3173 	return TRUE;
3174     return FALSE;
3175 }
3176 
3177 /* Check to see if there is anyone around to capture. */
3178 
3179 int
3180 can_capture_neighbor(Unit *unit)
3181 {
3182     int d, x, y;
3183     Side *side2;
3184     UnitView *uview;
3185 
3186     for_all_directions(d) {
3187 	if (interior_point_in_dir(unit->x, unit->y, d, &x, &y)) {
3188 	    /* Should perhaps iterate over occs also, so that they can be
3189 	    captured even if the transport is not? */
3190 	    for_all_view_stack(unit->side, x, y, uview) {
3191 		side2 = side_n(uview->siden);
3192 		if (!allied_side(unit->side, side2)) {
3193 		    if (uu_capture(unit->type, uview->type) > 0) {
3194 			/* need some other way to change move order quickly */
3195 			return TRUE;
3196 		    }
3197 		}
3198 	    }
3199 	}
3200     }
3201     return FALSE;
3202 }
3203 
3204 /* check if our first occupant can capture something.  Doesn't look at
3205    other occupants. */
3206 
3207 int
3208 occupant_can_capture_neighbor(Unit *unit)
3209 {
3210     Unit *occ = unit->occupant;
3211 
3212     if (occ != NULL && has_acp_left(occ) && occ->side == unit->side) {
3213 	if (can_capture_neighbor(occ)) {
3214 	    return TRUE;
3215 	}
3216     }
3217     return FALSE;
3218 }
3219 
3220 /* Find the closes unit, first prefering bases, and then transports. */
3221 
3222 int
3223 find_closest_unit(Side *side, int x0, int y0, int maxdist, int (*pred)(int x, int y)/*pred*/, int *rxp, int *ryp)
3224 {
3225 #if 0
3226     Unit *unit;
3227     int u, dist;
3228     int found = FALSE;
3229 
3230     for_all_unit_types(u) {
3231 	if (u_is_base(u)) {
3232 	    for (unit = NULL /* side_strategy(side)->unitlist[u]*/; unit != NULL; unit = unit->mlist) {
3233 		if (alive(unit) &&
3234 		    (dist = distance(x0, y0, unit->x, unit->y)) <= maxdist) {
3235 		    if ((*pred)(unit->x, unit->y)) {
3236 			maxdist = dist - 1;
3237 			*rxp = unit->x;  *ryp = unit->y;
3238 			found = TRUE;
3239 		    }
3240 		}
3241 	    }
3242 	}
3243     }
3244     if (found) {
3245 	return TRUE;
3246     }
3247     for_all_unit_types(u) {
3248 	if (!u_is_base(u) && u_is_transport(u)) {
3249 	    for (unit = NULL /*side_strategy(side)->unitlist[u]*/; unit != NULL; unit = unit->mlist) {
3250 		if (alive(unit)
3251 		    && distance(x0, y0, unit->x, unit->y) <= maxdist) {
3252 		    if ((*pred)(unit->x, unit->y)) {
3253 			maxdist = dist - 1;
3254 			*rxp = unit->x;  *ryp = unit->y;
3255 			found = TRUE;
3256 		    }
3257 		}
3258 	    }
3259 	}
3260     }
3261     if (found) {
3262 	return TRUE;
3263     }
3264     /* (What's the point of finding a non-base/non-transport?) */
3265     for_all_unit_types(u) {
3266 	if (!u_is_base(u) && !u_is_transport(u)) {
3267 	    for (unit = NULL/*side_strategy(side)->unitlist[u]*/; unit != NULL; unit = unit->mlist) {
3268 		if (alive(unit)
3269 		    && distance(x0, y0, unit->x, unit->y) <= maxdist) {
3270 		    if ((*pred)(unit->x, unit->y)) {
3271 			maxdist = dist - 1;
3272 			*rxp = unit->x;  *ryp = unit->y;
3273 			found = TRUE;
3274 		    }
3275 		}
3276 	    }
3277 	}
3278     }
3279     if (found) {
3280 	return TRUE;
3281     }
3282 #endif
3283     return FALSE;
3284 }
3285 
3286 #if 0		/* Unused. */
3287 
3288 /* Returns the type of missing supplies. */
3289 
3290 int
3291 out_of_ammo(Unit *unit)
3292 {
3293     int u = unit->type, m;
3294 
3295     for_all_material_types(m) {
3296 	if (um_consumption_per_attack(u, m) > 0 && unit->supply[m] <= 0)
3297 	    return m;
3298     }
3299     return (-1);
3300 }
3301 
3302 #endif
3303 
3304 #if 0 	/* The two functions below are unused. */
3305 int
3306 usable_cell(Unit *unit, int x, int y)
3307 {
3308     int u = unit->type;
3309     UnitView *uview;
3310 
3311     if (!could_live_on(u, terrain_at(x, y)))
3312       return FALSE;
3313     for_all_view_stack(unit->side, x, y, uview) {
3314 	if (allied_side(uview->side, unit->side)
3315 	    && could_carry(uview->type, u))
3316 	  return TRUE;
3317     }
3318     return FALSE;
3319 }
3320 
3321 Task *explorechain;
3322 
3323 int
3324 explorable_cell(x, y)
3325 int x, y;
3326 {
3327     return (terrain_view(tmpside, x, y) == UNSEEN);
3328 }
3329 
3330 #endif
3331 
3332 /* Test whether the given location is an unknown cell that we can get
3333    next to. */
3334 /* (should consider testing "within vision range", but that might
3335    require LOS tests and be expensive) */
3336 
3337 static int
3338 reachable_unknown(int x, int y)
3339 {
3340     /* Only interior cells are reachable. */
3341     if (!inside_area(x, y))
3342       return FALSE;
3343     if (terrain_view(tmpside, x, y) == UNSEEN) {
3344     	if (adj_known_ok_terrain(x, y, tmpside, tmpunit->type)) {
3345 	    return TRUE;
3346 	} else {
3347 	    return FALSE;
3348 	}
3349     } else {
3350 	return FALSE;
3351     }
3352 }
3353 
3354 /* Test whether the given location has an adjacent cell that is ok for
3355    the given type to be out in the open. */
3356 
3357 static int
3358 adj_known_ok_terrain(int x, int y, Side *side, int u)
3359 {
3360     int dir, x1, y1, t;
3361 
3362     if (!inside_area(x, y))
3363       return FALSE;
3364     for_all_directions(dir) {
3365 	if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
3366 	    if (terrain_view(side, x1, y1) == UNSEEN)
3367 	      continue;
3368 	    t = terrain_at(x1, y1);
3369 	    if (!terrain_always_impassable(u, t))
3370 	      return TRUE;
3371 	}
3372     }
3373     return FALSE;
3374 }
3375 
3376 /* Go to the nearest cell that we can see how to get to. */
3377 
3378 static int
3379 explore_reachable_cell(Unit *unit, int range)
3380 {
3381     int x, y;
3382 
3383     if (g_see_all() || g_terrain_seen())
3384       return FALSE;
3385     tmpunit = unit;
3386     tmpside = unit->side;
3387     DMprintf("%s searching within %d for cell to explore -",
3388 	     unit_desig(unit), range);
3389     if (search_around(unit->x, unit->y, range, reachable_unknown, &x, &y, 1)) {
3390 	set_move_to_task(unit, x, y, 1);
3391 	DMprintf("found one at %d,%d\n", x, y);
3392 	return TRUE;
3393     }
3394     DMprintf("found nothing\n");
3395     return FALSE;
3396 }
3397 
3398 /* Estimate the usual number of turns to finish construction. */
3399 
3400 int
3401 normal_completion_time(int u, int u2)
3402 {
3403     int acp = 0;
3404 
3405     acp = type_max_acp(u); /* (Watch for performance issues here.) */
3406     if (acp == 0 || uu_cp_per_build(u, u2) == 0)
3407       return (-1);
3408     return (u_cp(u2) - uu_creation_cp(u, u2)) /
3409       (uu_cp_per_build(u, u2) * acp);
3410 }
3411 
3412 /* Similar, but using a specific unit and also accounting for toolup
3413    time. */
3414 
3415 int
3416 est_completion_time(Unit *unit, int u2)
3417 {
3418     int u, tooluptime, tp, acp;
3419 
3420     u = unit->type;
3421     if (!could_create(u, u2))
3422       return (-1);
3423     acp = type_max_acp(u); /* (Watch for performance issues here.) */
3424     tooluptime = 0;
3425     tp = (unit->tooling ? unit->tooling[u2] : 0);
3426     if (tp < uu_tp_to_build(u, u2)) {
3427 	if (uu_acp_to_toolup(u, u2) < 1
3428 	    || uu_tp_per_toolup(u, u2) <= 0
3429 	    || acp <= 0)
3430 	  return (-1);
3431 	tooluptime = ((uu_tp_to_build(u, u2) - tp) * uu_acp_to_toolup(u, u2))
3432 	 / (uu_tp_per_toolup(u, u2) * acp);
3433     }
3434     return tooluptime + normal_completion_time(unit->type, u2);
3435 }
3436 
3437 //! Maybe set side goal for a material, if needed for completion of u.
3438 
3439 void
3440 maybe_set_materials_goal(Unit *unit, int u2)
3441 {
3442     int u = NONUTYPE;
3443     int m = NONMTYPE;
3444     Side *side = NULL;
3445     int conc = 0, cpb = 0;
3446     Goal *goal = NULL;
3447 
3448     assert_error(in_play(unit), "Attempted to access an out-of-play unit");
3449     assert_error(is_unit_type(u2), "Attempted to use an invalid utype");
3450     u = unit->type;
3451     side = unit->side;
3452     for_all_material_types(m) {
3453 	conc = um_consumption_on_creation(u2, m);
3454 	cpb = um_consumption_per_built(u2, m);
3455 	cpb += (um_consumption_per_cp(u2, m) * uu_cp_per_build(u, u2));
3456 	if (((0 < conc) || (0 < cpb)) && side_has_treasury(side, m)
3457 	    && um_takes_from_treasury(u, m)) {
3458 	    goal = create_goal(GOAL_HAS_MATERIAL_TYPE, side, TRUE);
3459 	    goal->args[0] = m;
3460 	    goal->args[1] = 3 * conc + cpb;
3461 	    add_goal(side, goal);
3462 	}
3463     }
3464 }
3465 
3466 /* A unit runs low on supplies at the halfway point.  Formula is the same
3467    no matter how/if occupants eat transports' supplies or whether
3468    unit can borrow from treasury. Unless we could put in reservations
3469    on the supplies, we would simply be making assumptions that might
3470    not bear out when it actually comes time to consume the supplies. */
3471 
3472 int
3473 past_halfway_point(Unit *unit)
3474 {
3475     int u = unit->type, m = NONMTYPE, rsplypct = -1;
3476 
3477     for_all_material_types(m) {
3478 	if (((um_base_consumption(u, m) > 0)
3479 	     || (um_consumption_per_move(u, m) > 0))
3480 	    && (unit->transport == NULL)) {
3481             if (unit->side)
3482                 rsplypct = unit_doctrine(unit)->resupply_percent;
3483             else
3484                 rsplypct = 50;
3485             if (unit->supply[m] <= ((rsplypct * um_storage_x(u, m)) / 100))
3486                 return TRUE;
3487 	}
3488     }
3489     return FALSE;
3490 }
3491