1 /**
2  * @file
3  * @brief melee_attack class and associated melee_attack methods
4  */
5 
6 #include "AppHdr.h"
7 
8 #include "melee-attack.h"
9 
10 #include <algorithm>
11 #include <cstdio>
12 #include <cstdlib>
13 #include <cstring>
14 
15 #include "areas.h"
16 #include "art-enum.h"
17 #include "attitude-change.h"
18 #include "bloodspatter.h"
19 #include "chardump.h"
20 #include "cloud.h"
21 #include "delay.h"
22 #include "english.h"
23 #include "env.h"
24 #include "exercise.h"
25 #include "fineff.h"
26 #include "god-conduct.h"
27 #include "god-item.h"
28 #include "god-passive.h" // passive_t::convert_orcs
29 #include "hints.h"
30 #include "item-prop.h"
31 #include "mapdef.h"
32 #include "message.h"
33 #include "mon-behv.h"
34 #include "mon-poly.h"
35 #include "mon-tentacle.h"
36 #include "religion.h"
37 #include "shout.h"
38 #include "spl-damage.h"
39 #include "state.h"
40 #include "stepdown.h"
41 #include "stringutil.h"
42 #include "tag-version.h"
43 #include "target.h"
44 #include "terrain.h"
45 #include "transform.h"
46 #include "traps.h"
47 #include "unwind.h"
48 #include "view.h"
49 #include "xom.h"
50 
51 #ifdef NOTE_DEBUG_CHAOS_BRAND
52     #define NOTE_DEBUG_CHAOS_EFFECTS
53 #endif
54 
55 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
56     #include "notes.h"
57 #endif
58 
59 /*
60  **************************************************
61  *             BEGIN PUBLIC FUNCTIONS             *
62  **************************************************
63 */
melee_attack(actor * attk,actor * defn,int attack_num,int effective_attack_num,bool is_cleaving)64 melee_attack::melee_attack(actor *attk, actor *defn,
65                            int attack_num, int effective_attack_num,
66                            bool is_cleaving)
67     :  // Call attack's constructor
68     ::attack(attk, defn),
69 
70     attack_number(attack_num), effective_attack_number(effective_attack_num),
71     cleaving(is_cleaving), is_riposte(false), is_projected(false), roll_dist(0),
72     wu_jian_attack(WU_JIAN_ATTACK_NONE),
73     wu_jian_number_of_targets(1)
74 {
75     attack_occurred = false;
76     damage_brand = attacker->damage_brand(attack_number);
77     init_attack(SK_UNARMED_COMBAT, attack_number);
78     if (weapon && !using_weapon())
79         wpn_skill = SK_FIGHTING;
80 
81     attack_position = attacker->pos();
82 }
83 
can_reach()84 bool melee_attack::can_reach()
85 {
86     return attk_type == AT_HIT && weapon && weapon_reach(*weapon) > REACH_NONE
87            || flavour_has_reach(attk_flavour)
88            || is_projected;
89 }
90 
handle_phase_attempted()91 bool melee_attack::handle_phase_attempted()
92 {
93     // Skip invalid and dummy attacks.
94     if (defender && (!adjacent(attack_position, defender->pos())
95                      && !can_reach())
96         || attk_flavour == AF_CRUSH
97            && (!attacker->can_constrict(defender, true)
98                || attacker->is_monster() && attacker->mid == MID_PLAYER))
99     {
100         --effective_attack_number;
101 
102         return false;
103     }
104 
105     if (attacker->is_player() && defender && defender->is_monster())
106     {
107         // Unrands with secondary effects that can harm nearby friendlies.
108         // Don't prompt for confirmation (and leak information about the
109         // monster's position) if the player can't see the monster.
110         if (weapon && is_unrandom_artefact(*weapon, UNRAND_DEVASTATOR)
111             && you.can_see(*defender))
112         {
113 
114             targeter_smite hitfunc(attacker, 1, 1, 1, false);
115             hitfunc.set_aim(defender->pos());
116 
117             if (stop_attack_prompt(hitfunc, "attack", nullptr, nullptr,
118                                    defender->as_monster()))
119             {
120                 cancel_attack = true;
121                 return false;
122             }
123         }
124         else if (weapon &&
125                 ((is_unrandom_artefact(*weapon, UNRAND_SINGING_SWORD)
126                   && !silenced(you.pos()))
127                  || is_unrandom_artefact(*weapon, UNRAND_VARIABILITY))
128                  && you.can_see(*defender))
129         {
130             targeter_radius hitfunc(&you, LOS_NO_TRANS);
131 
132             if (stop_attack_prompt(hitfunc, "attack",
133                                    [](const actor *act)
134                                    {
135                                        return !god_protects(act->as_monster());
136                                    }, nullptr, defender->as_monster()))
137             {
138                 cancel_attack = true;
139                 return false;
140             }
141         }
142         else if (weapon && is_unrandom_artefact(*weapon, UNRAND_TORMENT)
143                  && you.can_see(*defender))
144         {
145             targeter_radius hitfunc(&you, LOS_NO_TRANS);
146 
147             if (stop_attack_prompt(hitfunc, "attack",
148                                    [] (const actor *m)
149                                    {
150                                        return !m->res_torment();
151                                    },
152                                    nullptr, defender->as_monster()))
153             {
154                 cancel_attack = true;
155                 return false;
156             }
157         }
158         else if (weapon && is_unrandom_artefact(*weapon, UNRAND_ARC_BLADE)
159                  && you.can_see(*defender))
160         {
161             vector<const actor *> exclude;
162             if (!safe_discharge(defender->pos(), exclude))
163             {
164                 cancel_attack = true;
165                 return false;
166             }
167         }
168         else if (weapon && is_unrandom_artefact(*weapon, UNRAND_POWER)
169                  && you.can_see(*defender))
170         {
171             targeter_beam hitfunc(&you, 4, ZAP_SWORD_BEAM, 100, 0, 0);
172             hitfunc.beam.aimed_at_spot = false;
173             hitfunc.set_aim(defender->pos());
174 
175             if (stop_attack_prompt(hitfunc, "attack",
176                                    [](const actor *act)
177                                    {
178                                        return !god_protects(act->as_monster());
179                                    }, nullptr, defender->as_monster()))
180             {
181                 cancel_attack = true;
182                 return false;
183             }
184         }
185         else if (!cleave_targets.empty())
186         {
187             targeter_cleave hitfunc(attacker, defender->pos());
188             if (stop_attack_prompt(hitfunc, "attack"))
189             {
190                 cancel_attack = true;
191                 return false;
192             }
193         }
194         else if (stop_attack_prompt(defender->as_monster(), false,
195                                     attack_position))
196         {
197             cancel_attack = true;
198             return false;
199         }
200     }
201 
202     if (attacker->is_player())
203     {
204         // Set delay now that we know the attack won't be cancelled.
205         if (!is_riposte
206              && (wu_jian_attack == WU_JIAN_ATTACK_NONE))
207         {
208             you.time_taken = you.attack_delay().roll();
209         }
210 
211         const caction_type cact_typ = is_riposte ? CACT_RIPOSTE : CACT_MELEE;
212         if (weapon)
213         {
214             if (weapon->base_type == OBJ_WEAPONS)
215                 if (is_unrandom_artefact(*weapon)
216                     && get_unrand_entry(weapon->unrand_idx)->type_name)
217                 {
218                     count_action(cact_typ, weapon->unrand_idx);
219                 }
220                 else
221                     count_action(cact_typ, weapon->sub_type);
222             else if (weapon->base_type == OBJ_STAVES)
223                 count_action(cact_typ, WPN_STAFF);
224         }
225         else
226             count_action(cact_typ, -1, -1); // unarmed subtype/auxtype
227     }
228     else
229     {
230         // Only the first attack costs any energy.
231         if (!effective_attack_number)
232         {
233             int energy = attacker->as_monster()->action_energy(EUT_ATTACK);
234             int delay = attacker->attack_delay().roll();
235             dprf(DIAG_COMBAT, "Attack delay %d, multiplier %1.1f", delay, energy * 0.1);
236             ASSERT(energy > 0);
237             ASSERT(delay > 0);
238 
239             attacker->as_monster()->speed_increment
240                 -= div_rand_round(energy * delay, 10);
241         }
242 
243         // Statues and other special monsters which have AT_NONE need to lose
244         // energy, but otherwise should exit the melee attack now.
245         if (attk_type == AT_NONE)
246             return false;
247     }
248 
249     if (attacker != defender && !is_riposte)
250     {
251         // Allow setting of your allies' target, etc.
252         attacker->attacking(defender);
253 
254         check_autoberserk();
255     }
256 
257     // Xom thinks fumbles are funny...
258     if (attacker->fumbles_attack())
259     {
260         // ... and thinks fumbling when trying to hit yourself is just
261         // hilarious.
262         xom_is_stimulated(attacker == defender ? 200 : 10);
263         return false;
264     }
265     // Non-fumbled self-attacks due to confusion are still pretty funny, though.
266     else if (attacker == defender && attacker->confused())
267         xom_is_stimulated(100);
268 
269     // Any attack against a monster we're afraid of has a chance to fail
270     if (attacker->is_player() && defender &&
271         you.afraid_of(defender->as_monster()) && one_chance_in(3))
272     {
273         mprf("You attempt to attack %s, but flinch away in fear!",
274              defender->name(DESC_THE).c_str());
275         return false;
276     }
277 
278     if (attk_flavour == AF_SHADOWSTAB
279         && defender && !defender->can_see(*attacker))
280     {
281         if (you.see_cell(attack_position))
282         {
283             mprf("%s strikes at %s from the darkness!",
284                  attacker->name(DESC_THE, true).c_str(),
285                  defender->name(DESC_THE).c_str());
286         }
287         to_hit = AUTOMATIC_HIT;
288         needs_message = false;
289     }
290     else if (attacker->is_monster()
291              && attacker->type == MONS_DROWNED_SOUL)
292     {
293         to_hit = AUTOMATIC_HIT;
294     }
295 
296     attack_occurred = true;
297 
298     // Check for player practicing dodging
299     if (defender && defender->is_player())
300         practise_being_attacked();
301 
302     return true;
303 }
304 
handle_phase_dodged()305 bool melee_attack::handle_phase_dodged()
306 {
307     did_hit = false;
308 
309     if (needs_message)
310     {
311         // TODO: Unify these, placed player_warn_miss here so I can remove
312         // player_attack
313         if (attacker->is_player())
314             player_warn_miss();
315         else
316         {
317             mprf("%s%s misses %s%s",
318                  atk_name(DESC_THE).c_str(),
319                  evasion_margin_adverb().c_str(),
320                  defender_name(true).c_str(),
321                  attack_strength_punctuation(damage_done).c_str());
322         }
323     }
324 
325     if (attacker->is_player())
326     {
327         // Upset only non-sleeping non-fleeing monsters if we missed.
328         if (!defender->asleep() && !mons_is_fleeing(*defender->as_monster()))
329             behaviour_event(defender->as_monster(), ME_WHACK, attacker);
330     }
331 
332     if (defender->is_player())
333         count_action(CACT_DODGE, DODGE_EVASION);
334 
335     if (attacker != defender && adjacent(defender->pos(), attack_position)
336         && attacker->alive() && defender->can_see(*attacker)
337         && !defender->cannot_act() && !defender->confused()
338         && (!defender->is_player() || (!you.duration[DUR_LIFESAVING]
339                                        && !attacker->as_monster()->neutral()))
340         && !mons_aligned(attacker, defender) // confused friendlies attacking
341         // Retaliation only works on the first attack in a round.
342         // FIXME: player's attack is -1, even for auxes
343         && effective_attack_number <= 0)
344     {
345         if (defender->is_player()
346                 ? you.has_mutation(MUT_REFLEXIVE_HEADBUTT)
347                 : mons_species(mons_base_type(*defender->as_monster()))
348                     == MONS_MINOTAUR)
349         {
350             do_minotaur_retaliation();
351         }
352 
353         // Retaliations can kill!
354         if (!attacker->alive())
355             return false;
356 
357         if (defender->is_player() && player_equip_unrand(UNRAND_STARLIGHT))
358             do_starlight();
359 
360         if (defender->is_player())
361         {
362             const bool using_lbl = defender->weapon()
363                 && item_attack_skill(*defender->weapon()) == SK_LONG_BLADES;
364             const bool using_fencers = player_equip_unrand(UNRAND_FENCERS)
365                 && (!defender->weapon()
366                     || is_melee_weapon(*defender->weapon()));
367             const int chance = using_lbl + using_fencers;
368 
369             if (x_chance_in_y(chance, 3) && !is_riposte) // no ping-pong!
370                 riposte();
371 
372             // Retaliations can kill!
373             if (!attacker->alive())
374                 return false;
375         }
376     }
377 
378     return true;
379 }
380 
apply_black_mark_effects()381 void melee_attack::apply_black_mark_effects()
382 {
383     // Less reliable effects for players.
384     if (attacker->is_player()
385         && you.has_mutation(MUT_BLACK_MARK)
386         && one_chance_in(5)
387         || attacker->is_monster()
388            && attacker->as_monster()->has_ench(ENCH_BLACK_MARK))
389     {
390         if (!defender->alive())
391             return;
392 
393         switch (random2(3))
394         {
395             case 0:
396                 antimagic_affects_defender(damage_done * 8);
397                 break;
398             case 1:
399                 defender->weaken(attacker, 6);
400                 break;
401             case 2:
402                 defender->drain(attacker, false, damage_done);
403                 break;
404         }
405     }
406 }
407 
do_ooze_engulf()408 void melee_attack::do_ooze_engulf()
409 {
410     if (attacker->is_player()
411         && you.has_mutation(MUT_ENGULF)
412         && defender->alive()
413         && !defender->as_monster()->has_ench(ENCH_WATER_HOLD)
414         && attacker->can_constrict(defender, true, true)
415         && coinflip())
416     {
417         defender->as_monster()->add_ench(mon_enchant(ENCH_WATER_HOLD, 1,
418                                                      attacker, 1));
419         mprf("You engulf %s in ooze!", defender->name(DESC_THE).c_str());
420         // Smothers sticky flame.
421         defender->expose_to_element(BEAM_WATER, 0);
422     }
423 }
424 
425 /* An attack has been determined to have hit something
426  *
427  * Handles to-hit effects for both attackers and defenders,
428  * Determines damage and passes off execution to handle_phase_damaged
429  * Also applies weapon brands
430  *
431  * Returns true if combat should continue, false if it should end here.
432  */
handle_phase_hit()433 bool melee_attack::handle_phase_hit()
434 {
435     did_hit = true;
436     perceived_attack = true;
437     bool hit_woke_orc = false;
438 
439     if (attacker->is_player())
440     {
441         if (crawl_state.game_is_hints())
442             Hints.hints_melee_counter++;
443 
444         // TODO: Remove this (placed here so I can get rid of player_attack)
445         if (have_passive(passive_t::convert_orcs)
446             && mons_genus(defender->mons_species()) == MONS_ORC
447             && !defender->is_summoned()
448             && !defender->as_monster()->is_shapeshifter()
449             && you.see_cell(defender->pos()) && defender->asleep())
450         {
451             hit_woke_orc = true;
452         }
453     }
454 
455     damage_done = 0;
456     // Slimify does no damage and serves as an on-hit effect, handle it
457     if (attacker->is_player() && you.duration[DUR_SLIMIFY]
458         && mon_can_be_slimified(defender->as_monster())
459         && !cleaving)
460     {
461         // Bail out after sliming so we don't get aux unarmed and
462         // attack a fellow slime.
463         slimify_monster(defender->as_monster());
464         you.duration[DUR_SLIMIFY] = 0;
465 
466         return false;
467     }
468 
469     // This does more than just calculate the damage, it also sets up
470     // messages, etc. It also wakes nearby creatures on a failed stab,
471     // meaning it could have made the attacked creature vanish. That
472     // will be checked early in player_monattack_hit_effects
473     damage_done += calc_damage();
474 
475     bool stop_hit = false;
476     // Check if some hit-effect killed the monster.
477     if (attacker->is_player())
478         stop_hit = !player_monattk_hit_effects();
479 
480     // check_unrand_effects is safe to call with a dead defender, so always
481     // call it, even if the hit effects said to stop.
482     if (stop_hit)
483     {
484         check_unrand_effects();
485         return false;
486     }
487 
488     if (damage_done > 0 || flavour_triggers_damageless(attk_flavour))
489     {
490         if (!handle_phase_damaged())
491             return false;
492 
493         // TODO: Remove this, (placed here to remove player_attack)
494         if (attacker->is_player() && hit_woke_orc)
495         {
496             // Call function of orcs first noticing you, but with
497             // beaten-up conversion messages (if applicable).
498             beogh_follower_convert(defender->as_monster(), true);
499         }
500     }
501     else if (needs_message)
502     {
503         attack_verb = attacker->is_player()
504                       ? attack_verb
505                       : attacker->conj_verb(mons_attack_verb());
506 
507         // TODO: Clean this up if possible, checking atype for do / does is ugly
508         mprf("%s %s %s but %s no damage.",
509              attacker->name(DESC_THE).c_str(),
510              attack_verb.c_str(),
511              defender_name(true).c_str(),
512              attacker->is_player() ? "do" : "does");
513     }
514 
515     // Check for weapon brand & inflict that damage too
516     apply_damage_brand();
517 
518     // Fireworks when using Serpent's Lash to kill.
519     if (!defender->alive()
520         && defender->as_monster()->can_bleed()
521         && wu_jian_has_momentum(wu_jian_attack))
522     {
523         blood_spray(defender->pos(), defender->as_monster()->type,
524                     damage_done / 5);
525         defender->as_monster()->flags |= MF_EXPLODE_KILL;
526     }
527 
528     if (check_unrand_effects())
529         return false;
530 
531     if (damage_done > 0)
532     {
533         apply_black_mark_effects();
534         do_ooze_engulf();
535     }
536 
537     if (attacker->is_player())
538     {
539         // Always upset monster regardless of damage.
540         // However, successful stabs inhibit shouting.
541         behaviour_event(defender->as_monster(), ME_WHACK, attacker,
542                         coord_def(), !stab_attempt);
543 
544         // [ds] Monster may disappear after behaviour event.
545         if (!defender->alive())
546             return true;
547     }
548     else if (defender->is_player())
549     {
550         // These effects (mutations right now) are only triggered when
551         // the player is hit, each of them will verify their own required
552         // parameters.
553         do_passive_freeze();
554         emit_foul_stench();
555     }
556 
557     return true;
558 }
559 
handle_phase_damaged()560 bool melee_attack::handle_phase_damaged()
561 {
562     if (!attack::handle_phase_damaged())
563         return false;
564 
565     return true;
566 }
567 
handle_phase_aux()568 bool melee_attack::handle_phase_aux()
569 {
570     if (attacker->is_player()
571         && !cleaving
572         && wu_jian_attack != WU_JIAN_ATTACK_TRIGGERED_AUX
573         && !is_projected)
574     {
575         // returns whether an aux attack successfully took place
576         // additional attacks from cleave don't get aux
577         if (!defender->as_monster()->friendly()
578             && adjacent(defender->pos(), attack_position))
579         {
580             player_aux_unarmed();
581         }
582 
583         // Don't print wounds after the first attack with Gyre/Gimble.
584         // DUR_CLEAVE and Gyre/Gimble interact poorly together at the moment,
585         // so don't try to skip print_wounds in that case.
586         if (!(weapon && is_unrandom_artefact(*weapon, UNRAND_GYRE)
587               && !you.duration[DUR_CLEAVE]))
588         {
589             print_wounds(*defender->as_monster());
590         }
591     }
592 
593     return true;
594 }
595 
596 /**
597  * Handle effects that fire when the defender (the target of the attack) is
598  * killed.
599  *
600  * @return  Not sure; it seems to never be checked & always be true?
601  */
handle_phase_killed()602 bool melee_attack::handle_phase_killed()
603 {
604     // Wyrmbane needs to be notified of deaths, including ones due to aux
605     // attacks, but other users of melee_effects() don't want to possibly
606     // be called twice. Adding another entry for a single artefact would
607     // be overkill, so here we call it by hand. check_unrand_effects()
608     // avoided triggering Wyrmbane's death effect earlier in the attack.
609     if (unrand_entry && weapon && weapon->unrand_idx == UNRAND_WYRMBANE)
610     {
611         unrand_entry->melee_effects(weapon, attacker, defender,
612                                                true, special_damage);
613     }
614 
615     return attack::handle_phase_killed();
616 }
617 
_handle_spectral_brand(const actor & attacker,const actor & defender)618 static void _handle_spectral_brand(const actor &attacker, const actor &defender)
619 {
620     if (you.triggered_spectral || !defender.alive())
621         return;
622     you.triggered_spectral = true;
623     spectral_weapon_fineff::schedule(attacker, defender);
624 }
625 
handle_phase_end()626 bool melee_attack::handle_phase_end()
627 {
628     if (!cleave_targets.empty() && !simu)
629     {
630         attack_cleave_targets(*attacker, cleave_targets, attack_number,
631                               effective_attack_number, wu_jian_attack,
632                               is_projected);
633     }
634 
635     // Check for passive mutation effects.
636     if (defender->is_player() && defender->alive() && attacker != defender)
637     {
638         mons_do_eyeball_confusion();
639         mons_do_tendril_disarm();
640     }
641 
642     if (attacker->alive()
643         && attacker->is_monster()
644         && attacker->as_monster()->has_ench(ENCH_ROLLING))
645     {
646         attacker->as_monster()->del_ench(ENCH_ROLLING);
647     }
648 
649     if (attacker->is_player() && defender)
650     {
651         if (damage_brand == SPWPN_SPECTRAL)
652             _handle_spectral_brand(*attacker, *defender);
653         // Use the Nessos hack to give the player glaive of the guard spectral too
654         if (weapon && is_unrandom_artefact(*weapon, UNRAND_GUARD))
655             _handle_spectral_brand(*attacker, *defender);
656     }
657 
658     return attack::handle_phase_end();
659 }
660 
661 /* Initiate the processing of the attack
662  *
663  * Called from the main code (fight.cc), this method begins the actual combat
664  * for a particular attack and is responsible for looping through each of the
665  * appropriate phases (which then can call other related phases within
666  * themselves).
667  *
668  * Returns whether combat was completely successful
669  *      If combat was not successful, it could be any number of reasons, like
670  *      the defender or attacker dying during the attack? or a defender moving
671  *      from its starting position.
672  */
attack()673 bool melee_attack::attack()
674 {
675     if (!cleaving)
676     {
677         cleave_setup();
678         if (!handle_phase_attempted())
679             return false;
680     }
681 
682     if (attacker != defender && attacker->is_monster()
683         && mons_self_destructs(*attacker->as_monster()))
684     {
685         attacker->self_destruct();
686         return did_hit = perceived_attack = true;
687     }
688 
689     string saved_gyre_name;
690     if (weapon && is_unrandom_artefact(*weapon, UNRAND_GYRE))
691     {
692         saved_gyre_name = get_artefact_name(*weapon);
693         set_artefact_name(*weapon, cleaving ? "quick blade \"Gimble\""
694                                             : "quick blade \"Gyre\"");
695     }
696 
697     // Restore gyre's name before we return. We cannot use an unwind_var here
698     // because the precise address of the ARTEFACT_NAME_KEY property might
699     // change, for example if a summoned item is reset.
700     ON_UNWIND
701     {
702         if (!saved_gyre_name.empty() && weapon
703                 && is_unrandom_artefact(*weapon, UNRAND_GYRE))
704         {
705             set_artefact_name(*weapon, saved_gyre_name);
706         }
707     };
708 
709     // Attacker might have died from effects of cleaving handled prior to this
710     if (!attacker->alive())
711         return false;
712 
713     // We might have killed the kraken target by cleaving a tentacle.
714     if (!defender->alive())
715     {
716         handle_phase_killed();
717         handle_phase_end();
718         return attack_occurred;
719     }
720 
721     // Apparently I'm insane for believing that we can still stay general past
722     // this point in the combat code, mebe I am! --Cryptic
723 
724     // Calculate various ev values and begin to check them to determine the
725     // correct handle_phase_ handler.
726     const int ev = defender->evasion(ev_ignore::none, attacker);
727     ev_margin = test_hit(to_hit, ev, !attacker->is_player());
728     bool shield_blocked = attack_shield_blocked(true);
729 
730     // Stuff for god conduct, this has to remain here for scope reasons.
731     god_conduct_trigger conducts[3];
732 
733     if (attacker->is_player() && attacker != defender)
734     {
735         set_attack_conducts(conducts, *defender->as_monster(),
736                             you.can_see(*defender));
737 
738         if (player_under_penance(GOD_ELYVILON)
739             && god_hates_your_god(GOD_ELYVILON)
740             && ev_margin >= 0
741             && one_chance_in(20))
742         {
743             simple_god_message(" blocks your attack.", GOD_ELYVILON);
744             handle_phase_end();
745             return false;
746         }
747         // Check for stab (and set stab_attempt and stab_bonus)
748         player_stab_check();
749         // Make sure we hit if we passed the stab check.
750         if (stab_attempt && stab_bonus > 0)
751         {
752             ev_margin = AUTOMATIC_HIT;
753             shield_blocked = false;
754         }
755 
756         // Serpent's Lash does not miss
757         if (wu_jian_has_momentum(wu_jian_attack))
758            ev_margin = AUTOMATIC_HIT;
759     }
760 
761     if (shield_blocked)
762         handle_phase_blocked();
763     else
764     {
765         if (attacker != defender
766             && (adjacent(defender->pos(), attack_position) || is_projected)
767             && !is_riposte)
768         {
769             // Check for defender Spines
770             do_spines();
771 
772             // Spines can kill! With Usk's pain bond, they can even kill the
773             // defender.
774             if (!attacker->alive() || !defender->alive())
775                 return false;
776         }
777 
778         if (ev_margin >= 0)
779         {
780             bool cont = handle_phase_hit();
781 
782             attacker_sustain_passive_damage();
783 
784             if (!cont)
785             {
786                 if (!defender->alive())
787                     handle_phase_killed();
788                 handle_phase_end();
789                 return false;
790             }
791         }
792         else
793             handle_phase_dodged();
794     }
795 
796     // don't crash on banishment
797     if (!defender->pos().origin())
798         handle_noise(defender->pos());
799 
800     // Noisy weapons.
801     if (attacker->is_player()
802         && weapon
803         && is_artefact(*weapon)
804         && artefact_property(*weapon, ARTP_NOISE))
805     {
806         noisy_equipment();
807     }
808 
809     alert_defender();
810 
811     if (!defender->alive())
812         handle_phase_killed();
813 
814     handle_phase_aux();
815 
816     handle_phase_end();
817 
818     return attack_occurred;
819 }
820 
check_autoberserk()821 void melee_attack::check_autoberserk()
822 {
823     if (defender->is_monster() && mons_is_firewood(*defender->as_monster()))
824         return;
825 
826     if (attacker->is_player())
827     {
828         for (int i = EQ_FIRST_EQUIP; i < NUM_EQUIP; ++i)
829         {
830             const item_def *item = you.slot_item(static_cast<equipment_type>(i));
831             if (!item)
832                 continue;
833 
834             if (!is_artefact(*item))
835                 continue;
836 
837             if (x_chance_in_y(artefact_property(*item, ARTP_ANGRY), 100))
838             {
839                 attacker->go_berserk(true);
840                 return;
841             }
842         }
843     }
844     else
845     {
846         for (int i = MSLOT_WEAPON; i <= MSLOT_JEWELLERY; ++i)
847         {
848             const item_def *item =
849                 attacker->as_monster()->mslot_item(static_cast<mon_inv_type>(i));
850             if (!item)
851                 continue;
852 
853             if (!is_artefact(*item))
854                 continue;
855 
856             if (x_chance_in_y(artefact_property(*item, ARTP_ANGRY), 100))
857             {
858                 attacker->go_berserk(true);
859                 return;
860             }
861         }
862     }
863 }
864 
check_unrand_effects()865 bool melee_attack::check_unrand_effects()
866 {
867     if (unrand_entry && unrand_entry->melee_effects && weapon)
868     {
869         const bool died = !defender->alive();
870 
871         // Don't trigger the Wyrmbane death effect yet; that is done in
872         // handle_phase_killed().
873         if (weapon->unrand_idx == UNRAND_WYRMBANE && died)
874             return true;
875 
876         // Recent merge added damage_done to this method call
877         unrand_entry->melee_effects(weapon, attacker, defender,
878                                     died, damage_done);
879         return !defender->alive(); // may have changed
880     }
881 
882     return false;
883 }
884 
885 class AuxAttackType
886 {
887 public:
AuxAttackType(int _damage,string _name)888     AuxAttackType(int _damage, string _name) :
889     damage(_damage), name(_name) { };
890 public:
get_damage() const891     virtual int get_damage() const { return damage; };
get_brand() const892     virtual int get_brand() const { return SPWPN_NORMAL; };
get_name() const893     virtual string get_name() const { return name; };
get_verb() const894     virtual string get_verb() const { return get_name(); };
895 protected:
896     const int damage;
897     const string name;
898 };
899 
900 class AuxConstrict: public AuxAttackType
901 {
902 public:
AuxConstrict()903     AuxConstrict()
904     : AuxAttackType(0, "grab") { };
905 };
906 
907 class AuxKick: public AuxAttackType
908 {
909 public:
AuxKick()910     AuxKick()
911     : AuxAttackType(5, "kick") { };
912 
get_damage() const913     int get_damage() const override
914     {
915         if (you.has_usable_hooves())
916         {
917             // Max hoof damage: 10.
918             return damage + you.get_mutation_level(MUT_HOOVES) * 5 / 3;
919         }
920 
921         if (you.has_usable_talons())
922         {
923             // Max talon damage: 9.
924             return damage + 1 + you.get_mutation_level(MUT_TALONS);
925         }
926 
927         // Max spike damage: 8.
928         // ... yes, apparently tentacle spikes are "kicks".
929         return damage + you.get_mutation_level(MUT_TENTACLE_SPIKE);
930     }
931 
get_verb() const932     string get_verb() const override
933     {
934         if (you.has_usable_talons())
935             return "claw";
936         if (you.get_mutation_level(MUT_TENTACLE_SPIKE))
937             return "pierce";
938         return name;
939     }
940 
get_name() const941     string get_name() const override
942     {
943         if (you.get_mutation_level(MUT_TENTACLE_SPIKE))
944             return "tentacle spike";
945         return name;
946     }
947 };
948 
949 class AuxHeadbutt: public AuxAttackType
950 {
951 public:
AuxHeadbutt()952     AuxHeadbutt()
953     : AuxAttackType(5, "headbutt") { };
954 
get_damage() const955     int get_damage() const override
956     {
957         return damage + you.get_mutation_level(MUT_HORNS) * 3;
958     }
959 };
960 
961 class AuxPeck: public AuxAttackType
962 {
963 public:
AuxPeck()964     AuxPeck()
965     : AuxAttackType(6, "peck") { };
966 };
967 
968 class AuxTailslap: public AuxAttackType
969 {
970 public:
AuxTailslap()971     AuxTailslap()
972     : AuxAttackType(6, "tail-slap") { };
973 
get_damage() const974     int get_damage() const override
975     {
976         return damage + max(0, you.get_mutation_level(MUT_STINGER) * 2 - 1)
977                       + you.get_mutation_level(MUT_ARMOURED_TAIL) * 4;
978     }
979 
get_brand() const980     int get_brand() const override
981     {
982         return you.get_mutation_level(MUT_STINGER) ? SPWPN_VENOM : SPWPN_NORMAL;
983     }
984 };
985 
986 class AuxPunch: public AuxAttackType
987 {
988 public:
AuxPunch()989     AuxPunch()
990     : AuxAttackType(5, "punch") { };
991 
get_damage() const992     int get_damage() const override
993     {
994         const int base_dam = damage + you.skill_rdiv(SK_UNARMED_COMBAT, 1, 2);
995 
996         if (you.form == transformation::blade_hands)
997             return base_dam + 6;
998 
999         if (you.has_usable_claws())
1000             return base_dam + roll_dice(you.has_claws(), 3);
1001 
1002         return base_dam;
1003     }
1004 
get_name() const1005     string get_name() const override
1006     {
1007         if (you.form == transformation::blade_hands)
1008             return "slash";
1009 
1010         if (you.has_usable_claws())
1011             return "claw";
1012 
1013         if (you.has_usable_tentacles())
1014             return "tentacle-slap";
1015 
1016         return name;
1017     }
1018 
1019 };
1020 
1021 class AuxBite: public AuxAttackType
1022 {
1023 public:
AuxBite()1024     AuxBite()
1025     : AuxAttackType(0, "bite") { };
1026 
get_damage() const1027     int get_damage() const override
1028     {
1029         const int fang_damage = you.has_usable_fangs() * 2;
1030         if (you.get_mutation_level(MUT_ANTIMAGIC_BITE))
1031             return fang_damage + div_rand_round(you.get_hit_dice(), 3);
1032 
1033         const int str_damage = div_rand_round(max(you.strength()-10, 0), 5);
1034 
1035         if (you.get_mutation_level(MUT_ACIDIC_BITE))
1036             return fang_damage + str_damage;
1037 
1038         return fang_damage + str_damage;
1039     }
1040 
get_brand() const1041     int get_brand() const override
1042     {
1043         if (you.get_mutation_level(MUT_ANTIMAGIC_BITE))
1044             return SPWPN_ANTIMAGIC;
1045 
1046         if (you.get_mutation_level(MUT_ACIDIC_BITE))
1047             return SPWPN_ACID;
1048 
1049         return SPWPN_NORMAL;
1050     }
1051 };
1052 
1053 class AuxPseudopods: public AuxAttackType
1054 {
1055 public:
AuxPseudopods()1056     AuxPseudopods()
1057     : AuxAttackType(4, "bludgeon") { };
1058 
get_damage() const1059     int get_damage() const override
1060     {
1061         return damage * you.has_usable_pseudopods();
1062     }
1063 };
1064 
1065 class AuxTentacles: public AuxAttackType
1066 {
1067 public:
AuxTentacles()1068     AuxTentacles()
1069     : AuxAttackType(12, "squeeze") { };
1070 };
1071 
1072 static const AuxConstrict   AUX_CONSTRICT = AuxConstrict();
1073 static const AuxKick        AUX_KICK = AuxKick();
1074 static const AuxPeck        AUX_PECK = AuxPeck();
1075 static const AuxHeadbutt    AUX_HEADBUTT = AuxHeadbutt();
1076 static const AuxTailslap    AUX_TAILSLAP = AuxTailslap();
1077 static const AuxPunch       AUX_PUNCH = AuxPunch();
1078 static const AuxBite        AUX_BITE = AuxBite();
1079 static const AuxPseudopods  AUX_PSEUDOPODS = AuxPseudopods();
1080 static const AuxTentacles   AUX_TENTACLES = AuxTentacles();
1081 
1082 static const AuxAttackType* const aux_attack_types[] =
1083 {
1084     &AUX_CONSTRICT,
1085     &AUX_KICK,
1086     &AUX_HEADBUTT,
1087     &AUX_PECK,
1088     &AUX_TAILSLAP,
1089     &AUX_PUNCH,
1090     &AUX_BITE,
1091     &AUX_PSEUDOPODS,
1092     &AUX_TENTACLES,
1093 };
1094 
1095 
1096 /* Setup all unarmed (non attack_type) variables
1097  *
1098  * Clears any previous unarmed attack information and sets everything from
1099  * noise_factor to verb and damage. Called after player_aux_choose_uc_attack
1100  */
player_aux_setup(unarmed_attack_type atk)1101 void melee_attack::player_aux_setup(unarmed_attack_type atk)
1102 {
1103     const int num_aux_objs = ARRAYSZ(aux_attack_types);
1104     const int num_aux_atks = UNAT_LAST_ATTACK - UNAT_FIRST_ATTACK + 1;
1105     COMPILE_CHECK(num_aux_objs == num_aux_atks);
1106 
1107     ASSERT(atk >= UNAT_FIRST_ATTACK);
1108     ASSERT(atk <= UNAT_LAST_ATTACK);
1109     const AuxAttackType* const aux = aux_attack_types[atk - UNAT_FIRST_ATTACK];
1110 
1111     aux_damage = aux->get_damage();
1112     damage_brand = (brand_type)aux->get_brand();
1113     aux_attack = aux->get_name();
1114     aux_verb = aux->get_verb();
1115 
1116     if (wu_jian_attack != WU_JIAN_ATTACK_NONE)
1117         wu_jian_attack = WU_JIAN_ATTACK_TRIGGERED_AUX;
1118 
1119     if (atk == UNAT_BITE
1120         && _vamp_wants_blood_from_monster(defender->as_monster()))
1121     {
1122         damage_brand = SPWPN_VAMPIRISM;
1123     }
1124 }
1125 
1126 /**
1127  * Decide whether the player gets a bonus punch attack.
1128  *
1129  * Partially random.
1130  *
1131  * @return  Whether the player gets a bonus punch aux attack on this attack.
1132  */
player_gets_aux_punch()1133 bool melee_attack::player_gets_aux_punch()
1134 {
1135     if (!get_form()->can_offhand_punch())
1136         return false;
1137 
1138     // roll for punch chance based on uc skill & armour penalty
1139     if (!attacker->fights_well_unarmed(attacker_armour_tohit_penalty
1140                                        + attacker_shield_tohit_penalty))
1141     {
1142         return false;
1143     }
1144 
1145     // No punching with a shield or 2-handed wpn.
1146     // Octopodes aren't affected by this, though!
1147     if (you.arm_count() <= 2 && !you.has_usable_offhand())
1148         return false;
1149 
1150     // Octopodes get more tentacle-slaps.
1151     return x_chance_in_y(you.arm_count() > 2 ? 3 : 2, 6);
1152 }
1153 
player_aux_test_hit()1154 bool melee_attack::player_aux_test_hit()
1155 {
1156     // XXX We're clobbering did_hit
1157     did_hit = false;
1158 
1159     const int evasion = defender->evasion(ev_ignore::none, attacker);
1160 
1161     if (player_under_penance(GOD_ELYVILON)
1162         && god_hates_your_god(GOD_ELYVILON)
1163         && to_hit >= evasion
1164         && one_chance_in(20))
1165     {
1166         simple_god_message(" blocks your attack.", GOD_ELYVILON);
1167         return false;
1168     }
1169 
1170     bool auto_hit = one_chance_in(30);
1171 
1172     if (to_hit >= evasion || auto_hit)
1173         return true;
1174 
1175     mprf("Your %s misses %s.", aux_attack.c_str(),
1176          defender->name(DESC_THE).c_str());
1177 
1178     return false;
1179 }
1180 
1181 /* Controls the looping on available unarmed attacks
1182  *
1183  * As the master method for unarmed player combat, this loops through
1184  * available unarmed attacks, determining whether they hit and - if so -
1185  * calculating and applying their damage.
1186  *
1187  * Returns (defender dead)
1188  */
player_aux_unarmed()1189 bool melee_attack::player_aux_unarmed()
1190 {
1191     unwind_var<brand_type> save_brand(damage_brand);
1192 
1193     for (int i = UNAT_FIRST_ATTACK; i <= UNAT_LAST_ATTACK; ++i)
1194     {
1195         if (!defender->alive())
1196             break;
1197 
1198         unarmed_attack_type atk = static_cast<unarmed_attack_type>(i);
1199 
1200         if (!_extra_aux_attack(atk))
1201             continue;
1202 
1203         // Determine and set damage and attack words.
1204         player_aux_setup(atk);
1205 
1206         if (atk == UNAT_CONSTRICT && !attacker->can_constrict(defender, true))
1207             continue;
1208 
1209         to_hit = random2(calc_your_to_hit_unarmed());
1210 
1211         handle_noise(defender->pos());
1212         alert_nearby_monsters();
1213 
1214         // [ds] kraken can flee when near death, causing the tentacle
1215         // the player was beating up to "die" and no longer be
1216         // available to answer questions beyond this point.
1217         // handle_noise stirs up all nearby monsters with a stick, so
1218         // the player may be beating up a tentacle, but the main body
1219         // of the kraken still gets a chance to act and submerge
1220         // tentacles before we get here.
1221         if (!defender->alive())
1222             return true;
1223 
1224         if (player_aux_test_hit())
1225         {
1226             // Upset the monster.
1227             behaviour_event(defender->as_monster(), ME_WHACK, attacker);
1228             if (!defender->alive())
1229                 return true;
1230 
1231             if (attack_shield_blocked(true))
1232                 continue;
1233             if (player_aux_apply(atk))
1234                 return true;
1235         }
1236     }
1237 
1238     return false;
1239 }
1240 
player_aux_apply(unarmed_attack_type atk)1241 bool melee_attack::player_aux_apply(unarmed_attack_type atk)
1242 {
1243     did_hit = true;
1244 
1245     count_action(CACT_MELEE, -1, atk); // aux_attack subtype/auxtype
1246 
1247     aux_damage  = player_stat_modify_damage(aux_damage);
1248 
1249     aux_damage  = random2(aux_damage);
1250 
1251     aux_damage  = player_apply_fighting_skill(aux_damage, true);
1252 
1253     aux_damage  = player_apply_misc_modifiers(aux_damage);
1254 
1255     aux_damage  = player_apply_slaying_bonuses(aux_damage, true);
1256 
1257     aux_damage  = player_apply_final_multipliers(aux_damage);
1258 
1259     if (atk == UNAT_CONSTRICT)
1260         aux_damage = 0;
1261     else
1262         aux_damage = apply_defender_ac(aux_damage);
1263 
1264     aux_damage = inflict_damage(aux_damage, BEAM_MISSILE);
1265     damage_done = aux_damage;
1266 
1267     if (defender->alive())
1268     {
1269         if (atk == UNAT_CONSTRICT)
1270             attacker->start_constricting(*defender);
1271 
1272         if (damage_done > 0 || atk == UNAT_CONSTRICT)
1273         {
1274             player_announce_aux_hit();
1275 
1276             if (damage_brand == SPWPN_ACID)
1277                 defender->splash_with_acid(&you, 3);
1278 
1279             if (damage_brand == SPWPN_VENOM && coinflip())
1280                 poison_monster(defender->as_monster(), &you);
1281 
1282             // Normal vampiric biting attack, not if already got stabbing special.
1283             if (damage_brand == SPWPN_VAMPIRISM
1284                 && you.has_mutation(MUT_VAMPIRISM)
1285                 && (!stab_attempt || stab_bonus <= 0))
1286             {
1287                 _player_vampire_draws_blood(defender->as_monster(), damage_done);
1288             }
1289 
1290             if (damage_brand == SPWPN_ANTIMAGIC && you.has_mutation(MUT_ANTIMAGIC_BITE)
1291                 && damage_done > 0)
1292             {
1293                 const bool spell_user = defender->antimagic_susceptible();
1294 
1295                 antimagic_affects_defender(damage_done * 32);
1296 
1297                 // MP drain suppressed under Pakellas, but antimagic still applies.
1298                 if (!have_passive(passive_t::no_mp_regen) || spell_user)
1299                 {
1300                     mprf("You %s %s %s.",
1301                          have_passive(passive_t::no_mp_regen) ? "disrupt" : "drain",
1302                          defender->as_monster()->pronoun(PRONOUN_POSSESSIVE).c_str(),
1303                          spell_user ? "magic" : "power");
1304                 }
1305 
1306                 if (!have_passive(passive_t::no_mp_regen)
1307                     && you.magic_points != you.max_magic_points
1308                     && !defender->as_monster()->is_summoned()
1309                     && !mons_is_firewood(*defender->as_monster()))
1310                 {
1311                     int drain = random2(damage_done * 2) + 1;
1312                     // Augment mana drain--1.25 "standard" effectiveness at 0 mp,
1313                     // 0.25 at mana == max_mana
1314                     drain = (int)((1.25 - you.magic_points / you.max_magic_points)
1315                                   * drain);
1316                     if (drain)
1317                     {
1318                         mpr("You feel invigorated.");
1319                         inc_mp(drain);
1320                     }
1321                 }
1322             }
1323         }
1324         else // no damage was done
1325         {
1326             mprf("You %s %s%s.",
1327                  aux_verb.c_str(),
1328                  defender->name(DESC_THE).c_str(),
1329                  you.can_see(*defender) ? ", but do no damage" : "");
1330         }
1331     }
1332     else // defender was just alive, so this call should be ok?
1333         player_announce_aux_hit();
1334 
1335     if (defender->as_monster()->hit_points < 1)
1336     {
1337         handle_phase_killed();
1338         return true;
1339     }
1340 
1341     return false;
1342 }
1343 
player_announce_aux_hit()1344 void melee_attack::player_announce_aux_hit()
1345 {
1346     mprf("You %s %s%s%s",
1347          aux_verb.c_str(),
1348          defender->name(DESC_THE).c_str(),
1349          debug_damage_number().c_str(),
1350          attack_strength_punctuation(damage_done).c_str());
1351 }
1352 
player_why_missed()1353 string melee_attack::player_why_missed()
1354 {
1355     const int ev = defender->evasion(ev_ignore::none, attacker);
1356     const int combined_penalty =
1357         attacker_armour_tohit_penalty + attacker_shield_tohit_penalty;
1358     if (to_hit < ev && to_hit + combined_penalty >= ev)
1359     {
1360         const bool armour_miss =
1361             (attacker_armour_tohit_penalty
1362              && to_hit + attacker_armour_tohit_penalty >= ev);
1363         const bool shield_miss =
1364             (attacker_shield_tohit_penalty
1365              && to_hit + attacker_shield_tohit_penalty >= ev);
1366 
1367         const item_def *armour = you.slot_item(EQ_BODY_ARMOUR, false);
1368         const string armour_name = armour ? armour->name(DESC_BASENAME)
1369                                           : string("armour");
1370 
1371         if (armour_miss && !shield_miss)
1372             return "Your " + armour_name + " prevents you from hitting ";
1373         else if (shield_miss && !armour_miss)
1374             return "Your shield prevents you from hitting ";
1375         else
1376             return "Your shield and " + armour_name
1377                    + " prevent you from hitting ";
1378     }
1379 
1380     return "You" + evasion_margin_adverb() + " miss ";
1381 }
1382 
player_warn_miss()1383 void melee_attack::player_warn_miss()
1384 {
1385     did_hit = false;
1386 
1387     mprf("%s%s.",
1388          player_why_missed().c_str(),
1389          defender->name(DESC_THE).c_str());
1390 }
1391 
1392 // A couple additive modifiers that should be applied to both unarmed and
1393 // armed attacks.
player_apply_misc_modifiers(int damage)1394 int melee_attack::player_apply_misc_modifiers(int damage)
1395 {
1396     if (you.duration[DUR_MIGHT] || you.duration[DUR_BERSERK])
1397         damage += 1 + random2(10);
1398 
1399     return damage;
1400 }
1401 
1402 // Multipliers to be applied to the final (pre-stab, pre-AC) damage.
1403 // It might be tempting to try to pick and choose what pieces of the damage
1404 // get affected by such multipliers, but putting them at the end is the
1405 // simplest effect to understand if they aren't just going to be applied
1406 // to the base damage of the weapon.
player_apply_final_multipliers(int damage)1407 int melee_attack::player_apply_final_multipliers(int damage)
1408 {
1409     // cleave damage modifier
1410     if (cleaving)
1411         damage = cleave_damage_mod(damage);
1412 
1413     // martial damage modifier (wu jian)
1414     damage = martial_damage_mod(damage);
1415 
1416     // Palentonga rolling charge bonus
1417     if (roll_dist > 0)
1418     {
1419         // + 1/3rd base per distance rolled, up to double at dist 3.
1420         const int extra_dam = damage * roll_dist / 3;
1421         damage += extra_dam > damage ? damage : extra_dam;
1422     }
1423 
1424     // not additive, statues are supposed to be bad with tiny toothpicks but
1425     // deal crushing blows with big weapons
1426     if (you.form == transformation::statue)
1427         damage = div_rand_round(damage * 3, 2);
1428 
1429     // Can't affect much of anything as a shadow.
1430     if (you.form == transformation::shadow)
1431         damage = div_rand_round(damage, 2);
1432 
1433     if (you.duration[DUR_WEAK])
1434         damage = div_rand_round(damage * 3, 4);
1435 
1436     if (you.duration[DUR_CONFUSING_TOUCH])
1437         return 0;
1438 
1439     return damage;
1440 }
1441 
set_attack_verb(int damage)1442 void melee_attack::set_attack_verb(int damage)
1443 {
1444     if (!attacker->is_player())
1445         return;
1446 
1447     int weap_type = WPN_UNKNOWN;
1448 
1449     if (Options.has_fake_lang(flang_t::grunt))
1450         damage = HIT_STRONG + 1;
1451 
1452     if (!weapon)
1453         weap_type = WPN_UNARMED;
1454     else if (weapon->base_type == OBJ_STAVES)
1455         weap_type = WPN_STAFF;
1456     else if (weapon->base_type == OBJ_WEAPONS
1457              && !is_range_weapon(*weapon))
1458     {
1459         weap_type = weapon->sub_type;
1460     }
1461 
1462     // All weak hits with weapons look the same.
1463     if (damage < HIT_WEAK
1464         && weap_type != WPN_UNARMED)
1465     {
1466         if (weap_type != WPN_UNKNOWN)
1467             attack_verb = "hit";
1468         else
1469             attack_verb = "clumsily bash";
1470         return;
1471     }
1472 
1473     // Take normal hits into account. If the hit is from a weapon with
1474     // more than one damage type, randomly choose one damage type from
1475     // it.
1476     monster_type defender_genus = mons_genus(defender->type);
1477     switch (weapon ? single_damage_type(*weapon) : -1)
1478     {
1479     case DAM_PIERCE:
1480         if (damage < HIT_MED)
1481             attack_verb = "puncture";
1482         else if (damage < HIT_STRONG)
1483             attack_verb = "impale";
1484         else
1485         {
1486             if (defender->is_monster()
1487                 && defender_visible
1488                 && defender_genus == MONS_HOG)
1489             {
1490                 attack_verb = "spit";
1491                 verb_degree = "like the proverbial pig";
1492             }
1493             else if (defender_genus == MONS_CRAB
1494                      && Options.has_fake_lang(flang_t::grunt))
1495             {
1496                 attack_verb = "attack";
1497                 verb_degree = "'s weak point";
1498             }
1499             else
1500             {
1501                 static const char * const pierce_desc[][2] =
1502                 {
1503                     {"spit", "like a pig"},
1504                     {"skewer", "like a kebab"},
1505                     {"stick", "like a pincushion"},
1506                     {"perforate", "like a sieve"}
1507                 };
1508                 const int choice = random2(ARRAYSZ(pierce_desc));
1509                 attack_verb = pierce_desc[choice][0];
1510                 verb_degree = pierce_desc[choice][1];
1511             }
1512         }
1513         break;
1514 
1515     case DAM_SLICE:
1516         if (damage < HIT_MED)
1517             attack_verb = "slash";
1518         else if (damage < HIT_STRONG)
1519             attack_verb = "slice";
1520         else if (defender_genus == MONS_OGRE)
1521         {
1522             attack_verb = "dice";
1523             verb_degree = "like an onion";
1524         }
1525         else if (defender_genus == MONS_SKELETON)
1526         {
1527             attack_verb = "fracture";
1528             verb_degree = "into splinters";
1529         }
1530         else if (defender_genus == MONS_HOG)
1531         {
1532             attack_verb = "carve";
1533             verb_degree = "like the proverbial ham";
1534         }
1535         else if ((defender_genus == MONS_TENGU
1536                   || get_mon_shape(defender_genus) == MON_SHAPE_BIRD)
1537                  && one_chance_in(3))
1538         {
1539             attack_verb = "carve";
1540             verb_degree = "like a turkey";
1541         }
1542         else if ((defender_genus == MONS_YAK || defender_genus == MONS_YAKTAUR)
1543                  && Options.has_fake_lang(flang_t::grunt))
1544         {
1545             attack_verb = "shave";
1546         }
1547         else
1548         {
1549             static const char * const slice_desc[][2] =
1550             {
1551                 {"open",    "like a pillowcase"},
1552                 {"slice",   "like a ripe choko"},
1553                 {"cut",     "into ribbons"},
1554                 {"carve",   "like a ham"},
1555                 {"chop",    "into pieces"}
1556             };
1557             const int choice = random2(ARRAYSZ(slice_desc));
1558             attack_verb = slice_desc[choice][0];
1559             verb_degree = slice_desc[choice][1];
1560         }
1561         break;
1562 
1563     case DAM_BLUDGEON:
1564         if (damage < HIT_MED)
1565             attack_verb = one_chance_in(4) ? "thump" : "sock";
1566         else if (damage < HIT_STRONG)
1567             attack_verb = "bludgeon";
1568         else if (defender_genus == MONS_SKELETON)
1569         {
1570             attack_verb = "shatter";
1571             verb_degree = "into splinters";
1572         }
1573         else if (defender->type == MONS_GREAT_ORB_OF_EYES)
1574         {
1575             attack_verb = "splatter";
1576             verb_degree = "into a gooey mess";
1577         }
1578         else
1579         {
1580             static const char * const bludgeon_desc[][2] =
1581             {
1582                 {"crush",   "like a grape"},
1583                 {"beat",    "like a drum"},
1584                 {"hammer",  "like a gong"},
1585                 {"pound",   "like an anvil"},
1586                 {"flatten", "like a pancake"}
1587             };
1588             const int choice = random2(ARRAYSZ(bludgeon_desc));
1589             attack_verb = bludgeon_desc[choice][0];
1590             verb_degree = bludgeon_desc[choice][1];
1591         }
1592         break;
1593 
1594     case DAM_WHIP:
1595         if (damage < HIT_MED)
1596             attack_verb = "whack";
1597         else if (damage < HIT_STRONG)
1598             attack_verb = "thrash";
1599         else
1600         {
1601             if (defender->holiness() & (MH_HOLY | MH_NATURAL | MH_DEMONIC))
1602             {
1603                 attack_verb = "punish";
1604                 verb_degree = ", causing immense pain";
1605                 break;
1606             }
1607             else
1608                 attack_verb = "devastate";
1609         }
1610         break;
1611 
1612     case -1: // unarmed
1613     {
1614         const FormAttackVerbs verbs = get_form(you.form)->uc_attack_verbs;
1615         if (verbs.weak != nullptr)
1616         {
1617             if (damage < HIT_WEAK)
1618                 attack_verb = verbs.weak;
1619             else if (damage < HIT_MED)
1620                 attack_verb = verbs.medium;
1621             else if (damage < HIT_STRONG)
1622                 attack_verb = verbs.strong;
1623             else
1624                 attack_verb = verbs.devastating;
1625             break;
1626         }
1627 
1628         if (you.damage_type() == DVORP_CLAWING)
1629         {
1630             if (damage < HIT_WEAK)
1631                 attack_verb = "scratch";
1632             else if (damage < HIT_MED)
1633                 attack_verb = "claw";
1634             else if (damage < HIT_STRONG)
1635                 attack_verb = "mangle";
1636             else
1637                 attack_verb = "eviscerate";
1638         }
1639         else if (you.damage_type() == DVORP_TENTACLE)
1640         {
1641             if (damage < HIT_WEAK)
1642                 attack_verb = "tentacle-slap";
1643             else if (damage < HIT_MED)
1644                 attack_verb = "bludgeon";
1645             else if (damage < HIT_STRONG)
1646                 attack_verb = "batter";
1647             else
1648                 attack_verb = "thrash";
1649         }
1650         else
1651         {
1652             if (damage < HIT_WEAK)
1653                 attack_verb = "hit";
1654             else if (damage < HIT_MED)
1655                 attack_verb = "punch";
1656             else if (damage < HIT_STRONG)
1657                 attack_verb = "pummel";
1658             else if (defender->is_monster()
1659                      && mons_genus(defender->type) == MONS_FORMICID)
1660             {
1661                 attack_verb = "squash";
1662                 verb_degree = "like the proverbial ant";
1663             }
1664             else
1665             {
1666                 static const char * const punch_desc[][2] =
1667                 {
1668                     {"pound",     "into fine dust"},
1669                     {"pummel",    "like a punching bag"},
1670                     {"pulverise", ""},
1671                     {"squash",    "like an ant"}
1672                 };
1673                 const int choice = random2(ARRAYSZ(punch_desc));
1674                 // XXX: could this distinction work better?
1675                 if (choice == 0
1676                     && defender->is_monster()
1677                     && mons_has_blood(defender->type))
1678                 {
1679                     attack_verb = "beat";
1680                     verb_degree = "into a bloody pulp";
1681                 }
1682                 else
1683                 {
1684                     attack_verb = punch_desc[choice][0];
1685                     verb_degree = punch_desc[choice][1];
1686                 }
1687             }
1688         }
1689         break;
1690     }
1691 
1692     case WPN_UNKNOWN:
1693     default:
1694         attack_verb = "hit";
1695         break;
1696     }
1697 }
1698 
player_exercise_combat_skills()1699 void melee_attack::player_exercise_combat_skills()
1700 {
1701     if (defender && defender->is_monster()
1702         && !mons_is_firewood(*defender->as_monster()))
1703     {
1704         practise_hitting(weapon);
1705     }
1706 }
1707 
1708 /*
1709  * Applies god conduct for weapon ego
1710  *
1711  * Using speed brand as a chei worshipper, or holy/unholy/wizardly weapons etc
1712  */
player_weapon_upsets_god()1713 void melee_attack::player_weapon_upsets_god()
1714 {
1715     if (weapon
1716         && (weapon->base_type == OBJ_WEAPONS || weapon->base_type == OBJ_STAVES)
1717         && god_hates_item_handling(*weapon))
1718     {
1719         did_god_conduct(god_hates_item_handling(*weapon), 2);
1720     }
1721 }
1722 
1723 /* Apply player-specific effects as well as brand damage.
1724  *
1725  * Called after damage is calculated, but before unrand effects and before
1726  * damage is dealt.
1727  *
1728  * Returns true if combat should continue, false if it should end here.
1729  */
player_monattk_hit_effects()1730 bool melee_attack::player_monattk_hit_effects()
1731 {
1732     player_weapon_upsets_god();
1733 
1734     // Don't even check vampire bloodletting if the monster has already
1735     // been reset (for example, a spectral weapon who noticed in
1736     // player_stab_check that it shouldn't exist anymore).
1737     if (defender->type == MONS_NO_MONSTER)
1738         return false;
1739 
1740     // Thirsty vampires will try to use a stabbing situation to draw blood.
1741     if (you.has_mutation(MUT_VAMPIRISM)
1742         && damage_done > 0
1743         && stab_attempt
1744         && stab_bonus > 0)
1745     {
1746         _player_vampire_draws_blood(defender->as_monster(), damage_done, true);
1747     }
1748 
1749     if (!defender->alive())
1750         return false;
1751 
1752     // These effects apply only to monsters that are still alive:
1753 
1754     // Returns true if the hydra was killed by the decapitation, in which case
1755     // nothing more should be done to the hydra.
1756     if (consider_decapitation(damage_done))
1757         return false;
1758 
1759     return true;
1760 }
1761 
handle_noise(const coord_def & pos)1762 void melee_attack::handle_noise(const coord_def & pos)
1763 {
1764     // Successful stabs make no noise.
1765     if (stab_attempt)
1766         return;
1767 
1768     int loudness = damage_done / 4;
1769 
1770     // All non-stab melee attacks make some noise.
1771     loudness = max(1, loudness);
1772 
1773     // Cap melee noise at shouting volume.
1774     loudness = min(12, loudness);
1775 
1776     noisy(loudness, pos, attacker->mid);
1777 }
1778 
1779 /**
1780  * If appropriate, chop a head off the defender. (Usually a hydra.)
1781  *
1782  * @param dam           The damage done in the attack that may or may not chop
1783   *                     off a head.
1784  * @param damage_type   The type of damage done in the attack.
1785  * @return              Whether the defender was killed by the decapitation.
1786  */
consider_decapitation(int dam,int damage_type)1787 bool melee_attack::consider_decapitation(int dam, int damage_type)
1788 {
1789     const int dam_type = (damage_type != -1) ? damage_type :
1790                                                attacker->damage_type();
1791     if (!attack_chops_heads(dam, dam_type))
1792         return false;
1793 
1794     decapitate(dam_type);
1795 
1796     if (!defender->alive())
1797         return true;
1798 
1799     // Only living hydras get to regenerate heads.
1800     if (!(defender->holiness() & MH_NATURAL))
1801         return false;
1802 
1803     // What's the largest number of heads the defender can have?
1804     const int limit = defender->type == MONS_LERNAEAN_HYDRA ? 27
1805                                                             : MAX_HYDRA_HEADS;
1806 
1807     if (attacker->damage_brand() == SPWPN_FLAMING)
1808     {
1809         if (defender_visible)
1810             mpr("The flame cauterises the wound!");
1811         return false;
1812     }
1813 
1814     int heads = defender->heads();
1815     if (heads >= limit - 1)
1816         return false; // don't overshoot the head limit!
1817 
1818     simple_monster_message(*defender->as_monster(), " grows two more!");
1819     defender->as_monster()->num_heads += 2;
1820     defender->heal(8 + random2(8));
1821 
1822     return false;
1823 }
1824 
1825 /**
1826  * Can the given actor lose its heads? (Is it hydra or hydra-like?)
1827  *
1828  * @param defender  The actor in question.
1829  * @return          Whether the given actor is susceptible to head-choppage.
1830  */
actor_can_lose_heads(const actor * defender)1831 static bool actor_can_lose_heads(const actor* defender)
1832 {
1833     if (defender->is_monster()
1834         && defender->as_monster()->has_hydra_multi_attack()
1835         && defender->type != MONS_SPECTRAL_THING
1836         && defender->as_monster()->mons_species() != MONS_SERPENT_OF_HELL)
1837     {
1838         return true;
1839     }
1840 
1841     return false;
1842 }
1843 
1844 /**
1845  * Does this attack chop off one of the defender's heads? (Generally only
1846  * relevant for hydra defenders)
1847  *
1848  * @param dam           The damage done in the attack in question.
1849  * @param dam_type      The vorpal_damage_type of the attack.
1850  * @param wpn_brand     The brand_type of the attack.
1851  * @return              Whether the attack will chop off a head.
1852  */
attack_chops_heads(int dam,int dam_type)1853 bool melee_attack::attack_chops_heads(int dam, int dam_type)
1854 {
1855     // hydras and hydra-like things only.
1856     if (!actor_can_lose_heads(defender))
1857         return false;
1858 
1859     // no decapitate on riposte (Problematic)
1860     if (is_riposte)
1861         return false;
1862 
1863     // Monster attackers+defenders have only a 25% chance of making the
1864     // chop-check to prevent runaway head inflation.
1865     // XXX: Tentatively making an exception for spectral weapons
1866     const bool player_spec_weap = attacker->is_monster()
1867                                     && attacker->type == MONS_SPECTRAL_WEAPON
1868                                     && attacker->as_monster()->summoner
1869                                         == MID_PLAYER;
1870     if (attacker->is_monster() && defender->is_monster()
1871         && !player_spec_weap && !one_chance_in(4))
1872     {
1873         return false;
1874     }
1875 
1876     // Only cutting implements.
1877     if (dam_type != DVORP_SLICING && dam_type != DVORP_CHOPPING
1878         && dam_type != DVORP_CLAWING)
1879     {
1880         return false;
1881     }
1882 
1883     // Small claws are not big enough.
1884     if (dam_type == DVORP_CLAWING && attacker->has_claws() < 3)
1885         return false;
1886 
1887     // You need to have done at least some damage.
1888     if (dam <= 0 || dam < 4 && coinflip())
1889         return false;
1890 
1891     // ok, good enough!
1892     return true;
1893 }
1894 
1895 /**
1896  * Decapitate the (hydra or hydra-like) defender!
1897  *
1898  * @param dam_type      The vorpal_damage_type of the attack.
1899  */
decapitate(int dam_type)1900 void melee_attack::decapitate(int dam_type)
1901 {
1902     // Player hydras don't gain or lose heads.
1903     ASSERT(defender->is_monster());
1904 
1905     const char *verb = nullptr;
1906 
1907     if (dam_type == DVORP_CLAWING)
1908     {
1909         static const char *claw_verbs[] = { "rip", "tear", "claw" };
1910         verb = RANDOM_ELEMENT(claw_verbs);
1911     }
1912     else
1913     {
1914         static const char *slice_verbs[] =
1915         {
1916             "slice", "lop", "chop", "hack"
1917         };
1918         verb = RANDOM_ELEMENT(slice_verbs);
1919     }
1920 
1921     int heads = defender->heads();
1922     if (heads == 1) // will be zero afterwards
1923     {
1924         if (defender_visible)
1925         {
1926             mprf("%s %s %s last head off!",
1927                  atk_name(DESC_THE).c_str(),
1928                  attacker->conj_verb(verb).c_str(),
1929                  apostrophise(defender_name(true)).c_str());
1930         }
1931 
1932         if (!defender->is_summoned())
1933         {
1934             bleed_onto_floor(defender->pos(), defender->type,
1935                              defender->as_monster()->hit_points, true);
1936         }
1937 
1938         if (!simu)
1939             defender->hurt(attacker, INSTANT_DEATH);
1940 
1941         return;
1942     }
1943 
1944     if (defender_visible)
1945     {
1946         mprf("%s %s one of %s heads off!",
1947              atk_name(DESC_THE).c_str(),
1948              attacker->conj_verb(verb).c_str(),
1949              apostrophise(defender_name(true)).c_str());
1950     }
1951 
1952     defender->as_monster()->num_heads--;
1953 }
1954 
1955 /**
1956  * Apply passive retaliation damage from hitting acid monsters.
1957  */
attacker_sustain_passive_damage()1958 void melee_attack::attacker_sustain_passive_damage()
1959 {
1960     // If the defender has been cleaned up, it's too late for anything.
1961     if (!defender->alive())
1962         return;
1963 
1964     if (!mons_class_flag(defender->type, M_ACID_SPLASH))
1965         return;
1966 
1967     if (attacker->res_acid() >= 3)
1968         return;
1969 
1970     if (!adjacent(attacker->pos(), defender->pos()) || is_riposte)
1971         return;
1972 
1973     const int acid_strength = resist_adjust_damage(attacker, BEAM_ACID, 5);
1974 
1975     // Spectral weapons can't be corroded (but can take acid damage).
1976     const bool avatar = attacker->is_monster()
1977                         && mons_is_avatar(attacker->as_monster()->type);
1978 
1979     if (!avatar)
1980     {
1981         if (x_chance_in_y(acid_strength + 1, 30))
1982             attacker->corrode_equipment();
1983     }
1984 
1985     if (attacker->is_player())
1986         mpr(you.hands_act("burn", "!"));
1987     else
1988     {
1989         simple_monster_message(*attacker->as_monster(),
1990                                " is burned by acid!");
1991     }
1992     attacker->hurt(defender, roll_dice(1, acid_strength), BEAM_ACID,
1993                    KILLED_BY_ACID);
1994 }
1995 
staff_damage(skill_type skill)1996 int melee_attack::staff_damage(skill_type skill)
1997 {
1998     if (x_chance_in_y(attacker->skill(SK_EVOCATIONS, 200)
1999                     + attacker->skill(skill, 100), 3000))
2000     {
2001         return random2((attacker->skill(skill, 100)
2002                       + attacker->skill(SK_EVOCATIONS, 50)) / 80);
2003     }
2004     return 0;
2005 }
2006 
apply_staff_damage()2007 bool melee_attack::apply_staff_damage()
2008 {
2009     if (!weapon)
2010         return false;
2011 
2012     if (attacker->is_player() && you.get_mutation_level(MUT_NO_ARTIFICE))
2013         return false;
2014 
2015     if (weapon->base_type != OBJ_STAVES)
2016         return false;
2017 
2018     skill_type sk = staff_skill(static_cast<stave_type>(weapon->sub_type));
2019 
2020     switch (weapon->sub_type)
2021     {
2022     case STAFF_AIR:
2023         special_damage =
2024             resist_adjust_damage(defender, BEAM_ELECTRICITY, staff_damage(sk));
2025 
2026         if (special_damage)
2027         {
2028             special_damage_message =
2029                 make_stringf(
2030                     "%s %s electrocuted%s",
2031                     defender->name(DESC_THE).c_str(),
2032                     defender->conj_verb("are").c_str(),
2033                     attack_strength_punctuation(special_damage).c_str());
2034             special_damage_flavour = BEAM_ELECTRICITY;
2035         }
2036 
2037         break;
2038 
2039     case STAFF_COLD:
2040         special_damage =
2041             resist_adjust_damage(defender, BEAM_COLD, staff_damage(sk));
2042 
2043         if (special_damage)
2044         {
2045             special_damage_message =
2046                 make_stringf(
2047                     "%s freeze%s %s%s",
2048                     attacker->name(DESC_THE).c_str(),
2049                     attacker->is_player() ? "" : "s",
2050                     defender->name(DESC_THE).c_str(),
2051                     attack_strength_punctuation(special_damage).c_str());
2052             special_damage_flavour = BEAM_COLD;
2053         }
2054         break;
2055 
2056     case STAFF_EARTH:
2057         special_damage = staff_damage(sk) * 4 / 3;
2058         special_damage = apply_defender_ac(special_damage, 0, ac_type::triple);
2059 
2060         if (special_damage > 0)
2061         {
2062             special_damage_message =
2063                 make_stringf(
2064                     "%s %s %s%s",
2065                     attacker->name(DESC_THE).c_str(),
2066                     attacker->conj_verb("shatter").c_str(),
2067                     defender->name(DESC_THE).c_str(),
2068                     attack_strength_punctuation(special_damage).c_str());
2069         }
2070         break;
2071 
2072     case STAFF_FIRE:
2073         special_damage =
2074             resist_adjust_damage(defender, BEAM_FIRE, staff_damage(sk));
2075 
2076         if (special_damage)
2077         {
2078             special_damage_message =
2079                 make_stringf(
2080                     "%s burn%s %s%s",
2081                     attacker->name(DESC_THE).c_str(),
2082                     attacker->is_player() ? "" : "s",
2083                     defender->name(DESC_THE).c_str(),
2084                     attack_strength_punctuation(special_damage).c_str());
2085             special_damage_flavour = BEAM_FIRE;
2086 
2087             if (defender->is_player())
2088                 maybe_melt_player_enchantments(BEAM_FIRE, special_damage);
2089         }
2090         break;
2091 
2092     case STAFF_POISON:
2093         special_damage =
2094             resist_adjust_damage(defender, BEAM_POISON, staff_damage(sk));
2095 
2096         if (special_damage)
2097         {
2098             special_damage_message =
2099                 make_stringf(
2100                     "%s envenom%s %s%s",
2101                     attacker->name(DESC_THE).c_str(),
2102                     attacker->is_player() ? "" : "s",
2103                     defender->name(DESC_THE).c_str(),
2104                     attack_strength_punctuation(special_damage).c_str());
2105             special_damage_flavour = BEAM_POISON;
2106         }
2107         break;
2108 
2109     case STAFF_DEATH:
2110         special_damage =
2111             resist_adjust_damage(defender, BEAM_NEG, staff_damage(sk));
2112 
2113         if (special_damage)
2114         {
2115             special_damage_message =
2116                 make_stringf(
2117                     "%s %s in agony%s",
2118                     defender->name(DESC_THE).c_str(),
2119                     defender->conj_verb("writhe").c_str(),
2120                     attack_strength_punctuation(special_damage).c_str());
2121 
2122             attacker->god_conduct(DID_EVIL, 4);
2123         }
2124         break;
2125 
2126     case STAFF_CONJURATION:
2127         special_damage = staff_damage(sk);
2128         special_damage = apply_defender_ac(special_damage);
2129 
2130         if (special_damage > 0)
2131         {
2132             special_damage_message =
2133                 make_stringf(
2134                     "%s %s %s%s",
2135                     attacker->name(DESC_THE).c_str(),
2136                     attacker->conj_verb("blast").c_str(),
2137                     defender->name(DESC_THE).c_str(),
2138                     attack_strength_punctuation(special_damage).c_str());
2139         }
2140         break;
2141 
2142 #if TAG_MAJOR_VERSION == 34
2143     case STAFF_SUMMONING:
2144     case STAFF_POWER:
2145     case STAFF_ENCHANTMENT:
2146     case STAFF_ENERGY:
2147     case STAFF_WIZARDRY:
2148 #endif
2149         break;
2150 
2151     default:
2152         die("Invalid staff type: %d", weapon->sub_type);
2153     }
2154 
2155     if (special_damage || special_damage_flavour)
2156     {
2157         dprf(DIAG_COMBAT, "Staff damage to %s: %d, flavour: %d",
2158              defender->name(DESC_THE).c_str(),
2159              special_damage, special_damage_flavour);
2160 
2161         if (needs_message && !special_damage_message.empty())
2162             mpr(special_damage_message);
2163 
2164         inflict_damage(special_damage, special_damage_flavour);
2165         if (special_damage > 0)
2166         {
2167             defender->expose_to_element(special_damage_flavour, 2);
2168             // XXX: this is messy, but poisoning from the staff of poison
2169             // should happen after damage.
2170             if (defender->alive() && special_damage_flavour == BEAM_POISON)
2171                 defender->poison(attacker, 2);
2172         }
2173     }
2174 
2175     return true;
2176 }
2177 
calc_to_hit(bool random)2178 int melee_attack::calc_to_hit(bool random)
2179 {
2180     int mhit = attack::calc_to_hit(random);
2181     if (mhit == AUTOMATIC_HIT)
2182         return AUTOMATIC_HIT;
2183 
2184     return mhit;
2185 }
2186 
post_roll_to_hit_modifiers(int mhit,bool random)2187 int melee_attack::post_roll_to_hit_modifiers(int mhit, bool random)
2188 {
2189     int modifiers = attack::post_roll_to_hit_modifiers(mhit, random);
2190 
2191     // Just trying to touch is easier than trying to damage.
2192     if (you.duration[DUR_CONFUSING_TOUCH])
2193         modifiers += maybe_random_div(you.dex(), 2, random);
2194 
2195     // Rolling charges feel bad when they miss, so make them miss less often.
2196     if (roll_dist > 0)
2197         modifiers += 5; // matching UC form to-hit bonuses
2198 
2199     if (attacker->is_player() && !weapon && get_form()->unarmed_hit_bonus)
2200     {
2201         // TODO: Review this later (transformations getting extra hit
2202         // almost across the board seems bad) - Cryp71c
2203         modifiers += UC_FORM_TO_HIT_BONUS;
2204     }
2205 
2206     return modifiers;
2207 }
2208 
player_stab_check()2209 void melee_attack::player_stab_check()
2210 {
2211     if (!is_projected)
2212         attack::player_stab_check();
2213 }
2214 
2215 /**
2216  * Can we get a good stab with this weapon?
2217  */
player_good_stab()2218 bool melee_attack::player_good_stab()
2219 {
2220     return wpn_skill == SK_SHORT_BLADES
2221            || you.get_mutation_level(MUT_PAWS)
2222            || player_equip_unrand(UNRAND_HOOD_ASSASSIN)
2223               && (!weapon || is_melee_weapon(*weapon));
2224 }
2225 
2226 /* Select the attack verb for attacker
2227  *
2228  * If klown, select randomly from klown_attack, otherwise check for any special
2229  * case attack verbs (tentacles or door/fountain-mimics) and if all else fails,
2230  * select an attack verb from attack_types based on the ENUM value of attk_type.
2231  *
2232  * Returns (attack_verb)
2233  */
mons_attack_verb()2234 string melee_attack::mons_attack_verb()
2235 {
2236     static const char *klown_attack[] =
2237     {
2238         "hit",
2239         "poke",
2240         "prod",
2241         "flog",
2242         "pound",
2243         "slap",
2244         "tickle",
2245         "defenestrate",
2246         "sucker-punch",
2247         "elbow",
2248         "pinch",
2249         "strangle-hug",
2250         "squeeze",
2251         "tease",
2252         "eye-gouge",
2253         "karate-kick",
2254         "headlock",
2255         "wrestle",
2256         "trip-wire",
2257         "kneecap"
2258     };
2259 
2260     if (attacker->type == MONS_KILLER_KLOWN && attk_type == AT_HIT)
2261         return RANDOM_ELEMENT(klown_attack);
2262 
2263     //XXX: then why give them it in the first place?
2264     if (attk_type == AT_TENTACLE_SLAP && mons_is_tentacle(attacker->type))
2265         return "slap";
2266 
2267     return mon_attack_name(attk_type);
2268 }
2269 
mons_attack_desc()2270 string melee_attack::mons_attack_desc()
2271 {
2272     if (!you.can_see(*attacker))
2273         return "";
2274 
2275     string ret;
2276     int dist = (attack_position - defender->pos()).rdist();
2277     if (dist > 1)
2278     {
2279         ASSERT(can_reach());
2280         ret = " from afar";
2281     }
2282 
2283     if (weapon && !mons_class_is_animated_weapon(attacker->type))
2284         ret += " with " + weapon->name(DESC_A);
2285 
2286     return ret;
2287 }
2288 
announce_hit()2289 void melee_attack::announce_hit()
2290 {
2291     if (!needs_message || attk_flavour == AF_CRUSH)
2292         return;
2293 
2294     if (attacker->is_monster())
2295     {
2296         mprf("%s %s %s%s%s%s",
2297              atk_name(DESC_THE).c_str(),
2298              attacker->conj_verb(mons_attack_verb()).c_str(),
2299              defender_name(true).c_str(),
2300              debug_damage_number().c_str(),
2301              mons_attack_desc().c_str(),
2302              attack_strength_punctuation(damage_done).c_str());
2303     }
2304     else
2305     {
2306         if (!verb_degree.empty() && verb_degree[0] != ' '
2307             && verb_degree[0] != ',' && verb_degree[0] != '\'')
2308         {
2309             verb_degree = " " + verb_degree;
2310         }
2311 
2312         mprf("You %s %s%s%s%s",
2313              attack_verb.c_str(),
2314              defender->name(DESC_THE).c_str(),
2315              verb_degree.c_str(), debug_damage_number().c_str(),
2316              attack_strength_punctuation(damage_done).c_str());
2317     }
2318 }
2319 
2320 // Returns if the target was actually poisoned by this attack
mons_do_poison()2321 bool melee_attack::mons_do_poison()
2322 {
2323     int amount = 1;
2324 
2325     if (attk_flavour == AF_POISON_STRONG)
2326     {
2327         amount = random_range(attacker->get_hit_dice() * 11 / 3,
2328                               attacker->get_hit_dice() * 13 / 2);
2329     }
2330     else
2331     {
2332         amount = random_range(attacker->get_hit_dice() * 2,
2333                               attacker->get_hit_dice() * 4);
2334     }
2335 
2336     if (attacker->as_monster()->has_ench(ENCH_CONCENTRATE_VENOM))
2337     {
2338         return curare_actor(attacker, defender, 2, "concentrated venom",
2339                             attacker->name(DESC_PLAIN));
2340     }
2341 
2342     if (!defender->poison(attacker, amount))
2343         return false;
2344 
2345     if (needs_message)
2346     {
2347         mprf("%s poisons %s!",
2348                 atk_name(DESC_THE).c_str(),
2349                 defender_name(true).c_str());
2350     }
2351 
2352     return true;
2353 }
2354 
mons_do_napalm()2355 void melee_attack::mons_do_napalm()
2356 {
2357     if (defender->res_sticky_flame())
2358         return;
2359 
2360     if (one_chance_in(3))
2361     {
2362         if (needs_message)
2363         {
2364             mprf("%s %s covered in liquid flames%s",
2365                  defender_name(false).c_str(),
2366                  defender->conj_verb("are").c_str(),
2367                  attack_strength_punctuation(special_damage).c_str());
2368         }
2369 
2370         if (defender->is_player())
2371             napalm_player(random2avg(7, 3) + 1, atk_name(DESC_A));
2372         else
2373         {
2374             napalm_monster(
2375                 defender->as_monster(),
2376                 attacker,
2377                 min(4, 1 + random2(attacker->get_hit_dice())/2));
2378         }
2379     }
2380 }
2381 
_print_resist_messages(actor * defender,int base_damage,beam_type flavour)2382 static void _print_resist_messages(actor* defender, int base_damage,
2383                                    beam_type flavour)
2384 {
2385     // check_your_resists is used for the player case to get additional
2386     // effects such as Xom amusement, melting of icy effects, etc.
2387     // mons_adjust_flavoured is used for the monster case to get all of the
2388     // special message handling ("The ice beast melts!") correct.
2389     // XXX: there must be a nicer way to do this, especially because we're
2390     // basically calculating the damage twice in the case where messages
2391     // are needed.
2392     if (defender->is_player())
2393         (void)check_your_resists(base_damage, flavour, "");
2394     else
2395     {
2396         bolt beam;
2397         beam.flavour = flavour;
2398         (void)mons_adjust_flavoured(defender->as_monster(),
2399                                     beam,
2400                                     base_damage,
2401                                     true);
2402     }
2403 }
2404 
mons_attack_effects()2405 bool melee_attack::mons_attack_effects()
2406 {
2407     // may have died earlier, due to e.g. pain bond
2408     // we could continue with the rest of their attack, but it's a minefield
2409     // of potential crashes. so, let's not.
2410     if (attacker->is_monster() && invalid_monster(attacker->as_monster()))
2411         return false;
2412 
2413     // Monsters attacking themselves don't get attack flavour.
2414     // The message sequences look too weird. Also, stealing
2415     // attacks aren't handled until after the damage msg. Also,
2416     // no attack flavours for dead defenders
2417     if (attacker != defender && defender->alive())
2418     {
2419         mons_apply_attack_flavour();
2420 
2421         if (needs_message && !special_damage_message.empty())
2422             mpr(special_damage_message);
2423 
2424         if (special_damage > 0)
2425         {
2426             inflict_damage(special_damage, special_damage_flavour);
2427             special_damage = 0;
2428             special_damage_message.clear();
2429             special_damage_flavour = BEAM_NONE;
2430         }
2431     }
2432 
2433     if (defender->is_player())
2434         practise_being_hit();
2435 
2436     // A tentacle may have banished its own parent/sibling and thus itself.
2437     if (!attacker->alive())
2438         return false;
2439 
2440     // consider_decapitation() returns true if the defender was killed
2441     // by the decapitation, in which case we should stop the rest of the
2442     // attack, too.
2443     if (consider_decapitation(damage_done,
2444                               attacker->damage_type(attack_number)))
2445     {
2446         return false;
2447     }
2448 
2449     if (attacker != defender && attk_flavour == AF_TRAMPLE)
2450         do_knockback();
2451 
2452     special_damage = 0;
2453     special_damage_message.clear();
2454     special_damage_flavour = BEAM_NONE;
2455 
2456     // Defender banished. Bail since the defender is still alive in the
2457     // Abyss.
2458     if (defender->is_banished())
2459         return false;
2460 
2461     if (!defender->alive())
2462         return attacker->alive();
2463 
2464     // Bail if the monster is attacking itself without a weapon, since
2465     // intrinsic monster attack flavours aren't applied for self-attacks.
2466     if (attacker == defender && !weapon)
2467         return false;
2468 
2469     return true;
2470 }
2471 
mons_apply_attack_flavour()2472 void melee_attack::mons_apply_attack_flavour()
2473 {
2474     // Most of this is from BWR 4.1.2.
2475 
2476     attack_flavour flavour = attk_flavour;
2477     if (flavour == AF_CHAOTIC)
2478         flavour = random_chaos_attack_flavour();
2479 
2480     const int base_damage = flavour_damage(flavour, attacker->get_hit_dice());
2481 
2482     // Note that if damage_done == 0 then this code won't be reached
2483     // unless the flavour is in flavour_triggers_damageless.
2484     switch (flavour)
2485     {
2486     default:
2487         // Just to trigger special melee damage effects for regular attacks
2488         // (e.g. Qazlal's elemental adaptation).
2489         defender->expose_to_element(BEAM_MISSILE, 2);
2490         break;
2491 
2492     case AF_MUTATE:
2493         if (one_chance_in(4))
2494         {
2495             defender->malmutate(you.can_see(*attacker) ?
2496                 apostrophise(attacker->name(DESC_PLAIN)) + " mutagenic touch" :
2497                 "mutagenic touch");
2498         }
2499         break;
2500 
2501     case AF_POISON:
2502     case AF_POISON_STRONG:
2503     case AF_REACH_STING:
2504         if (one_chance_in(3))
2505             mons_do_poison();
2506         break;
2507 
2508     case AF_FIRE:
2509         special_damage =
2510             resist_adjust_damage(defender,
2511                                  BEAM_FIRE,
2512                                  base_damage);
2513         special_damage_flavour = BEAM_FIRE;
2514 
2515         if (needs_message && base_damage)
2516         {
2517             mprf("%s %s engulfed in flames%s",
2518                  defender_name(false).c_str(),
2519                  defender->conj_verb("are").c_str(),
2520                  attack_strength_punctuation(special_damage).c_str());
2521 
2522             _print_resist_messages(defender, base_damage, BEAM_FIRE);
2523         }
2524 
2525         defender->expose_to_element(BEAM_FIRE, 2);
2526         break;
2527 
2528     case AF_COLD:
2529         special_damage =
2530             resist_adjust_damage(defender,
2531                                  BEAM_COLD,
2532                                  base_damage);
2533         special_damage_flavour = BEAM_COLD;
2534 
2535         if (needs_message && base_damage)
2536         {
2537             mprf("%s %s %s%s",
2538                  atk_name(DESC_THE).c_str(),
2539                  attacker->conj_verb("freeze").c_str(),
2540                  defender_name(true).c_str(),
2541                  attack_strength_punctuation(special_damage).c_str());
2542 
2543             _print_resist_messages(defender, base_damage, BEAM_COLD);
2544         }
2545 
2546         defender->expose_to_element(BEAM_COLD, 2);
2547         break;
2548 
2549     case AF_ELEC:
2550         special_damage =
2551             resist_adjust_damage(defender,
2552                                  BEAM_ELECTRICITY,
2553                                  base_damage);
2554         special_damage_flavour = BEAM_ELECTRICITY;
2555 
2556         if (needs_message && base_damage)
2557         {
2558             mprf("%s %s %s%s",
2559                  atk_name(DESC_THE).c_str(),
2560                  attacker->conj_verb("shock").c_str(),
2561                  defender_name(true).c_str(),
2562                  attack_strength_punctuation(special_damage).c_str());
2563 
2564             _print_resist_messages(defender, base_damage, BEAM_ELECTRICITY);
2565         }
2566 
2567         dprf(DIAG_COMBAT, "Shock damage: %d", special_damage);
2568         defender->expose_to_element(BEAM_ELECTRICITY, 2);
2569         break;
2570 
2571         // Combines drain speed and vampiric.
2572     case AF_SCARAB:
2573         if (x_chance_in_y(3, 5))
2574             drain_defender_speed();
2575 
2576         // deliberate fall-through
2577     case AF_VAMPIRIC:
2578         if (!actor_is_susceptible_to_vampirism(*defender))
2579             break;
2580 
2581         if (defender->stat_hp() < defender->stat_maxhp())
2582         {
2583             int healed = resist_adjust_damage(defender, BEAM_NEG,
2584                                               1 + random2(damage_done));
2585             if (healed)
2586             {
2587                 attacker->heal(healed);
2588                 if (needs_message)
2589                 {
2590                     mprf("%s %s strength from %s injuries!",
2591                          atk_name(DESC_THE).c_str(),
2592                          attacker->conj_verb("draw").c_str(),
2593                          def_name(DESC_ITS).c_str());
2594                 }
2595             }
2596         }
2597         break;
2598 
2599     case AF_DRAIN_STR:
2600     case AF_DRAIN_INT:
2601     case AF_DRAIN_DEX:
2602         if (one_chance_in(20) || one_chance_in(3))
2603         {
2604             stat_type drained_stat = (flavour == AF_DRAIN_STR ? STAT_STR :
2605                                       flavour == AF_DRAIN_INT ? STAT_INT
2606                                                               : STAT_DEX);
2607             defender->drain_stat(drained_stat, 1);
2608         }
2609         break;
2610 
2611     case AF_BLINK:
2612         // blinking can kill, delay the call
2613         if (one_chance_in(3))
2614             blink_fineff::schedule(attacker);
2615         break;
2616 
2617     case AF_BLINK_WITH:
2618         if (coinflip())
2619             blink_fineff::schedule(attacker, defender);
2620         break;
2621 
2622     case AF_CONFUSE:
2623         if (attk_type == AT_SPORE)
2624         {
2625             if (defender->is_unbreathing())
2626                 break;
2627 
2628             monster *attkmon = attacker->as_monster();
2629             attkmon->set_hit_dice(attkmon->get_experience_level() - 1);
2630             if (attkmon->get_experience_level() <= 0)
2631                 attacker->as_monster()->suicide();
2632 
2633             if (defender_visible)
2634             {
2635                 mprf("%s %s engulfed in a cloud of spores!",
2636                      defender->name(DESC_THE).c_str(),
2637                      defender->conj_verb("are").c_str());
2638             }
2639         }
2640 
2641         if (one_chance_in(3))
2642         {
2643             defender->confuse(attacker,
2644                               1 + random2(3+attacker->get_hit_dice()));
2645         }
2646         break;
2647 
2648     case AF_DRAIN:
2649         if (coinflip())
2650             drain_defender();
2651         break;
2652 
2653     case AF_POISON_PARALYSE:
2654     {
2655         // Doesn't affect the poison-immune.
2656         if (defender->is_player() && you.duration[DUR_DIVINE_STAMINA] > 0)
2657         {
2658             mpr("Your divine stamina protects you from poison!");
2659             break;
2660         }
2661         else if (defender->res_poison() >= 3)
2662             break;
2663 
2664         // Same frequency as AF_POISON and AF_POISON_STRONG.
2665         if (one_chance_in(3))
2666         {
2667             int dmg = random_range(attacker->get_hit_dice() * 3 / 2,
2668                                    attacker->get_hit_dice() * 5 / 2);
2669             defender->poison(attacker, dmg);
2670         }
2671 
2672         // Try to apply either paralysis or slowing, with the normal 2/3
2673         // chance to resist with rPois.
2674         if (one_chance_in(6))
2675         {
2676             if (defender->res_poison() <= 0 || one_chance_in(3))
2677                 defender->paralyse(attacker, roll_dice(1, 3));
2678         }
2679         else if (defender->res_poison() <= 0 || one_chance_in(3))
2680             defender->slow_down(attacker, roll_dice(1, 3));
2681 
2682         break;
2683     }
2684 
2685     case AF_REACH_TONGUE:
2686     case AF_ACID:
2687         defender->splash_with_acid(attacker, 3);
2688         break;
2689 
2690     case AF_CORRODE:
2691         defender->corrode_equipment(atk_name(DESC_THE).c_str());
2692         break;
2693 
2694     case AF_DISTORT:
2695         distortion_affects_defender();
2696         break;
2697 
2698     case AF_RAGE:
2699         if (!one_chance_in(3) || !defender->can_go_berserk())
2700             break;
2701 
2702         if (needs_message)
2703         {
2704             mprf("%s %s %s!",
2705                  atk_name(DESC_THE).c_str(),
2706                  attacker->conj_verb("infuriate").c_str(),
2707                  defender_name(true).c_str());
2708         }
2709 
2710         defender->go_berserk(false);
2711         break;
2712 
2713     case AF_STICKY_FLAME:
2714         mons_do_napalm();
2715         break;
2716 
2717     case AF_CHAOTIC:
2718         chaos_affects_defender();
2719         break;
2720 
2721     case AF_STEAL:
2722         // Ignore monsters, for now.
2723         if (!defender->is_player())
2724             break;
2725 
2726         attacker->as_monster()->steal_item_from_player();
2727         break;
2728 
2729     case AF_HOLY:
2730         if (defender->holy_wrath_susceptible())
2731             special_damage = attk_damage * 0.75;
2732 
2733         if (needs_message && special_damage)
2734         {
2735             mprf("%s %s %s%s",
2736                  atk_name(DESC_THE).c_str(),
2737                  attacker->conj_verb("sear").c_str(),
2738                  defender_name(true).c_str(),
2739                  attack_strength_punctuation(special_damage).c_str());
2740 
2741         }
2742         break;
2743 
2744     case AF_ANTIMAGIC:
2745         antimagic_affects_defender(attacker->get_hit_dice() * 12);
2746 
2747         if (mons_genus(attacker->type) == MONS_VINE_STALKER
2748             && attacker->is_monster())
2749         {
2750             const bool spell_user = defender->antimagic_susceptible();
2751 
2752             if (you.can_see(*attacker) || you.can_see(*defender))
2753             {
2754                 mprf("%s drains %s %s.",
2755                      attacker->name(DESC_THE).c_str(),
2756                      defender->pronoun(PRONOUN_POSSESSIVE).c_str(),
2757                      spell_user ? "magic" : "power");
2758             }
2759 
2760             monster* vine = attacker->as_monster();
2761             if (vine->has_ench(ENCH_ANTIMAGIC)
2762                 && (defender->is_player()
2763                     || (!defender->as_monster()->is_summoned()
2764                         && !mons_is_firewood(*defender->as_monster()))))
2765             {
2766                 mon_enchant me = vine->get_ench(ENCH_ANTIMAGIC);
2767                 vine->lose_ench_duration(me, random2(damage_done) + 1);
2768                 simple_monster_message(*attacker->as_monster(),
2769                                        spell_user
2770                                        ? " looks very invigorated."
2771                                        : " looks invigorated.");
2772             }
2773         }
2774         break;
2775 
2776     case AF_PAIN:
2777         pain_affects_defender();
2778         break;
2779 
2780     case AF_ENSNARE:
2781         if (one_chance_in(3))
2782             ensnare(defender);
2783         break;
2784 
2785     case AF_CRUSH:
2786         if (needs_message)
2787         {
2788             mprf("%s %s %s.",
2789                  atk_name(DESC_THE).c_str(),
2790                  attacker->conj_verb("grab").c_str(),
2791                  defender_name(true).c_str());
2792         }
2793         attacker->start_constricting(*defender);
2794         // if you got grabbed, interrupt stair climb and passwall
2795         if (defender->is_player())
2796             stop_delay(true);
2797         break;
2798 
2799     case AF_ENGULF:
2800         if (x_chance_in_y(2, 3)
2801             && attacker->can_constrict(defender, true, true))
2802         {
2803             const bool watery = attacker->type != MONS_QUICKSILVER_OOZE;
2804             if (defender->is_player() && !you.duration[DUR_WATER_HOLD])
2805             {
2806                 you.duration[DUR_WATER_HOLD] = 10;
2807                 you.props["water_holder"].get_int() = attacker->as_monster()->mid;
2808                 you.props["water_hold_substance"].get_string() = watery ? "water" : "ooze";
2809             }
2810             else if (defender->is_monster()
2811                      && !defender->as_monster()->has_ench(ENCH_WATER_HOLD))
2812             {
2813                 defender->as_monster()->add_ench(mon_enchant(ENCH_WATER_HOLD, 1,
2814                                                              attacker, 1));
2815             }
2816             else
2817                 return; //Didn't apply effect; no message
2818 
2819             if (needs_message)
2820             {
2821                 mprf("%s %s %s%s!",
2822                      atk_name(DESC_THE).c_str(),
2823                      attacker->conj_verb("engulf").c_str(),
2824                      defender_name(true).c_str(),
2825                      watery ? " in water" : "");
2826             }
2827         }
2828 
2829         defender->expose_to_element(BEAM_WATER, 0);
2830         break;
2831 
2832     case AF_PURE_FIRE:
2833         if (attacker->type == MONS_FIRE_VORTEX)
2834             attacker->as_monster()->suicide(-10);
2835 
2836         special_damage = defender->apply_ac(base_damage, 0, ac_type::half);
2837         special_damage = resist_adjust_damage(defender,
2838                                               BEAM_FIRE,
2839                                               special_damage);
2840 
2841         if (needs_message && special_damage)
2842         {
2843             mprf("%s %s %s!",
2844                     atk_name(DESC_THE).c_str(),
2845                     attacker->conj_verb("burn").c_str(),
2846                     defender_name(true).c_str());
2847 
2848             _print_resist_messages(defender, special_damage, BEAM_FIRE);
2849         }
2850 
2851         defender->expose_to_element(BEAM_FIRE, 2);
2852         break;
2853 
2854     case AF_DRAIN_SPEED:
2855         if (x_chance_in_y(3, 5))
2856             drain_defender_speed();
2857         break;
2858 
2859     case AF_VULN:
2860         if (one_chance_in(3))
2861         {
2862             bool visible_effect = false;
2863             if (defender->is_player())
2864             {
2865                 if (!you.duration[DUR_LOWERED_WL])
2866                     visible_effect = true;
2867                 you.increase_duration(DUR_LOWERED_WL, 20 + random2(20), 40);
2868             }
2869             else
2870             {
2871                 // Halving the WL of targets with infinite wills has no effect
2872                 if (defender->as_monster()->willpower() == WILL_INVULN)
2873                     break;
2874                 if (!defender->as_monster()->has_ench(ENCH_LOWERED_WL))
2875                     visible_effect = true;
2876                 mon_enchant lowered_wl(ENCH_LOWERED_WL, 1, attacker,
2877                                        (20 + random2(20)) * BASELINE_DELAY);
2878                 defender->as_monster()->add_ench(lowered_wl);
2879             }
2880 
2881             if (needs_message && visible_effect)
2882             {
2883                 mprf("%s willpower is stripped away!",
2884                      def_name(DESC_ITS).c_str());
2885             }
2886         }
2887         break;
2888 
2889     case AF_SHADOWSTAB:
2890         attacker->as_monster()->del_ench(ENCH_INVIS, true);
2891         break;
2892 
2893     case AF_DROWN:
2894         if (attacker->type == MONS_DROWNED_SOUL)
2895             attacker->as_monster()->suicide(-1000);
2896 
2897         if (defender->res_water_drowning() <= 0)
2898         {
2899             special_damage = attacker->get_hit_dice() * 3 / 4
2900                             + random2(attacker->get_hit_dice() * 3 / 4);
2901             special_damage_flavour = BEAM_WATER;
2902             kill_type = KILLED_BY_WATER;
2903 
2904             if (needs_message)
2905             {
2906                 mprf("%s %s %s%s",
2907                     atk_name(DESC_THE).c_str(),
2908                     attacker->conj_verb("drown").c_str(),
2909                     defender_name(true).c_str(),
2910                     attack_strength_punctuation(special_damage).c_str());
2911             }
2912         }
2913         break;
2914 
2915     case AF_WEAKNESS:
2916         if (coinflip())
2917             defender->weaken(attacker, 12);
2918         break;
2919     }
2920 }
2921 
do_passive_freeze()2922 void melee_attack::do_passive_freeze()
2923 {
2924     if (you.has_mutation(MUT_PASSIVE_FREEZE)
2925         && attacker->alive()
2926         && adjacent(you.pos(), attacker->as_monster()->pos()))
2927     {
2928         bolt beam;
2929         beam.flavour = BEAM_COLD;
2930         beam.thrower = KILL_YOU;
2931 
2932         monster* mon = attacker->as_monster();
2933 
2934         const int orig_hurted = random2(11);
2935         int hurted = mons_adjust_flavoured(mon, beam, orig_hurted);
2936 
2937         if (!hurted)
2938             return;
2939 
2940         simple_monster_message(*mon, " is very cold.");
2941 
2942         mon->hurt(&you, hurted);
2943 
2944         if (mon->alive())
2945         {
2946             mon->expose_to_element(BEAM_COLD, orig_hurted);
2947             print_wounds(*mon);
2948         }
2949     }
2950 }
2951 
mons_do_eyeball_confusion()2952 void melee_attack::mons_do_eyeball_confusion()
2953 {
2954     if (you.has_mutation(MUT_EYEBALLS)
2955         && attacker->alive()
2956         && adjacent(you.pos(), attacker->as_monster()->pos())
2957         && x_chance_in_y(you.get_mutation_level(MUT_EYEBALLS), 20))
2958     {
2959         const int ench_pow = you.get_mutation_level(MUT_EYEBALLS) * 30;
2960         monster* mon = attacker->as_monster();
2961 
2962         if (mon->check_willpower(ench_pow) <= 0)
2963         {
2964             mprf("The eyeballs on your body gaze at %s.",
2965                  mon->name(DESC_THE).c_str());
2966 
2967             if (!mon->clarity())
2968             {
2969                 mon->add_ench(mon_enchant(ENCH_CONFUSION, 0, &you,
2970                                           30 + random2(100)));
2971             }
2972         }
2973     }
2974 }
2975 
mons_do_tendril_disarm()2976 void melee_attack::mons_do_tendril_disarm()
2977 {
2978     monster* mon = attacker->as_monster();
2979     // some rounding errors here, but not significant
2980     const int adj_mon_hd = mon->is_fighter() ? mon->get_hit_dice() * 3 / 2
2981                                              : mon->get_hit_dice();
2982 
2983     if (you.get_mutation_level(MUT_TENDRILS)
2984         && one_chance_in(5)
2985         && (random2(you.dex()) > adj_mon_hd
2986             || random2(you.strength()) > adj_mon_hd))
2987     {
2988         item_def* mons_wpn = mon->disarm();
2989         if (mons_wpn)
2990         {
2991             mprf("Your tendrils lash around %s %s and pull it to the ground!",
2992                  apostrophise(mon->name(DESC_THE)).c_str(),
2993                  mons_wpn->name(DESC_PLAIN).c_str());
2994         }
2995     }
2996 }
2997 
do_spines()2998 void melee_attack::do_spines()
2999 {
3000     // Monsters only get struck on their first attack per round
3001     if (attacker->is_monster() && effective_attack_number > 0)
3002         return;
3003 
3004     if (defender->is_player())
3005     {
3006         const int mut = you.get_mutation_level(MUT_SPINY);
3007 
3008         if (mut && attacker->alive() && coinflip())
3009         {
3010             int dmg = random_range(mut,
3011                 div_rand_round(you.experience_level * 2, 3) + mut * 3);
3012             int hurt = attacker->apply_ac(dmg);
3013 
3014             dprf(DIAG_COMBAT, "Spiny: dmg = %d hurt = %d", dmg, hurt);
3015 
3016             if (hurt <= 0)
3017                 return;
3018 
3019             simple_monster_message(*attacker->as_monster(),
3020                                    " is struck by your spines.");
3021 
3022             attacker->hurt(&you, hurt);
3023         }
3024     }
3025     else if (defender->as_monster()->is_spiny())
3026     {
3027         // Thorn hunters can attack their own brambles without injury
3028         if (defender->type == MONS_BRIAR_PATCH
3029             && attacker->type == MONS_THORN_HUNTER
3030             // Dithmenos' shadow can't take damage, don't spam.
3031             || attacker->type == MONS_PLAYER_SHADOW)
3032         {
3033             return;
3034         }
3035 
3036         if (attacker->alive() && one_chance_in(3))
3037         {
3038             int dmg = roll_dice(5, 4);
3039             int hurt = attacker->apply_ac(dmg);
3040             dprf(DIAG_COMBAT, "Spiny: dmg = %d hurt = %d", dmg, hurt);
3041 
3042             if (hurt <= 0)
3043                 return;
3044             if (you.can_see(*defender) || attacker->is_player())
3045             {
3046                 mprf("%s %s struck by %s %s.", attacker->name(DESC_THE).c_str(),
3047                      attacker->conj_verb("are").c_str(),
3048                      defender->name(DESC_ITS).c_str(),
3049                      defender->type == MONS_BRIAR_PATCH ? "thorns"
3050                                                         : "spines");
3051             }
3052             attacker->hurt(defender, hurt, BEAM_MISSILE, KILLED_BY_SPINES);
3053         }
3054     }
3055 }
3056 
emit_foul_stench()3057 void melee_attack::emit_foul_stench()
3058 {
3059     monster* mon = attacker->as_monster();
3060 
3061     if (you.has_mutation(MUT_FOUL_STENCH)
3062         && attacker->alive()
3063         && adjacent(you.pos(), mon->pos()))
3064     {
3065         const int mut = you.get_mutation_level(MUT_FOUL_STENCH);
3066 
3067         if (damage_done > 0 && x_chance_in_y(mut * 3 - 1, 20)
3068             && !cell_is_solid(mon->pos())
3069             && !cloud_at(mon->pos()))
3070         {
3071             mpr("You emit a cloud of foul miasma!");
3072             place_cloud(CLOUD_MIASMA, mon->pos(), 5 + random2(6), &you);
3073         }
3074     }
3075 }
3076 
do_minotaur_retaliation()3077 void melee_attack::do_minotaur_retaliation()
3078 {
3079     if (!defender->is_player())
3080     {
3081         // monsters have no STR or DEX
3082         if (x_chance_in_y(2, 5))
3083         {
3084             int hurt = attacker->apply_ac(random2(21));
3085             if (you.see_cell(defender->pos()))
3086             {
3087                 const string defname = defender->name(DESC_THE);
3088                 mprf("%s furiously retaliates!", defname.c_str());
3089                 if (hurt <= 0)
3090                 {
3091                     mprf("%s headbutts %s, but does no damage.", defname.c_str(),
3092                          attacker->name(DESC_THE).c_str());
3093                 }
3094                 else
3095                 {
3096                     mprf("%s headbutts %s%s", defname.c_str(),
3097                          attacker->name(DESC_THE).c_str(),
3098                          attack_strength_punctuation(hurt).c_str());
3099                 }
3100             }
3101             if (hurt > 0)
3102             {
3103                 attacker->hurt(defender, hurt, BEAM_MISSILE,
3104                                KILLED_BY_HEADBUTT);
3105             }
3106         }
3107         return;
3108     }
3109 
3110     if (!form_keeps_mutations())
3111     {
3112         // You are in a non-minotaur form.
3113         return;
3114     }
3115     // This will usually be 2, but could be 3 if the player mutated more.
3116     const int mut = you.get_mutation_level(MUT_HORNS);
3117 
3118     if (5 * you.strength() + 7 * you.dex() > random2(600))
3119     {
3120         // Use the same damage formula as a regular headbutt.
3121         int dmg = 5 + mut * 3;
3122         dmg = player_stat_modify_damage(dmg);
3123         dmg = random2(dmg);
3124         dmg = player_apply_fighting_skill(dmg, true);
3125         dmg = player_apply_misc_modifiers(dmg);
3126         dmg = player_apply_slaying_bonuses(dmg, true);
3127         dmg = player_apply_final_multipliers(dmg);
3128         int hurt = attacker->apply_ac(dmg);
3129 
3130         mpr("You furiously retaliate!");
3131         dprf(DIAG_COMBAT, "Retaliation: dmg = %d hurt = %d", dmg, hurt);
3132         if (hurt <= 0)
3133         {
3134             mprf("You headbutt %s, but do no damage.",
3135                  attacker->name(DESC_THE).c_str());
3136             return;
3137         }
3138         else
3139         {
3140             mprf("You headbutt %s%s",
3141                  attacker->name(DESC_THE).c_str(),
3142                  attack_strength_punctuation(hurt).c_str());
3143             attacker->hurt(&you, hurt);
3144         }
3145     }
3146 }
3147 
3148 /** For UNRAND_STARLIGHT's dazzle effect, only against monsters.
3149  */
do_starlight()3150 void melee_attack::do_starlight()
3151 {
3152     static const vector<string> dazzle_msgs = {
3153         "@The_monster@ is blinded by the light from your cloak!",
3154         "@The_monster@ is temporarily struck blind!",
3155         "@The_monster@'s sight is seared by the starlight!",
3156         "@The_monster@'s vision is obscured by starry radiance!",
3157     };
3158 
3159     if (one_chance_in(5) && dazzle_monster(attacker->as_monster(), 100))
3160     {
3161         string msg = *random_iterator(dazzle_msgs);
3162         msg = do_mon_str_replacements(msg, *attacker->as_monster(), S_SILENT);
3163         mpr(msg);
3164     }
3165 }
3166 
3167 
3168 /**
3169  * Launch a long blade counterattack against the attacker. No sanity checks;
3170  * caller beware!
3171  *
3172  * XXX: might be wrong for deep elf blademasters with a long blade in only
3173  * one hand
3174  */
riposte()3175 void melee_attack::riposte()
3176 {
3177     if (you.see_cell(defender->pos()))
3178     {
3179         mprf("%s riposte%s.", defender->name(DESC_THE).c_str(),
3180              defender->is_player() ? "" : "s");
3181     }
3182     melee_attack attck(defender, attacker, 0, effective_attack_number + 1);
3183     attck.is_riposte = true;
3184     attck.attack();
3185 }
3186 
do_knockback(bool trample)3187 bool melee_attack::do_knockback(bool trample)
3188 {
3189     if (defender->is_stationary())
3190         return false; // don't even print a message
3191 
3192     if (attacker->cannot_move())
3193         return false;
3194 
3195     const int size_diff =
3196         attacker->body_size(PSIZE_BODY) - defender->body_size(PSIZE_BODY);
3197     const coord_def old_pos = defender->pos();
3198     const coord_def new_pos = old_pos + old_pos - attack_position;
3199 
3200     if (!x_chance_in_y(size_diff + 3, 6)
3201         // need a valid tile
3202         || !defender->is_habitable_feat(env.grid(new_pos))
3203         // don't trample anywhere the attacker can't follow
3204         || !attacker->is_habitable_feat(env.grid(old_pos))
3205         // don't trample into a monster - or do we want to cause a chain
3206         // reaction here?
3207         || actor_at(new_pos)
3208         // Prevent trample/drown combo when flight is expiring
3209         || defender->is_player() && need_expiration_warning(new_pos)
3210         || defender->is_constricted())
3211     {
3212         if (needs_message)
3213         {
3214             if (defender->is_constricted())
3215             {
3216                 mprf("%s %s held in place!",
3217                      defender_name(false).c_str(),
3218                      defender->conj_verb("are").c_str());
3219             }
3220             else
3221             {
3222                 mprf("%s %s %s ground!",
3223                      defender_name(false).c_str(),
3224                      defender->conj_verb("hold").c_str(),
3225                      defender->pronoun(PRONOUN_POSSESSIVE).c_str());
3226             }
3227         }
3228 
3229         return false;
3230     }
3231 
3232     if (needs_message)
3233     {
3234         const bool can_stumble = !defender->airborne()
3235                                   && !defender->incapacitated();
3236         const string verb = can_stumble ? "stumble" : "are shoved";
3237         mprf("%s %s backwards!",
3238              defender_name(false).c_str(),
3239              defender->conj_verb(verb).c_str());
3240     }
3241 
3242     // Schedule following _before_ actually trampling -- if the defender
3243     // is a player, a shaft trap will unload the level. If trampling will
3244     // somehow fail, move attempt will be ignored.
3245     if (trample)
3246         trample_follow_fineff::schedule(attacker, old_pos);
3247 
3248     if (defender->is_player())
3249     {
3250         move_player_to_grid(new_pos, false);
3251         // Interrupt stair travel and passwall.
3252         stop_delay(true);
3253     }
3254     else
3255         defender->move_to_pos(new_pos);
3256 
3257     return true;
3258 }
3259 
3260 /**
3261  * Find the list of targets to cleave after hitting the main target.
3262  */
cleave_setup()3263 void melee_attack::cleave_setup()
3264 {
3265     // Don't cleave on a self-attack attack.
3266     if (attacker->pos() == defender->pos())
3267         return;
3268 
3269     // Allow Gyre & Gimble to 'cleave' when projected, but not other attacks.
3270     if (is_projected)
3271     {
3272         if (weapon && is_unrandom_artefact(*weapon, UNRAND_GYRE))
3273             cleave_targets.push_back(defender);
3274         return;
3275     }
3276 
3277     // We need to get the list of the remaining potential targets now because
3278     // if the main target dies, its position will be lost.
3279     get_cleave_targets(*attacker, defender->pos(), cleave_targets,
3280                        attack_number);
3281     // We're already attacking this guy.
3282     cleave_targets.pop_front();
3283 }
3284 
3285 // cleave damage modifier for additional attacks: 70% of base damage
cleave_damage_mod(int dam)3286 int melee_attack::cleave_damage_mod(int dam)
3287 {
3288     if (weapon && is_unrandom_artefact(*weapon, UNRAND_GYRE))
3289         return dam;
3290     return div_rand_round(dam * 7, 10);
3291 }
3292 
3293 // Martial strikes get modified by momentum and maneuver specific damage mods.
martial_damage_mod(int dam)3294 int melee_attack::martial_damage_mod(int dam)
3295 {
3296     if (wu_jian_has_momentum(wu_jian_attack))
3297         dam = div_rand_round(dam * 14, 10);
3298 
3299     if (wu_jian_attack == WU_JIAN_ATTACK_LUNGE)
3300         dam = div_rand_round(dam * 12, 10);
3301 
3302     if (wu_jian_attack == WU_JIAN_ATTACK_WHIRLWIND)
3303         dam = div_rand_round(dam * 8, 10);
3304 
3305     return dam;
3306 }
3307 
chaos_affect_actor(actor * victim)3308 void melee_attack::chaos_affect_actor(actor *victim)
3309 {
3310     ASSERT(victim); // XXX: change to actor &victim
3311     melee_attack attk(victim, victim);
3312     attk.weapon = nullptr;
3313     attk.fake_chaos_attack = true;
3314     attk.chaos_affects_defender();
3315     if (!attk.special_damage_message.empty()
3316         && you.can_see(*victim))
3317     {
3318         mpr(attk.special_damage_message);
3319     }
3320 }
3321 
3322 /**
3323  * Does the player get to use the given aux attack during this melee attack?
3324  *
3325  * Partially random.
3326  *
3327  * @param atk   The type of aux attack being considered.
3328  * @return      Whether the player may use the given aux attack.
3329  */
_extra_aux_attack(unarmed_attack_type atk)3330 bool melee_attack::_extra_aux_attack(unarmed_attack_type atk)
3331 {
3332     if (atk != UNAT_CONSTRICT
3333         && you.strength() + you.dex() <= random2(50))
3334     {
3335         return false;
3336     }
3337 
3338     if (wu_jian_attack != WU_JIAN_ATTACK_NONE
3339         && !x_chance_in_y(1, wu_jian_number_of_targets))
3340     {
3341        // Reduces aux chance proportionally to number of
3342        // enemies attacked with a martial attack
3343        return false;
3344     }
3345 
3346     switch (atk)
3347     {
3348     case UNAT_CONSTRICT:
3349         return you.get_mutation_level(MUT_CONSTRICTING_TAIL) >= 2
3350                 || you.has_mutation(MUT_TENTACLE_ARMS)
3351                     && you.has_usable_tentacle();
3352 
3353     case UNAT_KICK:
3354         return you.has_usable_hooves()
3355                || you.has_usable_talons()
3356                || you.get_mutation_level(MUT_TENTACLE_SPIKE);
3357 
3358     case UNAT_PECK:
3359         return you.get_mutation_level(MUT_BEAK) && !one_chance_in(3);
3360 
3361     case UNAT_HEADBUTT:
3362         return you.get_mutation_level(MUT_HORNS) && !one_chance_in(3);
3363 
3364     case UNAT_TAILSLAP:
3365         return you.has_tail()
3366             // constricting tails are too slow to slap
3367             && !you.has_mutation(MUT_CONSTRICTING_TAIL)
3368             && coinflip();
3369 
3370     case UNAT_PSEUDOPODS:
3371         return you.has_usable_pseudopods() && !one_chance_in(3);
3372 
3373     case UNAT_TENTACLES:
3374         return you.has_usable_tentacles() && !one_chance_in(3);
3375 
3376     case UNAT_BITE:
3377         return you.get_mutation_level(MUT_ANTIMAGIC_BITE)
3378                || (you.has_usable_fangs()
3379                    || you.get_mutation_level(MUT_ACIDIC_BITE))
3380                    && x_chance_in_y(2, 5);
3381 
3382     case UNAT_PUNCH:
3383         return player_gets_aux_punch();
3384 
3385     default:
3386         return false;
3387     }
3388 }
3389 
3390 // TODO: Potentially move this, may or may not belong here (may not
3391 // even belong as its own function, could be integrated with the general
3392 // to-hit method
3393 // Returns the to-hit for your extra unarmed attacks.
3394 // DOES NOT do the final roll (i.e., random2(your_to_hit)).
calc_your_to_hit_unarmed()3395 int melee_attack::calc_your_to_hit_unarmed()
3396 {
3397     int your_to_hit;
3398 
3399     your_to_hit = 1300
3400                 + you.dex() * 75
3401                 + you.skill(SK_FIGHTING, 30);
3402     your_to_hit /= 100;
3403 
3404     your_to_hit -= 5 * you.inaccuracy();
3405 
3406     if (you.get_mutation_level(MUT_EYEBALLS))
3407         your_to_hit += 2 * you.get_mutation_level(MUT_EYEBALLS) + 1;
3408 
3409     if (you.duration[DUR_VERTIGO])
3410         your_to_hit -= 5;
3411 
3412     if (you.confused())
3413         your_to_hit -= 5;
3414 
3415     your_to_hit += slaying_bonus();
3416 
3417     return your_to_hit;
3418 }
3419 
using_weapon() const3420 bool melee_attack::using_weapon() const
3421 {
3422     return weapon && is_melee_weapon(*weapon);
3423 }
3424 
weapon_damage()3425 int melee_attack::weapon_damage()
3426 {
3427     if (!using_weapon())
3428         return 0;
3429 
3430     return property(*weapon, PWPN_DAMAGE);
3431 }
3432 
calc_mon_to_hit_base()3433 int melee_attack::calc_mon_to_hit_base()
3434 {
3435     const bool fighter = attacker->is_monster()
3436                          && attacker->as_monster()->is_fighter();
3437     return mon_to_hit_base(attacker->get_hit_dice(), fighter, false);
3438 }
3439 
3440 /**
3441  * Add modifiers to the base damage.
3442  * Currently only relevant for monsters.
3443  */
apply_damage_modifiers(int damage)3444 int melee_attack::apply_damage_modifiers(int damage)
3445 {
3446     ASSERT(attacker->is_monster());
3447     monster *as_mon = attacker->as_monster();
3448 
3449     // Berserk/mighted monsters get bonus damage.
3450     if (as_mon->has_ench(ENCH_MIGHT) || as_mon->has_ench(ENCH_BERSERK))
3451         damage = damage * 3 / 2;
3452 
3453     if (as_mon->has_ench(ENCH_IDEALISED))
3454         damage *= 2; // !
3455 
3456     if (as_mon->has_ench(ENCH_WEAK))
3457         damage = damage * 2 / 3;
3458 
3459     // If the defender is asleep, the attacker gets a stab.
3460     if (defender && (defender->asleep()
3461                      || (attk_flavour == AF_SHADOWSTAB
3462                          &&!defender->can_see(*attacker))))
3463     {
3464         damage = damage * 5 / 2;
3465         dprf(DIAG_COMBAT, "Stab damage vs %s: %d",
3466              defender->name(DESC_PLAIN).c_str(),
3467              damage);
3468     }
3469 
3470     if (cleaving)
3471         damage = cleave_damage_mod(damage);
3472 
3473     return damage;
3474 }
3475 
calc_damage()3476 int melee_attack::calc_damage()
3477 {
3478     // Constriction deals damage over time, not when grabbing.
3479     if (attk_flavour == AF_CRUSH)
3480         return 0;
3481 
3482     return attack::calc_damage();
3483 }
3484 
3485 /* TODO: This code is only used from melee_attack methods, but perhaps it
3486  * should be ambigufied and moved to the actor class
3487  * Should life protection protect from this?
3488  *
3489  * Should eventually remove in favour of player/monster symmetry
3490  *
3491  * Called when stabbing and for bite attacks.
3492  *
3493  * Returns true if blood was drawn.
3494  */
_player_vampire_draws_blood(const monster * mon,const int damage,bool needs_bite_msg)3495 bool melee_attack::_player_vampire_draws_blood(const monster* mon, const int damage,
3496                                                bool needs_bite_msg)
3497 {
3498     ASSERT(you.has_mutation(MUT_VAMPIRISM));
3499 
3500     if (!_vamp_wants_blood_from_monster(mon) ||
3501         (!adjacent(defender->pos(), attack_position) && needs_bite_msg))
3502     {
3503         return false;
3504     }
3505 
3506     // Now print message, need biting unless already done (never for bat form!)
3507     if (needs_bite_msg && you.form != transformation::bat)
3508     {
3509         mprf("You bite %s, and draw %s blood!",
3510              mon->name(DESC_THE, true).c_str(),
3511              mon->pronoun(PRONOUN_POSSESSIVE).c_str());
3512     }
3513     else
3514     {
3515         mprf("You draw %s blood!",
3516              apostrophise(mon->name(DESC_THE, true)).c_str());
3517     }
3518 
3519     // Regain hp.
3520     if (you.hp < you.hp_max)
3521     {
3522         int heal = 2 + random2(damage);
3523         heal += random2(damage);
3524         if (heal > you.experience_level)
3525             heal = you.experience_level;
3526 
3527         if (heal > 0 && !you.duration[DUR_DEATHS_DOOR])
3528         {
3529             inc_hp(heal);
3530             canned_msg(MSG_GAIN_HEALTH);
3531         }
3532     }
3533 
3534     return true;
3535 }
3536 
apply_damage_brand(const char * what)3537 bool melee_attack::apply_damage_brand(const char *what)
3538 {
3539     // Staff damage overrides any brands
3540     return apply_staff_damage() || attack::apply_damage_brand(what);
3541 }
3542 
_vamp_wants_blood_from_monster(const monster * mon)3543 bool melee_attack::_vamp_wants_blood_from_monster(const monster* mon)
3544 {
3545     return you.has_mutation(MUT_VAMPIRISM)
3546            && !you.vampire_alive
3547            && actor_is_susceptible_to_vampirism(*mon)
3548            && mons_has_blood(mon->type);
3549 }
3550