1 /**
2 * @file
3 * @brief functions used during combat
4 */
5
6 #include "AppHdr.h"
7
8 #include "fight.h"
9
10 #include <algorithm>
11 #include <cmath>
12 #include <cstdio>
13 #include <cstdlib>
14 #include <cstring>
15
16 #include "art-enum.h"
17 #include "coord.h"
18 #include "coordit.h"
19 #include "delay.h"
20 #include "english.h"
21 #include "env.h"
22 #include "fineff.h"
23 #include "fprop.h"
24 #include "god-passive.h" // passive_t::shadow_attacks
25 #include "hints.h"
26 #include "invent.h"
27 #include "item-prop.h"
28 #include "item-use.h"
29 #include "melee-attack.h"
30 #include "message.h"
31 #include "misc.h"
32 #include "mon-behv.h"
33 #include "mon-cast.h"
34 #include "mon-place.h"
35 #include "mon-util.h"
36 #include "options.h"
37 #include "ouch.h"
38 #include "player.h"
39 #include "prompt.h"
40 #include "random-var.h"
41 #include "religion.h"
42 #include "shopping.h"
43 #include "spl-summoning.h"
44 #include "state.h"
45 #include "stringutil.h"
46 #include "target.h"
47 #include "terrain.h"
48 #include "transform.h"
49 #include "traps.h"
50 #include "travel.h"
51
52 /**
53 * What are the odds of an HD-checking confusion effect (e.g. Confusing Touch,
54 * Fungus Form, SPWPN_CHAOS maybe) to confuse a monster of the given HD?
55 *
56 * @param HD The current hit dice (level) of the monster to confuse.
57 * @return A percentage chance (0-100) of confusing that monster.
58 * (Except it tops out at 80%.)
59 */
melee_confuse_chance(int HD)60 int melee_confuse_chance(int HD)
61 {
62 return max(80 * (24 - HD) / 24, 0);
63 }
64
65 /**
66 * Return the odds of an attack with the given to-hit bonus hitting a defender with the
67 * given EV, rounded to the nearest percent.
68 *
69 * @return To-hit percent between 0 and 100 (inclusive).
70 */
to_hit_pct(const monster_info & mi,attack & atk,bool melee)71 int to_hit_pct(const monster_info& mi, attack &atk, bool melee)
72 {
73 const int to_land = atk.calc_pre_roll_to_hit(false);
74 int ev = mi.ev;
75 if (to_land >= AUTOMATIC_HIT)
76 return 100;
77
78 if (ev <= 0)
79 return 100 - MIN_HIT_MISS_PERCENTAGE / 2;
80
81 int hits = 0;
82 for (int rolled_mhit = 0; rolled_mhit < to_land; rolled_mhit++)
83 {
84 // Apply post-roll manipulations:
85 int adjusted_mhit = rolled_mhit + mi.lighting_modifiers();
86
87 adjusted_mhit += atk.post_roll_to_hit_modifiers(adjusted_mhit, false);
88
89 // Duplicates ranged_attack::post_roll_to_hit_modifiers().
90 if (!melee && mi.is(MB_REPEL_MSL))
91 adjusted_mhit -= (adjusted_mhit + 1) / 2;
92
93 if (adjusted_mhit >= ev)
94 hits++;
95 }
96
97 double hit_chance = ((double)hits) / to_land;
98 // Apply Bayes Theorem to account for auto hit and miss.
99 hit_chance = hit_chance * (1 - MIN_HIT_MISS_PERCENTAGE / 200.0) + (1 - hit_chance) * MIN_HIT_MISS_PERCENTAGE / 200.0;
100
101 return (int)(hit_chance*100);
102 }
103
104 /**
105 * Return the base to-hit bonus that a monster with the given HD gets.
106 * @param hd The hit dice (level) of the monster.
107 * @param skilled Does the monster have bonus to-hit from the fighter or archer flag?
108 * @param ranged Is this attack ranged or melee?
109 *
110 * @return A base to-hit value, before equipment, statuses, etc.
111 */
mon_to_hit_base(int hd,bool skilled,bool ranged)112 int mon_to_hit_base(int hd, bool skilled, bool ranged)
113 {
114 if (ranged)
115 {
116 const int hd_mult = skilled ? 15 : 9;
117 return 18 + hd * hd_mult / 6;
118 }
119 const int hd_mult = skilled ? 25 : 15;
120 return 18 + hd * hd_mult / 10;
121 }
122
mon_shield_bypass(int hd)123 int mon_shield_bypass(int hd)
124 {
125 return 15 + hd * 2 / 3;
126 }
127
128 /**
129 * Return the odds of a monster attack with the given to-hit bonus hitting the given ev with the
130 * given EV, rounded to the nearest percent.
131 *
132 * TODO: deduplicate with to_hit_pct().
133 *
134 * @return To-hit percent between 0 and 100 (inclusive).
135 */
mon_to_hit_pct(int to_land,int ev)136 int mon_to_hit_pct(int to_land, int ev)
137 {
138 if (to_land >= AUTOMATIC_HIT)
139 return 100;
140
141 if (ev <= 0)
142 return 100 - MIN_HIT_MISS_PERCENTAGE / 2;
143
144 ++to_land; // per calc_to_hit()
145
146 int hits = 0;
147 for (int ev1 = 0; ev1 < ev; ev1++)
148 for (int ev2 = 0; ev2 < ev; ev2++)
149 hits += max(0, to_land - (ev1 + ev2));
150
151 double hit_chance = ((double)hits) / (to_land * ev * ev);
152
153 // Apply Bayes Theorem to account for auto hit and miss.
154 hit_chance = hit_chance * (1 - MIN_HIT_MISS_PERCENTAGE / 200.0)
155 + (1 - hit_chance) * MIN_HIT_MISS_PERCENTAGE / 200.0;
156
157 return (int)(hit_chance*100);
158 }
159
mon_beat_sh_pct(int bypass,int sh)160 int mon_beat_sh_pct(int bypass, int sh)
161 {
162 if (sh <= 0)
163 return 100;
164
165 sh *= 2; // per shield_bonus()
166
167 int hits = 0;
168 for (int sh1 = 0; sh1 < sh; sh1++)
169 {
170 for (int sh2 = 0; sh2 < sh; sh2++)
171 {
172 int adj_sh = (sh1 + sh2) / (3*2) - 1;
173 hits += max(0, bypass - adj_sh);
174 }
175 }
176 const int denom = sh * sh * bypass;
177 return hits * 100 / denom;
178 }
179
180 /**
181 * Switch from a bad weapon to melee.
182 *
183 * This function assumes some weapon is being wielded.
184 * @return whether a swap did occur.
185 */
_autoswitch_to_melee()186 static bool _autoswitch_to_melee()
187 {
188 bool penance;
189 if (is_melee_weapon(*you.weapon())
190 && !needs_handle_warning(*you.weapon(), OPER_ATTACK, penance))
191 {
192 return false;
193 }
194
195 int item_slot;
196 if (you.equip[EQ_WEAPON] == letter_to_index('a'))
197 item_slot = letter_to_index('b');
198 else if (you.equip[EQ_WEAPON] == letter_to_index('b'))
199 item_slot = letter_to_index('a');
200 else
201 return false;
202
203 if (!is_melee_weapon(you.inv[item_slot]))
204 return false;
205
206 return wield_weapon(true, item_slot);
207 }
208
209 /**
210 * Handle melee combat between attacker and defender.
211 *
212 * Works using the new fight rewrite. For a monster attacking, this method
213 * loops through all their available attacks, instantiating a new melee_attack
214 * for each attack. Combat effects should not go here, if at all possible. This
215 * is merely a wrapper function which is used to start combat.
216 *
217 * @param[in] attacker,defender The (non-null) participants in the attack.
218 * Either may be killed as a result of the attack.
219 * @param[out] did_hit If non-null, receives true if the attack hit the
220 * defender, and false otherwise.
221 * @param simu Is this a simulated attack? Disables a few problematic
222 * effects such as blood spatter and distortion teleports.
223 *
224 * @return Whether the attack took time (i.e. wasn't cancelled).
225 */
fight_melee(actor * attacker,actor * defender,bool * did_hit,bool simu)226 bool fight_melee(actor *attacker, actor *defender, bool *did_hit, bool simu)
227 {
228 ASSERT(attacker); // XXX: change to actor &attacker
229 ASSERT(defender); // XXX: change to actor &defender
230
231 // A dead defender would result in us returning true without actually
232 // taking an action.
233 ASSERT(defender->alive());
234
235 if (defender->is_player())
236 {
237 ASSERT(!crawl_state.game_is_arena());
238 // Friendly and good neutral monsters won't attack unless confused.
239 if (attacker->as_monster()->wont_attack()
240 && !mons_is_confused(*attacker->as_monster())
241 && !attacker->as_monster()->has_ench(ENCH_INSANE))
242 {
243 return false;
244 }
245
246 // In case the monster hasn't noticed you, bumping into it will
247 // change that.
248 behaviour_event(attacker->as_monster(), ME_ALERT, defender);
249 }
250 else if (attacker->is_player())
251 {
252 ASSERT(!crawl_state.game_is_arena());
253 // Can't damage orbs this way.
254 if (mons_is_projectile(defender->type) && !you.confused())
255 {
256 you.turn_is_over = false;
257 return false;
258 }
259
260 if (!simu && Options.auto_switch
261 && you.weapon()
262 && _autoswitch_to_melee())
263 {
264 return true; // Is this right? We did take time, but we didn't melee
265 }
266
267 melee_attack attk(&you, defender);
268
269 if (simu)
270 attk.simu = true;
271
272 // We're trying to hit a monster, break out of travel/explore now.
273 interrupt_activity(activity_interrupt::hit_monster,
274 defender->as_monster());
275
276 // Check if the player is fighting with something unsuitable,
277 // or someone unsuitable.
278 if (you.can_see(*defender) && !simu
279 && !wielded_weapon_check(attk.weapon))
280 {
281 you.turn_is_over = false;
282 return false;
283 }
284
285 if (!attk.attack())
286 {
287 // Attack was cancelled or unsuccessful...
288 if (attk.cancel_attack)
289 you.turn_is_over = false;
290 return !attk.cancel_attack;
291 }
292
293 if (did_hit)
294 *did_hit = attk.did_hit;
295
296 if (!simu && will_have_passive(passive_t::shadow_attacks))
297 dithmenos_shadow_melee(defender);
298
299 return true;
300 }
301
302 // If execution gets here, attacker != Player, so we can safely continue
303 // with processing the number of attacks a monster has without worrying
304 // about unpredictable or weird results from players.
305
306 const int nrounds = attacker->as_monster()->has_hydra_multi_attack()
307 ? attacker->heads() + MAX_NUM_ATTACKS - 1
308 : MAX_NUM_ATTACKS;
309 coord_def pos = defender->pos();
310
311 // Melee combat, tell attacker to wield its melee weapon.
312 attacker->as_monster()->wield_melee_weapon();
313
314 int effective_attack_number = 0;
315 int attack_number;
316 for (attack_number = 0; attack_number < nrounds && attacker->alive();
317 ++attack_number, ++effective_attack_number)
318 {
319 if (!attacker->alive())
320 return false;
321
322 // Monster went away?
323 if (!defender->alive()
324 || defender->pos() != pos
325 || defender->is_banished())
326 {
327 if (attacker == defender
328 || !attacker->as_monster()->has_multitargeting())
329 {
330 break;
331 }
332
333 // Hydras can try and pick up a new monster to attack to
334 // finish out their round. -cao
335 bool end = true;
336 for (adjacent_iterator i(attacker->pos()); i; ++i)
337 {
338 if (*i == you.pos()
339 && !mons_aligned(attacker, &you))
340 {
341 attacker->as_monster()->foe = MHITYOU;
342 attacker->as_monster()->target = you.pos();
343 defender = &you;
344 end = false;
345 break;
346 }
347
348 monster* mons = monster_at(*i);
349 if (mons && !mons_aligned(attacker, mons))
350 {
351 defender = mons;
352 end = false;
353 pos = mons->pos();
354 break;
355 }
356 }
357
358 // No adjacent hostiles.
359 if (end)
360 break;
361 }
362
363 if (!simu && attacker->is_monster()
364 && mons_attack_spec(*attacker->as_monster(), attack_number, true)
365 .flavour == AF_KITE
366 && attacker->as_monster()->foe_distance() == 1
367 && attacker->reach_range() == REACH_TWO
368 && x_chance_in_y(3, 5))
369 {
370 monster* mons = attacker->as_monster();
371 coord_def foepos = mons->get_foe()->pos();
372 coord_def hopspot = mons->pos() - (foepos - mons->pos()).sgn();
373
374 bool found = false;
375 if (!monster_habitable_grid(mons, env.grid(hopspot)) ||
376 actor_at(hopspot))
377 {
378 for (adjacent_iterator ai(mons->pos()); ai; ++ai)
379 {
380 if (ai->distance_from(foepos) != 2)
381 continue;
382 else
383 {
384 if (monster_habitable_grid(mons, env.grid(*ai))
385 && !actor_at(*ai))
386 {
387 hopspot = *ai;
388 found = true;
389 break;
390 }
391 }
392 }
393 }
394 else
395 found = true;
396
397 if (found)
398 {
399 const bool could_see = you.can_see(*mons);
400 if (mons->move_to_pos(hopspot))
401 {
402 if (could_see || you.can_see(*mons))
403 {
404 mprf("%s hops backward while attacking.",
405 mons->name(DESC_THE, true).c_str());
406 }
407 mons->speed_increment -= 2; // Add a small extra delay
408 }
409 }
410 }
411
412 melee_attack melee_attk(attacker, defender, attack_number,
413 effective_attack_number);
414
415 if (simu)
416 melee_attk.simu = true;
417
418 // If the attack fails out, keep effective_attack_number up to
419 // date so that we don't cause excess energy loss in monsters
420 if (!melee_attk.attack())
421 effective_attack_number = melee_attk.effective_attack_number;
422 else if (did_hit && !(*did_hit))
423 *did_hit = melee_attk.did_hit;
424
425 fire_final_effects();
426 }
427
428 return true;
429 }
430
431 /**
432 * If the given attacker attacks the given defender right now, what kind of
433 * extra-damage "stab" attack can the attacker perform, if any?
434 *
435 * @param attacker The attacker; may be null.
436 * @param defender The defender.
437 * @param actual True if we're actually committing to a stab, false if we're
438 * just checking for display purposes.
439 * @return The best (most damaging) kind of stab available to the
440 * attacker against this defender, or STAB_NO_STAB.
441 */
find_stab_type(const actor * attacker,const actor & defender,bool actual)442 stab_type find_stab_type(const actor *attacker,
443 const actor &defender,
444 bool actual)
445 {
446 const monster* def = defender.as_monster();
447
448 // Stabbing monsters is unchivalric, and disabled under TSO!
449 // When just checking for display purposes, still indicate when monsters
450 // are sleeping/paralysed etc.
451 if (actual && attacker && attacker->is_player()
452 && def && have_passive(passive_t::no_stabbing))
453 {
454 return STAB_NO_STAB;
455 }
456
457 // No stabbing monsters that cannot fight (e.g. plants) or monsters
458 // the attacker can't see (either due to invisibility or being behind
459 // opaque clouds).
460 if (def && mons_is_firewood(*def))
461 return STAB_NO_STAB;
462
463 if (attacker && !attacker->can_see(defender))
464 return STAB_NO_STAB;
465
466 // sleeping
467 if (defender.asleep())
468 return STAB_SLEEPING;
469
470 // paralysed
471 if (defender.paralysed())
472 return STAB_PARALYSED;
473
474 // petrified
475 if (defender.petrified())
476 return STAB_PETRIFIED;
477
478 // petrifying
479 if (def && def->petrifying())
480 return STAB_PETRIFYING;
481
482 // held in a net
483 if (def && def->caught())
484 return STAB_HELD_IN_NET;
485
486 // invisible
487 if (attacker && !attacker->visible_to(&defender))
488 return STAB_INVISIBLE;
489
490 // fleeing
491 if (def && mons_is_fleeing(*def))
492 return STAB_FLEEING;
493
494 // allies
495 if (def && def->friendly())
496 return STAB_ALLY;
497
498 // confused (but not perma-confused)
499 if (def && mons_is_confused(*def, false))
500 return STAB_CONFUSED;
501
502 // Distracted (but not batty); this only applies to players.
503 if (attacker && attacker->is_player()
504 && def && def->foe != MHITYOU && !mons_is_batty(*def))
505 {
506 return STAB_DISTRACTED;
507 }
508
509 return STAB_NO_STAB;
510 }
511
512 /**
513 * What bonus does this type of stab give the player when attacking?
514 *
515 * @param The type of stab in question; e.g. STAB_SLEEPING.
516 * @return The bonus the stab gives. Note that this is used as a divisor for
517 * damage, so the larger the value we return here, the less bonus
518 * damage will be done.
519 */
stab_bonus_denom(stab_type stab)520 int stab_bonus_denom(stab_type stab)
521 {
522 // XXX: if we don't get rid of this logic, turn it into a static array.
523 switch (stab)
524 {
525 case STAB_NO_STAB:
526 case NUM_STABS:
527 return 0;
528 case STAB_SLEEPING:
529 case STAB_PARALYSED:
530 case STAB_PETRIFIED:
531 return 1;
532 default:
533 return 4;
534 }
535 }
536
is_boolean_resist(beam_type flavour)537 static bool is_boolean_resist(beam_type flavour)
538 {
539 switch (flavour)
540 {
541 case BEAM_ELECTRICITY:
542 case BEAM_MIASMA:
543 case BEAM_STICKY_FLAME:
544 case BEAM_WATER: // water asphyxiation damage,
545 // bypassed by being water inhabitant.
546 case BEAM_POISON:
547 case BEAM_POISON_ARROW:
548 return true;
549 default:
550 return false;
551 }
552 }
553
554 // Gets the percentage of the total damage of this damage flavour that can
555 // be resisted.
get_resistible_fraction(beam_type flavour)556 static inline int get_resistible_fraction(beam_type flavour)
557 {
558 switch (flavour)
559 {
560 // Drowning damage from water is resistible by being a water thing, or
561 // otherwise asphyx resistant.
562 case BEAM_WATER:
563 return 40;
564
565 // Assume ice storm and throw icicle are mostly solid.
566 case BEAM_ICE:
567 return 40;
568
569 // 50/50 split of elec and sonic damage.
570 case BEAM_THUNDER:
571 return 50;
572
573 case BEAM_LAVA:
574 return 55;
575
576 case BEAM_POISON_ARROW:
577 return 70;
578
579 default:
580 return 100;
581 }
582 }
583
_beam_to_resist(const actor * defender,beam_type flavour)584 static int _beam_to_resist(const actor* defender, beam_type flavour)
585 {
586 switch (flavour)
587 {
588 case BEAM_FIRE:
589 case BEAM_LAVA:
590 return defender->res_fire();
591 case BEAM_DAMNATION:
592 return defender->res_damnation();
593 case BEAM_STEAM:
594 return defender->res_steam();
595 case BEAM_COLD:
596 case BEAM_ICE:
597 return defender->res_cold();
598 case BEAM_WATER:
599 return defender->res_water_drowning();
600 case BEAM_ELECTRICITY:
601 case BEAM_THUNDER:
602 case BEAM_STUN_BOLT:
603 return defender->res_elec();
604 case BEAM_NEG:
605 case BEAM_PAIN:
606 case BEAM_MALIGN_OFFERING:
607 case BEAM_VAMPIRIC_DRAINING:
608 return defender->res_negative_energy();
609 case BEAM_ACID:
610 return defender->res_acid();
611 case BEAM_POISON:
612 case BEAM_POISON_ARROW:
613 return defender->res_poison();
614 case BEAM_HOLY:
615 return defender->res_holy_energy();
616 default:
617 return 0;
618 }
619 }
620
621
622 /**
623 * Adjusts damage for elemental resists, electricity and poison.
624 *
625 * For players, damage is reduced to 1/2, 1/3, or 1/5 if res has values 1, 2,
626 * or 3, respectively. "Boolean" resists (rElec, rPois) reduce damage to 1/3.
627 * rN is a special case that reduces damage to 1/2, 1/4, 0 instead.
628 *
629 * For monsters, damage is reduced to 1/2, 1/5, and 0 for 1/2/3 resistance.
630 * "Boolean" resists give 1/3, 1/6, 0 instead.
631 *
632 * @param defender The victim of the attack.
633 * @param flavour The type of attack having its damage adjusted.
634 * (Does not necessarily imply the attack is a beam.)
635 * @param rawdamage The base damage, to be adjusted by resistance.
636 * @return The amount of damage done, after resists are applied.
637 */
resist_adjust_damage(const actor * defender,beam_type flavour,int rawdamage)638 int resist_adjust_damage(const actor* defender, beam_type flavour, int rawdamage)
639 {
640 const int res = _beam_to_resist(defender, flavour);
641 if (!res)
642 return rawdamage;
643
644 const bool is_mon = defender->is_monster();
645
646 const int resistible_fraction = get_resistible_fraction(flavour);
647
648 int resistible = rawdamage * resistible_fraction / 100;
649 const int irresistible = rawdamage - resistible;
650
651 if (res > 0)
652 {
653 const bool immune_at_3_res = is_mon
654 || flavour == BEAM_NEG
655 || flavour == BEAM_PAIN
656 || flavour == BEAM_MALIGN_OFFERING
657 || flavour == BEAM_VAMPIRIC_DRAINING
658 || flavour == BEAM_HOLY
659 || flavour == BEAM_POISON
660 // just the resistible part
661 || flavour == BEAM_POISON_ARROW;
662
663 if (immune_at_3_res && res >= 3 || res > 3)
664 resistible = 0;
665 else
666 {
667 // Is this a resist that claims to be boolean for damage purposes?
668 const int bonus_res = (is_boolean_resist(flavour) ? 1 : 0);
669
670 // Monster resistances are stronger than player versions.
671 if (is_mon)
672 resistible /= 1 + bonus_res + res * res;
673 else if (flavour == BEAM_NEG
674 || flavour == BEAM_PAIN
675 || flavour == BEAM_MALIGN_OFFERING
676 || flavour == BEAM_VAMPIRIC_DRAINING)
677 {
678 resistible /= res * 2;
679 }
680 else
681 resistible /= (3 * res + 1) / 2 + bonus_res;
682 }
683 }
684 else if (res < 0)
685 resistible = resistible * 15 / 10;
686
687 return max(resistible + irresistible, 0);
688 }
689
690 // Reduce damage by AC.
691 // In most cases, we want AC to mostly stop weak attacks completely but affect
692 // strong ones less, but the regular formula is too hard to apply well to cases
693 // when damage is spread into many small chunks.
694 //
695 // Every point of damage is processed independently. Every point of AC has
696 // an independent 1/81 chance of blocking that damage.
697 //
698 // AC 20 stops 22% of damage, AC 40 -- 39%, AC 80 -- 63%.
apply_chunked_AC(int dam,int ac)699 int apply_chunked_AC(int dam, int ac)
700 {
701 double chance = pow(80.0/81, ac);
702 uint64_t cr = chance * (((uint64_t)1) << 32);
703
704 int hurt = 0;
705 for (int i = 0; i < dam; i++)
706 if (rng::get_uint32() < cr)
707 hurt++;
708
709 return hurt;
710 }
711
712 ///////////////////////////////////////////////////////////////////////////
713
wielded_weapon_check(const item_def * weapon,string attack_verb)714 bool wielded_weapon_check(const item_def *weapon, string attack_verb)
715 {
716 bool penance = false;
717 if (you.received_weapon_warning
718 || (weapon
719 && !needs_handle_warning(*weapon, OPER_ATTACK, penance)
720 && is_melee_weapon(*weapon))
721 || you.confused())
722 {
723 return true;
724 }
725
726 // Don't pester the player if they're using UC, in treeform,
727 // or if they don't have any melee weapons yet.
728 if (!weapon
729 && (you.skill(SK_UNARMED_COMBAT) > 0
730 || you.form == transformation::tree
731 || !any_of(you.inv.begin(), you.inv.end(),
732 [](item_def &it)
733 { return is_melee_weapon(it) && can_wield(&it); })))
734 {
735 return true;
736 }
737
738 string prompt;
739 prompt = make_stringf("Really %s while wielding %s?",
740 attack_verb.size() ? attack_verb.c_str() : "attack",
741 weapon ? weapon->name(DESC_YOUR).c_str() : "nothing");
742 if (penance)
743 prompt += " This could place you under penance!";
744
745 const bool result = yesno(prompt.c_str(), true, 'n');
746
747 if (!result)
748 canned_msg(MSG_OK);
749
750 learned_something_new(HINT_WIELD_WEAPON); // for hints mode Rangers
751
752 // Don't warn again if you decide to continue your attack.
753 if (result)
754 you.received_weapon_warning = true;
755
756 return result;
757 }
758
759 /**
760 * Should the given attacker cleave into the given victim with an axe or axe-
761 * like weapon?
762 *
763 * @param attacker The creature doing the cleaving.
764 * @param defender The potential cleave-ee.
765 * @return True if the defender is an enemy of the defender; false
766 * otherwise.
767 */
_dont_harm(const actor & attacker,const actor & defender)768 static bool _dont_harm(const actor &attacker, const actor &defender)
769 {
770 if (mons_aligned(&attacker, &defender))
771 return true;
772
773 if (defender.is_player())
774 return attacker.wont_attack();
775
776 if (attacker.is_player())
777 {
778 return defender.wont_attack()
779 || mons_attitude(*defender.as_monster()) == ATT_NEUTRAL;
780 }
781
782 return false;
783 }
784
785 /**
786 * Force cleave attacks. Used for melee actions that don't have targets, e.g.
787 * attacking empty space (otherwise, cleaving is handled in melee_attack).
788 *
789 * @param target the nominal target of the original attack.
790 * @return whether there were cleave targets relative to the player and `target`.
791 */
force_player_cleave(coord_def target)792 bool force_player_cleave(coord_def target)
793 {
794 list<actor*> cleave_targets;
795 get_cleave_targets(you, target, cleave_targets);
796
797 if (!cleave_targets.empty())
798 {
799 targeter_cleave hitfunc(&you, target);
800 if (stop_attack_prompt(hitfunc, "attack"))
801 return true;
802
803 if (!you.fumbles_attack())
804 attack_cleave_targets(you, cleave_targets);
805 return true;
806 }
807
808 return false;
809 }
810
attack_cleaves(const actor & attacker,int which_attack)811 bool attack_cleaves(const actor &attacker, int which_attack)
812 {
813 const item_def* weap = attacker.weapon(which_attack);
814
815 return weap && item_attack_skill(*weap) == SK_AXES
816 || attacker.is_player()
817 && (you.form == transformation::storm
818 || you.duration[DUR_CLEAVE]);
819 }
820
821 /**
822 * List potential cleave targets (adjacent hostile creatures), including the
823 * defender itself.
824 *
825 * @param attacker[in] The attacking creature.
826 * @param def[in] The location of the targeted defender.
827 * @param targets[out] A list to be populated with targets.
828 * @param which_attack The attack_number (default -1, which uses the default weapon).
829 */
get_cleave_targets(const actor & attacker,const coord_def & def,list<actor * > & targets,int which_attack)830 void get_cleave_targets(const actor &attacker, const coord_def& def,
831 list<actor*> &targets, int which_attack)
832 {
833 // Prevent scanning invalid coordinates if the attacker dies partway through
834 // a cleave (due to hitting explosive creatures, or perhaps other things)
835 if (!attacker.alive())
836 return;
837
838 if (actor_at(def))
839 targets.push_back(actor_at(def));
840
841 if (attack_cleaves(attacker, which_attack))
842 {
843 const coord_def atk = attacker.pos();
844 coord_def atk_vector = def - atk;
845 const int dir = random_choose(-1, 1);
846
847 for (int i = 0; i < 7; ++i)
848 {
849 atk_vector = rotate_adjacent(atk_vector, dir);
850
851 actor *target = actor_at(atk + atk_vector);
852 if (target && !_dont_harm(attacker, *target))
853 targets.push_back(target);
854 }
855 }
856
857 // fake cleaving: gyre and gimble's extra attacks are implemented as
858 // cleaving attacks on enemies already in `targets`
859 const item_def* weap = attacker.weapon(which_attack);
860 if (weap && is_unrandom_artefact(*weap, UNRAND_GYRE))
861 {
862 list<actor*> new_targets;
863 for (actor* targ : targets)
864 {
865 new_targets.push_back(targ);
866 new_targets.push_back(targ);
867 }
868 targets = new_targets;
869 }
870 }
871
872 /**
873 * Attack a provided list of cleave targets.
874 *
875 * @param attacker The attacking creature.
876 * @param targets The targets to cleave.
877 * @param attack_number ?
878 * @param effective_attack_number ?
879 */
attack_cleave_targets(actor & attacker,list<actor * > & targets,int attack_number,int effective_attack_number,wu_jian_attack_type wu_jian_attack,bool is_projected)880 void attack_cleave_targets(actor &attacker, list<actor*> &targets,
881 int attack_number, int effective_attack_number,
882 wu_jian_attack_type wu_jian_attack,
883 bool is_projected)
884 {
885 if (attacker.is_player())
886 {
887 const item_def* weap = attacker.weapon(attack_number);
888
889 if ((wu_jian_attack == WU_JIAN_ATTACK_WHIRLWIND
890 || wu_jian_attack == WU_JIAN_ATTACK_WALL_JUMP
891 || wu_jian_attack == WU_JIAN_ATTACK_TRIGGERED_AUX)
892 && !(weap && is_unrandom_artefact(*weap, UNRAND_GYRE)))
893 {
894 return; // WJC AOE attacks don't cleave, but G&G use cleaving
895 // XXX: If a player under Xom wrath gets cleaving while using G&G and
896 // worshiping Wu they'll be able to cleave their Wu attacks.
897 }
898 }
899
900 while (attacker.alive() && !targets.empty())
901 {
902 actor* def = targets.front();
903
904 if (def && def->alive() && !_dont_harm(attacker, *def)
905 && (is_projected || adjacent(attacker.pos(), def->pos())))
906 {
907 melee_attack attck(&attacker, def, attack_number,
908 ++effective_attack_number, true);
909
910 attck.wu_jian_attack = wu_jian_attack;
911 attck.is_projected = is_projected;
912 attck.attack();
913 }
914 targets.pop_front();
915 }
916 }
917
918 /**
919 * What skill is required to reach mindelay with a weapon? May be >27.
920 * @param weapon The weapon to be considered.
921 * @returns The level of the relevant skill you must reach.
922 */
weapon_min_delay_skill(const item_def & weapon)923 int weapon_min_delay_skill(const item_def &weapon)
924 {
925 const int speed = property(weapon, PWPN_SPEED);
926 const int mindelay = weapon_min_delay(weapon, false);
927 return (speed - mindelay) * 2;
928 }
929
930 /**
931 * How fast will this weapon get from your skill training?
932 *
933 * @param weapon the weapon to be considered.
934 * @param check_speed whether to take it into account if the weapon has the
935 * speed brand.
936 * @return How many aut the fastest possible attack with this weapon would take.
937 */
weapon_min_delay(const item_def & weapon,bool check_speed)938 int weapon_min_delay(const item_def &weapon, bool check_speed)
939 {
940 const int base = property(weapon, PWPN_SPEED);
941 int min_delay = base/2;
942
943 // Short blades can get up to at least unarmed speed.
944 if (item_attack_skill(weapon) == SK_SHORT_BLADES && min_delay > 5)
945 min_delay = 5;
946
947 // All weapons have min delay 7 or better
948 if (min_delay > 7)
949 min_delay = 7;
950
951 // ...except crossbows...
952 if (item_attack_skill(weapon) == SK_CROSSBOWS && min_delay < 10)
953 min_delay = 10;
954
955 // ... and unless it would take more than skill 27 to get there.
956 // Round up the reduction from skill, so that min delay is rounded down.
957 min_delay = max(min_delay, base - (MAX_SKILL_LEVEL + 1)/2);
958
959 if (check_speed && get_weapon_brand(weapon) == SPWPN_SPEED)
960 {
961 min_delay *= 2;
962 min_delay /= 3;
963 }
964
965 // never go faster than speed 3 (ie 3.33 attacks per round)
966 if (min_delay < 3)
967 min_delay = 3;
968
969 return min_delay;
970 }
971
mons_weapon_damage_rating(const item_def & launcher)972 int mons_weapon_damage_rating(const item_def &launcher)
973 {
974 return property(launcher, PWPN_DAMAGE) + launcher.plus;
975 }
976
977 // Returns a rough estimate of damage from firing/throwing missile.
mons_missile_damage(monster * mons,const item_def * launch,const item_def * missile)978 int mons_missile_damage(monster* mons, const item_def *launch,
979 const item_def *missile)
980 {
981 if (!missile || (!launch && !is_throwable(mons, *missile)))
982 return 0;
983
984 const int missile_damage = property(*missile, PWPN_DAMAGE) / 2 + 1;
985 const int launch_damage = launch? property(*launch, PWPN_DAMAGE) : 0;
986 return max(0, launch_damage + missile_damage);
987 }
988
mons_usable_missile(monster * mons,item_def ** launcher)989 int mons_usable_missile(monster* mons, item_def **launcher)
990 {
991 *launcher = nullptr;
992 item_def *launch = nullptr;
993 for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i)
994 {
995 if (item_def *item = mons->mslot_item(static_cast<mon_inv_type>(i)))
996 {
997 if (is_range_weapon(*item))
998 launch = item;
999 }
1000 }
1001
1002 const item_def *missiles = mons->missiles();
1003 if (launch && missiles && !missiles->launched_by(*launch))
1004 launch = nullptr;
1005
1006 const int fdam = mons_missile_damage(mons, launch, missiles);
1007
1008 if (!fdam)
1009 return NON_ITEM;
1010 else
1011 {
1012 *launcher = launch;
1013 return missiles->index();
1014 }
1015 }
1016
bad_attack(const monster * mon,string & adj,string & suffix,bool & would_cause_penance,coord_def attack_pos)1017 bool bad_attack(const monster *mon, string& adj, string& suffix,
1018 bool& would_cause_penance, coord_def attack_pos)
1019 {
1020 ASSERT(mon); // XXX: change to const monster &mon
1021 ASSERT(!crawl_state.game_is_arena());
1022
1023 if (!you.can_see(*mon))
1024 return false;
1025
1026 if (attack_pos == coord_def(0, 0))
1027 attack_pos = you.pos();
1028
1029 adj.clear();
1030 suffix.clear();
1031 would_cause_penance = false;
1032
1033 if (is_sanctuary(mon->pos()) || is_sanctuary(attack_pos))
1034 suffix = ", despite your sanctuary";
1035
1036 if (you.duration[DUR_LIFESAVING]
1037 && mon->holiness() & (MH_NATURAL | MH_PLANT))
1038 {
1039 suffix = " while asking for your life to be spared";
1040 would_cause_penance = true;
1041 }
1042
1043 if (you_worship(GOD_JIYVA) && mons_is_slime(*mon)
1044 && !(mon->is_shapeshifter() && (mon->flags & MF_KNOWN_SHIFTER)))
1045 {
1046 would_cause_penance = true;
1047 return true;
1048 }
1049
1050 if (mon->friendly())
1051 {
1052 if (god_hates_attacking_friend(you.religion, *mon))
1053 {
1054 adj = "your ally ";
1055
1056 monster_info mi(mon, MILEV_NAME);
1057 if (!mi.is(MB_NAME_UNQUALIFIED))
1058 adj += "the ";
1059
1060 would_cause_penance = true;
1061
1062 }
1063 else
1064 {
1065 adj = "your ";
1066
1067 monster_info mi(mon, MILEV_NAME);
1068 if (mi.is(MB_NAME_UNQUALIFIED))
1069 adj += "ally ";
1070 }
1071
1072 return true;
1073 }
1074
1075 if (mon->neutral() && is_good_god(you.religion))
1076 {
1077 adj += "neutral ";
1078 if (you_worship(GOD_SHINING_ONE) || you_worship(GOD_ELYVILON))
1079 would_cause_penance = true;
1080 }
1081 else if (mon->wont_attack())
1082 {
1083 adj += "non-hostile ";
1084 if (you_worship(GOD_SHINING_ONE) || you_worship(GOD_ELYVILON))
1085 would_cause_penance = true;
1086 }
1087
1088 return !adj.empty() || !suffix.empty();
1089 }
1090
stop_attack_prompt(const monster * mon,bool beam_attack,coord_def beam_target,bool * prompted,coord_def attack_pos)1091 bool stop_attack_prompt(const monster* mon, bool beam_attack,
1092 coord_def beam_target, bool *prompted,
1093 coord_def attack_pos)
1094 {
1095 ASSERT(mon); // XXX: change to const monster &mon
1096 bool penance = false;
1097
1098 if (prompted)
1099 *prompted = false;
1100
1101 if (crawl_state.disables[DIS_CONFIRMATIONS])
1102 return false;
1103
1104 if (you.confused() || !you.can_see(*mon))
1105 return false;
1106
1107 string adj, suffix;
1108 if (!bad_attack(mon, adj, suffix, penance, attack_pos))
1109 return false;
1110
1111 // Listed in the form: "your rat", "Blork the orc".
1112 string mon_name = mon->name(DESC_PLAIN);
1113 if (starts_with(mon_name, "the ")) // no "your the Royal Jelly" nor "the the RJ"
1114 mon_name = mon_name.substr(4); // strlen("the ")
1115 if (!starts_with(adj, "your"))
1116 adj = "the " + adj;
1117 mon_name = adj + mon_name;
1118 string verb;
1119 if (beam_attack)
1120 {
1121 verb = "fire ";
1122 if (beam_target == mon->pos())
1123 verb += "at ";
1124 else
1125 {
1126 verb += "in " + apostrophise(mon_name) + " direction";
1127 mon_name = "";
1128 }
1129 }
1130 else
1131 verb = "attack ";
1132
1133 const string prompt = make_stringf("Really %s%s%s?%s",
1134 verb.c_str(), mon_name.c_str(), suffix.c_str(),
1135 penance ? " This attack would place you under penance!" : "");
1136
1137 if (prompted)
1138 *prompted = true;
1139
1140 if (yesno(prompt.c_str(), false, 'n'))
1141 return false;
1142 else
1143 {
1144 canned_msg(MSG_OK);
1145 return true;
1146 }
1147 }
1148
stop_attack_prompt(targeter & hitfunc,const char * verb,function<bool (const actor * victim)> affects,bool * prompted,const monster * defender)1149 bool stop_attack_prompt(targeter &hitfunc, const char* verb,
1150 function<bool(const actor *victim)> affects,
1151 bool *prompted, const monster *defender)
1152 {
1153 if (crawl_state.disables[DIS_CONFIRMATIONS])
1154 return false;
1155
1156 if (crawl_state.which_god_acting() == GOD_XOM)
1157 return false;
1158
1159 if (you.confused())
1160 return false;
1161
1162 string adj, suffix;
1163 bool penance = false;
1164 bool defender_ok = true;
1165 counted_monster_list victims;
1166 for (distance_iterator di(hitfunc.origin, false, true, LOS_RADIUS); di; ++di)
1167 {
1168 if (hitfunc.is_affected(*di) <= AFF_NO)
1169 continue;
1170
1171 const monster* mon = monster_at(*di);
1172 if (!mon || !you.can_see(*mon))
1173 continue;
1174
1175 if (affects && !affects(mon))
1176 continue;
1177
1178 string adjn, suffixn;
1179 bool penancen = false;
1180 if (bad_attack(mon, adjn, suffixn, penancen))
1181 {
1182 // record the adjectives for the first listed, or
1183 // first that would cause penance
1184 if (victims.empty() || penancen && !penance)
1185 adj = adjn, suffix = suffixn, penance = penancen;
1186
1187 victims.add(mon);
1188
1189 if (defender && defender == mon)
1190 defender_ok = false;
1191 }
1192 }
1193
1194 if (victims.empty())
1195 return false;
1196
1197 // Listed in the form: "your rat", "Blork the orc".
1198 string mon_name = victims.describe(DESC_PLAIN);
1199 if (starts_with(mon_name, "the ")) // no "your the Royal Jelly" nor "the the RJ"
1200 mon_name = mon_name.substr(4); // strlen("the ")
1201 if (!starts_with(adj, "your"))
1202 adj = "the " + adj;
1203 mon_name = adj + mon_name;
1204
1205 const string prompt = make_stringf("Really %s%s %s%s?%s",
1206 verb, defender_ok ? " near" : "", mon_name.c_str(),
1207 suffix.c_str(),
1208 penance ? " This attack would place you under penance!" : "");
1209
1210 if (prompted)
1211 *prompted = true;
1212
1213 if (yesno(prompt.c_str(), false, 'n'))
1214 return false;
1215 else
1216 {
1217 canned_msg(MSG_OK);
1218 return true;
1219 }
1220 }
1221
rude_stop_summoning_reason()1222 string rude_stop_summoning_reason()
1223 {
1224 if (you.duration[DUR_TOXIC_RADIANCE])
1225 return "toxic aura";
1226
1227 if (you.duration[DUR_NOXIOUS_BOG])
1228 return "noxious bog";
1229
1230 if (you.duration[DUR_VORTEX])
1231 return "polar vortex";
1232
1233 return "";
1234 }
1235
1236 /**
1237 * Does the player have a hostile duration up that would/could cause
1238 * a summon to be abjured? If so, prompt the player as to whether they
1239 * want to continue to create their summon. Note that this prompt is never a
1240 * penance prompt, because we don't cause penance when monsters enter line of
1241 * sight when OTR is active, regardless of how they entered LOS.
1242 *
1243 * @param verb The verb to be used in the prompt. Defaults to "summon".
1244 * @return True if the player wants to abort.
1245 */
rude_stop_summoning_prompt(string verb)1246 bool rude_stop_summoning_prompt(string verb)
1247 {
1248 string which = rude_stop_summoning_reason();
1249
1250 if (which.empty())
1251 return false;
1252
1253 if (crawl_state.disables[DIS_CONFIRMATIONS])
1254 return false;
1255
1256 if (crawl_state.which_god_acting() == GOD_XOM)
1257 return false;
1258
1259 string prompt = make_stringf("Really %s while emitting a %s?",
1260 verb.c_str(), which.c_str());
1261
1262 if (yesno(prompt.c_str(), false, 'n'))
1263 return false;
1264 else
1265 {
1266 canned_msg(MSG_OK);
1267 return true;
1268 }
1269 }
1270
can_reach_attack_between(coord_def source,coord_def target)1271 bool can_reach_attack_between(coord_def source, coord_def target)
1272 {
1273 const coord_def delta(target - source);
1274 const int grid_distance(delta.rdist());
1275 const coord_def first_middle(source + delta / 2);
1276 const coord_def second_middle(target - delta / 2);
1277
1278 return grid_distance == 2
1279 // And with no dungeon furniture in the way of the reaching
1280 // attack;
1281 && (feat_is_reachable_past(env.grid(first_middle))
1282 || feat_is_reachable_past(env.grid(second_middle)))
1283 // The foe should be on the map (not stepped from time).
1284 && in_bounds(target);
1285 }
1286