1 /* The combat-related actions of Xconq.
2 Copyright (C) 1987-1989, 1991-2000 Stanley T. Shebs.
3 Copyright (C) 2004-2005 Eric A. McDonald.
4
5 Xconq is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version. See the file COPYING. */
9
10 #include "conq.h"
11 #include "kernel.h"
12
13 static int one_attack(Unit *atker, Unit *defender);
14 static void fire_on_unit(Unit *atker, Unit *other);
15 static void attack_unit(Unit *atker, Unit *other);
16 static void maybe_hit_unit(Unit *atker, Unit *other, int fire, int fallsoff);
17 static void hit_unit(Unit *unit, int hit, Unit *atker);
18 static void model_1_attack(Unit *unit, int x, int y);
19 static int real_attack_value(Unit *unit);
20 static int real_defense_value(Unit *unit);
21
22 static void reckon_damage(int fire);
23 static void reckon_damage_here(int x, int y);
24 static void report_damage(Unit *unit, Unit *atker, Unit *mainunit, int fire);
25 static int will_report_damage(Unit *unit);
26 static void rescue_one_occupant(Unit *occ);
27 static int retreat_unit(Unit *unit, Unit *atker);
28 static int retreat_in_dir(Unit *unit, int dir);
29 static void attempt_to_capture_unit(Unit *atker, Unit *other);
30 static void capture_unit_2(Unit *unit, Unit *pris, Side *prevside);
31 static void capture_occupant(Unit *unit, Unit *pris, Unit *occ, Side *newside);
32 static void detonate_on_cell(int x, int y);
33 static void hit_unit_with_detonation(Unit *unit, int hit, Unit *atker);
34 static int found_blocking_elevation(int u, int ux, int uy, int uz, int u2, int u2x, int u2y, int u2z);
35 static Unit *mobile_enemy_threat(Unit *unit, int range);
36 static int enough_ammo_to_attack_unit(Unit *unit, Unit *other);
37 static int enough_ammo_to_fire_at_unit(Unit *unit, Unit *other);
38 static int enough_ammo_to_fire_one_round(Unit *unit);
39
40 static void damage_terrain(int u, int x, int y);
41 static int damaged_terrain_type(int t);
42
43 /* We can't declare all the action functions as static because some of them
44 are in other files, but don't let them be visible to all files. */
45
46 #undef DEF_ACTION
47 #define DEF_ACTION(name,code,args,prepfn,netprepfn,DOFN,checkfn,ARGDECL,doc) \
48 extern int DOFN ARGDECL;
49
50 #include "action.def"
51
52 /* Destruction stuff. */
53
54 typedef enum {
55 DESTRUCT_ORDINARY = 0,
56 DESTRUCT_VANISH
57 } DestructionResult;
58
59 static DestructionResult determine_destruction_result(Unit *unit);
60
61 /* Detonation stuff. */
62
63 int max_u_detonate_effect_range = -1;
64
65 int max_t_detonate_effect_range = -1;
66
67 int max_detonate_on_approach_range = -1;
68
69 /* Remember what the main units involved are, so display is handled
70 relative to them and not to any occupants. */
71
72 static Unit *amain, *omain;
73
74 int numsoundplays;
75
76 /* Useful, little functions. */
77
78 //! Chance that u will hit u2 by firing.
79
80 int
fire_hit_chance(int u,int u2)81 fire_hit_chance(int u, int u2)
82 {
83 return (uu_fire_hit(u, u2) != -1) ? uu_fire_hit(u, u2) : uu_hit(u, u2);
84 }
85
86 //! Damage that u does to u2 by firing.
87
88 int
fire_damage(int u,int u2)89 fire_damage(int u, int u2)
90 {
91 return (uu_fire_damage(u, u2) != -1) ? uu_fire_damage(u, u2)
92 : uu_damage(u, u2);
93 }
94
95 //! Chance for u to capture independent u2.
96
97 int
indep_capture_chance(int u,int u2)98 indep_capture_chance(int u, int u2)
99 {
100 return (uu_indep_capture(u, u2) != -1) ? uu_indep_capture(u, u2)
101 : uu_capture(u, u2);
102 }
103
104 //! Chance for u to capture u2 on a given side?
105
106 int
capture_chance(int u,int u2,Side * side2)107 capture_chance(int u, int u2, Side *side2)
108 {
109 if (side2 != indepside)
110 return uu_capture(u, u2);
111 return (indep_capture_chance(u, u2));
112 }
113
114 /* Unit/utype status/analysis functions. */
115
116 //! Could u fire at u2?
117 /*! \todo Remove ACP restrictions. */
118
119 int
could_fire_at(int u,int u2)120 could_fire_at(int u, int u2)
121 {
122 assert_error(is_unit_type(u), "Expected an unit type but did not get one");
123 assert_error(is_unit_type(u2), "Expected an unit type but did not get one");
124 if (!u_acp_independent(u) && !u_acp_to_fire(u))
125 return FALSE;
126 return ((g_combat_model() == 0) && (0 < fire_hit_chance(u, u2)));
127 }
128
129 //! Could u fire at >= 1 utypes?
130
131 int
could_fire_at_any(int u)132 could_fire_at_any(int u)
133 {
134 int u2 = NONUTYPE;
135
136 assert_error(is_unit_type(u), "Expected an unit type but did not get one");
137 for_all_unit_types(u2) {
138 if (could_fire_at(u, u2))
139 return TRUE;
140 }
141 return FALSE;
142 }
143
144 //! Can a given unit fire?
145 /*!
146 \todo Get rid of combat models.
147 \todo Get rid of ACP restrictions.
148 \todo Consider 'consumption-per-fire' and 'hit-by'.
149 */
150
151 int
can_fire(Unit * actor,Unit * firer)152 can_fire(Unit *actor, Unit *firer)
153 {
154 int rslt = A_ANY_OK;
155 int acp = 0;
156 int u2 = NONUTYPE;
157 int m2 = NONMTYPE;
158
159 /* In combat model 1, we can't attack units by firing. */
160 if (g_combat_model() == 1)
161 return A_ANY_ERROR;
162 /* Can the ACP source and firer act? */
163 if (!valid(rslt = can_act(actor, firer)))
164 return rslt;
165 u2 = firer->type;
166 /* Is there enough ACP to fire? */
167 acp = u_acp_to_fire(u2);
168 if (acp < 1)
169 return A_ANY_CANNOT_DO;
170 if (!has_enough_acp(actor, acp))
171 return A_ANY_NO_ACP;
172 /* Check whether we can attack from inside a transport. */
173 if (firer->transport
174 && uu_occ_combat(u2, firer->transport->type) == 0)
175 return A_ANY_OCC_CANNOT_DO;
176 /* We have to have a minimum level of supply to be able to attack. */
177 for_all_material_types(m2) {
178 if (firer->supply[m2] < um_to_fire(u2, m2))
179 return A_ANY_NO_MATERIAL;
180 }
181 return rslt;
182 }
183
184 //! Can a given unit fire at a given utype?
185
186 int
can_fire_at(Unit * actor,Unit * firer,int u)187 can_fire_at(Unit *actor, Unit *firer, int u)
188 {
189 int rslt = A_ANY_OK;
190
191 assert_error(is_unit_type(u), "Expected an unit type but did not get one");
192 if (!could_fire_at(firer->type, u))
193 return A_ANY_CANNOT_DO;
194 if (!valid(rslt = can_fire(actor, firer)))
195 return rslt;
196 return rslt;
197 }
198
199 //! Can a given unit fire at >= 1 enemy utypes?
200
201 int
can_fire_at_any(Unit * actor,Unit * firer)202 can_fire_at_any(Unit *actor, Unit *firer)
203 {
204 int rslt = A_ANY_OK;
205 int u = NONUTYPE;
206
207 for_all_unit_types(u) {
208 if (valid(rslt = can_fire_at(actor, firer, u)))
209 return A_ANY_OK;
210 }
211 return A_ANY_CANNOT_DO;
212 }
213
214 //! Could u hit u2 by attacks?
215 /*! \todo Remove ACP restrictions. */
216
217 int
could_attack(int u,int u2)218 could_attack(int u, int u2)
219 {
220 if (!u_acp_independent(u) && !uu_acp_to_attack(u, u2))
221 return FALSE;
222 return (((g_combat_model() == 0) && uu_hit(u, u2))
223 || ((g_combat_model() == 1) && u_attack(u)));
224 }
225
226 //! Could the given utype attack >= 1 utypes?
227
228 int
could_attack_any(int u)229 could_attack_any(int u)
230 {
231 int u2 = NONUTYPE;
232
233 for_all_unit_types(u2) {
234 if (could_attack(u, u2))
235 return TRUE;
236 }
237 return FALSE;
238 }
239
240 //! Can a given unit attack a given utype?
241 /*!
242 \todo Get rid of ACP restrictions.
243 \todo Consider 'consumption-per-attack' and 'hit-by'.
244 */
245
246 int
can_attack(Unit * actor,Unit * attacker,int u3)247 can_attack(Unit *actor, Unit *attacker, int u3)
248 {
249 int rslt = A_ANY_OK;
250 int acp = 0;
251 int u2 = NONUTYPE;
252 int m = NONMTYPE;
253
254 if (!valid(rslt = can_act(actor, attacker)))
255 return rslt;
256 u2 = attacker->type;
257 /* Is there enough ACP to attack? */
258 acp = uu_acp_to_attack(u2, u3);
259 if (acp < 1)
260 return A_ANY_CANNOT_DO;
261 if (!has_enough_acp(actor, acp))
262 return A_ANY_NO_ACP;
263 /* Check whether we can attack from inside a transport. */
264 if (attacker->transport
265 && uu_occ_combat(u2, attacker->transport->type) == 0)
266 return A_ANY_OCC_CANNOT_DO;
267 /* We have to have a minimum level of supply to be able to attack. */
268 for_all_material_types(m) {
269 if (attacker->supply[m] < um_to_attack(u2, m))
270 return A_ANY_NO_MATERIAL;
271 }
272 return rslt;
273 }
274
275 //! Can a given unit attack >= 1 enemy utypes?
276
277 int
can_attack_any(Unit * actor,Unit * attacker)278 can_attack_any(Unit *actor, Unit *attacker)
279 {
280 int rslt = A_ANY_OK;
281 int u = NONUTYPE;
282
283 for_all_unit_types(u) {
284 if (valid(rslt = can_attack(actor, attacker, u)))
285 return A_ANY_OK;
286 }
287 return A_ANY_CANNOT_DO;
288 }
289
290 //! Could u hit u2 by attacks or fire?
291
292 int
could_hit(int u,int u2)293 could_hit(int u, int u2)
294 {
295 return (could_attack(u, u2) || could_fire_at(u, u2));
296 }
297
298 //! Can given unit detonate?
299 /* \todo Remove ACP restrictions. */
300
301 int
can_detonate(Unit * actor,Unit * detonator)302 can_detonate(Unit *actor, Unit *detonator)
303 {
304 int rslt = A_ANY_OK;
305 int acp = 0;
306 int u2 = NONUTYPE;
307
308 /* Can actor and detonator act? */
309 if (!valid(rslt = can_act(actor, detonator)))
310 return rslt;
311 u2 = detonator->type;
312 /* The unit must actually be able to detonate. */
313 acp = u_acp_to_detonate(u2);
314 if (acp < 1)
315 return A_ANY_CANNOT_DO;
316 if (!has_enough_acp(actor, acp))
317 return A_ANY_NO_ACP;
318 return rslt;
319 }
320
321 //! Could u capture u2 by fire?
322 /*
323 \todo Get rid of combat models.
324 \todo Remove ACP restrictions.
325 */
326
327 int
could_capture_by_fire(int u,int u2,Side * oside)328 could_capture_by_fire(int u, int u2, Side *oside)
329 {
330 if (!u_acp_independent(u) && !u_acp_to_fire(u))
331 return FALSE;
332 if (g_combat_model() == 1)
333 return FALSE;
334 if (fire_hit_chance(u, u2))
335 return (0 < capture_chance(u, u2, oside));
336 return FALSE;
337 }
338
339 //! Could u capture u2 by attacks?
340 /*
341 \todo Get rid of combat models.
342 \todo Remove ACP restrictions.
343 */
344
345 int
could_capture_by_attacks(int u,int u2,Side * oside)346 could_capture_by_attacks(int u, int u2, Side *oside)
347 {
348 if (!u_acp_independent(u) && !uu_acp_to_attack(u, u2))
349 return FALSE;
350 /* Since capture only can happen through overrun actions in
351 combat model 1, and such actions are blocked if u_attack(u) is
352 zero (see model_1_attack) we return 0. Exception: if there is
353 an encounter result specified, we return the capture chance
354 (if non-zero) since the encounter code is executed before the
355 check of u_attack(u). */
356 if ((g_combat_model() == 1) && !u_attack(u))
357 return FALSE;
358 if (((g_combat_model() == 0) && uu_hit(u, u2)) || (g_combat_model() == 1))
359 return (0 < capture_chance(u, u2, oside));
360 return FALSE;
361 }
362
363 //! Could u capture u2 by direct capture?
364 /*
365 \todo Get rid of combat models.
366 \todo Remove ACP restrictions.
367 */
368
369 int
could_capture_by_capture(int u,int u2,Side * oside)370 could_capture_by_capture(int u, int u2, Side *oside)
371 {
372 if (!u_acp_independent(u) && !uu_acp_to_capture(u, u2))
373 return FALSE;
374 if (g_combat_model() == 1)
375 return FALSE;
376 return (0 < capture_chance(u, u2, oside));
377 }
378
379 //! Could u capture u2 by any means?
380
381 int
could_capture(int u,int u2,Side * oside)382 could_capture(int u, int u2, Side *oside)
383 {
384 return (could_capture_by_attacks(u, u2, oside)
385 || could_capture_by_fire(u, u2, oside)
386 || could_capture_by_capture(u, u2, oside));
387 }
388
389 //! Could the given utype capture any utypes by any means?
390
391 int
could_capture_any(int u)392 could_capture_any(int u)
393 {
394 int u2 = NONUTYPE;
395
396 for_all_unit_types(u2) {
397 /* Capture can happen by both direct action and as result of an
398 attack in combat model 0. */
399 if (g_combat_model() == 0) {
400 if ((uu_acp_to_attack(u, u2) > 0
401 || uu_acp_to_capture(u, u2) > 0)
402 && (uu_capture(u, u2) > 0 || uu_indep_capture(u, u2) > 0))
403 return TRUE;
404 /* Capture can only happen as result of an attack in combat model 1. */
405 } else if (g_combat_model() == 1) {
406 if (uu_acp_to_attack(u, u2) > 0
407 && (uu_capture(u, u2) > 0 || uu_indep_capture(u, u2) > 0))
408 return TRUE;
409 }
410 }
411 return FALSE;
412 }
413
414 //! Can a given unit capture a given utype on a given side?
415
416 int
can_capture(Unit * actor,Unit * captor,int u3,Side * eside)417 can_capture(Unit *actor, Unit *captor, int u3, Side *eside)
418 {
419 int rslt = A_ANY_OK;
420 int acp = 0;
421 int u2 = NONUTYPE;
422 int m = NONMTYPE;
423
424 /* Can actor and captor act? */
425 if (!valid(rslt = can_act(actor, captor)))
426 return rslt;
427 u2 = captor->type;
428 /* We can't capture units on our side. */
429 if (actor->side == eside)
430 return A_ANY_ERROR;
431 /* Enough ACP to capture? */
432 acp = uu_acp_to_capture(u2, u3);
433 if (acp < 1)
434 return A_ANY_CANNOT_DO;
435 if (!has_enough_acp(actor, acp))
436 return A_ANY_NO_ACP;
437 /* Is there even a chance to capture the enemy utype? */
438 if (capture_chance(u2, u3, eside) == 0)
439 return A_ANY_CANNOT_DO;
440 /* We have to have a minimum level of supply to be able to capture. */
441 /* (attack material table not really appropriate) */
442 for_all_material_types(m) {
443 if (captor->supply[m] < um_to_attack(u2, m))
444 return A_ANY_NO_MATERIAL;
445 }
446 return rslt;
447 }
448
449 /* Attack action. */
450
451 /* This is an attack on a given unit at a given level of commitment. */
452
453 int
prep_attack_action(Unit * unit,Unit * unit2,Unit * defender,int n)454 prep_attack_action(Unit *unit, Unit *unit2, Unit *defender, int n)
455 {
456 if (unit == NULL || unit->act == NULL || unit2 == NULL)
457 return FALSE;
458 unit->act->nextaction.type = ACTION_ATTACK;
459 unit->act->nextaction.args[0] = defender->id;
460 unit->act->nextaction.args[1] = n;
461 unit->act->nextaction.actee = unit2->id;
462 return TRUE;
463 }
464
465 int
do_attack_action(Unit * unit,Unit * unit2,Unit * defender,int n)466 do_attack_action(Unit *unit, Unit *unit2, Unit *defender, int n)
467 {
468 int u2 = unit2->type, u3 = defender->type;
469 int withdrawchance, surrenderchance;
470
471 action_point(unit2->side, defender->x, defender->y);
472 action_point(defender->side, defender->x, defender->y);
473 /* Defender might be a type that can sneak away to avoid attack. */
474 withdrawchance = uu_withdraw_per_attack(u2, u3);
475 if (withdrawchance > 0) {
476 if (probability(withdrawchance)) {
477 if (retreat_unit(defender, unit2)) {
478 if (alive(unit))
479 use_up_acp(unit, uu_acp_to_attack(u2, u3));
480 return A_ANY_DONE;
481 }
482 }
483 }
484 /* Defender might instead choose to surrender right off. */
485 surrenderchance = uu_surrender_per_attack(u2, u3);
486 if (surrenderchance > 0
487 && unit2->side
488 && unit2->side->tech[u3] >= u_tech_to_own(u3)) {
489 if (probability(surrenderchance)) {
490 capture_unit(unit2, defender, H_UNIT_CAPTURED);
491 if (alive(unit))
492 use_up_acp(unit, uu_acp_to_attack(u2, u3));
493 return A_ANY_DONE;
494 }
495 }
496 /* Carry out a normal attack. */
497 one_attack(unit, defender);
498 if (alive(unit))
499 use_up_acp(unit, uu_acp_to_attack(u2, u3));
500 /* The defender in an attack has to take time to defend itself. */
501 if (alive(defender))
502 use_up_acp(defender, uu_acp_to_defend(u2, u3));
503 return A_ANY_DONE;
504 }
505
506 int
check_attack_action(Unit * unit,Unit * unit2,Unit * defender,int n)507 check_attack_action(Unit *unit, Unit *unit2, Unit *defender, int n)
508 {
509 int u, u2, u3, u2x, u2y, dfx, dfy, dist;
510 int rslt = A_ANY_OK;
511
512 /* In combat model 1, we can't attack units directly. */
513 if (g_combat_model() == 1)
514 return A_ANY_ERROR;
515 if (!in_play(defender))
516 return A_ANY_ERROR;
517 /* We can't attack ourselves. */
518 if (unit2 == defender)
519 return A_ANY_ERROR;
520 if (!valid(rslt = can_attack(unit, unit2, defender->type)))
521 return rslt;
522 if (!indep(unit2) && unit2->side == defender->side)
523 return A_ANY_ERROR;
524 u = unit->type;
525 u2 = unit2->type;
526 u3 = defender->type;
527 u2x = unit2->x; u2y = unit2->y;
528 dfx = defender->x; dfy = defender->y;
529 dist = distance(u2x, u2y, dfx, dfy);
530 if (dist < uu_attack_range_min(u2, u3))
531 return A_ANY_TOO_NEAR;
532 if (dist > uu_attack_range(u2, u3))
533 return A_ANY_TOO_FAR;
534 if (uu_hit(u2, u3) <= 0)
535 return A_ATTACK_CANNOT_HIT;
536 /* (should prorate ammo needs by intensity of attack) */
537 if (!enough_ammo_to_attack_unit(unit2, defender))
538 return A_ANY_NO_AMMO;
539 /* Allow attacks even if zero damage, this amounts to "harassment". */
540 return A_ANY_OK;
541 }
542
543 /* Overrun action. */
544
545 /* Overrun is an attempt to occupy a given cell that may include attempts
546 to attack and/or capture any units in the way. */
547
548 int
prep_overrun_action(Unit * unit,Unit * unit2,int x,int y,int z,int n)549 prep_overrun_action(Unit *unit, Unit *unit2, int x, int y, int z, int n)
550 {
551 if (unit == NULL || unit->act == NULL || unit2 == NULL)
552 return FALSE;
553 unit->act->nextaction.type = ACTION_OVERRUN;
554 unit->act->nextaction.args[0] = x;
555 unit->act->nextaction.args[1] = y;
556 unit->act->nextaction.args[2] = z;
557 unit->act->nextaction.args[3] = n;
558 unit->act->nextaction.actee = unit2->id;
559 return TRUE;
560 }
561
562 int
do_overrun_action(Unit * unit,Unit * unit2,int x,int y,int z,int n)563 do_overrun_action(Unit *unit, Unit *unit2, int x, int y, int z, int n)
564 {
565 int u, u2, u3, acpused, mpcost, acpcost, speed, ox, oy, oz;
566 int withdrawchance, surrenderchance, gotonext;
567 Unit *defender;
568
569 u = unit->type; u2 = unit2->type;
570 ox = unit2->x; oy = unit2->y; oz = unit2->z;
571 action_point(unit2->side, x, y);
572 acpused = 0;
573 switch(g_combat_model()) {
574 case 0:
575 /* Attack every defender in turn. */
576 for_all_stack(x, y, defender) {
577 u3 = defender->type;
578 /* Don't attack any of our buddies. */
579 if (unit_trusts_unit(unit2, defender))
580 continue;
581 action_point(defender->side, x, y);
582 gotonext = FALSE;
583 /* Defender might be a type that can sneak away to avoid
584 attack. */
585 withdrawchance = uu_withdraw_per_attack(u2, u3);
586 if (withdrawchance > 0) {
587 if (probability(withdrawchance)) {
588 if (retreat_unit(defender, unit2)) {
589 gotonext = TRUE;
590 }
591 }
592 }
593 /* Defender might instead choose to surrender right off. */
594 surrenderchance = uu_surrender_per_attack(u2, u3);
595 if (surrenderchance > 0
596 && unit2->side
597 && unit2->side->tech[u3] >= u_tech_to_own(u3)) {
598 if (probability(surrenderchance)) {
599 capture_unit(unit2, defender, H_UNIT_CAPTURED);
600 gotonext = TRUE;
601 }
602 }
603 /* If any units were captured, the captor may have become
604 a garrison. If so, then the captor is considered dead.
605 Check for this before trying to fire/attack from (-1,-1)!
606 (If the last unit in the stack is the one garrisoned,
607 then this is a kind of Pyrrhic victory. For the
608 time being, we will count this as a failure so that major
609 surgery does not have to be done on the overrun code.)
610 */
611 if (!alive(unit2))
612 return A_OVERRUN_FAILED;
613 /* (should automatically prefer direct attack if better odds) */
614 if (valid(check_fire_at_action(unit, unit, defender, -1))) {
615 do_fire_at_action(unit, unit, defender, -1);
616 acpused += u_acp_to_fire(u2);
617 /* Unlike the case below, target acp consumption has
618 already been dealt with in fire_on_unit, which is
619 called by do_fire_at_action. */
620 }
621 else if (valid(check_attack_action(unit, unit, defender, 100))) {
622 if (!gotonext)
623 one_attack(unit2, defender);
624 if (alive(unit))
625 use_up_acp(unit, uu_acp_to_attack(u2, u3));
626 acpused += uu_acp_to_attack(u2, u3);
627 /* The target of an attack has to take time to defend
628 itself. */
629 if (!gotonext && alive(defender)) {
630 use_up_acp(defender, uu_acp_to_defend(u, u3));
631 }
632 }
633 }
634 if (!alive(unit2))
635 return A_OVERRUN_FAILED;
636 if (in_blocking_zoc(unit2, x, y, z))
637 return A_OVERRUN_FAILED;
638 /* Try to enter the cleared cell now - might still have
639 friendlies filling it up already, so check first. We don't
640 check for type_survives_in_cell, so suicide by deadly
641 terrain remains possible. */
642 if (type_can_occupy_cell(unit2->type, x, y)) {
643 mpcost = move_unit(unit2, x, y);
644 /* Note that we'll say the action succeeded even if
645 the cell did not have enough room for us to actually
646 be in it, which is a little weird. */
647 /* Now add up any extra movement costs of entering the new cell. */
648 mpcost += zoc_move_cost(unit2, ox, oy, oz);
649 acpcost = 0;
650 speed = unit_speed(unit2, x, y);
651 if (speed > 0)
652 acpcost = (mpcost * 100) / speed;
653 acpcost -= acpused;
654 if (acpcost > 0) {
655 /* Take the movement cost out of the moving unit if
656 possible. */
657 use_up_acp(unit2, acpcost);
658 }
659 /* Count the unit as having actually moved. */
660 if (unit2->act)
661 ++(unit2->act->actualmoves);
662 }
663 break;
664 case 1:
665 model_1_attack(unit2, x, y);
666 break;
667 default:
668 break;
669 }
670 return A_OVERRUN_SUCCEEDED;
671 }
672
673 int
check_overrun_action(Unit * unit,Unit * unit2,int x,int y,int z,int n)674 check_overrun_action(Unit *unit, Unit *unit2, int x, int y, int z, int n)
675 {
676 int u, u2, u2x, u2y, u2z, u3, totcost, speed, mpavail, m;
677 Unit *defender;
678
679 if (!in_play(unit))
680 return A_ANY_ERROR;
681 if (!in_play(unit2))
682 return A_ANY_ERROR;
683 if (!inside_area(x, y))
684 return A_ANY_ERROR;
685 if (n == 0)
686 return A_ANY_ERROR;
687 u = unit->type;
688 u2 = unit2->type;
689 if (!has_enough_acp(unit, 1))
690 return A_ANY_NO_ACP;
691 /* Check whether we can attack from inside a transport. */
692 if (unit2->transport && uu_occ_combat(u2, unit2->transport->type) == 0)
693 return A_ANY_OCC_CANNOT_DO;
694 u2x = unit2->x; u2y = unit2->y; u2z = unit2->z;
695 /* We have to be in the same cell or an adjacent one. */
696 if (!between(0, distance(u2x, u2y, x, y), 1))
697 return A_ANY_TOO_FAR;
698 /* Now start looking at the move costs. */
699 u3 = (unit2->transport ? unit2->transport->type : NONUTYPE);
700 totcost = total_move_cost(u2, u3, u2x, u2y, u2z, x, y, u2z);
701 speed = unit_speed(unit2, x, y);
702 mpavail = (unit->act->acp * speed) / 100;
703 /* Zero mp always disallows movement, unless intra-cell. */
704 if (mpavail <= 0 && !(u2x == x && u2y == y && u2z == u2z))
705 return A_MOVE_NO_MP;
706 /* The free mp might get us enough moves, so add it before comparing. */
707 if (mpavail + u_free_mp(u2) < totcost)
708 return A_MOVE_NO_MP;
709 /* We have to have a minimum level of supply to be able to attack. */
710 for_all_material_types(m) {
711 if (unit2->supply[m] < um_to_attack(u2, m))
712 return A_ANY_NO_MATERIAL;
713 }
714 switch(g_combat_model()) {
715 case 0:
716 for_all_stack(x, y, defender) {
717 /* If we can't attack the unit, then we can't possibly overrun. */
718 if (uu_acp_to_attack(u2, defender->type) == 0)
719 return A_OVERRUN_CANNOT_HIT;
720 /* (should test if units here can be attacked en masse) */
721 /* (should prorate ammo needs by intensity of overrun) */
722 if (!enough_ammo_to_attack_unit(unit2, defender))
723 return A_ANY_NO_AMMO;
724 }
725 break;
726 case 1:
727 /* Assume we can always attempt to overrun */
728 break;
729 default:
730 return A_ANY_ERROR;
731 }
732 return A_ANY_OK;
733 }
734
735 /* Return true if the attacker defeated the defender, and can therefore
736 try to move into the defender's old position. */
737
738 static int
one_attack(Unit * atker,Unit * defender)739 one_attack(Unit *atker, Unit *defender)
740 {
741 int ua = atker->type, ud = defender->type;
742 int ax = atker->x, ay = atker->y, ox = defender->x, oy = defender->y;
743 int counter;
744 Side *as = atker->side, *os = defender->side;
745 SideMask observers;
746
747 amain = atker; omain = defender;
748 attack_unit(atker, defender);
749 /* Do a counterattack if appropriate. We must be able to counterattack,
750 and have enough acp to do so. */
751 counter = uu_counterattack(ua, ud);
752 if (counter > 0
753 && has_enough_acp(defender, uu_acp_to_defend(ua, ud))
754 && alive(atker)) {
755 /* (should use value to set strength/commitment of counterattack) */
756 attack_unit(defender, atker);
757 }
758 if (0 /* recording attacks */) {
759 observers = add_side_to_set(atker->side, NOSIDES);
760 observers = add_side_to_set(defender->side, observers);
761 /* (should let other watching sides see event also) */
762 record_event(H_UNIT_ASSAULTED, observers, atker->id, defender->id,
763 ox, oy);
764 }
765 reckon_damage(FALSE);
766 #if (0)
767 see_exact(as, ax, ay);
768 see_exact(as, ox, oy);
769 see_exact(os, ax, ay);
770 see_exact(os, ox, oy);
771 #else
772 see_cell(as, ax, ay);
773 see_cell(as, ox, oy);
774 see_cell(os, ax, ay);
775 see_cell(os, ox, oy);
776 #endif
777 update_cell_display(as, ax, ay, UPDATE_ALWAYS);
778 update_cell_display(as, ox, oy, UPDATE_ALWAYS);
779 update_cell_display(os, ax, ay, UPDATE_ALWAYS);
780 update_cell_display(os, ox, oy, UPDATE_ALWAYS);
781 all_see_cell(ax, ay);
782 all_see_cell(ox, oy);
783 if (!alive(atker))
784 return FALSE;
785 attempt_to_capture_unit(atker, defender);
786 /* If the defender was not captured, it might turn the tables! */
787 /* (Note that we cannot cache the attacker's and defender's types,
788 because the type might have changed due to damage, and we want
789 to know if the new type might countercapture.) */
790 if (alive(defender)
791 && alive(atker)
792 && defender->side == os
793 && uu_countercapture(atker->type, defender->type) > 0) {
794 attempt_to_capture_unit(defender, atker);
795 }
796 return (alive(atker) && unit_at(ox, oy) == NULL);
797 }
798
799 /* Implement "combat model 1", which emulates Civ-type games. Combat
800 is multi-round, ending in the demise of one or the other combatant,
801 plus anything stacked with it. */
802
803 static void
model_1_attack(Unit * unit,int x,int y)804 model_1_attack(Unit *unit, int x, int y)
805 {
806 int u = unit->type, u2, u3, d, maxdef, def, att, winround, limit, dmg;
807 Obj *choice, *chtype, *chutype, *chside;
808 Unit *unit2, *defender, *occ, *defocc, *victim;
809 char *hitmovietype;
810 Side *side3;
811 SideMask sidemask;
812 int weightstotal = 0;
813
814 for_all_stack(x, y, unit2) {
815 u2 = unit2->type;
816 if (u_encounter_result(u2) != lispnil) {
817 /* The encounter result overrides any other outcome. */
818 choice = choose_from_weighted_list(u_encounter_result(u2),
819 &weightstotal, FALSE);
820 if (is_quoted_lisp(choice))
821 choice = eval(choice);
822 if (consp(choice))
823 chtype = car(choice);
824 else
825 chtype = choice;
826 if (match_keyword(chtype, K_UNIT)) {
827 /* Dig out a unit type spec. */
828 chutype = cadr(choice);
829 if (consp(chutype)) {
830 chside = cadr(chutype);
831 chutype = car(chutype);
832 } else {
833 chside = lispnil;
834 }
835 u3 = utype_from_name(c_string(chutype));
836 /* Pick out a side if one was specified. */
837 if (numberp(chside)) {
838 side3 = side_n(c_number(chside));
839 } else {
840 side3 = unit->side;
841 }
842 /* Change the unit's type. */
843 change_unit_type(unit2, u3, H_UNIT_TYPE_CHANGED, side3);
844 /* Changing the type will kill the unit if there is no room
845 for the new type. */
846 if (alive(unit2)) {
847 unit2->hp = unit2->hp2 = u_hp(u3);
848 /* We still need to explicitly change the unit side,
849 if the new type is allowed on the same side as
850 the old type. The 'newside' argument to
851 'change_unit_type' is only used when the new type
852 cannot be on the same side as the old type. */
853 change_unit_side(unit2, side3, -1, unit);
854 /* The unit is changing side voluntarily, so set the
855 original side also. */
856 set_unit_origside(unit2, unit2->side);
857 }
858 } else if (match_keyword(chtype, K_VANISH)) {
859 kill_unit(unit2, H_UNIT_VANISHED);
860 } else {
861 run_warning("Unknown encounter type %s", c_string(chtype));
862 }
863 if (consp(choice) && stringp(caddr(choice))) {
864 notify(unit->side, "%s", c_string(caddr(choice)));
865 }
866 return;
867 }
868 }
869 /* Non-combat units can't do anything. Note: this means that unarmed
870 units (settlers etc) cannot capture empty cities, since this happens by
871 overrun action. Should perhaps allow to proceed if attacking an empty
872 advanced type? However, in that case capture_chance must be changed,
873 since it right now disallows capture by unarmed units. */
874 if (u_attack(u) == 0)
875 return;
876 /* Choose the defender of a stack. */
877 maxdef = -1;
878 defender = NULL;
879 for_all_stack(x, y, unit2) {
880 /* Don't attack any of our buddies. */
881 if (unit_trusts_unit(unit, unit2))
882 continue;
883 /* Identify the best defender. */
884 def = real_defense_value(unit2);
885 if (def > maxdef) {
886 maxdef = def;
887 defender = unit2;
888 }
889 }
890 /* If the location is empty for some reason, escape quietly. */
891 if (defender == NULL)
892 return;
893 /* For cities, find a defender. */
894 if (u_advanced(defender->type)) {
895 /* We don't want defenseless occs (facilities) to count as defenders,
896 * so initialize maxdefs to 0 instead of -1. */
897 maxdef = 0;
898 defocc = NULL;
899 for_all_occupants(defender, occ) {
900 u2 = occ->type;
901 def = u_defend(u2);
902 if (def > maxdef) {
903 maxdef = def;
904 defocc = occ;
905 }
906 }
907 /* Cities with no defenders can be captured directly. */
908 if (defocc == NULL) {
909 attempt_to_capture_unit(unit, defender);
910 return;
911 }
912 defender = defocc;
913 }
914 d = defender->type;
915 limit = 100;
916 while (alive(unit) && alive(defender) && limit-- > 0) {
917 att = real_attack_value(unit);
918 def = real_defense_value(defender);
919 winround = probability((att * 100) / (att + def));
920 if (winround) {
921 dmg = roll_dice(uu_damage(u, d));
922 victim = defender;
923 defender->hp2 -= dmg;
924 } else {
925 dmg = roll_dice(uu_damage(d, u));
926 victim = unit;
927 unit->hp2 -= dmg;
928 }
929 if (defender->hp2 <= 0) {
930 report_combat(unit, defender, "destroy");
931 } else if (unit->hp2 <= 0) {
932 report_combat(defender, unit, "destroy");
933 }
934 /* Pick the hit movie to show. */
935 hitmovietype = (char *)((victim->hp2 <= 0) ? "death" : "hit-short");
936 /* Make up the list of sides that will see it. */
937 sidemask = NOSIDES;
938 for_all_sides(side3) {
939 /* Let all sides that can see the attacker's cell see it. */
940 if (units_visible(side3, victim->x, victim->y))
941 sidemask = add_side_to_set(side3, sidemask);
942 }
943 /* Show the movie. */
944 for_all_sides(side3) {
945 if (side_in_set(side3, sidemask))
946 schedule_movie(side3, hitmovietype, victim->id);
947 }
948 play_movies(ALLSIDES);
949 damage_unit(unit, combat_dmg, defender);
950 damage_unit(defender, combat_dmg, unit);
951 }
952 /* The whole stack dies if its defender dies. */
953 if (!alive(defender) && defender->transport == NULL) {
954 for_all_stack(x, y, unit2) {
955 if (!u_advanced(unit2->type)) {
956 /* (should except spies etc?) */
957 unit2->hp2 = 0;
958 report_combat(unit, unit2, "destroy");
959 }
960 }
961 reckon_damage_here(x, y);
962 /* But capture cities instead of destroying them. */
963 for_all_stack(x, y, unit2) {
964 if (u_advanced(unit2->type)) {
965 attempt_to_capture_unit(unit, unit2);
966 }
967 }
968 /* Occupants of city may be damaged. */
969 reckon_damage_here(x, y);
970 }
971 }
972
973 /* The real attack value of a unit after adjustments for combat exp,
974 terrain effects, occupants etc. */
975
976 int
real_attack_value(Unit * unit)977 real_attack_value(Unit *unit)
978 {
979 Unit *unit2;
980 int t, u, att, effect;
981
982 u = unit->type;
983 att = u_attack(u);
984 /* First factor in combat experience. */
985 if (u_cxp_max(u) > 0 && unit->cxp > 0) {
986 effect = u_full_cxp_affects_attack(u);
987 if (effect != 100) {
988 att *= (100 + (effect - 100) * unit->cxp / u_cxp_max(u)) / 100;
989 }
990 }
991 /* Then factor in terrain effects. */
992 t = terrain_at(unit->x, unit->y);
993 effect = ut_terrain_affects_attack(u, t);
994 if (effect != 100) {
995 att *= effect / 100;
996 }
997 /* Then factor in effect of transport. */
998 if (unit->transport) {
999 if (is_active(unit->transport)) {
1000 effect = uu_transport_affects_attack(unit->transport->type, u);
1001 if (effect != 100) {
1002 att *= effect / 100;
1003 }
1004 }
1005 }
1006 /* Then factor in effect of occupants. */
1007 for_all_occupants(unit, unit2) {
1008 if (is_active(unit2)) {
1009 effect = uu_occ_affects_attack(unit2->type, u);
1010 if (effect != 100) {
1011 att *= effect / 100;
1012 }
1013 }
1014 }
1015 /* Then factor in effect of friendly units in the same cell. */
1016 for_all_stack_with_occs(unit->x, unit->y, unit2) {
1017 if (is_active(unit2)
1018 && trusted_side(unit2->side, unit->side)) {
1019 effect = uu_neighbour_affects_attack(unit2->type, u);
1020 if (effect != 100) {
1021 att *= effect / 100;
1022 }
1023 }
1024 }
1025 return att;
1026 }
1027
1028
1029 /* The real defense value of a unit after adjustments for combat exp,
1030 terrain effects, occupants etc. */
1031
1032 int
real_defense_value(Unit * unit)1033 real_defense_value(Unit *unit)
1034 {
1035 Unit *unit2;
1036 int t, u, def, effect;
1037
1038 u = unit->type;
1039 def = u_defend(u);
1040 /* First factor in combat experience. */
1041 if (u_cxp_max(u) > 0 && unit->cxp > 0) {
1042 effect = u_full_cxp_affects_defense(u);
1043 if (effect != 100) {
1044 def *= (100 + (effect - 100) * unit->cxp / u_cxp_max(u)) / 100;
1045 }
1046 }
1047 /* Then factor in terrain effects. */
1048 t = terrain_at(unit->x, unit->y);
1049 effect = ut_terrain_affects_defense(u, t);
1050 if (effect != 100) {
1051 def *= effect / 100;
1052 }
1053 /* Then factor in effect of transport. */
1054 if (unit->transport) {
1055 if (is_active(unit->transport)) {
1056 effect = uu_transport_affects_defense(unit->transport->type, u);
1057 if (effect != 100) {
1058 def *= effect / 100;
1059 }
1060 }
1061 }
1062 /* Then factor in effect of occupants. */
1063 for_all_occupants(unit, unit2) {
1064 if (is_active(unit2)) {
1065 effect = uu_occ_affects_defense(unit2->type, u);
1066 if (effect != 100) {
1067 def *= effect / 100;
1068 }
1069 }
1070 }
1071 /* Then factor in effect of friendly units in the same cell. */
1072 for_all_stack_with_occs(unit->x, unit->y, unit2) {
1073 if (is_active(unit2)
1074 && trusted_side(unit2->side, unit->side)) {
1075 effect = uu_neighbour_affects_defense(unit2->type, u);
1076 if (effect != 100) {
1077 def *= effect / 100;
1078 }
1079 }
1080 }
1081 return def;
1082 }
1083
1084
1085 /* Fire-at action. */
1086
1087 /* Shooting at a given unit. */
1088
1089 int
prep_fire_at_action(Unit * unit,Unit * unit2,Unit * unit3,int m)1090 prep_fire_at_action(Unit *unit, Unit *unit2, Unit *unit3, int m)
1091 {
1092 if (unit == NULL || unit->act == NULL || unit2 == NULL || unit3 == NULL)
1093 return FALSE;
1094 unit->act->nextaction.type = ACTION_FIRE_AT;
1095 unit->act->nextaction.args[0] = unit3->id;
1096 unit->act->nextaction.args[1] = m;
1097 unit->act->nextaction.actee = unit2->id;
1098 return TRUE;
1099 }
1100
1101 int
do_fire_at_action(Unit * unit,Unit * unit2,Unit * unit3,int m)1102 do_fire_at_action(Unit *unit, Unit *unit2, Unit *unit3, int m)
1103 {
1104 int ux = unit->x, uy = unit->y, ox, oy, oz;
1105 SideMask sidemask;
1106 Side *side;
1107
1108 action_point(unit2->side, unit3->x, unit3->y);
1109 action_point(unit3->side, unit3->x, unit3->y);
1110 /* Make up the list of sides that will see the fire. */
1111 sidemask = NOSIDES;
1112 for_all_sides(side) {
1113 /* Let all sides that can see the attacker's cell see it. */
1114 if (units_visible(side, unit2->x, unit2->y))
1115 sidemask = add_side_to_set(side, sidemask);
1116 /* Let all sides that can see the unit3's cell see it. */
1117 if (units_visible(side, unit3->x, unit3->y))
1118 sidemask = add_side_to_set(side, sidemask);
1119 }
1120 /* Show the fire. */
1121 for_all_sides(side) {
1122 if (side_in_set(side, sidemask))
1123 update_fire_at_display(side, unit, unit3, m, TRUE);
1124 }
1125 ox = unit3->x; oy = unit3->y; oz = unit3->z;
1126 amain = unit; omain = unit3;
1127 fire_on_unit(unit, unit3);
1128 reckon_damage(TRUE);
1129 if (alive(unit))
1130 use_up_acp(unit, u_acp_to_fire(unit2->type));
1131 /* if (alive(unit3)) use_up_acp(unit3, 1); */
1132 /* Each side sees what happened to its own unit. */
1133 update_unit_display(unit2->side, unit2, TRUE);
1134 if (unit != unit2)
1135 update_unit_display(unit->side, unit, TRUE);
1136 update_unit_display(unit3->side, unit3, TRUE);
1137 /* The attacking side also sees the remote cell. */
1138 update_cell_display(unit2->side, ox, oy, UPDATE_ALWAYS);
1139 update_cell_display(unit3->side, ox, oy, UPDATE_ALWAYS);
1140 /* Victim might see something in attacker's cell. */
1141 update_cell_display(unit3->side, ux, uy, UPDATE_ALWAYS);
1142 /* Actually, everybody might be seeing the combat. */
1143 all_see_cell(ux, uy);
1144 all_see_cell(ox, oy);
1145 /* Permit attempts to capture after fire, but only from adjacent
1146 or same cell. */
1147 if (distance(unit2->x, unit2->y, unit3->x, unit3->y) < 2) {
1148 Side *os = unit3->side;
1149
1150 attempt_to_capture_unit(unit2, unit3);
1151 /* If the unit3 was not captured, it might turn the tables! */
1152 /* (Note that we cannot cache the attacker's and unit3's
1153 types, because the type might have changed due to damage,
1154 and we want to know if the new type might countercapture.) */
1155 if (alive(unit3)
1156 && alive(unit2)
1157 && unit3->side == os
1158 && uu_countercapture(unit2->type, unit3->type) > 0) {
1159 attempt_to_capture_unit(unit3, unit2);
1160 }
1161 }
1162 /* Always expend the ammo (but only if m is a valid mtype). */
1163 return A_ANY_DONE;
1164 }
1165
1166 /* Test a fire action for plausibility. */
1167
1168 int
check_fire_at_action(Unit * unit,Unit * unit2,Unit * unit3,int m)1169 check_fire_at_action(Unit *unit, Unit *unit2, Unit *unit3, int m)
1170 {
1171 int u, u2, u3, ux, uy, uz, dist;
1172 int rslt = A_ANY_OK;
1173
1174 if (!valid(rslt = can_fire(unit, unit2)))
1175 return rslt;
1176 if (!in_play(unit3))
1177 return A_ANY_ERROR;
1178 /* We can't attack ourselves. */
1179 if (unit2 == unit3)
1180 return A_ANY_ERROR;
1181 u = unit->type; u2 = unit2->type; u3 = unit3->type;
1182 ux = unit->x; uy = unit->y; uz = unit->z;
1183 /* Check that target is in range. */
1184 dist = distance(ux, uy, unit3->x, unit3->y);
1185 if (dist > u_range(u2))
1186 return A_ANY_TOO_FAR;
1187 if (dist < u_range_min(u2))
1188 return A_ANY_TOO_NEAR;
1189 if (!indep(unit2) && unit2->side == unit3->side)
1190 return A_ANY_ERROR;
1191 /* Attackers won't fire at anything they know they can't hit. */
1192 if (fire_hit_chance(u2, u3) <= 0)
1193 return A_FIRE_CANNOT_HIT;
1194 /* Check intervening elevations. */
1195 if (found_blocking_elevation(u2, ux, uy, uz, u3,
1196 unit3->x, unit3->y, unit3->z))
1197 return A_FIRE_BLOCKED;
1198 /* Check for enough of right kind of ammo
1199 (this feature is not used anywhere in the code.
1200 (-1) is always passed as m). */
1201 if (is_material_type(m)) {
1202 if (unit->supply[m] == 0) {
1203 return A_ANY_NO_AMMO;
1204 }
1205 } else {
1206 if (!enough_ammo_to_fire_at_unit(unit2, unit3))
1207 return A_ANY_NO_AMMO;
1208 }
1209 return A_ANY_OK;
1210 }
1211
1212 /* Fire-into action. */
1213
1214 /* Shooting at a given location. */
1215
1216 int
prep_fire_into_action(Unit * unit,Unit * unit2,int x,int y,int z,int m)1217 prep_fire_into_action(Unit *unit, Unit *unit2, int x, int y, int z, int m)
1218 {
1219 if (unit == NULL || unit->act == NULL || unit2 == NULL)
1220 return FALSE;
1221 unit->act->nextaction.type = ACTION_FIRE_INTO;
1222 unit->act->nextaction.args[0] = x;
1223 unit->act->nextaction.args[1] = y;
1224 unit->act->nextaction.args[2] = z;
1225 unit->act->nextaction.args[3] = m;
1226 unit->act->nextaction.actee = unit2->id;
1227 return TRUE;
1228 }
1229
1230 /* One can always shoot, if the cell is visible, but there might not
1231 not be anything to hit! No counterattacks when shooting, and the
1232 results might not be visible to the shooter. */
1233
1234 int
do_fire_into_action(Unit * unit,Unit * unit2,int x,int y,int z,int m)1235 do_fire_into_action(Unit *unit, Unit *unit2, int x, int y, int z, int m)
1236 {
1237 int ux = unit->x, uy = unit->y, ox, oy, oz;
1238 SideMask sidemask;
1239 Unit *other;
1240 Side *side;
1241
1242 /* Make up the list of sides that will see the fire. */
1243 sidemask = NOSIDES;
1244 for_all_sides(side) {
1245 /* Let all sides that can see the attacker's cell see it. */
1246 if (units_visible(side, unit2->x, unit2->y))
1247 sidemask = add_side_to_set(side, sidemask);
1248 /* Let all sides that can see the fired-into cell see it. */
1249 if (units_visible(side, x, y))
1250 sidemask = add_side_to_set(side, sidemask);
1251 }
1252 /* Show the fire. */
1253 for_all_sides(side) {
1254 if (side_in_set(side, sidemask))
1255 update_fire_into_display(side, unit2, x, y, z, m, TRUE);
1256 }
1257 /* If any units at target, hit them. */
1258 for_all_stack(x, y, other) {
1259 ox = other->x; oy = other->y; oz = other->z;
1260 amain = unit; omain = other;
1261 fire_on_unit(unit2, other);
1262 reckon_damage(TRUE);
1263 /* Each side sees what happened to its unit that is being hit. */
1264 update_unit_display(other->side, other, TRUE);
1265 /* The attacking side also sees the remote cell. */
1266 update_cell_display(unit->side, ox, oy, UPDATE_ALWAYS);
1267 update_cell_display(other->side, ox, oy, UPDATE_ALWAYS);
1268 /* Victim might see something in attacker's cell. */
1269 update_cell_display(other->side, ux, uy, UPDATE_ALWAYS);
1270 /* Actually, everybody might be seeing the combat. */
1271 all_see_cell(ux, uy);
1272 all_see_cell(ox, oy);
1273 /* don't take moves though! */
1274 }
1275 /* Firing side gets just one update. */
1276 update_unit_display(unit2->side, unit2, TRUE);
1277 if (unit != unit2)
1278 update_unit_display(unit->side, unit, TRUE);
1279 if (alive(unit))
1280 use_up_acp(unit, u_acp_to_fire(unit2->type));
1281 /* Always expend the ammo (but only if m is a valid material). */
1282 /* We're always "successful", even though the bombardment may have
1283 had little or no actual effect. */
1284 return A_ANY_DONE;
1285 }
1286
1287 /* Test a shoot action for plausibility. */
1288
1289 int
check_fire_into_action(Unit * unit,Unit * unit2,int x,int y,int z,int m)1290 check_fire_into_action(Unit *unit, Unit *unit2, int x, int y, int z, int m)
1291 {
1292 int u, u2, u2x, u2y, u2z, dist;
1293 int rslt = A_ANY_OK;
1294
1295 if (!valid(rslt = can_fire(unit, unit2)))
1296 return rslt;
1297 u2x = unit2->x; u2y = unit2->y; u2z = unit2->z;
1298 /* Check that target location is meaningful. */
1299 if (!inside_area(x, y))
1300 return A_FIRE_INTO_OUTSIDE_WORLD;
1301 u = unit->type;
1302 u2 = unit2->type;
1303 /* Check that target is in range. */
1304 dist = distance(u2x, u2y, x, y);
1305 if (dist > u_range(u2))
1306 return A_ANY_TOO_FAR;
1307 if (dist < u_range_min(u2))
1308 return A_ANY_TOO_NEAR;
1309 /* Check intervening elevations and terrain. */
1310 if (found_blocking_elevation(u2, u2x, u2y, u2z, NONUTYPE, x, y, z))
1311 return A_FIRE_BLOCKED;
1312 /* Check for enough of right kind of ammo. */
1313 if (is_material_type(m)) {
1314 if (unit->supply[m] == 0)
1315 return A_ANY_NO_AMMO;
1316 } else {
1317 if (!enough_ammo_to_fire_one_round(unit2))
1318 return A_ANY_NO_AMMO;
1319 }
1320 return A_ANY_OK;
1321 }
1322
1323 static int tmpx0, tmpy0, tmpe0, tmpe1, tmpdist01, cellwid, fangle;
1324
1325 static int elevation_blocks(int x, int y);
1326
1327 static int
elevation_blocks(int x,int y)1328 elevation_blocks(int x, int y)
1329 {
1330 int interpol, celldist0, dist0, el;
1331
1332 celldist0 = distance(tmpx0, tmpy0, x, y);
1333 dist0 = celldist0 * cellwid;
1334 interpol = (tmpe1 - tmpe0) - (fangle * tmpdist01) / 100;
1335 /* The following is * dist0^2 / tmpdist01^2, but arranged to avoid
1336 overflow. */
1337 interpol *= dist0;
1338 interpol /= tmpdist01;
1339 interpol *= dist0;
1340 interpol /= tmpdist01;
1341 interpol += (fangle * dist0) / 100;
1342 interpol += tmpe0;
1343 el = (elevations_defined() ? elev_at(x, y) : 0);
1344 el += t_thickness(terrain_at(x, y));
1345 Dprintf("Arc at %d: %d, elev %d\n", celldist0, interpol, el);
1346 return (el > interpol);
1347 }
1348
1349 int
found_blocking_elevation(int u,int ux,int uy,int uz,int u2,int u2x,int u2y,int u2z)1350 found_blocking_elevation(int u, int ux, int uy, int uz,
1351 int u2, int u2x, int u2y, int u2z)
1352 {
1353 int t, weaponheight, bodyheight, rslt, x1, y1;
1354
1355 /* Note that a flat world with no elevation defined could
1356 still have thick terrain that screens. */
1357 /* (should detect absence of thick terrain and return immed) */
1358 /* Adjacent cells can't be screened by elevation. */
1359 /* (should accommodate possibility that target is at top of
1360 cliff in adj and back away from its edge, thus screened;
1361 need to recog cliffs vs slopes in terrain) */
1362 if (distance(ux, uy, u2x, u2y) <= 1)
1363 return FALSE;
1364 tmpx0 = ux; tmpy0 = uy;
1365 t = terrain_at(ux, uy);
1366 tmpe0 = (elevations_defined() ? elev_at(ux, uy) : 0);
1367 weaponheight = ut_weapon_height(u, t);
1368 tmpe0 += weaponheight;
1369 tmpe1 = (elevations_defined() ? elev_at(u2x, u2y) : 0);
1370 bodyheight = ut_body_height(u, t);
1371 tmpe1 += bodyheight;
1372 cellwid = area.cellwidth;
1373 if (cellwid <= 0)
1374 cellwid = 1;
1375 fangle = u_fire_angle_max(u);
1376 tmpdist01 = distance(ux, uy, u2x, u2y) * cellwid;
1377 Dprintf("Checking arc: %d,%d el %d -> %d,%d el %d\n", ux, uy, tmpe0, u2x, u2y, tmpe1);
1378 rslt = search_straight_line(ux, uy, u2x, u2y, elevation_blocks, &x1, &y1);
1379 if (rslt)
1380 Dprintf("blocked at %d,%d\n", x1, y1);
1381 return rslt;
1382 }
1383
1384 static void
fire_on_unit(Unit * atker,Unit * other)1385 fire_on_unit(Unit *atker, Unit *other)
1386 {
1387 int m, dist = distance(atker->x, atker->y, other->x, other->y), consump;
1388 int a = NONUTYPE, o = NONUTYPE;
1389
1390 if (alive(atker) && alive(other)) {
1391 a = atker->type; o = other->type;
1392 if (enough_ammo_to_fire_at_unit(atker, other)) {
1393 maybe_hit_unit(atker, other, TRUE, (dist > u_hit_falloff_range(a)));
1394 if (alive(atker)) {
1395 for_all_material_types(m) {
1396 consump = um_hit_by(o, m);
1397 if (consump > 0) {
1398 consump *= um_consumption_per_fire(a, m);
1399 if (consump < 0)
1400 consump =
1401 um_hit_by(o, m) * um_consumption_per_attack(a, m);
1402 atker->supply[m] -= consump;
1403 }
1404 }
1405 }
1406 /* The *victim* can lose acp. */
1407 if (alive(other))
1408 use_up_acp(other, uu_acp_to_be_fired_on(o, atker->type));
1409 }
1410 }
1411 /* (should ping victim to see if it wants to respond to the attack?) */
1412 }
1413
1414 /* Test to see if enough ammo is available to make the attack. Need
1415 enough of *all* types - semi-bogus but too complicated otherwise? */
1416
1417 int
enough_ammo_to_attack_unit(Unit * unit,Unit * other)1418 enough_ammo_to_attack_unit(Unit *unit, Unit *other)
1419 {
1420 int m, hitby;
1421
1422 for_all_material_types(m) {
1423 hitby = um_hit_by(other->type, m);
1424 if (0 < hitby)
1425 if (unit->supply[m] < \
1426 hitby * um_consumption_per_attack(unit->type, m))
1427 return FALSE;
1428 }
1429 return TRUE;
1430 }
1431
1432 int
enough_ammo_to_fire_at_unit(Unit * unit,Unit * other)1433 enough_ammo_to_fire_at_unit(Unit *unit, Unit *other)
1434 {
1435 int m, hitby, consump;
1436
1437 for_all_material_types(m) {
1438 hitby = um_hit_by(other->type, m);
1439 if (0 < hitby) {
1440 consump = hitby * um_consumption_per_fire(unit->type, m);
1441 if (0 > consump)
1442 consump = hitby * um_consumption_per_attack(unit->type, m);
1443 if (unit->supply[m] < consump)
1444 return FALSE;
1445 }
1446 }
1447 return TRUE;
1448 }
1449
1450 /* Since we don't know what we are firing at, we test if we have enough of
1451 all ammo types required to fire at any target that we can hit. */
1452
1453 int
enough_ammo_to_fire_one_round(Unit * unit)1454 enough_ammo_to_fire_one_round(Unit *unit)
1455 {
1456 int m;
1457
1458 for_all_material_types(m) {
1459 if (unit->supply[m] < um_consumption_per_fire(unit->type, m))
1460 return FALSE;
1461 }
1462 return TRUE;
1463 }
1464
1465 /* Single attack, no counterattack. Check and use ammo - usage
1466 independent of outcome, but types used depend on unit types
1467 involved. */
1468
1469 static void
attack_unit(Unit * atker,Unit * other)1470 attack_unit(Unit *atker, Unit *other)
1471 {
1472 int m, hitby;
1473 int a = NONUTYPE, o = NONUTYPE;
1474
1475 if (alive(atker) && alive(other)) {
1476 a = atker->type; o = other->type;
1477 if (enough_ammo_to_attack_unit(atker, other)) {
1478 maybe_hit_unit(atker, other, FALSE, FALSE);
1479 if (alive(atker)) {
1480 for_all_material_types(m) {
1481 hitby = um_hit_by(o, m);
1482 if (hitby > 0) {
1483 atker->supply[m] -=
1484 um_consumption_per_attack(a, m) * hitby;
1485 }
1486 }
1487 }
1488 }
1489 }
1490 /* (should ping victim to see if it wants to respond to the attack?) */
1491 }
1492
1493 /* Make a single hit and maybe hit some passengers also. Power of hit
1494 is constant, but chance is affected by terrain, quality, and
1495 occupants' protective abilities. If a hit is successful, it may
1496 have consequences on the defender's occupants, but limited by the
1497 protection that the transport provides. */
1498
1499 static void
maybe_hit_unit(Unit * atker,Unit * other,int fire,int fallsoff)1500 maybe_hit_unit(Unit *atker, Unit *other, int fire, int fallsoff)
1501 {
1502 int chance, t, hit = 0, a = atker->type, o = other->type;
1503 int teffect, cxpeffect, cxpmax, effect, prot;
1504 int dist = 0, distdiff = 0, rangefoff = 0, rangemax = 0, hitrdist = 0,
1505 hitrfoff = 0, hitrmax = 0;
1506 int retrchance = 0;
1507 int dmgspec;
1508 char *hitmovietype;
1509 Unit *occ, *unit2;
1510 Side *side3;
1511 SideMask sidemask;
1512
1513 Dprintf("%s tries to hit %s", unit_desig(atker), unit_desig(other));
1514 if (fire)
1515 chance = fire_hit_chance(a, o);
1516 else
1517 chance = uu_hit(a, o);
1518 /* Combat experience tends to raise the hit chance, so do that
1519 first, reduces roundoff weirdnesses later. */
1520 cxpmax = u_cxp_max(a);
1521 if (cxpmax > 0 && atker->cxp > 0) {
1522 cxpeffect = uu_hit_cxp(a, o);
1523 if (cxpeffect != 100) {
1524 effect = 100 + (atker->cxp * (cxpeffect - 100)) / cxpmax;
1525 chance = (chance * effect) / 100;
1526 }
1527 }
1528 /* (should modify due to cxp of defender too) */
1529 /* Account for terrain effects. */
1530 t = terrain_at(atker->x, atker->y);
1531 if (fire && ut_fire_attack_terrain_effect(a, t) != -1)
1532 teffect = ut_fire_attack_terrain_effect(a, t);
1533 else
1534 teffect = ut_attack_terrain_effect(a, t);
1535 chance = (chance * teffect) / 100;
1536 t = terrain_at(other->x, other->y);
1537 if (fire && ut_fire_defend_terrain_effect(o, t) != -1)
1538 teffect = ut_fire_defend_terrain_effect(o, t);
1539 else
1540 teffect = ut_defend_terrain_effect(o, t);
1541 chance = (chance * teffect) / 100;
1542 /* Also account for aux terrain effects. */
1543 /* (should also account for direction of attack in the case of
1544 borders, and possibly connectors as well) */
1545 if (any_aux_terrain_defined()) {
1546 for_all_aux_terrain_types(t) {
1547 if (aux_terrain_defined(t)) {
1548 if (aux_terrain_at(atker->x, atker->y, t)) {
1549 if (fire && ut_fire_attack_terrain_effect(a, t) != -1)
1550 teffect = ut_fire_attack_terrain_effect(a, t);
1551 else
1552 teffect = ut_attack_terrain_effect(a, t);
1553 chance = (chance * teffect) / 100;
1554 }
1555 if (aux_terrain_at(other->x, other->y, t)) {
1556 if (fire && ut_fire_defend_terrain_effect(o, t) != -1)
1557 teffect = ut_fire_defend_terrain_effect(o, t);
1558 else
1559 teffect = ut_defend_terrain_effect(o, t);
1560 chance = (chance * teffect) / 100;
1561 }
1562 }
1563 }
1564 }
1565 /* Account for protection by occupants. */
1566 for_all_occupants(other, occ) {
1567 if (in_play(occ) && completed(occ)) {
1568 prot = uu_protection(occ->type, o);
1569 if (prot != 100)
1570 chance = (chance * prot) / 100;
1571 }
1572 }
1573 /* Account for protection by neighbours. */
1574 for_all_stack(other->x, other->y, unit2) {
1575 if (unit2 != other
1576 && in_play(unit2)
1577 && completed(unit2)
1578 && unit2->side == other->side) {
1579 prot = uu_stack_protection(unit2->type, o);
1580 if (prot != 100)
1581 chance = (chance * prot) / 100;
1582 }
1583 }
1584 /* Note that this code differs from that above in that it treats
1585 all units in the same cell equally, whether occs, suboccs or
1586 cellmates. This also means that one occ can protect another occ
1587 in the same transport. Moreover, the protective unit also
1588 protects itself. These rules simulate real situations such as
1589 when triple-A protects all nearby units (including itself)
1590 against bombers, or when a city wall protects all other
1591 occupants against ground attack. */
1592 for_all_stack_with_occs(other->x, other->y, unit2) {
1593 if (is_active(unit2)
1594 /* We also extend protection to our buddies! */
1595 && trusted_side(unit2->side, other->side)) {
1596 /* This is when a unit, such as triple-A, extends unique
1597 protection against a specific attacker, such as
1598 bombers, to all other units in the cell. */
1599 prot = uu_cellwide_protection_against(unit2->type, a);
1600 if (prot != 100)
1601 chance = (chance * prot) / 100;
1602 /* This is when a unit (such as a garrison) specifically
1603 protects a second unit, such as a fort (but not other
1604 nearby units), against all forms of attack. It thus
1605 works the same way as uu_protection and
1606 uu_stack_protection. */
1607 prot = uu_cellwide_protection_for(unit2->type, o);
1608 if (prot != 100)
1609 chance = (chance * prot) / 100;
1610 }
1611 }
1612 /* Determine how distance affects ranged fire.
1613 First, perform a [sanity] check to make sure that we are only
1614 executing this segment if the unit is firing and the effect of
1615 that fire falls off over distance.
1616 Second, calculate distance to target, and the difference,
1617 distdiff, between this and the falloff range.
1618 Third, calculate linear interpolant (slope of falloff effect) by
1619 determining the difference between rangemax and rangefoff and
1620 the difference between hitrmax and hitrfoff.
1621 Finally, interpolate the falloff effect, provided distdiff. */
1622 if (fire && fallsoff) {
1623 dist = distance(atker->x, atker->y, other->x, other->y);
1624 rangefoff = u_hit_falloff_range(a);
1625 distdiff = dist - rangefoff;
1626 if (0 < distdiff) {
1627 rangemax = u_range(a);
1628 hitrfoff = fire_hit_chance(a, o);
1629 hitrmax = (uu_hit_max_range_effect(a, o) * hitrfoff) / 100;
1630 hitrdist =
1631 hitrfoff +
1632 (distdiff * (hitrmax - hitrfoff)) / (rangemax - rangefoff);
1633 Dprintf(", fire distance is %d", dist);
1634 Dprintf(", fire falloff modifier is %d%%", hitrdist);
1635 chance = (chance * hitrdist) / 100;
1636 }
1637 /* else no falloff */
1638 }
1639 Dprintf(", probability of hit is %d%%", chance);
1640 /* Compute the hit itself. */
1641 if (probability(chance)) {
1642 if (fire)
1643 dmgspec = fire_damage(a, o);
1644 else
1645 dmgspec = uu_damage(a, o);
1646 /* Account for attacker's experience. */
1647 if (cxpmax > 0 && atker->cxp > 0) {
1648 cxpeffect = uu_damage_cxp(a, o);
1649 if (cxpeffect != 100) {
1650 effect = 100 + (atker->cxp * (cxpeffect - 100)) / cxpmax;
1651 dmgspec = multiply_dice(dmgspec, effect);
1652 }
1653 }
1654 hit = roll_dice(dmgspec);
1655 }
1656 if (hit > 0) {
1657 Dprintf(", damage will be %d hp", hit);
1658 } else {
1659 Dprintf(", missed");
1660 }
1661 /* (should record a raw statistic?) */
1662 /* Ablation is a chance for occupants or stack to take part of a
1663 hit themselves. */
1664 if (hit > 0) {
1665 /* (should decide how ablation computed) */
1666 }
1667 /* Affect the hitting unit's morale. Note that morale is still reduced
1668 even if the hit becomes 0 because of a successful retreat. */
1669 if (hit > 0) {
1670 change_morale(atker, 1, uu_morale_hit(a, o));
1671 }
1672 if (hit > 0) {
1673 retrchance = uu_retreat_chance(a, o);
1674 /* Chance of a retreat rises to 100% as morale goes to 0. */
1675 if (u_morale_max(o) > 0 && retrchance < 100)
1676 retrchance = 100 - ((100 - retrchance) * other->morale) /
1677 u_morale_max(o);
1678 if (probability(retrchance)) {
1679 if (retreat_unit(other, atker)) {
1680 /* It is possible that the unit died while trying to retreat
1681 into terrain where it vanishes. If so, we don't want to
1682 continue, since calling maybe_hit_unit recursively for an
1683 occupant will crash the game due to stack iteration for
1684 (-1,-1). Nor do we want to report its retreat since its
1685 demise already has been reported. */
1686 if (!alive(other)) {
1687 return;
1688 }
1689 report_combat(atker, other, "retreat");
1690 hit = 0; /* should only be reduced hit, may still be > 0 */
1691 }
1692 }
1693 }
1694 hit_unit(other, hit, atker);
1695 /* Pick the hit movie to show. */
1696 hitmovietype =
1697 (char *)((hit >= other->hp) ? "death" : ((hit > 0) ? "hit" : "miss"));
1698 /* Make up the list of sides that will see it. */
1699 sidemask = NOSIDES;
1700 for_all_sides(side3) {
1701 /* Let all sides that can see the attacker's cell see it. */
1702 if (units_visible(side3, atker->x, atker->y))
1703 sidemask = add_side_to_set(side3, sidemask);
1704 /* Let all sides that can see the defender's cell see it. */
1705 if (units_visible(side3, other->x, other->y))
1706 sidemask = add_side_to_set(side3, sidemask);
1707 }
1708 /* Show the movie. */
1709 for_all_sides(side3) {
1710 if (side_in_set(side3, sidemask))
1711 schedule_movie(side3, hitmovietype, other->id);
1712 }
1713 Dprintf("\n");
1714 /* Recurse into occupants, maybe hit them too. */
1715 /* It would really make sense to check for hit > 0 here and only
1716 hit occupants if the transport was hit. However, this will not
1717 work since some games require that occupants can be hit even
1718 if we miss the transport. */
1719 for_all_occupants(other, occ) {
1720 if (is_active(other)) {
1721 if (probability(uu_protection(o, occ->type)))
1722 maybe_hit_unit(atker, occ, fire, fallsoff);
1723 } else {
1724 /* No protection by incomplete transport. */
1725 maybe_hit_unit(atker, occ, fire, fallsoff);
1726 }
1727 }
1728 /* Some units might detonate automatically upon scoring a melee hit. */
1729 if (!fire && (0 < hit)
1730 && in_play(atker)
1731 && probability(u_detonate_with_attack(atker->type))
1732 && !was_detonated(atker)) {
1733 detonate_unit(atker, atker->x, atker->y, atker->z);
1734 }
1735 /* We get combat experience only if there could have been some damage. */
1736 if (chance > 0) {
1737 if (in_play(atker) && (atker->cxp < u_cxp_max(a)))
1738 atker->cxp += uu_cxp_per_combat(a, o);
1739 if (in_play(other) && (other->cxp < u_cxp_max(o)))
1740 other->cxp += uu_cxp_per_combat(o, a);
1741 /* Occupants already gained their experience in the recursive call. */
1742 }
1743 }
1744
1745 /* Do the hit itself. */
1746
1747 static void
hit_unit(Unit * unit,int hit,Unit * atker)1748 hit_unit(Unit *unit, int hit, Unit *atker)
1749 {
1750 int u = unit->type, u2, hpmin, dmgspec, tpdmg, tp;
1751 Side *aside;
1752 int gain = 0, loss = 0;
1753 int m = NONMTYPE;
1754
1755 /* Some units might detonate automatically upon being hit. */
1756 if (hit > 0
1757 && atker != NULL
1758 && probability(uu_detonate_on_hit(u, atker->type))
1759 && !was_detonated(unit)) {
1760 detonate_unit(unit, unit->x, unit->y, unit->z);
1761 /* If the detonating unit still exists, then continue on to
1762 normal damage computation. */
1763 }
1764 if (hit > 0) {
1765 /* Record the loss of hp. */
1766 unit->hp2 -= hit;
1767 if (atker != NULL) {
1768 /* Attacker might not be able to do any more damage. Note
1769 that the positioning of this code is such that all the
1770 usual side effects of combat happen, but the victim
1771 doesn't get any more worse off than it is already. */
1772 hpmin = uu_hp_min(atker->type, u);
1773 if (hpmin > 0 && hpmin > unit->hp2)
1774 unit->hp2 = hpmin;
1775 /* Affect the morale of the unit being hit. */
1776 change_morale(unit, -1, uu_morale_hit_by(atker->type, u));
1777 }
1778 /* Collateral damage may include loss of tooling. */
1779 if (unit->tooling && 1 /* any_tp_damage */) {
1780 for_all_unit_types(u2) {
1781 dmgspec = uu_tp_damage(u, u2);
1782 tpdmg = roll_dice(dmgspec);
1783 if (tpdmg != 0) {
1784 tp = unit->tooling[u2] - tpdmg;
1785 tp = max(0, min(tp, uu_tp_max(u, u2)));
1786 unit->tooling[u2] = tp;
1787 }
1788 }
1789 }
1790 }
1791 /* Maybe record for statistical analysis. */
1792 /* (this is only useful if code always goes through here - is that true?) */
1793 if (atker != NULL) {
1794 aside = atker->side;
1795 u2 = atker->type;
1796 if (aside->atkstats[u2] == NULL)
1797 aside->atkstats[u2] = (long *) xmalloc(numutypes * sizeof(long));
1798 if (aside->hitstats[u2] == NULL)
1799 aside->hitstats[u2] = (long *) xmalloc(numutypes * sizeof(long));
1800 ++((aside->atkstats[u2])[u]);
1801 (aside->hitstats[u2])[u] += hit;
1802 }
1803 /* Some units may detonate automatically just before dying. */
1804 if (hit > 0 && unit->hp2 <= 0
1805 && probability(u_detonate_on_death(u))
1806 && !was_detonated(unit)) {
1807 detonate_unit(unit, unit->x, unit->y, unit->z);
1808 }
1809 /* Treasury bonus and penalty due to unit destruction */
1810 if (hit > 0 && unit->hp2 <= 0) {
1811 for_all_material_types(m) {
1812 if (atker && side_has_treasury(atker->side, m)) {
1813 gain = um_treasury_gain_per_destroy(atker->type, m);
1814 atker->side->treasury[m] += gain;
1815 /* Clip treasury to bounds, if necessary. */
1816 atker->side->treasury[m] =
1817 min(atker->side->treasury[m], g_treasury_size());
1818 atker->side->treasury[m] = max(atker->side->treasury[m], 0);
1819 }
1820 if (side_has_treasury(unit->side, m)) {
1821 loss = um_treasury_loss_per_destroyed(unit->type, m);
1822 unit->side->treasury[m] -= loss;
1823 /* Clip treasury to bounds, if necessary. */
1824 unit->side->treasury[m] =
1825 min(unit->side->treasury[m], g_treasury_size());
1826 unit->side->treasury[m] = max(unit->side->treasury[m], 0);
1827 }
1828 }
1829 }
1830 /* CxP reward based on unit destruction */
1831 if (atker && hit > 0 && unit->hp2 <= 0 && (atker->cxp < u_cxp_max(u)))
1832 atker->cxp += uu_cxp_per_destroy(u, u2);
1833 /* Should wake the unit if it receives a tap from enemy. */
1834 /* (Perhaps we should worry about waking occupants according to
1835 recursive waking rules?) */
1836 if ((hit > 0) && is_active(unit))
1837 wake_unit(unit->side, unit, FALSE);
1838 }
1839
1840 /* Hits on the main units have to be done later, so that mutual
1841 destruction works properly. This function also does all the notifying. */
1842
1843 /* (What if occupants change type when killed, but transport vanishes?) */
1844
1845 static void
reckon_damage(int fire)1846 reckon_damage(int fire)
1847 {
1848 int ax = -1, ay = -1, ox = -1, oy = -1, range = -1;
1849 int o = NONUTYPE, a = NONUTYPE;
1850
1851 /* Entertain everybody. */
1852 play_movies(ALLSIDES);
1853 /* Report the damage in more detail, now before the actual damage
1854 is taken (which may cause many units to disappear). */
1855 /* Normally we report the defender and then the attacker's damage,
1856 but if the defender dies, we report its counterattack results
1857 first and its death second. */
1858 if (!(omain->hp2 <= 0 && amain->hp2 > 0)) {
1859 report_damage(omain, amain, omain, fire);
1860 if (!fire && will_report_damage(amain))
1861 report_damage(amain, omain, amain, fire);
1862 } else {
1863 if (!fire)
1864 report_damage(amain, omain, amain, fire);
1865 report_damage(omain, amain, omain, fire);
1866 }
1867 a = amain->type;
1868 o = omain->type;
1869 ax = amain->x; ay = amain->y;
1870 ox = omain->x; oy = omain->y;
1871 damage_unit(omain, combat_dmg, amain);
1872 damage_unit(amain, combat_dmg, omain);
1873 /* If the attacker or defender may have detonated,
1874 then look around to see what else was damaged. */
1875 range = max(range, max_u_detonate_effect_range);
1876 range = max(range, max_t_detonate_effect_range);
1877 if ((-1 < range) && u_detonate_with_attack(amain->type))
1878 reckon_damage_around(ax, ay, range, amain);
1879 if ((-1 < range)
1880 && (uu_detonate_on_capture(o, a) || uu_detonate_on_hit(o, a)
1881 || u_detonate_on_death(o)))
1882 reckon_damage_around(ox, oy, range, omain);
1883 }
1884
1885 /*! \todo Evil var for search. Replace with parambox instead. */
1886 Unit *tmp_unit_detonator = NULL;
1887
1888 static void
reckon_damage_here(int x,int y)1889 reckon_damage_here(int x, int y)
1890 {
1891 Unit *unit = NULL, *nextunit = NULL;
1892
1893 /* NOTE: Use special iterator because units may get pulled out from
1894 under us while we are iterating. */
1895 for (unit = unit_at(x, y); unit; unit = nextunit) {
1896 nextunit = unit->nexthere;
1897 damage_unit(unit, combat_dmg, tmp_unit_detonator);
1898 }
1899 }
1900
1901 void
reckon_damage_around(int x,int y,int r,Unit * detonator)1902 reckon_damage_around(int x, int y, int r, Unit *detonator)
1903 {
1904 tmp_unit_detonator = detonator;
1905 if (r > 0) {
1906 apply_to_area(x, y, r, reckon_damage_here);
1907 } else {
1908 reckon_damage_here(x, y);
1909 }
1910 tmp_unit_detonator = NULL;
1911 }
1912
1913 /* Given the units involved in combat, decide how to report it. The
1914 strings like "destroy" and "miss" are symbolic, don't normally
1915 appear in the final output. */
1916
1917 static void
report_damage(Unit * unit,Unit * atker,Unit * mainunit,int fire)1918 report_damage(Unit *unit, Unit *atker, Unit *mainunit, int fire)
1919 {
1920 Unit *occ;
1921
1922 /* Don't report supposed actions by dead units. */
1923 if (!alive(atker))
1924 return;
1925 if (unit->hp2 <= 0) {
1926 if (unit == mainunit) {
1927 report_combat(atker, unit, "destroy");
1928 } else {
1929 report_combat(atker, unit, "destroy-occupant");
1930 }
1931 } else if (unit->hp2 < unit->hp) {
1932 if (unit == mainunit) {
1933 report_combat(atker, unit, "hit");
1934 } else {
1935 report_combat(atker, unit, "hit-occupant");
1936 }
1937 } else {
1938 if (unit == mainunit) {
1939 if (fire) {
1940 report_combat(atker, unit, "miss-fire");
1941 } else {
1942 report_combat(atker, unit, "miss-attack");
1943 }
1944 } else {
1945 report_combat(atker, unit, "miss-occupant");
1946 }
1947 }
1948 for_all_occupants(unit, occ) {
1949 report_damage(occ, atker, mainunit, fire);
1950 }
1951 }
1952
1953 /* Return true if the given unit or one of its occupants has been
1954 damaged. */
1955
1956 static int
will_report_damage(Unit * unit)1957 will_report_damage(Unit *unit)
1958 {
1959 Unit *occ;
1960
1961 if (unit->hp2 < unit->hp)
1962 return TRUE;
1963 for_all_occupants(unit, occ) {
1964 if (will_report_damage(occ))
1965 return TRUE;
1966 }
1967 return FALSE;
1968 }
1969
1970 /* Make the intended damage become real, and do any consequences. */
1971 /* (should include agent unit with hevts if appropriate) */
1972 void
damage_unit(Unit * unit,enum damage_reasons dmgreason,Unit * agent)1973 damage_unit(Unit *unit, enum damage_reasons dmgreason, Unit *agent)
1974 {
1975 int newacp;
1976 HistEventType hevttype;
1977 Obj *dameff;
1978 Unit *occ;
1979 DestructionResult drslt = DESTRUCT_ORDINARY;
1980 int fkillunit = FALSE;
1981 int u = NONUTYPE, u2 = NONUTYPE, t = NONTTYPE, m = NONMTYPE, ms = NONMTYPE;
1982
1983 assert_error(unit, "Tried to damage a null unit.");
1984 /* Prepare table lookups. */
1985 u = unit->type;
1986 /* (At this point, detonation damage still counts as combat damage.) */
1987 #if (0)
1988 if (dmgreason == combat_dmg) {
1989 assert_error(agent, "Receiving damage from a null unit?!");
1990 u2 = agent->type;
1991 }
1992 #endif
1993 if (agent)
1994 u2 = agent->type;
1995 t = terrain_at(unit->x, unit->y);
1996 for_all_material_types(m) {
1997 if ((unit->supply[m] <= 0) && (um_hp_per_starve(u, m) > 0)) {
1998 ms = m;
1999 break;
2000 }
2001 }
2002 /* Process all the occupants first. */
2003 for_all_occupants(unit, occ) {
2004 damage_unit(occ, dmgreason, agent);
2005 }
2006 /* If no damage was recorded, just return. */
2007 if (unit->hp2 == unit->hp) {
2008 set_was_detonated(unit, FALSE);
2009 return;
2010 }
2011 /* If unit is to die, do the consequences. */
2012 if (unit->hp2 <= 0) {
2013 /* Decide whether the unit should vanish or wreck. */
2014 drslt = determine_destruction_result(unit);
2015 switch (drslt) {
2016 case DESTRUCT_VANISH:
2017 fkillunit = TRUE;
2018 break;
2019 case DESTRUCT_ORDINARY:
2020 fkillunit = (u_wrecked_type(unit->type) == NONUTYPE);
2021 if ((dmgreason == starvation_dmg) && is_material_type(ms))
2022 fkillunit = fkillunit
2023 && (um_wrecked_type_if_starved(u, ms) == NONUTYPE);
2024 else if ((dmgreason == combat_dmg) && is_unit_type(u2))
2025 fkillunit = fkillunit
2026 && (uu_wrecked_type_if_killed(u, u2) == NONUTYPE);
2027 else if ((dmgreason == attrition_dmg) && is_terrain_type(t))
2028 fkillunit = fkillunit
2029 && (ut_wrecked_type_if_attrited(u, t) == NONUTYPE);
2030 /* (Could add a case for dying in accidents.) */
2031 break;
2032 default:
2033 fkillunit = TRUE;
2034 run_warning("Unknown unit destruction result encountered.");
2035 } /* switch (drslt) */
2036 /* Make the unit vanish forthwith. */
2037 if (fkillunit) {
2038 rescue_occupants(unit);
2039 switch (dmgreason) {
2040 case combat_dmg:
2041 hevttype = H_UNIT_KILLED;
2042 break;
2043 case accident_dmg:
2044 hevttype = H_UNIT_DIED_IN_ACCIDENT;
2045 break;
2046 case attrition_dmg:
2047 hevttype = H_UNIT_DIED_FROM_ATTRITION;
2048 break;
2049 case starvation_dmg:
2050 hevttype = H_UNIT_STARVED;
2051 break;
2052 default:
2053 hevttype = (HistEventType)0;
2054 }
2055 kill_unit(unit, hevttype);
2056 /* Wreck the unit. */
2057 } else {
2058 switch (dmgreason) {
2059 case combat_dmg:
2060 hevttype = H_UNIT_WRECKED;
2061 wreck_unit(unit, hevttype, WRECK_TYPE_KILLED, u2, agent);
2062 break;
2063 case accident_dmg:
2064 hevttype = H_UNIT_WRECKED_IN_ACCIDENT;
2065 wreck_unit(unit, hevttype, WRECK_TYPE_UNSPECIFIED, -1, NULL);
2066 break;
2067 case attrition_dmg:
2068 hevttype = H_UNIT_WRECKED_FROM_ATTRITION;
2069 wreck_unit(unit, hevttype, WRECK_TYPE_ATTRITED, t, NULL);
2070 break;
2071 case starvation_dmg:
2072 hevttype = H_UNIT_STARVED;
2073 wreck_unit(unit, hevttype, WRECK_TYPE_STARVED, ms, NULL);
2074 break;
2075 default:
2076 hevttype = (HistEventType)0;
2077 wreck_unit(unit, hevttype, WRECK_TYPE_UNSPECIFIED, -1, NULL);
2078 }
2079 }
2080 } else {
2081 record_event(H_UNIT_DAMAGED, add_side_to_set(unit->side, NOSIDES),
2082 unit->id, unit->hp, unit->hp2);
2083 /* Change the unit's hp. */
2084 unit->hp = unit->hp2;
2085 /* Perhaps adjust the acp down. */
2086 if (unit->act != NULL
2087 && unit->act->acp > 0
2088 && (dameff = u_acp_damage_effect(unit->type)) != lispnil) {
2089 newacp = damaged_acp(unit, dameff);
2090 /* The damaged acp limits the remaining acp, rather than trying
2091 to do some sort of proportional adjustment, which would be
2092 hard to get right. */
2093 /* (should account for occupant effects on acp) */
2094 unit->act->acp = min(unit->act->acp, newacp);
2095 }
2096 }
2097 /* Clear any detonation flag that might have been set. */
2098 if (alive(unit))
2099 set_was_detonated(unit, FALSE);
2100 /* Let the unit's owner know about all this. */
2101 update_unit_display(unit->side, unit, TRUE);
2102 }
2103
2104 /* Figure out what the result of destroying the unit is. */
2105
2106 static DestructionResult
determine_destruction_result(Unit * unit)2107 determine_destruction_result (Unit *unit)
2108 {
2109 DestructionResult drslt = DESTRUCT_ORDINARY;
2110 int u = NONUTYPE;
2111 Obj *choice = NULL, *chtype = NULL;
2112 int weightstotal = 0;
2113
2114 assert_error(unit, "Tried to manipulate a null unit.");
2115 u = unit->type;
2116 if (!completed(unit))
2117 return DESTRUCT_VANISH;
2118 if (lispnil != u_destruction_result(u)) {
2119 choice = choose_from_weighted_list(u_destruction_result(u),
2120 &weightstotal, FALSE);
2121 chtype = consp(choice) ? car(choice) : choice;
2122 /* Make the unit unconditionally vanish. */
2123 if (match_keyword(chtype, K_VANISH)) {
2124 drslt = DESTRUCT_VANISH;
2125 }
2126 /* Lookup the result in the appropriate table. */
2127 else if ((lispnil == chtype) || match_keyword(chtype, K_TABLE)) {
2128 /* Use the ordinary result. */
2129 }
2130 else
2131 run_warning("Unknown destruction result encountered.");
2132 }
2133 return drslt;
2134 }
2135
2136 /* Occupants may be able to avoid the fate of their transport. */
2137
2138 void
rescue_occupants(Unit * unit)2139 rescue_occupants(Unit *unit)
2140 {
2141 Unit *occ;
2142
2143 for_all_occupants(unit, occ) {
2144 if (probability(uu_occ_escape(unit->type, occ->type))) {
2145 rescue_one_occupant(occ);
2146 }
2147 }
2148 }
2149
2150 static void
rescue_one_occupant(Unit * occ)2151 rescue_one_occupant(Unit *occ)
2152 {
2153 int rslt, dir, tmp, nx, ny;
2154 Unit *other;
2155
2156 /* Try to escape into another unit in this cell. */
2157 for_all_stack(occ->x, occ->y, other) {
2158 if (other != occ->transport) {
2159 rslt = check_enter_action(occ, occ, other);
2160 if (valid(rslt)) {
2161 do_enter_action(occ, occ, other);
2162 return;
2163 }
2164 }
2165 }
2166 /* Try to move into an adjacent cell. */
2167 for_all_directions_randomly(dir, tmp) {
2168 if (interior_point_in_dir(occ->x, occ->y, dir, &nx, &ny)) {
2169 if (retreat_in_dir(occ, dir))
2170 return;
2171 }
2172 }
2173 }
2174
2175 /* Retreat is a special kind of movement that a unit uses to avoid
2176 damage during combat. It bypasses some of the normal move rules. */
2177
2178 static int
retreat_unit(Unit * unit,Unit * atker)2179 retreat_unit(Unit *unit, Unit *atker)
2180 {
2181 int dir;
2182 extern int retreating_from;
2183
2184 /* First check that this type is able to retreat. */
2185 if (unit->act == NULL || !mobile(unit->type))
2186 return FALSE;
2187 retreating_from = atker->type;
2188 if (unit->x == atker->x && unit->y == atker->y) {
2189 dir = random_dir();
2190 } else {
2191 dir = approx_dir(unit->x - atker->x, unit->y - atker->y);
2192 }
2193 if (retreat_in_dir(unit, dir))
2194 return TRUE;
2195 if (flip_coin()) {
2196 if (retreat_in_dir(unit, left_dir(dir)))
2197 return TRUE;
2198 if (retreat_in_dir(unit, right_dir(dir)))
2199 return TRUE;
2200 } else {
2201 if (retreat_in_dir(unit, right_dir(dir)))
2202 return TRUE;
2203 if (retreat_in_dir(unit, left_dir(dir)))
2204 return TRUE;
2205 }
2206 retreating_from = NONUTYPE;
2207 return FALSE;
2208 }
2209
2210 /* Try to beat a retreat in the given direction. */
2211
2212 static int
retreat_in_dir(Unit * unit,int dir)2213 retreat_in_dir(Unit *unit, int dir)
2214 {
2215 int nx, ny, rslt;
2216 Unit *other;
2217 extern int retreating;
2218 extern int retreating_from;
2219
2220 /* (should it be possible for a unit to retreat out of the world?) */
2221 if (!interior_point_in_dir(unit->x, unit->y, dir, &nx, &ny))
2222 return FALSE;
2223 retreating = TRUE;
2224 rslt = check_move_action(unit, unit, nx, ny, unit->z);
2225 if (valid(rslt)) {
2226 unit->act->acp += uu_acp_retreat(unit->type, retreating_from);
2227 do_move_action(unit, unit, nx, ny, unit->z);
2228 retreating = FALSE;
2229 retreating_from = NONUTYPE;
2230 return TRUE;
2231 }
2232 /* No luck moving; see if there's a friendly unit to enter. */
2233 for_all_stack(nx, ny, other) {
2234 rslt = check_enter_action(unit, unit, other);
2235 if (valid(rslt)) {
2236 unit->act->acp += uu_acp_retreat(unit->type, retreating_from);
2237 do_enter_action(unit, unit, other);
2238 retreating = FALSE;
2239 retreating_from = NONUTYPE;
2240 return TRUE;
2241 }
2242 }
2243 return FALSE;
2244 }
2245
2246 /* Capture action. */
2247
2248 /* Prepare a capture action to be executed later. */
2249
2250 int
prep_capture_action(Unit * unit,Unit * unit2,Unit * unit3)2251 prep_capture_action(Unit *unit, Unit *unit2, Unit *unit3)
2252 {
2253 if (unit == NULL || unit->act == NULL || unit2 == NULL)
2254 return FALSE;
2255 unit->act->nextaction.type = ACTION_CAPTURE;
2256 unit->act->nextaction.args[0] = unit3->id;
2257 unit->act->nextaction.actee = unit2->id;
2258 return TRUE;
2259 }
2260
2261 /* Execute a capture action. */
2262
2263 int
do_capture_action(Unit * unit,Unit * unit2,Unit * unit3)2264 do_capture_action(Unit *unit, Unit *unit2, Unit *unit3)
2265 {
2266 int rslt;
2267
2268 attempt_to_capture_unit(unit2, unit3);
2269 use_up_acp(unit, uu_acp_to_capture(unit2->type, unit3->type));
2270 if (unit3->side == unit2->side)
2271 rslt = A_CAPTURE_SUCCEEDED;
2272 else
2273 rslt = A_CAPTURE_FAILED;
2274 return rslt;
2275 }
2276
2277 /* Check the validity of a capture action. */
2278
2279 int
check_capture_action(Unit * unit,Unit * unit2,Unit * unit3)2280 check_capture_action(Unit *unit, Unit *unit2, Unit *unit3)
2281 {
2282 int rslt = A_ANY_OK;
2283
2284 /* In combat model 1, we can't capture units directly. */
2285 if (g_combat_model() == 1)
2286 return A_ANY_ERROR;
2287 if (!in_play(unit3))
2288 return A_ANY_ERROR;
2289 if (!valid(rslt = can_capture(unit, unit2, unit3->type, unit3->side)))
2290 return rslt;
2291 /* We can't capture ourselves. */
2292 if (unit2 == unit3)
2293 return A_ANY_ERROR;
2294 if (distance(unit2->x, unit2->y, unit3->x, unit3->y) > 1)
2295 return A_ANY_TOO_FAR;
2296 return A_ANY_OK;
2297 }
2298
2299 /* Handle capture possibility and repulse/slaughter. */
2300
2301 /* The chance to capture an enemy is modified by several factors.
2302 Neutrals have a different chance to be captured, and presence of
2303 occupants should also has an effect. Can't capture anything that is
2304 on a kind of terrain that the capturer can't go on, unless victim has
2305 "bridge effect". */
2306
2307 /* (Need a little better treatment of committed assaults, where lack of
2308 success == death.) */
2309
2310 static void
attempt_to_capture_unit(Unit * atker,Unit * other)2311 attempt_to_capture_unit(Unit *atker, Unit *other)
2312 {
2313 int a = atker->type, o = other->type, chance, prot;
2314 int ox = other->x, oy = other->y;
2315 Unit *occ, *unit2;
2316 Side *as = atker->side, *os = other->side;
2317
2318 /* Low-tech sides are not going to succeed at even touching high-tech
2319 types, let alone capturing them. */
2320 if (atker->side
2321 && atker->side->tech[o] < u_tech_to_own(o))
2322 return;
2323 chance = capture_chance(a, o, other->side);
2324 if (alive(atker) && alive(other) && chance > 0) {
2325 if (impassable(atker, ox, oy) && !uu_bridge(o, a))
2326 return;
2327 /* Can possibly detonate on *any* attempt to capture! */
2328 if (probability(uu_detonate_on_capture(o, a))
2329 && !was_detonated(other)) {
2330 detonate_unit(other, other->x, other->y, other->z);
2331 /* Might not be possible to capture anything anymore. */
2332 if (!alive(atker) || !alive(other))
2333 return;
2334 /* Types of units might have changed, recalc things. */
2335 a = atker->type; o = other->type;
2336 as = atker->side; os = other->side;
2337 chance = capture_chance(a, o, other->side);
2338 }
2339 /* Occupants can protect the transport. */
2340 for_all_occupants(other, occ) {
2341 if (is_active(occ)) {
2342 if (g_prot_resists_capture()) {
2343 prot = uu_protection(occ->type, o);
2344 if (prot != 100)
2345 chance = (chance * prot) / 100;
2346 }
2347 prot = uu_occ_allows_capture_by(occ->type, a);
2348 if (prot != 100)
2349 chance = (chance * prot) / 100;
2350 prot = uu_occ_allows_capture_of(occ->type, o);
2351 if (prot != 100)
2352 chance = (chance * prot) / 100;
2353 }
2354 }
2355 /* Neighbors can protect neighbors. */
2356 for_all_stack(other->x, other->y, unit2) {
2357 if (unit2 != other
2358 && in_play(unit2)
2359 && completed(unit2)
2360 && unit2->side == other->side) {
2361 if (g_prot_resists_capture()) {
2362 prot = uu_stack_protection(unit2->type, o);
2363 if (prot != 100)
2364 chance = (chance * prot) / 100;
2365 }
2366 prot = uu_stack_neighbor_allows_capture_by(unit2->type, a);
2367 if (prot != 100)
2368 chance = (chance * prot) / 100;
2369 prot = uu_stack_neighbor_allows_capture_of(unit2->type, o);
2370 if (prot != 100)
2371 chance = (chance * prot) / 100;
2372 }
2373 }
2374 /* Note that this code differs from that above in that it
2375 treats all units in the same cell equally, whether occs,
2376 suboccs or part of the stack. This also means that one occ
2377 can protect another occ in the same transport. Moreover,
2378 the protective unit also protects itself. These rules
2379 simulate real situations such as when triple-A protects all
2380 nearby units (including itself) against bombers, or when a
2381 city wall protects all other occupants against ground
2382 attack. */
2383 for_all_stack_with_occs(other->x, other->y, unit2) {
2384 if (is_active(unit2)
2385 /* We also extend protection to our buddies! */
2386 && trusted_side(unit2->side, other->side)) {
2387 /* This is when a unit, such as triple-A, provides
2388 unique protection against a specific attacker, such
2389 as bombers, to all other units in the cell. */
2390 if (g_prot_resists_capture()) {
2391 prot = uu_cellwide_protection_against(unit2->type, a);
2392 if (prot != 100)
2393 chance = (chance * prot) / 100;
2394 }
2395 prot = uu_any_neighbor_allows_capture_by(unit2->type, a);
2396 if (prot != 100)
2397 chance = (chance * prot) / 100;
2398 /* This is when a unit (such as a garrison)
2399 specifically protects a second unit, such as a fort
2400 (but not other nearby units), against all forms of
2401 attack. It thus works the same way as uu_protection
2402 and uu_stack_protection. */
2403 if (g_prot_resists_capture()) {
2404 prot = uu_cellwide_protection_for(unit2->type, o);
2405 if (prot != 100)
2406 chance = (chance * prot) / 100;
2407 }
2408 prot = uu_any_neighbor_allows_capture_of(unit2->type, o);
2409 if (prot != 100)
2410 chance = (chance * prot) / 100;
2411 }
2412 }
2413 /* Test whether the capture actually happens. */
2414 if (probability(chance)) {
2415 capture_unit(atker, other, H_UNIT_CAPTURED);
2416 } else if (atker->transport != NULL &&
2417 (impassable(atker, ox, oy) ||
2418 impassable(atker, atker->x, atker->y))) {
2419 /* was the capture attempt a one-way trip? */
2420 /* (should fix the test above - needs to be more accurate) */
2421 report_combat(atker, other, "resist/slaughter");
2422 kill_unit(atker,
2423 H_UNIT_KILLED /* should be something appropriate */);
2424 } else {
2425 report_combat(atker, other, "resist");
2426 /* (should record failed attempt to capture?) */
2427 }
2428 if (chance > 0) {
2429 if (atker->cxp < u_cxp_max(a))
2430 atker->cxp += uu_cxp_per_capture(a, o);
2431 /* (should not increment if side just changed?) */
2432 if (other->cxp < u_cxp_max(o))
2433 other->cxp += uu_cxp_per_capture(o, a);
2434
2435 }
2436 }
2437 }
2438
2439 /* There are many consequences of a unit being captured. */
2440
2441 void
capture_unit(Unit * unit,Unit * pris,int captype)2442 capture_unit(Unit *unit, Unit *pris, int captype)
2443 {
2444 int u = unit->type, px = pris->x, py = pris->y;
2445 Unit *occ;
2446 Side *ps = pris->side, *us = unit->side, *newside;
2447 int newtype = NONUTYPE;
2448 int m = NONMTYPE;
2449 int gain = 0, loss = 0;
2450
2451 newside = unit->side;
2452 /* Return a unit to its original side if we are buds with that side. */
2453 if (pris->origside != newside && trusted_side(us, pris->origside))
2454 newside = pris->origside;
2455 /* Attempt to scuttle. */
2456 if (probability(uu_scuttle(pris->type, u))) {
2457 /* (should add terrain effect on success too) */
2458 /* (should characterize as a scuttle) */
2459 kill_unit(pris, H_UNIT_DISBANDED);
2460 }
2461 /* Attempt to change type if we are supposed to do so on capture. */
2462 /* If new type cannot be on new side, then the unit must die. */
2463 newtype = uu_changed_type_if_captured(pris->type, u);
2464 if (NONUTYPE != newtype) {
2465 if (!type_allowed_on_side(newtype, newside))
2466 kill_unit(pris, H_UNIT_KILLED);
2467 else
2468 change_unit_type(pris, newtype, H_UNIT_TYPE_CHANGED, newside);
2469 }
2470 /* If prisoner is not allowed on side, then make it go away. */
2471 if (!unit_allowed_on_side(pris, newside)) {
2472 kill_unit(pris, H_UNIT_KILLED);
2473 }
2474 /* Properly report capture outcome. */
2475 if (alive(pris)) {
2476 if (newside == pris->origside) {
2477 report_combat(unit, pris, "liberate");
2478 } else {
2479 report_combat(unit, pris, "capture");
2480 }
2481 /* Decide the fate of each occupant of our prisoner. */
2482 for_all_occupants(pris, occ) {
2483 /* Don't try to capture occs from trusted sides. */
2484 if (trusted_side(us, occ->side))
2485 continue;
2486 capture_occupant(unit, pris, occ, newside);
2487 }
2488 /* Treasury bonus and penalty due to unit capture */
2489 for_all_material_types(m) {
2490 if (side_has_treasury(unit->side, m)) {
2491 gain = um_treasury_gain_per_capture(unit->type, m);
2492 unit->side->treasury[m] += gain;
2493 /* Clip treasury to bounds, if necessary. */
2494 unit->side->treasury[m] =
2495 min(unit->side->treasury[m], g_treasury_size());
2496 unit->side->treasury[m] = max(unit->side->treasury[m], 0);
2497 }
2498 if (side_has_treasury(pris->side, m)) {
2499 loss = um_treasury_loss_per_captured(pris->type, m);
2500 pris->side->treasury[m] -= loss;
2501 /* Clip treasury to bounds, if necessary. */
2502 pris->side->treasury[m] =
2503 min(pris->side->treasury[m], g_treasury_size());
2504 pris->side->treasury[m] = max(pris->side->treasury[m], 0);
2505 }
2506 }
2507 /* The change of side itself. This happens recursively to any
2508 remaining occupants as well. */
2509 change_unit_side(pris, newside, captype, unit);
2510 /* Garrison the newly-captured unit with hp from the capturing unit. */
2511 garrison_unit(unit, pris);
2512 update_unit_display(unit->side, unit, TRUE);
2513 capture_unit_2(unit, pris, ps);
2514 /* The people at the new location may change sides immediately. */
2515 if (people_sides_defined()
2516 && any_people_side_changes
2517 && probability(people_surrender_chance(pris->type, px, py))) {
2518 change_people_side_around(px, py, pris->type, unit->side);
2519 }
2520 if (control_sides_defined()) {
2521 if (ut_control_range(pris->type, terrain_at(px, py)) >= 0) {
2522 change_control_side_around(px, py, pris->type, unit->side);
2523 }
2524 }
2525 kick_out_enemy_users(unit->side, px, py);
2526 /* Occupy the captured unit if possible. */
2527 if(valid(check_enter_action(unit, unit, pris))) {
2528 do_enter_action(unit, unit, pris);
2529 /* Else move into the same cell to guard it. */
2530 } else if (valid(check_move_action(unit, unit, px, py, 0))) {
2531 do_move_action(unit, unit, px, py, 0);
2532 }
2533 }
2534
2535 /* Update everybody's view of the situation. */
2536 see_exact(ps, px, py);
2537 update_cell_display(ps, px, py, UPDATE_ALWAYS);
2538 all_see_cell(px, py);
2539 }
2540
2541 /* Given that the main unit is going to be captured, decide what each occupant
2542 will do. */
2543
2544 static void
capture_occupant(Unit * unit,Unit * pris,Unit * occ,Side * newside)2545 capture_occupant(Unit *unit, Unit *pris, Unit *occ, Side *newside)
2546 {
2547 int u = unit->type, newtype = NONUTYPE;
2548 Unit *subocc;
2549
2550 /* Side change will actually happen later. */
2551 if (probability(uu_occ_escape(u, occ->type))) {
2552 /* The occupant escapes, along with all its suboccupants. */
2553 /* Retreat is not a perfect model, but close enough for now. */
2554 if (retreat_unit(occ, unit)) {
2555 /* Only report an escape if successful. */
2556 report_combat(unit, occ, "escape");
2557 }
2558 } else if (probability(uu_scuttle(occ->type, u))) {
2559 /* (should add terrain effect on success too) */
2560 /* (should characterize as a scuttle) */
2561 kill_unit(occ, H_UNIT_DISBANDED);
2562 /* Some occs may gracefully switch to their transport's new side. */
2563 } else if (u_match_transport_side(occ->type)) {
2564 newtype = uu_changed_type_if_captured(occ->type, u);
2565 if (NONUTYPE != newtype) {
2566 if (type_allowed_on_side(newtype, newside))
2567 change_unit_type(occ, newtype, H_UNIT_TYPE_CHANGED, newside);
2568 }
2569 for_all_occupants(occ, subocc) {
2570 capture_occupant(unit, occ, subocc, newside);
2571 }
2572 if ((NONUTYPE != newtype) && !type_allowed_on_side(newtype, newside))
2573 kill_unit(pris, H_UNIT_KILLED);
2574 /* Other occs may need to be forcefully captured. */
2575 } else if (capture_chance(u, occ->type, occ->side) > 0) {
2576 /* (TODO: Should use up ACP during capture of occs.) */
2577 for_all_occupants(occ, subocc) {
2578 capture_occupant(unit, occ, subocc, newside);
2579 }
2580 } else {
2581 /* Occupant can't live as a prisoner, but suboccs might, so recurse
2582 through them. */
2583 for_all_occupants(occ, subocc) {
2584 capture_occupant(unit, occ, subocc, newside);
2585 }
2586 /* Any suboccupants that didn't escape will die. */
2587 /* (what if subocc captured tho? should move elsewhere) */
2588 kill_unit(occ, H_UNIT_KILLED);
2589 }
2590 }
2591
2592 /* Do additional consequences of capture. */
2593
2594 static void
capture_unit_2(Unit * unit,Unit * pris,Side * prevside)2595 capture_unit_2(Unit *unit, Unit *pris, Side *prevside)
2596 {
2597 int chance, x, y, notesee;
2598 Unit *occ, *unit2;
2599 Side *newside;
2600 Unit *checkalways;
2601
2602 /* Our new unit's experience might be higher or lower, depending on what
2603 capture really means (change of crew perhaps). */
2604 pris->cxp = (pris->cxp * u_cxp_on_capture(pris->type)) / 100;
2605 pris->cxp = min(unit->cxp, u_cxp_max(pris->type));
2606 /* Getting captured is always bad for morale, but getting
2607 liberated is good. */
2608 if (pris->side == pris->origside)
2609 change_morale(pris, 1, ((pris->morale + 1) * 50) + 50);
2610 else
2611 pris->morale = pris->morale / 2;
2612 /* Clear any actions and plans. */
2613 /* Don't use init_unit_actorstate! We just want to cancel any pending
2614 action, but leave all the acp values untouched. */
2615 if (pris->act)
2616 pris->act->nextaction.type = ACTION_NONE;
2617 /* (should probably adjust side's acp sums by new unit's amounts) */
2618 init_unit_plan(pris);
2619 /* Maybe get a pile of interesting information. */
2620 /* Independent units won't know anything about other independents
2621 though (they're independent, eh?). */
2622 newside = unit->side;
2623 if (newside && !newside->see_all && prevside != indepside) {
2624 notesee = FALSE;
2625 chance = u_see_terrain_captured(pris->type);
2626 if (!g_terrain_seen() && probability(chance)) {
2627 for_all_cells(x, y) {
2628 if (terrain_view(prevside, x, y) != UNSEEN
2629 && terrain_view(newside, x, y) == UNSEEN) {
2630 set_terrain_view(newside, x, y,
2631 terrain_view(prevside, x, y));
2632
2633 /* Now view see-always units. */
2634 for_all_stack_with_occs(x, y, checkalways) {
2635 if (u_see_always(checkalways->type)) {
2636 see_exact(newside, x, y);
2637 add_cover(newside, x, y, 1);
2638 }
2639 }
2640
2641 update_cell_display(newside, x, y, UPDATE_ALWAYS);
2642 notesee = TRUE;
2643 }
2644 }
2645 }
2646 if (1 /* any see others chance for this type */) {
2647 for_all_side_units(prevside, unit2) {
2648 if (in_play(unit2)) {
2649 chance = uu_see_others_captured(pris->type, unit2->type);
2650 if (probability(chance)) {
2651 x = unit2->x; y = unit2->y;
2652 see_exact(newside, unit2->x, unit2->y);
2653 update_cell_display(newside, unit2->x, unit2->y,
2654 UPDATE_ALWAYS);
2655 for_all_stack_with_occs(x, y, checkalways) {
2656 if (u_see_always(checkalways->type)) {
2657 add_cover(newside, x, y, 1);
2658 }
2659 }
2660 notesee = TRUE;
2661 }
2662 }
2663 }
2664 }
2665 /* Only say something to the capturing side if we actually got
2666 any new information - after several captures, may not be
2667 anything new to find out. */
2668 if (notesee) {
2669 notify(newside, "Enemy information fell into your hands!");
2670 }
2671 }
2672 /* Likewise for occupants. */
2673 for_all_occupants(pris, occ) {
2674 capture_unit_2(unit, occ, prevside);
2675 }
2676 }
2677
2678 /* A detonate action just blasts the vicinity indiscriminately. */
2679
2680 int
prep_detonate_action(Unit * unit,Unit * unit2,int x,int y,int z)2681 prep_detonate_action(Unit *unit, Unit *unit2, int x, int y, int z)
2682 {
2683 if (unit == NULL || unit->act == NULL || unit2 == NULL)
2684 return FALSE;
2685 unit->act->nextaction.type = ACTION_DETONATE;
2686 unit->act->nextaction.args[0] = x;
2687 unit->act->nextaction.args[1] = y;
2688 unit->act->nextaction.args[2] = z;
2689 unit->act->nextaction.actee = unit2->id;
2690 return TRUE;
2691 }
2692
2693 int
do_detonate_action(Unit * unit,Unit * unit2,int x,int y,int z)2694 do_detonate_action(Unit *unit, Unit *unit2, int x, int y, int z)
2695 {
2696 int u2 = unit2->type;
2697
2698 detonate_unit(unit2, x, y, z);
2699 /* Note that if the maxrange is further than the actual range of this
2700 detonation, only just-damaged units will be looked at. */
2701 reckon_damage_around(x, y, max_u_detonate_effect_range, unit2);
2702 /* Unit might have detonated outside its range of effect, so need
2703 this to make its own damage is accounted for. */
2704 if (alive(unit2))
2705 reckon_damage_around(unit2->x, unit2->y, 0, unit2);
2706 use_up_acp(unit, u_acp_to_detonate(u2));
2707 return A_ANY_DONE;
2708 }
2709
2710 int
check_detonate_action(Unit * unit,Unit * unit2,int x,int y,int z)2711 check_detonate_action(Unit *unit, Unit *unit2, int x, int y, int z)
2712 {
2713 int u, u2;
2714 int rslt = A_ANY_OK;
2715
2716 if (!valid(rslt = can_detonate(unit, unit2)))
2717 return rslt;
2718 if (!inside_area(x, y))
2719 return A_ANY_ERROR;
2720 u = unit->type;
2721 u2 = unit2->type;
2722 /* Can only detonate in our own or an adjacent cell. */
2723 /* (In other words, the detonating unit doesn't get to teleport
2724 its detonation effects to any desired faraway location.) */
2725 if (distance(unit2->x, unit2->y, x, y) > 1)
2726 return A_ANY_TOO_FAR;
2727 return A_ANY_OK;
2728 }
2729
2730 static int tmpdetx, tmpdety;
2731
2732 /* Actual detonation may occur by explicit action or automatically; this
2733 routine makes the detonation effects happen, pyrotechnics and all. */
2734
2735 int
detonate_unit(Unit * unit,int x,int y,int z)2736 detonate_unit(Unit *unit, int x, int y, int z)
2737 {
2738 int u = unit->type, dir, x1, y1, dmg, maxrange;
2739 Unit *unit2;
2740 Side *side;
2741
2742 if (max_u_detonate_effect_range < 0) {
2743 int u2, u3, range;
2744
2745 for_all_unit_types(u2) {
2746 for_all_unit_types(u3) {
2747 range = uu_detonation_range(u2, u3);
2748 max_u_detonate_effect_range =
2749 max(range, max_u_detonate_effect_range);
2750 }
2751 }
2752 }
2753 if (max_t_detonate_effect_range < 0) {
2754 int u2, t, range;
2755
2756 for_all_unit_types(u2) {
2757 for_all_terrain_types(t) {
2758 range = ut_detonation_range(u2, t);
2759 max_t_detonate_effect_range =
2760 max(range, max_t_detonate_effect_range);
2761 }
2762 }
2763 }
2764 report_combat(unit, NULL, "detonate");
2765 for_all_sides(side) {
2766 if (active_display(side)
2767 && (side_sees_unit(side, unit)
2768 || units_visible(side, unit->x, unit->y))) {
2769 schedule_movie(side, "flash", unit->x, unit->y);
2770 /* a hack */
2771 if (strstr(u_type_name(u), "nuclear")
2772 || strstr(u_type_name(u), "nuke"))
2773 schedule_movie(side, "nuke", x, y);
2774 }
2775 }
2776 set_was_detonated(unit, TRUE);
2777 /* Hit the detonating unit first. */
2778 hit_unit_with_detonation(unit, u_hp_per_detonation(u), NULL);
2779 /* Hit units at ground zero. */
2780 for_all_stack(x, y, unit2) {
2781 if (unit2 != unit) {
2782 hit_unit_with_detonation(unit2,
2783 uu_detonation_damage_at(u, unit2->type),
2784 unit);
2785 }
2786 }
2787 damage_terrain(u, x, y);
2788 /* Hit units and/or terrain in adjacent cells, if this is defined. */
2789 if (max_u_detonate_effect_range >= 1) {
2790 for_all_directions(dir) {
2791 if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
2792 for_all_stack(x1, y1, unit2) {
2793 if (0 >= uu_detonation_range(u, unit2->type))
2794 continue;
2795 dmg = uu_detonation_damage_adj(u, unit2->type);
2796 hit_unit_with_detonation(unit2, dmg, unit);
2797 }
2798 }
2799 }
2800 }
2801 if (max_t_detonate_effect_range >= 1) {
2802 for_all_directions(dir) {
2803 if (point_in_dir(x, y, dir, &x1, &y1)) {
2804 if (0 >= ut_detonation_range(u, terrain_at(x1, y1)))
2805 continue;
2806 damage_terrain(u, x1, y1);
2807 }
2808 }
2809 }
2810 /* Hit units that are further away. */
2811 maxrange = max(max_u_detonate_effect_range, max_t_detonate_effect_range);
2812 if (maxrange >= 2) {
2813 tmpunit = unit;
2814 tmpdetx = x; tmpdety = y;
2815 apply_to_area(x, y, maxrange, detonate_on_cell);
2816 }
2817 /* (should test compatibility of any new terrain types with each other;
2818 only after changes over with) */
2819 /* Entertain everybody. */
2820 play_movies(ALLSIDES);
2821 return TRUE;
2822 }
2823
2824 static void
detonate_on_cell(int x,int y)2825 detonate_on_cell(int x, int y)
2826 {
2827 int dist, dmg, sdmg;
2828 Unit *unit2;
2829
2830 dist = distance(tmpdetx, tmpdety, x, y);
2831 if (dist > 1 && dist <= max_u_detonate_effect_range) {
2832 /* Since this code bypasses the occ recursion in
2833 maybe_hit_unit, we should also hit all occupants directly
2834 with the blast. */
2835 for_all_stack_with_occs(x, y, unit2) {
2836 if (dist <= uu_detonation_range(tmpunit->type, unit2->type)) {
2837 dmg = uu_detonation_damage_adj(tmpunit->type, unit2->type);
2838 /* Reduce by inverse square of the distance. */
2839 sdmg = (dmg * 100) / (dist * dist);
2840 dmg = prob_fraction(sdmg);
2841 hit_unit_with_detonation(unit2, dmg, tmpunit);
2842 }
2843 }
2844 }
2845 if (dist > 1
2846 && dist <= ut_detonation_range(tmpunit->type, terrain_at(x, y))) {
2847 damage_terrain(tmpunit->type, x, y);
2848 }
2849 }
2850
2851 static void
hit_unit_with_detonation(Unit * unit,int hit,Unit * atker)2852 hit_unit_with_detonation(Unit *unit, int hit, Unit *atker)
2853 {
2854 char *hitmovietype;
2855 Side *side;
2856
2857 hit_unit(unit, hit, atker);
2858 for_all_sides(side) {
2859 if (active_display(side)
2860 /* (should figure out visibility rules) */
2861 && (g_see_all()
2862 || side == unit->side
2863 || 0 /* visible to other sides */)) {
2864 hitmovietype = (char *)((hit >= unit->hp) ? "death" :
2865 ((hit > 0) ? "hit" : "miss"));
2866 schedule_movie(side, hitmovietype, unit->id);
2867 }
2868 }
2869 }
2870
2871 /* Do the effect of detonation on the terrain at the given location. */
2872
2873 void
damage_terrain(int u,int x,int y)2874 damage_terrain(int u, int x, int y)
2875 {
2876 int t, t2, dir;
2877
2878 /* Damage the cell's terrain. */
2879 t = terrain_at(x, y);
2880 if (probability(ut_detonation_damage(u, t))) {
2881 t2 = damaged_terrain_type(t);
2882 if (t2 == NONTTYPE) {
2883 run_warning(
2884 "Possibly invalid damaged terrain type from undamaged terrain type '%s'.",
2885 t_type_name(t));
2886 return;
2887 } else if (t2 != t) {
2888 change_terrain_type(x, y, t2);
2889 }
2890 }
2891 /* Apply to auxiliary terrain also. */
2892 if (1 /* if any aux terrain */) {
2893 for_all_terrain_types(t) {
2894 switch (t_subtype(t)) {
2895 case cellsubtype:
2896 /* We already did this one. */
2897 break;
2898 case bordersubtype:
2899 if (1 /* any damage possible */) {
2900 for_all_directions(dir) {
2901 if (border_at(x, y, dir, t)
2902 && probability(ut_detonation_damage(u, t))) {
2903 t2 = damaged_terrain_type(t);
2904 if (t2 == NONTTYPE) {
2905 set_border_at(x, y, dir, t, FALSE);
2906 } else if (t2 != t) {
2907 set_border_at(x, y, dir, t, FALSE);
2908 set_border_at(x, y, dir, t2, TRUE);
2909 /* There is potentially a problem with
2910 some game designs here; if the new
2911 type t2 can also be damaged by the
2912 detonation, then the loop here will
2913 damage it in turn, at least if t2
2914 *follows* t in the list of terrain
2915 types. Preventing this would
2916 require a lot of buffering, so it's
2917 left as a limitation for now. */
2918 }
2919 }
2920 }
2921 }
2922 break;
2923 case connectionsubtype:
2924 if (1 /* any damage possible */) {
2925 for_all_directions(dir) {
2926 if (connection_at(x, y, dir, t)
2927 && probability(ut_detonation_damage(u, t))) {
2928 t2 = damaged_terrain_type(t);
2929 if (t2 == NONTTYPE) {
2930 set_connection_at(x, y, dir, t, FALSE);
2931 } else if (t2 != t) {
2932 set_connection_at(x, y, dir, t, FALSE);
2933 set_connection_at(x, y, dir, t2, TRUE);
2934 /* Same issue here as with border
2935 damage. */
2936 }
2937 }
2938 }
2939 }
2940 break;
2941 case coatingsubtype:
2942 /* don't know how to damage coatings yet */
2943 break;
2944 }
2945 }
2946 }
2947 }
2948
2949 int
damaged_terrain_type(int t)2950 damaged_terrain_type(int t)
2951 {
2952 int t2, tot, othertot, test, sum, rslt;
2953
2954 tot = othertot = 0;
2955 for_all_terrain_types(t2) {
2956 if (t_subtype(t2) == t_subtype(t)) {
2957 tot += tt_damaged_type(t, t2);
2958 } else if (t_subtype(t) != cellsubtype) {
2959 othertot += tt_damaged_type(t, t2);
2960 }
2961 }
2962 rslt = NONTTYPE;
2963 if ((tot + othertot) > 0) {
2964 test = xrandom(tot + othertot);
2965 sum = 0;
2966 for_all_terrain_types(t2) {
2967 if (t_subtype(t2) == t_subtype(t)) {
2968 sum += tt_damaged_type(t, t2);
2969 if (test < sum) {
2970 rslt = t2;
2971 break;
2972 }
2973 }
2974 }
2975 }
2976 /* Random values between tot and othertot will have
2977 fallen through the loop, and the rslt is NONTTYPE,
2978 which indicates that the terrain must be removed
2979 if possible. */
2980 /* Paranoia check */
2981 if (rslt != NONTTYPE && t_subtype(rslt) != t_subtype(t))
2982 run_error("badness in damaged_terrain_type");
2983 return rslt;
2984 }
2985
2986 /* True if unit has enough occupants assigned to its defense. */
2987
2988 int
defended_by_occupants(Unit * unit)2989 defended_by_occupants(Unit *unit)
2990 {
2991 Unit *unit2;
2992 int defenders = 0;
2993
2994 for_all_occupants(unit, unit2) {
2995 if (is_active(unit2)
2996 && unit2->plan
2997 && unit2->plan->maingoal
2998 && unit2->plan->maingoal->type == GOAL_UNIT_OCCUPIED
2999 && unit2->plan->maingoal->args[0] == unit->id
3000 /* Occs that cannot defend the transport should not be
3001 assigned to occupy it, but check anyway for ability to
3002 protect. */
3003 && occ_can_defend_transport(unit2->type, unit->type)) {
3004 ++defenders;
3005 }
3006 }
3007 if (enough_to_garrison(unit, defenders))
3008 return TRUE;
3009 else
3010 return FALSE;
3011 }
3012
3013 /* True if first type can both occupy and protect second type. */
3014
3015 int
occ_can_defend_transport(int o,int t)3016 occ_can_defend_transport(int o, int t)
3017 {
3018 /* Never true if transport cannot carry occupant. */
3019 if (!could_carry(t, o))
3020 return FALSE;
3021 if (g_combat_model() == 0) {
3022 /* Specific protection of transport by its occupant.
3023 Note: zero protection is 100, full protection is 0! */
3024 if (uu_protection(o, t) < 100)
3025 return TRUE;
3026 /* Cellwide protection of all units by occupant. */
3027 if (uu_cellwide_protection_for(o, t) < 100)
3028 return TRUE;
3029 } else if (g_combat_model() == 1) {
3030 /* Advanced units are protected by any kind of occupant
3031 that has a positive defense value. */
3032 if (u_advanced(t) && u_defend(o) > 0) {
3033 return TRUE;
3034 /* Non-advanced units are protected by this table. */
3035 } else if (!u_advanced(t)
3036 /* Note: More than 100 is protection. */
3037 && uu_occ_affects_defense(o, t) > 100) {
3038 return TRUE;
3039 }
3040 }
3041 return FALSE;
3042 }
3043
3044 /* True if the specified number of defenders is sufficient to defend
3045 the unit. Note that this garrison does not have to exist! */
3046
3047 int
enough_to_garrison(Unit * unit,int defenders)3048 enough_to_garrison(Unit *unit, int defenders)
3049 {
3050 int u = unit->type;
3051 Unit *unit2;
3052
3053 /* We only worry about enemies within our tactical range. */
3054 unit2 = mobile_enemy_threat(unit, u_ai_enemy_alert_range(u));
3055 /* We are threatened by the enemy! */
3056 if (unit2 && defenders < u_ai_war_garrison(u))
3057 return FALSE;
3058 else if (defenders < u_ai_peace_garrison(u))
3059 /* No enemy in sight. */
3060 return FALSE;
3061 else
3062 return TRUE;
3063 }
3064
3065 /* Returns the first mobile enemy unit found within the specified
3066 range that can hit or capture our unit. */
3067
3068 Unit *
mobile_enemy_threat(Unit * unit,int range)3069 mobile_enemy_threat(Unit *unit, int range)
3070 {
3071 int x, y;
3072 Unit *unit2;
3073
3074 /* Go through all cells within the specified range. */
3075 for_all_cells_within_range(unit->x, unit->y, range, x, y) {
3076 if (!inside_area(x, y))
3077 continue;
3078 if (!terrain_visible(unit->side, x, y))
3079 continue;
3080 /* Important to count also occupants here. */
3081 for_all_stack_with_occs(x, y, unit2) {
3082 /* Only count visible mobile enemy units that actually
3083 threaten us. */
3084 if (is_active(unit2)
3085 /* The NULL side rarely attacks anybody :-) */
3086 && unit2->side
3087 && mobile(unit2->type)
3088 && side_sees_image(unit->side, unit2)
3089 && !trusted_side(unit->side, unit2->side)
3090 && (could_hit(unit2->type, unit->type)
3091 || capture_chance(unit2->type, unit->type, unit->side) > 0)) {
3092 return unit2;
3093 }
3094 }
3095 }
3096 return NULL;
3097 }
3098
3099