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