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