1 /* The movement-related actions of Xconq.
2    Copyright (C) 1987-1989, 1991-2000 Stanley T. Shebs.
3 
4 Xconq is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.  See the file COPYING.  */
8 
9 #include "conq.h"
10 #include "kernel.h"
11 
12 enum {
13     over_nothing = 0,
14     over_own = 1,
15     over_border = 2,
16     over_all = 3
17 };
18 
19 /* We can't declare all the action functions as static because some of them
20    are in other files, but don't let them be visible to all files. */
21 
22 #undef  DEF_ACTION
23 #define DEF_ACTION(name,code,args,prepfn,netprepfn,DOFN,checkfn,ARGDECL,doc)  \
24   extern int DOFN ARGDECL;
25 
26 #include "action.def"
27 
28 extern int retreating;
29 extern int retreating_from;
30 
31 extern void allocate_used_cells(Unit *unit);	/* From run.c */
32 extern void free_used_cells(Unit *unit); 	/* From unit.c */
33 
34 static int border_slide_possible(int u2, int ox, int oy, int nx, int ny);
35 static int unit_traverse_blockable_by(Unit *unit, Unit *unit2);
36 static int any_friendly_at(Unit *unit, int x, int y);
37 static int total_entry_cost(int u1, int u3, int x1, int y1, int z1, int u2, int x2, int y2, int z2);
38 static int number_member(int x, Obj *lis);
39 static int wind_value(Unit *unit, int angle, int force, Obj *effect, int maxval);
40 static void detonate_on_approach_around(Unit *unit);
41 static int damaged_value(Unit *unit, Obj *effect, int maxval);
42 static void try_detonate_on_approach(int x, int y);
43 static void test_blocking_zoc(int x, int y);
44 
45 static int maybe_react_to_move(Unit *unit, int ox, int oy);
46 static void consume_move_supplies(Unit *unit);
47 
48 static int tmprslt;
49 
50 /* Cache variables. */
51 int *cache__type_max_speed_from_any_occs = NULL;
52 int *cache__type_max_speed = NULL;
53 
54 /* Does the unit currently have enough MP? */
55 
56 int
has_enough_mp(Unit * unit,Unit * unit2,int mp)57 has_enough_mp(Unit *unit, Unit *unit2, int mp)
58 {
59     int u = NONUTYPE, u2 = NONUTYPE;
60 
61     assert_error(unit, "Tried to access a null unit.");
62     assert_error(unit2, "Tried to access a null unit.");
63     u = unit->type;
64     u2 = unit2->type;
65     if (!can_be_actor(unit))
66       return FALSE;
67     return (((unit->act->acp * unit_speed(unit2, unit2->x, unit2->y)) / 100
68 	     + u_free_mp(u2)) >= mp);
69 }
70 
71 /* Can the unit ever have enough MP at its present location? */
72 
73 int
can_have_enough_mp(Unit * unit,Unit * unit2,int mp)74 can_have_enough_mp(Unit *unit, Unit *unit2, int mp)
75 {
76     int u = NONUTYPE, u2 = NONUTYPE;
77 
78     assert_error(unit, "Tried to access a null unit.");
79     assert_error(unit2, "Tried to access a null unit.");
80     u = unit->type;
81     u2 = unit2->type;
82     if (!can_be_actor(unit))
83       return FALSE;
84     return ((((((u_acp_max(u) > 0) ? u_acp_max(u) : new_acp_for_turn(unit)) -
85 	       u_acp_min(u)) * unit_speed(unit2, unit2->x, unit2->y)) / 100
86 	     + u_free_mp(u2)) >= mp);
87 }
88 
89 /* What is the maximum speed that can be gained from having any occupant? */
90 /* (To do this calculation truthfully, we need to use a knapsack algorithm,
91     because it may be that some combination of occ types yields the best
92     results. Nonetheless, the function, in its current form, at least tells
93     us whether or not any occ type will boost the MP.) */
94 
95 int
type_max_speed_from_any_occs(int u)96 type_max_speed_from_any_occs(int u)
97 {
98     int u2 = NONUTYPE, u3 = NONUTYPE;
99     int speed = 0, maxspeed = 0;
100 
101     if (!cache__type_max_speed_from_any_occs) {
102 	cache__type_max_speed_from_any_occs = (int *)xmalloc(numutypes *
103 							     sizeof(int));
104 	for_all_unit_types(u3) {
105 	    for_all_unit_types(u2) {
106 		speed = uu_occ_adds_acp(u3, u2);
107 		if (speed) {
108 		    if (type_can_occupy_empty_type(u2, u3))
109 			maxspeed = max(speed, maxspeed);
110 		}
111 	    }
112 	    cache__type_max_speed_from_any_occs[u3] = maxspeed;
113 	}
114     }
115     return cache__type_max_speed_from_any_occs[u];
116 }
117 
118 /* What is the maximum speed an unit type can have, all things considered? */
119 
120 int
type_max_speed(int u)121 type_max_speed(int u)
122 {
123     int speed = 0;
124     int u3 = NONUTYPE;
125 
126     if (!cache__type_max_speed) {
127 	cache__type_max_speed = (int *)xmalloc(numutypes * sizeof(int));
128 	for_all_unit_types(u3) {
129 	    speed = u_speed(u3);
130 	    speed += type_max_speed_from_any_occs(u3);
131 	    /* (Should consider multiplicative effects.) */
132 	    cache__type_max_speed[u3] = speed;
133 	}
134     }
135     return cache__type_max_speed[u];
136 }
137 
138 //! Could uactor move umover?
139 /*!
140     \todo Allow ACP-indep movement.
141     \todo Should cache results.
142 */
143 
144 int
could_move(int uactor,int umover)145 could_move(int uactor, int umover)
146 {
147     int m = NONMTYPE;
148 
149     /* Sanity checks. */
150     assert_error(is_unit_type(uactor), "Attempted to access an invalid utype");
151     assert_error(is_unit_type(umover), "Attempted to access an invalid utype");
152     /* ACP-indep utypes cannot move. (For now.) */
153     if (u_acp_independent(uactor))
154       return FALSE;
155     /* Must cost > 0 ACP. (For now.) */
156     if (0 >= u_acp_to_move(umover))
157       return FALSE;
158     /* Must be able to have/acquire enough materials to move. */
159     for_all_material_types(m) {
160 	if (0 < um_to_move(umover, m)) {
161 	    if (!um_storage_x(umover, m)) {
162 		if (!um_takes_from_treasury(umover, m))
163 		  return FALSE;
164 	    }
165 	    else {
166 		if (um_storage_x(umover, m) < um_to_move(umover, m))
167 		  return FALSE;
168 	    }
169 	}
170 	if (0 < um_consumption_per_move(umover, m)) {
171 	    if (!um_storage_x(umover, m)) {
172 		if (!um_takes_from_treasury(umover, m))
173 		  return FALSE;
174 	    }
175 	    else {
176 		if (um_storage_x(umover, m) <
177 		    um_consumption_per_move(umover, m))
178 		  return FALSE;
179 	    }
180 	}
181     }
182     return TRUE;
183 }
184 
185 //! Can a given unit move?
186 /*!
187     \todo Allow ACP-indep movement.
188     \todo Allow taking from treasury.
189 */
190 
191 int
can_move(Unit * actor,Unit * mover)192 can_move(Unit *actor, Unit *mover)
193 {
194     int acp = 0, acpextra = 0;
195     int u2 = NONUTYPE;
196     int m = NONMTYPE;
197 
198     /* Is the ACP source unit in play? */
199     if (!in_play(actor))
200       return A_ANY_ERROR;
201     /* Is the unit-to-move in play? */
202     if (!in_play(mover))
203       return A_ANY_ERROR;
204     u2 = mover->type;
205     /* Check if the utype could ever move. */
206     if (!could_move(actor->type, u2))
207       return A_ANY_CANNOT_DO;
208     /* If ACP source unit is ACP-less,
209 	then it cannot supply ACP to the unit to be moved. */
210     if (!actor->act)
211       return A_ANY_CANNOT_DO;
212     acp = u_acp_to_move(u2);
213     /* If this action is a part of retreating, add more acp to represent the
214        motivational power of needing to run away... */
215     if (retreating && (actor == mover)) {
216 	if (retreating_from != NONUTYPE) {
217 	    acpextra = uu_acp_retreat(u2, retreating_from);
218 	}
219     }
220     /* Can ACP source unit ever have anough ACP to move? */
221     if (!can_have_enough_acp(actor, max(0, acp - acpextra)))
222       return A_ANY_CANNOT_DO;
223     /* Does ACP source unit have enough ACP to cause a move this turn? */
224     if (!has_enough_acp(actor, max(0, acp - acpextra)))
225       return A_ANY_NO_ACP;
226     /* Does mover unit have enough materials to act? */
227     if (!has_supply_to_act(mover))
228       return A_ANY_NO_MATERIAL;
229     /* Does mover unit have enough materials to move? */
230     for_all_material_types(m) {
231     	/* We have to have a minimum amount of a certain material to move. */
232 	if (mover->supply[m] < um_to_move(u2, m))
233 	  return A_ANY_NO_MATERIAL;
234     	/* We also need enough consumable materials (fuel) for the move. */
235 	if (mover->supply[m] < um_consumption_per_move(u2, m))
236 	  return A_ANY_NO_MATERIAL;
237     }
238     return A_ANY_OK;
239 }
240 
241 /* Movement actions. */
242 
243 /* Record a move action as the next to do. */
244 
245 int
prep_move_action(Unit * unit,Unit * unit2,int x,int y,int z)246 prep_move_action(Unit *unit, Unit *unit2, int x, int y, int z)
247 {
248     if (unit == NULL || unit->act == NULL || unit2 == NULL)
249       return FALSE;
250     unit->act->nextaction.type = ACTION_MOVE;
251     unit->act->nextaction.args[0] = x;
252     unit->act->nextaction.args[1] = y;
253     unit->act->nextaction.args[2] = z;
254     unit->act->nextaction.actee = unit2->id;
255     return TRUE;
256 }
257 
258 /* The basic act of moving.  This attempts to move and maybe fails,
259    but takes no corrective action.  Note that this requires space in
260    the destination cell, will not board, attack, etc - all that is
261    task- and plan-level behavior. */
262 
263 int
do_move_action(Unit * unit,Unit * unit2,int x,int y,int z)264 do_move_action(Unit *unit, Unit *unit2, int x, int y, int z)
265 {
266     int u, u2, t, rslt, speed, mpcost, acpcost, ox, oy, oz, ot, dist;
267     int nummoves, dirs[NUMDIRS], numdirs, i, ix, iy, ndist;
268 
269     u = unit->type;
270     u2 = unit2->type;
271     t = terrain_at(x, y);
272     ox = unit2->x;
273     oy = unit2->y;
274     oz = unit2->z;
275     ot = terrain_at(ox, oy);
276     speed = 100;
277     mpcost = 1;
278     dist = distance(ox, oy, x, y);
279     if (dist == 0) {
280 	/* Change of altitude within same cell and/or transport departure. */
281 	if (unit->transport != NULL && z == unit->z) {
282 		if (ut_vanishes_on(u2, t) && !can_move_via_conn(unit2, x, y)) {
283 		    notify(unit2->side, "%s vanishes into the %s!",
284 			   unit_handle(unit2->side, unit2), t_type_name(t));
285 		    kill_unit(unit2, H_UNIT_VANISHED);
286 		    rslt = A_MOVE_UNIT_GONE;
287 		} else if (ut_wrecks_on(u2, t)
288 			   && !can_move_via_conn(unit2, x, y)) {
289 		    notify(unit2->side, "%s wrecks in the %s!",
290 			   unit_handle(unit2->side, unit2), t_type_name(t));
291 		    if (u_wrecked_type(u2) == NONUTYPE) {
292 			/* Occupants always die if the wrecked unit
293 			   disappears. */
294 			kill_unit(unit, H_UNIT_WRECKED);
295 		    } else {
296 			/* Wreck the unit.  Note that we want to do the
297 			   wrecking even if the unit will vanish, so that
298 			   occupants can escape if allowed for. */
299 			if (!ut_vanishes_on(u_wrecked_type(u2), t)) {
300 			    speed = unit_speed(unit2, x, y);
301 			    mpcost = move_unit(unit2, x, y);
302 			}
303 			wreck_unit(unit2, H_UNIT_WRECKED, WRECK_TYPE_TERRAIN,
304 				   t, NULL);
305 			/* Now make it go away, taking unlucky occupants
306 			   with it. */
307 			if (ut_vanishes_on(u2, t)) {
308 			    kill_unit(unit2, H_UNIT_VANISHED);
309 			}
310 		    }
311 		    rslt = A_MOVE_UNIT_GONE;
312 	    } else {
313 	    	mpcost = move_unit(unit2, x, y);
314 	    }
315 	} else {
316 	    unit2->z = z;
317 	    /* (should do more stuff - LOS view might have changed) */
318 	}
319 	acpcost = 1;
320 	nummoves = 1;
321 	rslt = A_ANY_DONE;
322     } else if (dist == 1
323 	       || (dist == 2 && border_slide_possible(u2, ox, oy, x, y))) {
324 	/* Movement to an adjacent cell. */
325 	if (!inside_area(x, y)) {
326 	    /* (should notify all observers) */
327 	    notify(unit2->side, "%s leaves the area!",
328 		   unit_handle(unit2->side, unit2));
329 	    kill_unit(unit2, H_UNIT_LEFT_WORLD);
330 	    rslt = A_ANY_DONE;
331 	} else if (ut_vanishes_on(u2, t) && !can_move_via_conn(unit2, x, y)) {
332 	    notify(unit2->side, "%s vanishes into the %s!",
333 		   unit_handle(unit2->side, unit2), t_type_name(t));
334 	    kill_unit(unit2, H_UNIT_VANISHED);
335 	    rslt = A_MOVE_UNIT_GONE;
336 	} else if (ut_wrecks_on(u2, t) && !can_move_via_conn(unit2, x, y)) {
337 	    notify(unit2->side, "%s wrecks in the %s!",
338 		   unit_handle(unit2->side, unit2), t_type_name(t));
339 	    if (u_wrecked_type(u2) == NONUTYPE) {
340 		/* Occupants always die if the wrecked unit disappears. */
341 		kill_unit(unit, H_UNIT_WRECKED);
342 	    } else {
343 		/* Wreck the unit.  Note that we want to do the wrecking even
344 		   if the unit will vanish, so that occupants can escape if
345 		   allowed for. */
346 		if (!ut_vanishes_on(u_wrecked_type(u2), t)) {
347 		    speed = unit_speed(unit2, x, y);
348 		    mpcost = move_unit(unit2, x, y);
349 		}
350 		wreck_unit(unit2, H_UNIT_WRECKED, WRECK_TYPE_TERRAIN, t, NULL);
351 		/* Now make it go away, taking unlucky occupants with. */
352 		if (ut_vanishes_on(u2, t)) {
353 		    kill_unit(unit2, H_UNIT_VANISHED);
354 		}
355 	    }
356 	    rslt = A_MOVE_UNIT_GONE;
357 #if 0 /* seems better just to prevent via move costs... */
358 	} else if (ut_vanishes_on(u2, ot)
359 		   && (unit->transport == NULL
360 		       || uu_ferry_on_leave(unit->transport->type, u2) < over_own)
361 		   && !can_move_via_conn(unit2, x, y)) {
362 	    /* This case is if the unit is already on a connection in
363 	       hostile terrain and tries to move to hospitable terrain
364 	       without using a bridge, or if it is in a transport that
365 	       won't ferry across hostile terrain. */
366 	    notify(unit2->side, "%s vanishes into the %s!",
367 		   unit_handle(unit2->side, unit2), t_type_name(t));
368 	    kill_unit(unit2, H_UNIT_VANISHED);
369 	    rslt = A_MOVE_UNIT_GONE;
370 	} else if (ut_wrecks_on(u2, ot)
371 		   && (unit->transport == NULL
372 		       || uu_ferry_on_leave(unit->transport->type, u2) < over_own)
373 		   && !can_move_via_conn(unit2, x, y)) {
374 	    notify(unit2->side, "%s wrecks in the %s!",
375 		   unit_handle(unit2->side, unit2), t_type_name(t));
376 	    if (u_wrecked_type(u2) == NONUTYPE) {
377 		/* Occupants always die if the wrecked unit disappears. */
378 		kill_unit(unit, H_UNIT_WRECKED);
379 	    } else {
380 		/* Wreck the unit.  Note that we want to do the wrecking even
381 		   if the unit will vanish, so that occupants can escape if
382 		   allowed for. */
383 		wreck_unit(unit2);
384 		/* Now make it go away, taking unlucky occupants with. */
385 		if (ut_vanishes_on(u2, t)) {
386 		    kill_unit(unit2, H_UNIT_VANISHED);
387 		}
388 	    }
389 	    rslt = A_MOVE_UNIT_GONE;
390 #endif
391 	} else {
392 	    speed = unit_speed(unit2, x, y);
393 	    mpcost = move_unit(unit2, x, y);
394 	    /* ZOC move cost is added after action is done. */
395 	    mpcost += zoc_move_cost(unit2, ox, oy, oz);
396 	    rslt = A_ANY_DONE;
397 	}
398 	if (alive(unit)) {
399 	    if (speed > 0) {
400 		acpcost = (mpcost * 100) / speed;
401 	    } else {
402 		acpcost = 1;
403 	    }
404 	}
405 	nummoves = 1;
406     } else {
407 	/* Movement by a distance of at least 2.  This is similar to
408 	   the move-to task, but the unit uses up only mp and doesn't
409 	   get to contemplate directions. */
410 	mpcost = 0;
411 	acpcost = 0;
412 	nummoves = 0;
413 	/* Stop the iteration if the unit stumbles into a mine field. */
414 	while (dist > 0 && alive(unit2)) {
415 	    numdirs = choose_move_dirs(unit2, x, y, TRUE,
416 				       plausible_move_dir, sort_directions, dirs);
417 	    if (numdirs == 0)
418 	      break;
419 	    for (i = 0; i < numdirs; ++i) {
420 	    	/* Should be unit2 ? */
421 		interior_point_in_dir(unit->x, unit->y, dirs[i], &ix, &iy);
422 		if (type_can_occupy_cell(unit2->type, ix, iy)) {
423 		    ox = unit2->x;
424 		    oy = unit2->y;
425 		    oz = unit2->z;
426 		    speed = unit_speed(unit2, ix, iy);
427 		    mpcost += move_unit(unit2, ix, iy);
428 		    mpcost += zoc_move_cost(unit2, ox, oy, oz);
429 		    if (alive(unit)) {
430 			if (speed > 0) {
431 			    acpcost += (mpcost * 100 * 100) / speed;
432 			} else {
433 			    acpcost += 1;
434 			}
435 		    }
436 		    break;
437 		}
438 	    }
439 	    ndist = distance(unit2->x, unit2->y, x, y);
440 	    if (ndist >= dist)
441 	      break;
442 	    dist = ndist;
443 	    ++nummoves;
444 	}
445 	/* Scale accumulated cost back down. */
446 	acpcost /= 100;
447 	/* Make sure a sensible result is returned. */
448 	if (alive(unit2)) {
449 	    rslt = A_ANY_DONE;
450 	} else {
451 	    rslt = A_MOVE_UNIT_GONE;
452 	}
453     }
454     if (alive(unit)) {
455 	acpcost = max(acpcost, u_acp_to_move(u2));
456 	if (acpcost < 1)
457 	  acpcost = 1;
458 	use_up_acp(unit, acpcost);
459     }
460     /* Count the unit as having actually moved. */
461     if (alive(unit2) && unit2->act)
462       unit2->act->actualmoves += nummoves;
463     return rslt;
464 }
465 
466 int
check_move_action(Unit * unit,Unit * unit2,int x,int y,int z)467 check_move_action(Unit *unit, Unit *unit2, int x, int y, int z)
468 {
469     int u, u2, u3, ox, oy, oz, mpavail, totcost, speed, dist;
470     int acpavail = 0, acpextra = 0;
471     int rslt = A_ANY_OK;
472 
473     if (!valid(rslt = can_move(unit, unit2)))
474       return rslt;
475     /* Note that edge cell dests (used to leave the world) are allowed. */
476     if (!in_area(x, y))
477       return A_ANY_ERROR;
478     u = unit->type;  u2 = unit2->type;
479     ox = unit2->x;  oy = unit2->y;  oz = unit2->z;
480     acpavail = unit->act->acp;
481     /* If this action is a part of retreating, add more acp to represent the
482        motivational power of needing to run away... */
483     if (retreating && unit == unit2) {
484 	if (retreating_from != NONUTYPE) {
485 	    acpextra = uu_acp_retreat(u2, retreating_from);
486 	    acpavail += acpextra;
487 	}
488 #if (0)
489 	/* (should not have to modify the unit,
490 	    but succeeding calls need this) */
491 	unit->act->acp = acpavail;
492 #endif
493     }
494     /* Destination is outside the world and we're not allowed to leave. */
495     if (!inside_area(x, y) && u_mp_to_leave_world(u2) < 0)
496 	return A_MOVE_CANNOT_LEAVE_WORLD;
497     /* Check if the destination is within our move range. */
498     dist = distance(ox, oy, x, y);
499     if ((dist == 2 && !border_slide_possible(u, ox, oy, x, y))
500 	&& dist > u_move_range(u2))
501       return A_ANY_TOO_FAR;
502     /* Disallow nonzero altitudes for now. */
503     if (z > 0)
504       return A_ANY_TOO_FAR;
505     /* Check if the destination is in a blocking ZOC. */
506     if (in_blocking_zoc(unit, x, y, z))
507       return A_MOVE_BLOCKING_ZOC;
508     /* Now start looking at the move costs. */
509     u3 = (unit2->transport ? unit2->transport->type : NONUTYPE);
510     totcost = total_move_cost(u2, u3, ox, oy, oz, x, y, z);
511     speed = unit_speed(unit2, x, y);
512     /* Adjust total move cost accounting for extra ACP.
513 	Note that total move cost may be 0 in this case,
514 	even though that is usually not allowed. */
515     totcost = max(0, totcost - (acpextra * speed));
516     /* A unit with an adjusted speed of zero cannot move except within
517        a cell. */
518     if (speed == 0 && !(ox == x && oy == y && oz == z))
519       return A_ANY_CANNOT_DO;
520     /* Check if unit can possibly have enough mp for the move. */
521     if (!can_have_enough_mp(unit, unit2, totcost))
522       return A_ANY_CANNOT_DO; /* (Should have A_MOVE_CANNOT_DO) */
523     /* Now check if unit has enough mp this turn. */
524     mpavail = (acpavail * speed) / 100;
525     /* take into account acp-min in computing mpavail; Massimo */
526     if (u_acp_min(u) < 0)
527       mpavail = ((acpavail - u_acp_min(u)) * speed) / 100;
528     /* Zero mp always disallows movement, unless intra-cell. */
529     if (mpavail <= 0 && !(ox == x && oy == y && oz == z))
530       return A_MOVE_NO_MP;
531     /* The free mp might get us enough moves, so add it before comparing. */
532     if (mpavail + u_free_mp(u2) < totcost)
533       return A_MOVE_NO_MP;
534     /* If destination is too small or already full, we can't move into it. We
535     don't check for type_survives_in_cell, so players (both human and AIs)
536     are free to kill or wreck their units by moving into deadly terrain. In
537     most games, such moves are however prohibited either by limiting the
538     space or by setting mp-to-enter-terrain to 99. */
539     if (!type_can_occupy_cell(unit2->type, x, y))
540       return A_MOVE_DEST_FULL;
541     return A_ANY_OK;
542 }
543 
544 int
can_move_via_conn(Unit * unit,int nx,int ny)545 can_move_via_conn(Unit *unit, int nx, int ny)
546 {
547     int c, dir;
548 
549     if (numconntypes == 0)
550       return FALSE;
551     for_all_connection_types(c) {
552 	if (aux_terrain_defined(c)
553 	    && (dir = closest_dir(nx - unit->x, ny - unit->y)) >= 0
554 	    && connection_at(unit->x, unit->y, dir, c)
555 	    && !ut_vanishes_on(unit->type, c)
556 	    && !ut_wrecks_on(unit->type, c))
557 	  return TRUE;
558     }
559     return FALSE;
560 }
561 
562 int
border_slide_possible(int u,int ox,int oy,int nx,int ny)563 border_slide_possible(int u, int ox, int oy, int nx, int ny)
564 {
565     int dx, dy, dir, bx, by, bdir, b;
566 
567     dx = nx - ox;  dy = ny - oy;
568     /* Pick the cell on the right-hand side of the border in question.
569        Since the distance is 2, and border sliding only works for
570        cells on a "diagonal", choose the dir case-by-case. */
571     if (dx == -2 && dy == 1) {
572 	dir = NORTHWEST;
573     } else if (dx == -1) {
574 	if (dy == -1) {
575 	    dir = WEST;
576 	} else if (dy == 2) {
577 	    dir = NORTHEAST;
578 	} else {
579 	    return FALSE;
580 	}
581     } else if (dx == 1) {
582 	if (dy == -2) {
583 	    dir = SOUTHWEST;
584 	} else if (dy == 1) {
585 	    dir = EAST;
586 	} else {
587 	    return FALSE;
588 	}
589     } else if (dx == 2 && dy == -1) {
590 	dir = SOUTHEAST;
591     } else {
592 	return FALSE;
593     }
594     bdir = left_dir(left_dir(dir));
595     point_in_dir(ox, oy, dir, &bx, &by);
596     for_all_border_types(b) {
597 	if (aux_terrain_defined(b)
598 	    && border_at(bx, by, bdir, b)
599 	    && ut_mp_to_traverse(u, b) >= 0)
600 	  return TRUE;
601     }
602     return FALSE;
603 }
604 
605 int
unit_speed(Unit * unit,int nx,int ny)606 unit_speed(Unit *unit, int nx, int ny)
607 {
608     int u = unit->type, speed, x = unit->x, y = unit->y;
609     int dir, angle1, windval1, angle2, windval2;
610     int occeff, totocceff, totoccdenom;
611     Unit *occ;
612 
613     /* Dead units can sometimes end up here, but don't bother
614        complaining. */
615     if (unit->hp <= 0)
616       return 0;
617     speed = u_speed(u);
618     /* Consider additive/subtractive effects on speed. */
619     /* Effects by occupants: mobility assistance, encumberance, etc.... */
620     if (unit->occupant) {
621 	for_all_occupants(unit, occ) {
622 	    if (in_play(occ) && completed(occ)) {
623 		speed += uu_occ_adds_speed(u, occ->type);
624 	    }
625 	}
626     }
627     /* Consider multiplicative effects on speed. */
628     /* Effect due to damage. */
629     if (unit->hp < u_hp_max(u) && u_speed_damage_effect(u) != lispnil) {
630 	speed = damaged_value(unit, u_speed_damage_effect(u), speed);
631     }
632     /* The speed effect due to wind is an average of the wind effect
633        in the current cell and in the new cell. */
634     if (winds_defined() && u_speed_wind_effect(u) != lispnil) {
635 	dir = closest_dir(nx - x, ny - y);
636 	angle1 = angle_with(dir, wind_dir_at(x, y));
637 	windval1 = wind_value(unit, angle1, wind_force_at(x, y),
638 			      u_speed_wind_effect(u), 10000);
639 	angle2 = angle_with(dir, wind_dir_at(nx, ny));
640 	windval2 = wind_value(unit, angle2, wind_force_at(nx, ny),
641 			      u_speed_wind_effect(u), 10000);
642 	speed = (speed * ((windval1 + windval2) / 2)) / 100;
643     }
644     if (unit->occupant /* and any occupant speed factors */) {
645 	totocceff = 100;
646 	totoccdenom = 100;
647 	for_all_occupants(unit, occ) {
648 	    if (in_play(occ) && completed(occ)) {
649 		occeff = uu_occ_multiplies_speed(u, occ->type);
650 		if (occeff != 100) {
651 		    totocceff *= occeff;
652 		    totoccdenom *= 100;
653 		}
654 	    }
655     	}
656     	speed = (speed * totocceff) / totoccdenom;
657     }
658     /* Clip to limits. */
659     speed = max(speed, u_speed_min(u));
660     speed = min(speed, u_speed_max(u));
661     return speed;
662 }
663 
664 /* Compute and return value for a damaged unit, using a list of
665    (hp val) pairs and interpolating between them. */
666 
667 int
damaged_value(Unit * unit,Obj * effect,int maxval)668 damaged_value(Unit *unit, Obj *effect, int maxval)
669 {
670     int u, err, rslt;
671 
672     u = unit->type;
673     err = interpolate_in_list_ext(unit->hp, effect, 0, 0, 0, 0, u_hp(u),
674 				  maxval, &rslt);
675     if (err != 0) {
676 	run_warning("cannot get damaged value for %s at hp %d, using 100",
677 		    u_type_name(u), unit->hp);
678 	rslt = 100;
679     }
680     return rslt;
681 }
682 
683 /* Compute and return the wind's effect on a unit, using a list of lists
684    and interpolating between them. */
685 
686 int
wind_value(Unit * unit,int angle,int force,Obj * effect,int maxval)687 wind_value(Unit *unit, int angle, int force, Obj *effect, int maxval)
688 {
689     int err, rslt;
690     Obj *rest, *head, *key, *val;
691 
692     for_all_list(effect, rest) {
693 	head = car(rest);
694 	key = car(head);
695 	if ((numberp(key) && angle == c_number(key))
696 	    || (symbolp(key))
697 	    || (consp(key) && number_member(angle, key))) {
698 	    val = cadr(head);
699 	    if (numberp(val))
700 	      return c_number(val);
701 	    else {
702 		err = interpolate_in_list(force, val, &rslt);
703 		if (err == 0) {
704 		    return rslt;
705 		} else {
706 		    run_warning("no value for wind angle=%d force=%d",
707 				angle, force);
708 		    return maxval;
709 		}
710 	    }
711 	}
712     }
713     return maxval;
714 }
715 
716 int
number_member(int x,Obj * lis)717 number_member(int x, Obj *lis)
718 {
719     Obj *rest;
720 
721     if (lis == lispnil) {
722 	return FALSE;
723     } else if (!consp(lis)) {
724 	/* should probably be an error of some sort */
725 	return FALSE;
726     }
727     for (rest = lis; rest != lispnil; rest = cdr(rest)) {
728 	if (numberp(car(rest)) && x == c_number(car(rest)))
729 	  return TRUE;
730     }
731     return FALSE;
732 }
733 
734 /* Conduct the actual move (used in both normal moves and some
735    combat).  Note that the new x,y may be the same as the old; this
736    will happen if an occupant is getting off a transport but staying
737    in the same cell. */
738 
739 int
move_unit(Unit * unit,int nx,int ny)740 move_unit(Unit *unit, int nx, int ny)
741 {
742     int u = unit->type, u3, ox = unit->x, oy = unit->y, oz = unit->z;
743     int nz = oz;
744     SideMask observers;
745 
746     u3 = (unit->transport ? unit->transport->type : NONUTYPE);
747     maybe_lose_track(unit, nx, ny);
748     /* Make sure terrain usage is zeroed before moving. */
749     if (u_advanced(unit->type))
750     	free_used_cells(unit);
751     /* Disappear from the old location and appear at the new one. */
752     if (unit->transport == NULL /* should be unconditional, but bugs still */) {
753 	change_cell(unit, nx, ny);
754     } else {
755 	leave_cell(unit);
756 	enter_cell(unit, nx, ny);
757     }
758     if (0 /* record movement */) {
759 	observers = add_side_to_set(unit->side, NOSIDES);
760 	/* (should let other watching sides see event also) */
761 	record_event(H_UNIT_MOVED, observers, unit->id, nx, ny);
762     }
763     maybe_track(unit);
764     /* Movement may set off other people's alarms. */
765     maybe_react_to_move(unit, ox, oy);
766     /* Might have auto-detonations in response. */
767     if (max_detonate_on_approach_range >= 0) {
768 	detonate_on_approach_around(unit);
769 	/* A detonation might have been fatal, get out now if so. */
770 	if (!alive(unit))
771 	  return 1;
772     }
773     /* The people at the new location may change sides immediately. */
774     if (people_sides_defined()
775 	&& any_people_side_changes
776 	&& probability(people_surrender_chance(u, nx, ny))) {
777 	change_people_side_around(nx, ny, u, unit->side);
778     }
779     if (control_sides_defined()) {
780 	if (ut_control_range(u, terrain_at(nx, ny)) >= 0) {
781 	    change_control_side_around(nx, ny, u, unit->side);
782 	}
783     }
784     kick_out_enemy_users(unit->side, nx, ny);
785     /* Reallocating used cells already here is not strictly necessary since this is
786     also done at the start of run_production, the only place where terrain usage
787     matters. However, one may imagine future versions of the game where terrain
788     usage is evaluated elsewhere. It is therefore a good idea to update it as soon as
789     possible. */
790     if (u_advanced(unit->type))
791 	allocate_used_cells(unit);
792     /* Use up supplies as directed. */
793     consume_move_supplies(unit);
794     /* a hack */
795     update_cell_display(unit->side, ox, oy, UPDATE_ALWAYS);
796     /* Always return the mp cost, even if the mover died. */
797     return total_move_cost(u, u3, ox, oy, oz, nx, ny, nz);
798 }
799 
800 /* Given a location, change the controlling side. */
801 
802 void
change_control_side_around(int x,int y,int u,Side * side)803 change_control_side_around(int x, int y, int u, Side *side)
804 {
805     int con = control_side_at(x, y), s = side_number(side), pop;
806     Side *oldside, *peopside, *side2;
807 
808     /* Do nothing if the cell cannot be controlled by a side. */
809     if (con == NOCONTROL)
810       return;
811     /* Do nothing if we control this cell already. */
812     if (con == s)
813       return;
814     oldside = side_n(con);
815     /* Don't challenge control by our allies. */
816     if (trusted_side(side, oldside))
817       return;
818     /* Return an ally's area. */
819     if (people_sides_defined()
820 	&& (pop = people_side_at(x, y)) != NOBODY) {
821 	peopside = side_n(pop);
822 	if (trusted_side(side, peopside))
823 	  s = pop;
824     }
825     /* Change the cell's control. */
826     set_control_side_at(x, y, s);
827     /* All of our buddies get to see what goes on here. */
828     for_all_sides(side2) {
829 	if (side == side2 || trusted_side(side, side2)) {
830 	    add_cover(side2, x, y, 1);
831 	}
832     }
833     update_cell_display_all_sides(x, y, UPDATE_ALWAYS | UPDATE_ADJ);
834     /* Previous side(s) lose free coverage. */
835     for_all_sides(side2) {
836 	if (!trusted_side(side, side2)
837 	    && (oldside == side2 || trusted_side(oldside, side2))) {
838 	    add_cover(side2, x, y, -1);
839 	    /* Update coverage display. */
840 	    update_cell_display(side2, x, y, UPDATE_COVER);
841 	}
842     }
843     /* (should add ability to change adjacent cells also) */
844 }
845 
846 int
can_move_at_all(Unit * unit)847 can_move_at_all(Unit *unit)
848 {
849     return (type_max_speed(unit->type) > 0);
850 }
851 
852 /* This is true if the given location is in a blocking zoc for the unit. */
853 
854 int
in_blocking_zoc(Unit * unit,int x,int y,int z)855 in_blocking_zoc(Unit *unit, int x, int y, int z)
856 {
857     int u = unit->type, t = terrain_at(x, y), dir, x1, y1, t1, u2, range;
858     Unit *unit2;
859 
860     if (max_zoc_range < 0)
861       return FALSE;
862     if (max_zoc_range >= 0) {
863 	for_all_stack(x, y, unit2) {
864 	    u2 = unit2->type;
865 	    range = zoc_range(unit2, u);
866 	    if (range >= 0
867 		&& is_active(unit2)
868 		&& !trusted_side(unit->side, unit2->side) /* should make a better test */
869 		&& uu_mp_to_enter_own(unit->type, u2) < 0
870 		&& ut_zoc_into(u2, t)
871 		&& ut_zoc_from_terrain(u2, t) > 0)
872 	      return TRUE;
873 	}
874     }
875     if (max_zoc_range >= 1) {
876 	for_all_directions(dir) {
877 	    if (point_in_dir(x, y, dir, &x1, &y1)) {
878 		for_all_stack(x1, y1, unit2) {
879 		    u2 = unit2->type;
880 		    range = zoc_range(unit2, u);
881 		    t1 = terrain_at(x1, y1);
882 		    if (range >= 1
883 			&& is_active(unit2)
884 			&& unit_blockable_by(unit, unit2)
885 			&& ut_zoc_into(u2, t)
886 			&& ut_zoc_from_terrain(u2, t1) > 0)
887 		      return TRUE;
888 		    if (range >= 1
889 			&& is_active(unit2)
890 			&& unit_traverse_blockable_by(unit, unit2)
891 			&& distance(unit->x, unit->y, x1, y1) <= range
892 			/* (should check that zoc goes into old terrain) */
893 			&& ut_zoc_into(u2, t)
894 			&& ut_zoc_from_terrain(u2, t1) > 0
895 			&& !any_friendly_at(unit, x, y)
896 			)
897 		      return TRUE;
898 		}
899 	    }
900 	}
901     }
902     if (max_zoc_range >= 2) {
903 	tmprslt = FALSE;
904 	tmpunit = unit;
905 	apply_to_ring(x, y, 2, max_zoc_range, test_blocking_zoc);
906 	return tmprslt;
907     }
908     return FALSE;
909 }
910 
911 static void
test_blocking_zoc(int x,int y)912 test_blocking_zoc(int x, int y)
913 {
914     int u = tmpunit->type, u2, t = terrain_at(x, y), range;
915     Unit *unit2;
916 
917     for_all_stack(x, y, unit2) {
918 	u2 = unit2->type;
919 	range = zoc_range(unit2, u);
920 	if (range >= distance(x, y, tmpunit->x, tmpunit->y)
921 	    && is_active(unit2)
922 	    && unit_blockable_by(tmpunit, unit2)
923 	    && ut_zoc_into(u2, terrain_at(tmpunit->x, tmpunit->y))
924 	    && ut_zoc_from_terrain(u2, t) > 0)
925           tmprslt = TRUE;
926     }
927 }
928 
929 /* This is true if unit2 wants to block unit from moving. */
930 
931 int
unit_blockable_by(Unit * unit,Unit * unit2)932 unit_blockable_by(Unit *unit, Unit *unit2)
933 {
934     /* should make a better test than just trust - some types
935        might not need to side relationship? */
936     return (!trusted_side(unit->side, unit2->side)
937 	    && uu_mp_to_enter_zoc(unit->type, unit2->type) < 0);
938 }
939 
940 /* This is true if unit2 wants to block unit from moving. */
941 
942 int
unit_traverse_blockable_by(Unit * unit,Unit * unit2)943 unit_traverse_blockable_by(Unit *unit, Unit *unit2)
944 {
945     /* should make a better test than just trust - some types
946        might not need to side relationship? */
947     return (!trusted_side(unit->side, unit2->side)
948 	    && uu_mp_to_traverse_zoc(unit->type, unit2->type) < 0);
949 }
950 
951 int
any_friendly_at(Unit * unit,int x,int y)952 any_friendly_at(Unit *unit, int x, int y)
953 {
954     Unit *unit2;
955 
956     for_all_stack(x, y, unit2) {
957 	if (unit_trusts_unit(unit, unit2))
958 	  return TRUE;
959     }
960     return FALSE;
961 }
962 
963 /* Compute the number of move points that will be needed to do the given
964    move. */
965 
966 int
total_move_cost(int u,int u2,int x1,int y1,int z1,int x2,int y2,int z2)967 total_move_cost(int u, int u2, int x1, int y1, int z1, int x2, int y2, int z2)
968 {
969     int cost, ferry, b, c, conncost, travcost, dist, dir;
970 
971     if (z1 != 0 || z2 != 0) {
972     	/* should write these calcs eventually */
973     }
974     dist = distance(x1, y1, x2, y2);
975     if (dist == 0) {
976 	if (z2 != z1) {
977 	    /* (should have parms for up/down in same cell) */
978 	    return 1;
979 	} else {
980     	    /* Unit is leaving a transport and moving into the open here;
981     	       free of charge. */
982     	    return 0;
983     	}
984     } else if (dist == 1) {
985     	/* (fall through) */
986     } else {
987     	/* Border slide or multiple cell move. */
988     	/* (should implement) */
989     	return dist;
990     }
991     cost = 0;
992     ferry = 0;
993     if (u2 != NONUTYPE) {
994 	/* Charge for leaving the transport. */
995     	cost += uu_mp_to_leave(u, u2);
996     	/* See what type of ferrying we're going to get. */
997     	ferry = uu_ferry_on_leave(u2, u);
998     }
999     if (ferry < over_own) {
1000     	cost += ut_mp_to_leave(u, terrain_at(x1, y1));
1001 	/* Add in effect of any coatings at the origin. */
1002 	if (numcoattypes > 0) {
1003 	    for_all_terrain_types(c) {
1004 		if (t_is_coating(c)
1005 		    && aux_terrain_defined(c)
1006 		    && aux_terrain_at(x1, y1, c) > 0) {
1007 		    cost += ut_mp_to_leave(u, c);
1008 		}
1009 	    }
1010 	}
1011     }
1012     if (numbordtypes > 0 && ferry < over_border) {
1013 	/* Add any necessary border crossing costs. */
1014 	dir = closest_dir(x2 - x1, y2 - y1);
1015 	if (dir >= 0) {
1016 	    for_all_border_types(b) {
1017 		if (aux_terrain_defined(b)
1018 		    && border_at(x1, y1, dir, b)) {
1019 		    cost += ut_mp_to_enter(u, b);
1020 		}
1021 	    }
1022 	}
1023     }
1024     if (ferry < over_all) {
1025 	cost += ut_mp_to_enter(u, terrain_at(x2, y2));
1026 	/* Add in effect of any coatings at the destination. */
1027 	if (numcoattypes > 0) {
1028 	    for_all_terrain_types(c) {
1029 		if (t_is_coating(c)
1030 		    && aux_terrain_defined(c)
1031 		    && aux_terrain_at(x2, y2, c) > 0) {
1032 		    cost += ut_mp_to_enter(u, c);
1033 		}
1034 	    }
1035 	}
1036     }
1037     /* Use a connection traversal if it would be cheaper.  This is
1038        only automatic if the connection on/off costs are small enough,
1039        otherwise the unit has to do explicit actions to get on the
1040        connection and off again. */
1041     if (numconntypes > 0) {
1042 	/* Try each connection type to see if it's better. */
1043 	dir = closest_dir(x2 - x1, y2 - y1);
1044 	if (dir >= 0) {
1045 	    for_all_connection_types(c) {
1046 		if (aux_terrain_defined(c)
1047 		    && connection_at(x1, y1, dir, c)
1048 		    && ((travcost = ut_mp_to_traverse(u, c)) >= 0)) {
1049 		    conncost = ut_mp_to_enter(u, c)
1050 		      + travcost
1051 		      + ut_mp_to_leave(u, c);
1052 		    cost = min(cost, conncost);
1053 		}
1054 	    }
1055 	}
1056     }
1057     /* The cost of leaving the world is always an addon. */
1058     if (!inside_area(x2, y2)) {
1059     	cost += u_mp_to_leave_world(u);
1060     }
1061     /* Any (inter-cell) movement must always cost at least 1 mp. */
1062     if (cost < 1)
1063       cost = 1;
1064     return cost;
1065 }
1066 
1067 int
zoc_range(Unit * unit,int u2)1068 zoc_range(Unit *unit, int u2)
1069 {
1070     int u = unit->type;
1071 
1072     return (uu_zoc_range(u, u2)
1073 	    * ut_zoc_from_terrain(u, terrain_at(unit->x, unit->y))) / 100;
1074 }
1075 
1076 static int tmpmpcost, tmpox, tmpoy;
1077 
1078 static void zoc_cost_fn(int x, int y);
1079 
1080 int
zoc_move_cost(Unit * unit,int ox,int oy,int oz)1081 zoc_move_cost(Unit *unit, int ox, int oy, int oz)
1082 {
1083     int u = unit->type, u2, t1, t2, cost, mpcost, x, y, dir, x1, y1, range;
1084     Unit *unit2;
1085 
1086     /* If this is negative, ZOCs are not part of this game. */
1087     if (max_zoc_range < 0)
1088       return 0;
1089     if (!in_play(unit))
1090       return 0;
1091     mpcost = 0;
1092     x = unit->x;  y = unit->y;
1093     t1 = terrain_at(ox, oy);
1094     t2 = terrain_at(x, y);
1095     if (max_zoc_range == 0 || max_zoc_range == 1) {
1096         /* ZOCs of units in old cell. */
1097 	for_all_stack(ox, oy, unit2) {
1098 	    u2 = unit2->type;
1099             range = zoc_range(unit2, u);
1100 	    if (is_active(unit2)
1101 		&& unit2->side != unit->side
1102 		&& range >= 0
1103 		&& ut_zoc_into(u2, t1)
1104 		/* should account for from-terrain also */
1105 		)
1106 	      mpcost = max(mpcost, uu_mp_to_leave_zoc(u, u2));
1107 	}
1108 	/* ZOCs of units in new cell. */
1109 	for_all_stack(x, y, unit2) {
1110 	    u2 = unit2->type;
1111             range = zoc_range(unit2, u);
1112 	    if (is_active(unit2)
1113 		&& unit2->side != unit->side
1114 		&& range >= 0
1115 		&& ut_zoc_into(u2, t2))
1116 	      mpcost = max(mpcost, uu_mp_to_enter_zoc(u, u2));
1117 	}
1118     }
1119     if (max_zoc_range > 0) {
1120 	if (max_zoc_range == 1) {
1121 	    /* ZOCs may be into adjacent cells. */
1122 	    /* Look for everybody that was exerting ZOC into the old
1123                location. */
1124 	    /* (should calc with stacked units also) */
1125 	    for_all_directions(dir) {
1126 		if (point_in_dir(ox, oy, dir, &x1, &y1)) {
1127 		    for_all_stack(x1, y1, unit2) {
1128 			u2 = unit2->type;
1129 			range = zoc_range(unit2, u);
1130 			if (in_play(unit2) /* should be is_active? */
1131 			    && unit2->side != unit->side  /* and unfriendly */
1132 			    && range >= 1
1133 			    && ut_zoc_into(u2, t1)) {
1134 			    if (1 /* leaving zoc */) {
1135 				cost = uu_mp_to_leave_zoc(u, u2);
1136 			    } else {
1137 				cost = uu_mp_to_traverse_zoc(u, u2);
1138 			    }
1139 			    mpcost = max(mpcost, cost);
1140 			}
1141 			/* (and occupants?) */
1142 		    }
1143 		}
1144 	    }
1145 	    /* Look for everybody that is now exerting ZOC into the
1146                new location. */
1147 	    /* (should calc with stacked units also) */
1148 	    for_all_directions(dir) {
1149 		if (point_in_dir(x, y, dir, &x1, &y1)) {
1150 		    for_all_stack(x1, y1, unit2) {
1151 			u2 = unit2->type;
1152 			range = zoc_range(unit2, u);
1153 			if (is_active(unit2)
1154 			    && unit2->side != unit->side  /* and unfriendly */
1155 			    && range >= 1
1156 			    && ut_zoc_into(u2, t2)) {
1157 			    if (1 /* entering zoc */) {
1158 				cost = uu_mp_to_enter_zoc(u, u2);
1159 			    } else {
1160 				cost = uu_mp_to_traverse_zoc(u, u2);
1161 			    }
1162 			    mpcost = max(mpcost, cost);
1163 			}
1164 			/* (and occupants?) */
1165 		    }
1166 		}
1167 	    }
1168 	} else {
1169 	    tmpmpcost = mpcost;
1170 	    tmpox = ox;  tmpoy = oy;
1171 	    tmpunit = unit;
1172 	    apply_to_area(ox, oy, max_zoc_range + distance(ox, oy, x, y),
1173 			  zoc_cost_fn);
1174 	    mpcost = tmpmpcost;
1175 	}
1176     }
1177     return mpcost;
1178 }
1179 
1180 static int
allowed_in_zoc(Unit * unit,Unit * unit2)1181 allowed_in_zoc(Unit *unit, Unit *unit2)
1182 {
1183     /* should make a better test */
1184     return trusted_side(unit->side, unit2->side);
1185 }
1186 
1187 static void zoc_cost_fn(int x, int y);
1188 
1189 static void
zoc_cost_fn(int x,int y)1190 zoc_cost_fn(int x, int y)
1191 {
1192     int u, u2, x0, y0, dist, odist, range, cost;
1193     Unit *unit2;
1194 
1195     u = tmpunit->type;
1196     x0 = tmpunit->x;  y0 = tmpunit->y;
1197     dist = distance(x0, y0, x, y);
1198     odist = distance(tmpox, tmpoy, x, y);
1199     for_all_stack(x, y, unit2) {
1200 	if (is_active(unit2)) {
1201 	    u2 = unit2->type;
1202 	    range = zoc_range(unit2, u);
1203 	    if (dist <= range) {
1204 		if (odist <= range) {
1205 		    /* Traversing the unit2's ZOC. */
1206 		    if (!allowed_in_zoc(tmpunit, unit2)
1207 			&& ut_zoc_into(u2, terrain_at(x0, y0))) {
1208 			cost = uu_mp_to_traverse_zoc(u, u2);
1209 			tmpmpcost = max(tmpmpcost, cost);
1210 		    }
1211 		} else {
1212 		    /* Entering the unit2's ZOC. */
1213 		    if (!allowed_in_zoc(tmpunit, unit2)
1214 			&& ut_zoc_into(u2, terrain_at(x0, y0))) {
1215 			cost = uu_mp_to_enter_zoc(u, u2);
1216 			tmpmpcost = max(tmpmpcost, cost);
1217 		    }
1218 		}
1219 	    } else {
1220 		if (odist <= range) {
1221 		    /* Leaving the unit2's ZOC. */
1222 		    if (!allowed_in_zoc(tmpunit, unit2)
1223 			&& ut_zoc_into(u2, terrain_at(x0, y0))) {
1224 			cost = uu_mp_to_leave_zoc(u, u2);
1225 			tmpmpcost = max(tmpmpcost, cost);
1226 		    }
1227 		}
1228 	    }
1229 	}
1230     }
1231 }
1232 
1233 /* This is a hook to handle any reactions to the unit's successful move. */
1234 
1235 int
maybe_react_to_move(Unit * unit,int ox,int oy)1236 maybe_react_to_move(Unit *unit, int ox, int oy)
1237 {
1238     return 0;
1239 }
1240 
1241 static void
try_detonate_on_approach(int x,int y)1242 try_detonate_on_approach(int x, int y)
1243 {
1244     int dist;
1245     Unit *unit;
1246 
1247     dist = distance(tmpunit->x, tmpunit->y, x, y);
1248     for_all_stack(x, y, unit) {
1249 	if (unit->side != tmpunit->side
1250 	    && dist <= uu_detonate_approach_range(unit->type, tmpunit->type)
1251 	    /* (should make doctrine-based decision about whether to go off) */
1252 	    && !was_detonated(unit)
1253 	    ) {
1254 	    /* Found one, now set it off! */
1255 	    detonate_unit(unit, unit->x, unit->y, unit->z);
1256 	}
1257     }
1258 }
1259 
1260 /* For the given unit, search around the given area looking for other
1261    units might detonate just because of the proximity. */
1262 
1263 void
detonate_on_approach_around(Unit * unit)1264 detonate_on_approach_around(Unit *unit)
1265 {
1266     int maxrange;
1267 
1268     tmpunit = unit;
1269     apply_to_area(unit->x, unit->y, max_detonate_on_approach_range,
1270 		  try_detonate_on_approach);
1271     maxrange = max(max_u_detonate_effect_range, max_t_detonate_effect_range)
1272       + max_detonate_on_approach_range;
1273     reckon_damage_around(unit->x, unit->y, maxrange, unit);
1274 }
1275 
1276 /* Use up the supply consumed by a successful move.  Also, the move might
1277    have used up essentials and left the unit without its survival needs,
1278    so check for this case and maybe hit/kill the unit. */
1279 
1280 void
consume_move_supplies(Unit * unit)1281 consume_move_supplies(Unit *unit)
1282 {
1283     int u = unit->type, m, checkstarve = FALSE;
1284 
1285     for_all_material_types(m) {
1286 	if (um_consumption_per_move(u, m) > 0) {
1287 	    unit->supply[m] -= um_consumption_per_move(u, m);
1288 	    /* Don't let supply go below zero. */
1289 	    if (unit->supply[m] <= 0) {
1290 		unit->supply[m] = 0;
1291 		checkstarve = TRUE;
1292 	    }
1293 	}
1294     }
1295     if (checkstarve)
1296       maybe_starve(unit, FALSE);
1297     /* Trigger any supply alarms. */
1298     if (alive(unit)
1299     	&& unit->plan
1300     	&& !unit->plan->supply_is_low
1301     	&& past_halfway_point(unit)
1302     	) {
1303     	unit->plan->supply_is_low = TRUE;
1304     	update_unit_display(unit->side, unit, TRUE);
1305     }
1306 }
1307 
1308 /* Movement into another unit. */
1309 
1310 /* Record an enter action as the next to do. */
1311 
1312 int
prep_enter_action(Unit * unit,Unit * unit2,Unit * newtransport)1313 prep_enter_action(Unit *unit, Unit *unit2, Unit *newtransport)
1314 {
1315     if (unit == NULL || unit->act == NULL || unit2 == NULL
1316 	|| newtransport == NULL)
1317       return FALSE;
1318     unit->act->nextaction.type = ACTION_ENTER;
1319     unit->act->nextaction.args[0] = newtransport->id;
1320     unit->act->nextaction.actee = unit2->id;
1321     return TRUE;
1322 }
1323 
1324 int
do_enter_action(Unit * unit,Unit * unit2,Unit * newtransport)1325 do_enter_action(Unit *unit, Unit *unit2, Unit *newtransport)
1326 {
1327     int u2, u3, u4, ox, oy, oz, ot, nx, ny, nz, speed, acpcost, mpcost, rslt;
1328 
1329     u2 = unit2->type;
1330     ox = unit2->x;  oy = unit2->y;  oz = unit2->z;
1331     ot = terrain_at(ox, oy);
1332     u3 = newtransport->type;
1333     nx = newtransport->x;  ny = newtransport->y;  nz = newtransport->z;
1334     mpcost = 1;
1335     if (ut_vanishes_on(u2, ot)
1336 	&& uu_ferry_on_enter(u3, u2) < over_all
1337 	&& (unit->transport == NULL
1338 	    || uu_ferry_on_leave(unit->transport->type, u2) < over_own)
1339 	&& !can_move_via_conn(unit2, nx, ny)) {
1340 	/* This case is if the unit is already on a connection in
1341 	   hostile terrain and tries to enter without using a bridge,
1342 	   or if it is on a transport in hostile terrain that doesn't
1343 	   ferry. */
1344 	notify(unit2->side, "%s vanishes into the %s!",
1345 	       unit_handle(unit2->side, unit2), t_type_name(ot));
1346 	kill_unit(unit2, H_UNIT_VANISHED);
1347 	rslt = A_MOVE_UNIT_GONE;
1348     } else if (ut_wrecks_on(u2, ot)
1349 	       && uu_ferry_on_enter(u3, u2) < over_all
1350 	       && (unit->transport == NULL
1351 		   || uu_ferry_on_leave(unit->transport->type, u2) < over_own)
1352 	       && !can_move_via_conn(unit2, nx, ny)) {
1353 	notify(unit2->side, "%s wrecks in the %s!",
1354 	       unit_handle(unit2->side, unit2), t_type_name(ot));
1355 	if (u_wrecked_type(u2) == NONUTYPE) {
1356 	    /* Occupants always die if the wrecked unit disappears. */
1357 	    kill_unit(unit, H_UNIT_WRECKED);
1358 	} else {
1359 	    /* Wreck the unit.  Note that we want to do the wrecking even
1360 	       if the unit will vanish, so that occupants can escape if
1361 	       allowed for. */
1362 	    wreck_unit(unit, H_UNIT_WRECKED, WRECK_TYPE_TERRAIN, ot, NULL);
1363 	    /* Now make it go away, taking unlucky occupants with. */
1364 	    if (ut_vanishes_on(u2, ot)) {
1365 		kill_unit(unit2, H_UNIT_VANISHED);
1366 	    }
1367 	}
1368 	rslt = A_MOVE_UNIT_GONE;
1369     } else {
1370 	/* Change the unit's position. */
1371 	leave_cell(unit2);
1372 	enter_transport(unit2, newtransport);
1373 	/* Calculate how much acp has been used up. */
1374 	u4 = (unit2->transport ? unit2->transport->type : NONUTYPE);
1375 	mpcost = total_entry_cost(u2, u4, ox, oy, oz, u3, nx, ny, nz);
1376 	rslt = A_ANY_DONE;
1377     }
1378     if (alive(unit)) {
1379 	speed = unit_speed(unit2, nx, ny);
1380 	if (speed > 0) {
1381 	    acpcost = (mpcost * 100) / speed;
1382 	} else {
1383 	    acpcost = 1;
1384 	}
1385 	use_up_acp(unit, acpcost + uu_acp_to_enter(u2, u3));
1386     }
1387     return rslt;
1388 }
1389 
1390 int
check_enter_action(Unit * unit,Unit * unit2,Unit * newtransport)1391 check_enter_action(Unit *unit, Unit *unit2, Unit *newtransport)
1392 {
1393     int u, u2, unewtransport, u4, u2x, u2y;
1394     int unewtransportx, unewtransporty, totcost, speed, mpavail, m;
1395     int ox, oy, oz, nx, ny, nz;
1396 
1397     if (!in_play(unit))
1398       return A_ANY_ERROR;
1399     if (!in_play(unit2))
1400       return A_ANY_ERROR;
1401     if (!in_play(newtransport))
1402       return A_ANY_ERROR;
1403     u = unit->type;  u2 = unit2->type;  unewtransport = newtransport->type;
1404     if (uu_acp_to_enter(u2, unewtransport) < 1)
1405       return A_ANY_CANNOT_DO;
1406     if (!can_have_enough_acp(unit, uu_acp_to_enter(u2, unewtransport)))
1407       return A_ANY_CANNOT_DO;
1408     /* Can't enter self. */
1409     if (unit2 == newtransport)
1410       return A_ANY_ERROR;
1411     u2x = unit2->x;  u2y = unit2->y;
1412     unewtransportx = newtransport->x;  unewtransporty = newtransport->y;
1413     ox = unit2->x;  oy = unit2->y;  oz = unit2->z;
1414     nx = newtransport->x;  ny = newtransport->y;  nz = newtransport->z;
1415     if (!between(0, distance(ox, oy, nx, ny), 1))
1416       return A_ANY_ERROR;
1417     if (!sides_allow_entry(unit2, newtransport))
1418       return A_ANY_ERROR;
1419     if (!can_occupy(unit2, newtransport))
1420       return A_ANY_ERROR;
1421     if (!has_enough_acp(unit, uu_acp_to_enter(u2, unewtransport)))
1422       return A_ANY_NO_ACP;
1423     u4 = (unit2->transport ? unit2->transport->type : NONUTYPE);
1424     totcost = total_entry_cost(u2, u4, ox, oy, oz, unewtransport, nx, ny, nz);
1425     speed = unit_speed(unit2, unewtransportx, unewtransporty);
1426     if (speed > 0 && unit->act) {
1427 	mpavail = (unit->act->acp * speed) / 100;
1428     } else {
1429 	mpavail = 0;
1430     }
1431     /* If transport picks up the unit itself, no need to check mp. */
1432     if (uu_ferry_on_enter(unewtransport, u2) < over_all) {
1433 	/* Zero mp always disallows movement. */
1434 	if (mpavail <= 0)
1435 	  return A_MOVE_NO_MP;
1436 	/* The free mp might get us enough moves, so add it before
1437            comparing. */
1438 	if (mpavail + u_free_mp(u2) < totcost)
1439 	  return A_MOVE_NO_MP;
1440     }
1441     /* We have to have a minimum level of supply to be able to move. */
1442     for_all_material_types(m) {
1443 	if (unit2->supply[m] < um_to_move(u2, m))
1444 	  return A_ANY_NO_MATERIAL;
1445     }
1446     return A_ANY_OK;
1447 }
1448 
1449 /* This tests whether the relationship between the sides of a unit
1450    and a prospective transport allows for entry of the unit. */
1451 
1452 int
sides_allow_entry(Unit * unit,Unit * transport)1453 sides_allow_entry(Unit *unit, Unit *transport)
1454 {
1455     if (unit->side == indepside) {
1456     	if (transport->side == indepside) {
1457     	    return TRUE;
1458     	} else {
1459 	    /* (should fix this - table does not apply to indeps entering?) */
1460     	    return uu_can_enter_indep(unit->type, transport->type);
1461     	}
1462     } else {
1463     	if (transport->side == indepside) {
1464     	    return uu_can_enter_indep(unit->type, transport->type);
1465     	} else {
1466 	    /* Note that because this is for an explicit action, the unit
1467 	       must trust the transport, so the only test is whether the
1468 	       transports trusts the unit enough to have it as an occupant. */
1469     	    return unit_trusts_unit(transport, unit);
1470     	}
1471     }
1472 }
1473 
1474 /* This computes the total mp cost of entering a transport. */
1475 
1476 int
total_entry_cost(int u1,int u3,int x1,int y1,int z1,int u2,int x2,int y2,int z2)1477 total_entry_cost(int u1, int u3, int x1, int y1, int z1,
1478 		 int u2, int x2, int y2, int z2)
1479 {
1480     int cost = 0, ferryout, ferryin, t1, t2, b, dir, conncost, travcost, c;
1481 
1482     ferryout = over_nothing;
1483     ferryin = uu_ferry_on_enter(u2, u1);
1484     dir = closest_dir(x2 - x1, y2 - y1);
1485     if (u3 != NONUTYPE) {
1486 	/* Charge for leaving the transport. */
1487     	cost += uu_mp_to_leave(u1, u3);
1488     	ferryout = uu_ferry_on_leave(u3, u1);
1489     }
1490     /* (should include possibility of using conn to cross terrain) */
1491     /* Maybe add cost to leave terrain of own cell. */
1492     if (ferryout < over_own && ferryin < over_all && (dir >= 0)) {
1493 	t1 = terrain_at(x1, y1);
1494     	cost += ut_mp_to_leave(u1, t1);
1495     }
1496     /* Maybe add cost to cross one (or more) borders. */
1497     if (numbordtypes > 0 && ferryout < over_border && ferryin < over_border) {
1498 	if (dir >= 0) {
1499 	    for_all_border_types(b) {
1500 		if (aux_terrain_defined(b)
1501 		    && border_at(x1, y1, dir, b)) {
1502 		    cost += ut_mp_to_enter(u1, b);
1503 		}
1504 	    }
1505 	}
1506     }
1507     /* Maybe even have to pay cost of crossing destination's terrain. */
1508     if (ferryout < over_all && ferryin < over_own) {
1509 	t2 = terrain_at(x2, y2);
1510     	cost += ut_mp_to_enter(u1, t2);
1511     }
1512     /* Use a connection traversal if it would be cheaper.  This is
1513        only automatic if the connection on/off costs are small enough,
1514        otherwise the unit has to do explicit actions to get on the
1515        connection and off again. */
1516     if (numconntypes > 0) {
1517 	/* Try each connection type to see if it's better. */
1518 	if (dir >= 0) {
1519 	    for_all_connection_types(c) {
1520 		if (aux_terrain_defined(c)
1521 		    && connection_at(x1, y1, dir, c)
1522 		    && ((travcost = ut_mp_to_traverse(u1, c)) >= 0)) {
1523 		    conncost = ut_mp_to_enter(u1, c)
1524 		      + travcost
1525 		      + ut_mp_to_leave(u1, c);
1526 		    cost = min(cost, conncost);
1527 		}
1528 	    }
1529 	}
1530     }
1531     /* Add the actual cost of entry. */
1532     cost += uu_mp_to_enter(u1, u2);
1533     /* Movement must always cost at least 1 mp. */
1534     if (cost < 1)
1535       cost = 1;
1536     return cost;
1537 }
1538 
1539 /* This function now correctly calculates both the acp cost
1540 and the available acps. However, it does not take into account
1541 the fact that connections may make the terrain passable. Nor
1542 does it take into account the fact acps may be spent in order
1543 to leave the previous cell, or if there is room for the unit
1544 type within the terrain.  */
1545 
1546 int
terrain_always_impassable(int u,int t)1547 terrain_always_impassable(int u, int t)
1548 {
1549     int mpcost;
1550 
1551     if (ut_vanishes_on(u, t))
1552       return TRUE;
1553     if (ut_wrecks_on(u, t))
1554       return TRUE;
1555     /* Start with cost of entering a cell. */
1556     mpcost = ut_mp_to_enter(u, t);
1557     /* Check if we can have enough mps. */
1558     if (type_can_have_enough_mp(u, mpcost)) {
1559 	return FALSE;
1560     }
1561     return TRUE;
1562 }
1563 
1564 /* This function calculates if a unit type can move within a
1565 given terrain, i.e. from one cell to another of the same terrain
1566 type. It does take into account the possibility that acps may be
1567 spent on leaving a cell. It does not check if there is room for
1568 the unit within the given terrain or if the unit can survive
1569 on it. */
1570 
1571 int
type_can_move_in_terrain(int u,int t)1572 type_can_move_in_terrain(int u, int t)
1573 {
1574     int mpcost;
1575 
1576     /* Start with cost of entering a cell. */
1577     mpcost = ut_mp_to_enter(u, t);
1578     /* Add in cost for leaving the old cell. */
1579     mpcost += ut_mp_to_leave(u, t);
1580     /* Check if we can have enough mps. */
1581     if (type_can_have_enough_mp(u, mpcost)) {
1582 	return TRUE;
1583     }
1584     return FALSE;
1585 }
1586 
1587 int
type_can_have_enough_mp(int u,int mp)1588 type_can_have_enough_mp(int u, int mp)
1589 {
1590     int acpcost;
1591 
1592     /* Substract the free mps. */
1593     mp -= u_free_mp(u);
1594     /* But don't go negative. */
1595     mp = max(0, mp);
1596     if (0 != type_max_speed(u)) {
1597         /* Convert mps into acps. */
1598         acpcost = (mp * 100) / type_max_speed(u);
1599         /* Each move costs at least u_acp_to_move ... */
1600         acpcost = max(acpcost, u_acp_to_move(u));
1601         /* ... and also at least 1 acp no matter what. */
1602         acpcost = max(acpcost, 1);
1603         /* Check if we can have enough acps. */
1604         if (type_can_have_enough_acp(u, acpcost)) {
1605             return TRUE;
1606         }
1607     }
1608     return FALSE;
1609 }
1610 
1611