Add two new Ru sacrifices: resistance and eye
[crawl.git] / crawl-ref / source / attack.cc
1 /*
2  *  File:       attack.cc
3  *  Summary:    Methods of the attack class, generalized functions which may
4  *              be overloaded by inheriting classes.
5  *  Written by: Robert Burnham
6  */
7
8 #include "AppHdr.h"
9
10 #include "attack.h"
11
12 #include <algorithm>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <cstring>
16
17 #include "art-enum.h"
18 #include "delay.h"
19 #include "english.h"
20 #include "env.h"
21 #include "exercise.h"
22 #include "fight.h"
23 #include "fineff.h"
24 #include "godconduct.h"
25 #include "itemname.h"
26 #include "itemprop.h"
27 #include "message.h"
28 #include "misc.h"
29 #include "mon-behv.h"
30 #include "mon-clone.h"
31 #include "mon-death.h"
32 #include "mon-poly.h"
33 #include "spl-miscast.h"
34 #include "state.h"
35 #include "stepdown.h"
36 #include "stringutil.h"
37 #include "transform.h"
38 #include "xom.h"
39
40 /*
41  **************************************************
42  *             BEGIN PUBLIC FUNCTIONS             *
43  **************************************************
44 */
45 attack::attack(actor *attk, actor *defn, actor *blame)
46     : attacker(attk), defender(defn), responsible(blame ? blame : attk),
47       attack_occurred(false), cancel_attack(false), did_hit(false),
48       needs_message(false), attacker_visible(false), defender_visible(false),
49       perceived_attack(false), obvious_effect(false), to_hit(0),
50       damage_done(0), special_damage(0), aux_damage(0), min_delay(0),
51       final_attack_delay(0), special_damage_flavour(BEAM_NONE),
52       stab_attempt(false), stab_bonus(0), apply_bleeding(false),
53       ev_margin(0), weapon(nullptr),
54       damage_brand(SPWPN_NORMAL), wpn_skill(SK_UNARMED_COMBAT),
55       shield(nullptr), art_props(0), unrand_entry(nullptr),
56       attacker_to_hit_penalty(0), attack_verb("bug"), verb_degree(),
57       no_damage_message(), special_damage_message(), aux_attack(), aux_verb(),
58       attacker_armour_tohit_penalty(0), attacker_shield_tohit_penalty(0),
59       defender_shield(nullptr), miscast_level(-1), miscast_type(SPTYP_NONE),
60       miscast_target(nullptr), fake_chaos_attack(false), simu(false),
61       aux_source(""), kill_type(KILLED_BY_MONSTER)
62 {
63     // No effective code should execute, we'll call init_attack again from
64     // the child class, since initializing an attack will vary based the within
65     // type of attack actually being made (melee, ranged, etc.)
66 }
67
68 bool attack::handle_phase_attempted()
69 {
70     return true;
71 }
72
73 bool attack::handle_phase_blocked()
74 {
75     damage_done = 0;
76     return true;
77 }
78
79 bool attack::handle_phase_damaged()
80 {
81     // We have to check in_bounds() because removed kraken tentacles are
82     // temporarily returned to existence (without a position) when they
83     // react to damage.
84     if (defender->can_bleed()
85         && !defender->is_summoned()
86         && !defender->submerged()
87         && in_bounds(defender->pos())
88         && !simu)
89     {
90         int blood = damage_done;
91         if (blood > defender->stat_hp())
92             blood = defender->stat_hp();
93         if (blood)
94             blood_fineff::schedule(defender, defender->pos(), blood);
95     }
96
97     announce_hit();
98     // Inflict stored damage
99     damage_done = inflict_damage(damage_done);
100
101     // TODO: Unify these, added here so we can get rid of player_attack
102     if (attacker->is_player())
103     {
104         if (damage_done)
105             player_exercise_combat_skills();
106
107         if (defender->alive())
108         {
109             // Actually apply the bleeding effect, this can come from an
110             // aux claw or a main hand claw attack and up to now has not
111             // actually happened.
112             const int degree = you.has_usable_claws();
113             if (apply_bleeding && defender->can_bleed()
114                 && degree > 0 && damage_done > 0)
115             {
116                 defender->as_monster()->bleed(attacker,
117                                               3 + roll_dice(degree, 3),
118                                               degree);
119             }
120         }
121     }
122     else
123     {
124         if (!mons_attack_effects())
125             return false;
126     }
127
128     // It's okay if a monster took lethal damage, but we should stop
129     // the combat if it was already reset (e.g. a spectral weapon that
130     // took damage and then noticed that its caster is gone).
131     return defender->is_player() || !invalid_monster(defender->as_monster());
132 }
133
134 bool attack::handle_phase_killed()
135 {
136     monster * const mon = defender->as_monster();
137     if (!invalid_monster(mon))
138         monster_die(mon, attacker);
139
140     return true;
141 }
142
143 bool attack::handle_phase_end()
144 {
145     // This may invalidate both the attacker and defender.
146     fire_final_effects();
147
148     return true;
149 }
150
151 /**
152  * Calculate the to-hit for an attacker
153  *
154  * @param random If false, calculate average to-hit deterministically.
155  */
156 int attack::calc_to_hit(bool random)
157 {
158     int mhit = attacker->is_player() ?
159                 15 + (calc_stat_to_hit_base() / 2)
160               : calc_mon_to_hit_base();
161
162 #ifdef DEBUG_DIAGNOSTICS
163     const int base_hit = mhit;
164 #endif
165
166     // This if statement is temporary, it should be removed when the
167     // implementation of a more universal (and elegant) to-hit calculation
168     // is designed. The actual code is copied from the old mons_to_hit and
169     // player_to_hit methods.
170     if (attacker->is_player())
171     {
172         // fighting contribution
173         mhit += maybe_random_div(you.skill(SK_FIGHTING, 100), 100, random);
174
175         // weapon skill contribution
176         if (using_weapon())
177         {
178             if (wpn_skill != SK_FIGHTING)
179             {
180                 if (you.skill(wpn_skill) < 1 && player_in_a_dangerous_place())
181                     xom_is_stimulated(10); // Xom thinks that is mildly amusing.
182
183                 mhit += maybe_random_div(you.skill(wpn_skill, 100), 100,
184                                          random);
185             }
186         }
187         else if (you.form_uses_xl())
188             mhit += maybe_random_div(you.experience_level * 100, 100, random);
189         else
190         {                       // ...you must be unarmed or throwing
191             // Members of clawed species have presumably been using the claws,
192             // making them more practiced and thus more accurate in unarmed
193             // combat. They keep this benefit even when the claws are covered
194             // (or missing because of a change in form).
195             mhit += you.innate_mutation[MUT_CLAWS]
196                     && wpn_skill == SK_UNARMED_COMBAT ? 4 : 2;
197
198             mhit += maybe_random_div(you.skill(wpn_skill, 100), 100,
199                                      random);
200         }
201
202         // weapon bonus contribution
203         if (using_weapon())
204         {
205             if (weapon->base_type == OBJ_WEAPONS)
206             {
207                 mhit += weapon->plus;
208                 mhit += property(*weapon, PWPN_HIT);
209             }
210             else if (weapon->base_type == OBJ_STAVES)
211                 mhit += property(*weapon, PWPN_HIT);
212             else if (weapon->base_type == OBJ_RODS)
213             {
214                 mhit += property(*weapon, PWPN_HIT);
215                 mhit += weapon->special;
216             }
217         }
218
219         // slaying bonus
220         mhit += slaying_bonus(wpn_skill == SK_THROWING
221                               || (weapon && is_range_weapon(*weapon)
222                                          && using_weapon()));
223
224         // horror penalty
225         if (you.duration[DUR_HORROR])
226             mhit -= you.props[HORROR_PENALTY_KEY].get_int();
227
228         // hunger penalty
229         if (you.hunger_state == HS_STARVING)
230             mhit -= 3;
231
232         // armour penalty
233         mhit -= (attacker_armour_tohit_penalty + attacker_shield_tohit_penalty);
234
235         // mutation
236         if (player_mutation_level(MUT_EYEBALLS))
237             mhit += 2 * player_mutation_level(MUT_EYEBALLS) + 1;
238
239         // hit roll
240         mhit = maybe_random2(mhit, random);
241     }
242     else    // Monster to-hit.
243     {
244         if (using_weapon())
245             mhit += weapon->plus + property(*weapon, PWPN_HIT);
246
247         const int jewellery = attacker->as_monster()->inv[MSLOT_JEWELLERY];
248         if (jewellery != NON_ITEM
249             && mitm[jewellery].is_type(OBJ_JEWELLERY, RING_SLAYING))
250         {
251             mhit += mitm[jewellery].plus;
252         }
253
254         mhit += attacker->scan_artefacts(ARTP_SLAYING);
255
256         if (using_weapon() && weapon->base_type == OBJ_RODS)
257             mhit += weapon->special;
258     }
259
260     // Penalties for both players and monsters:
261     mhit -= 5 * attacker->inaccuracy();
262
263     // If you can't see yourself, you're a little less accurate.
264     if (!attacker->visible_to(attacker))
265         mhit -= 5;
266
267     if (attacker->confused())
268         mhit -= 5;
269
270     if (using_weapon() && is_unrandom_artefact(*weapon, UNRAND_WOE))
271         return AUTOMATIC_HIT;
272
273     // If no defender, we're calculating to-hit for debug-display
274     // purposes, so don't drop down to defender code below
275     if (defender == nullptr)
276         return mhit;
277
278     if (!defender->visible_to(attacker))
279         if (attacker->is_player())
280             mhit -= 6;
281         else
282             mhit = mhit * 65 / 100;
283     else
284     {
285         // This can only help if you're visible!
286         const int how_transparent = player_mutation_level(MUT_TRANSLUCENT_SKIN);
287         if (defender->is_player() && how_transparent)
288             mhit -= 2 * how_transparent;
289
290         if (defender->backlit(false))
291             mhit += 2 + random2(8);
292         else if (!attacker->nightvision()
293                  && defender->umbra())
294             mhit -= 2 + random2(4);
295     }
296     // Don't delay doing this roll until test_hit().
297     if (!attacker->is_player())
298         mhit = random2(mhit + 1);
299
300     dprf(DIAG_COMBAT, "%s: Base to-hit: %d, Final to-hit: %d",
301          attacker->name(DESC_PLAIN).c_str(),
302          base_hit, mhit);
303
304     return mhit;
305 }
306
307 /* Returns an actor's name
308  *
309  * Takes into account actor visibility/invisibility and the type of description
310  * to be used (capitalization, possessiveness, etc.)
311  */
312 string attack::actor_name(const actor *a, description_level_type desc,
313                           bool actor_visible)
314 {
315     return actor_visible ? a->name(desc) : anon_name(desc);
316 }
317
318 /* Returns an actor's pronoun
319  *
320  * Takes into account actor visibility
321  */
322 string attack::actor_pronoun(const actor *a, pronoun_type pron,
323                              bool actor_visible)
324 {
325     return actor_visible ? a->pronoun(pron) : anon_pronoun(pron);
326 }
327
328 /* Returns an anonymous actor's name
329  *
330  * Given the actor visible or invisible, returns the
331  * appropriate possessive pronoun.
332  */
333 string attack::anon_name(description_level_type desc)
334 {
335     switch (desc)
336     {
337     case DESC_NONE:
338         return "";
339     case DESC_YOUR:
340     case DESC_ITS:
341         return "its";
342     case DESC_THE:
343     case DESC_A:
344     case DESC_PLAIN:
345     default:
346         return "something";
347     }
348 }
349
350 /* Returns an anonymous actor's pronoun
351  *
352  * Given invisibility (whether out of LOS or just invisible), returns the
353  * appropriate possessive, inflexive, capitalised pronoun.
354  */
355 string attack::anon_pronoun(pronoun_type pron)
356 {
357     return decline_pronoun(GENDER_NEUTER, pron);
358 }
359
360 /* Initializes an attack, setting up base variables and values
361  *
362  * Does not make any changes to any actors, items, or the environment,
363  * in case the attack is cancelled or otherwise fails. Only initializations
364  * that are universal to all types of attacks should go into this method,
365  * any initialization properties that are specific to one attack or another
366  * should go into their respective init_attack.
367  *
368  * Although this method will get overloaded by the derived class, we are
369  * calling it from attack::attack(), before the overloading has taken place.
370  */
371 void attack::init_attack(skill_type unarmed_skill, int attack_number)
372 {
373     weapon          = attacker->weapon(attack_number);
374
375     wpn_skill       = weapon ? item_attack_skill(*weapon) : unarmed_skill;
376     if (attacker->is_player() && you.form_uses_xl())
377         wpn_skill = SK_FIGHTING; // for stabbing, mostly
378
379     attacker_armour_tohit_penalty =
380         div_rand_round(attacker->armour_tohit_penalty(true, 20), 20);
381     attacker_shield_tohit_penalty =
382         div_rand_round(attacker->shield_tohit_penalty(true, 20), 20);
383     to_hit          = calc_to_hit(true);
384
385     shield = attacker->shield();
386     defender_shield = defender ? defender->shield() : defender_shield;
387
388     if (weapon && weapon->base_type == OBJ_WEAPONS && is_artefact(*weapon))
389     {
390         artefact_properties(*weapon, art_props);
391         if (is_unrandom_artefact(*weapon))
392             unrand_entry = get_unrand_entry(weapon->special);
393     }
394
395     attacker_visible   = attacker->observable();
396     defender_visible   = defender && defender->observable();
397     needs_message      = (attacker_visible || defender_visible);
398
399     if (attacker->is_monster())
400     {
401         mon_attack_def mon_attk = mons_attack_spec(attacker->as_monster(),
402                                                    attack_number);
403
404         attk_type       = mon_attk.type;
405         attk_flavour    = mon_attk.flavour;
406
407         // Don't scale damage for YOU_FAULTLESS etc.
408         if (attacker->get_experience_level() == 0)
409             attk_damage = mon_attk.damage;
410         else
411         {
412             attk_damage = div_rand_round(mon_attk.damage
413                                              * attacker->get_hit_dice(),
414                                          attacker->get_experience_level());
415         }
416
417         if (attk_type == AT_WEAP_ONLY)
418         {
419             int weap = attacker->as_monster()->inv[MSLOT_WEAPON];
420             if (weap == NON_ITEM || is_range_weapon(mitm[weap]))
421                 attk_type = AT_NONE;
422             else
423                 attk_type = AT_HIT;
424         }
425         else if (attk_type == AT_TRUNK_SLAP && attacker->type == MONS_SKELETON)
426         {
427             // Elephant trunks have no bones inside.
428             attk_type = AT_NONE;
429         }
430     }
431     else
432     {
433         attk_type    = AT_HIT;
434         attk_flavour = AF_PLAIN;
435     }
436 }
437
438 void attack::alert_defender()
439 {
440     // Allow monster attacks to draw the ire of the defender. Player
441     // attacks are handled elsewhere.
442     if (perceived_attack
443         && defender->is_monster()
444         && attacker->is_monster()
445         && attacker->alive() && defender->alive()
446         && (defender->as_monster()->foe == MHITNOT || one_chance_in(3)))
447     {
448         behaviour_event(defender->as_monster(), ME_WHACK, attacker);
449     }
450
451     // If an enemy attacked a friend, set the pet target if it isn't set
452     // already, but not if sanctuary is in effect (pet target must be
453     // set explicitly by the player during sanctuary).
454     if (perceived_attack && attacker->alive()
455         && (defender->is_player() || defender->as_monster()->friendly())
456         && !attacker->is_player()
457         && !crawl_state.game_is_arena()
458         && !attacker->as_monster()->wont_attack())
459     {
460         if (defender->is_player())
461             interrupt_activity(AI_MONSTER_ATTACKS, attacker->as_monster());
462         if (you.pet_target == MHITNOT && env.sanctuary_time <= 0)
463             you.pet_target = attacker->mindex();
464     }
465 }
466
467 bool attack::distortion_affects_defender()
468 {
469     //jmf: blink frogs *like* distortion
470     // I think could be amended to let blink frogs "grow" like
471     // jellies do {dlb}
472     if (defender->is_monster()
473         && mons_genus(defender->type) == MONS_BLINK_FROG)
474     {
475         if (one_chance_in(5))
476         {
477             if (defender_visible)
478             {
479                 special_damage_message =
480                     make_stringf("%s %s in the distortional energy.",
481                                  defender_name(false).c_str(),
482                                  defender->conj_verb("bask").c_str());
483             }
484
485             defender->heal(1 + random2avg(7, 2), true); // heh heh
486         }
487         return false;
488     }
489
490     if (one_chance_in(3))
491     {
492         special_damage_message =
493             make_stringf("Space bends around %s.",
494             defender_name(false).c_str());
495         special_damage += 1 + random2avg(7, 2);
496         return false;
497     }
498
499     if (one_chance_in(3))
500     {
501         special_damage_message =
502             make_stringf("Space warps horribly around %s!",
503                          defender_name(false).c_str());
504         special_damage += 3 + random2avg(24, 2);
505         return false;
506     }
507
508     if (simu)
509         return false;
510
511     if (one_chance_in(3))
512     {
513         if (defender_visible)
514             obvious_effect = true;
515         if (!defender->no_tele(true, false))
516             blink_fineff::schedule(defender);
517         return false;
518     }
519
520     if (!one_chance_in(3))
521     {
522         if (defender_visible)
523             obvious_effect = true;
524         if (crawl_state.game_is_sprint() && defender->is_player()
525             || defender->no_tele())
526         {
527             if (defender->is_player())
528                 canned_msg(MSG_STRANGE_STASIS);
529         }
530         else if (coinflip())
531             distortion_tele_fineff::schedule(defender);
532         else
533             defender->teleport();
534         return false;
535     }
536
537     if (!player_in_branch(BRANCH_ABYSS) && coinflip())
538     {
539         if (defender_visible)
540             obvious_effect = true;
541
542         defender->banish(attacker, attacker->name(DESC_PLAIN, true));
543         return true;
544     }
545
546     return false;
547 }
548
549 void attack::antimagic_affects_defender(int pow)
550 {
551     obvious_effect =
552         enchant_actor_with_flavour(defender, nullptr, BEAM_DRAIN_MAGIC, pow);
553 }
554
555 void attack::pain_affects_defender()
556 {
557     if (defender->res_negative_energy())
558         return;
559
560     if (!one_chance_in(attacker->skill_rdiv(SK_NECROMANCY) + 1))
561     {
562         if (defender_visible)
563         {
564             special_damage_message =
565                 make_stringf("%s %s in agony.",
566                              defender->name(DESC_THE).c_str(),
567                              defender->conj_verb("writhe").c_str());
568         }
569         special_damage += random2(1 + attacker->skill_rdiv(SK_NECROMANCY));
570     }
571
572     attacker->god_conduct(DID_NECROMANCY, 4);
573 }
574
575 // TODO: Move this somewhere sane
576 enum chaos_type
577 {
578     CHAOS_CLONE,
579     CHAOS_POLY,
580     CHAOS_POLY_UP,
581     CHAOS_MAKE_SHIFTER,
582     CHAOS_MISCAST,
583     CHAOS_RAGE,
584     CHAOS_HEAL,
585     CHAOS_HASTE,
586     CHAOS_INVIS,
587     CHAOS_SLOW,
588     CHAOS_PARALYSIS,
589     CHAOS_PETRIFY,
590     NUM_CHAOS_TYPES
591 };
592
593 // XXX: We might want to vary the probabilities for the various effects
594 // based on whether the source is a weapon of chaos or a monster with
595 // AF_CHAOS.
596 void attack::chaos_affects_defender()
597 {
598     monster * const mon   = defender->as_monster();
599     const bool firewood   = mon && mons_is_firewood(mon);
600     const bool immune     = mon && mons_immune_magic(mon);
601     const bool is_natural = mon && defender->holiness() == MH_NATURAL;
602     const bool is_shifter = mon && mon->is_shapeshifter();
603     const bool can_clone  = mon && mons_clonable(mon, true);
604     const bool can_poly   = is_shifter || (defender->can_safely_mutate()
605                                            && !immune && !firewood);
606     const bool can_rage   = defender->can_go_berserk();
607     const bool can_slow   = !firewood;
608     const bool can_petrify= mon && !defender->res_petrify();
609
610     int clone_chance   = can_clone                      ?  1 : 0;
611     int poly_chance    = can_poly                       ?  1 : 0;
612     int poly_up_chance = can_poly                && mon ?  1 : 0;
613     int shifter_chance = can_poly  && is_natural && mon ?  1 : 0;
614     int rage_chance    = can_rage                       ? 10 : 0;
615     int miscast_chance = 10;
616     int slowpara_chance= can_slow                       ? 10 : 0;
617     int petrify_chance = can_slow && can_petrify        ? 10 : 0;
618
619     // Already a shifter?
620     if (is_shifter)
621         shifter_chance = 0;
622
623     // A chaos self-attack increases the chance of certain effects,
624     // due to a short-circuit/feedback/resonance/whatever.
625     if (attacker == defender)
626     {
627         clone_chance   *= 2;
628         poly_chance    *= 2;
629         poly_up_chance *= 2;
630         shifter_chance *= 2;
631         miscast_chance *= 2;
632
633         // Inform player that something is up.
634         if (!fake_chaos_attack && you.see_cell(defender->pos()))
635         {
636             if (defender->is_player())
637                 mpr("You give off a flash of multicoloured light!");
638             else if (you.can_see(defender))
639             {
640                 simple_monster_message(mon, " gives off a flash of"
641                                             " multicoloured light!");
642             }
643             else
644                 mpr("There is a flash of multicoloured light!");
645         }
646     }
647
648     // NOTE: Must appear in exact same order as in chaos_type enumeration.
649     int probs[NUM_CHAOS_TYPES] =
650     {
651         clone_chance,   // CHAOS_CLONE
652         poly_chance,    // CHAOS_POLY
653         poly_up_chance, // CHAOS_POLY_UP
654         shifter_chance, // CHAOS_MAKE_SHIFTER
655         miscast_chance, // CHAOS_MISCAST
656         rage_chance,    // CHAOS_RAGE
657
658         10,             // CHAOS_HEAL
659         slowpara_chance,// CHAOS_HASTE
660         10,             // CHAOS_INVIS
661
662         slowpara_chance,// CHAOS_SLOW
663         slowpara_chance,// CHAOS_PARALYSIS
664         petrify_chance, // CHAOS_PETRIFY
665     };
666
667     bolt beam;
668     beam.flavour = BEAM_NONE;
669
670     int choice = choose_random_weighted(probs, probs + NUM_CHAOS_TYPES);
671 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
672     string chaos_effect = "CHAOS effect: ";
673     switch (choice)
674     {
675     case CHAOS_CLONE:           chaos_effect += "clone"; break;
676     case CHAOS_POLY:            chaos_effect += "polymorph"; break;
677     case CHAOS_POLY_UP:         chaos_effect += "polymorph PPT_MORE"; break;
678     case CHAOS_MAKE_SHIFTER:    chaos_effect += "shifter"; break;
679     case CHAOS_MISCAST:         chaos_effect += "miscast"; break;
680     case CHAOS_RAGE:            chaos_effect += "berserk"; break;
681     case CHAOS_HEAL:            chaos_effect += "healing"; break;
682     case CHAOS_HASTE:           chaos_effect += "hasting"; break;
683     case CHAOS_INVIS:           chaos_effect += "invisible"; break;
684     case CHAOS_SLOW:            chaos_effect += "slowing"; break;
685     case CHAOS_PARALYSIS:       chaos_effect += "paralysis"; break;
686     case CHAOS_PETRIFY:         chaos_effect += "petrify"; break;
687     default:                    chaos_effect += "(other)"; break;
688     }
689
690     take_note(Note(NOTE_MESSAGE, 0, 0, chaos_effect.c_str()), true);
691 #endif
692
693     switch (static_cast<chaos_type>(choice))
694     {
695     case CHAOS_CLONE:
696     {
697         ASSERT(can_clone);
698         ASSERT(clone_chance > 0);
699         ASSERT(defender->is_monster());
700
701         if (monster *clone = clone_mons(mon, true, &obvious_effect))
702         {
703             if (obvious_effect)
704             {
705                 special_damage_message =
706                     make_stringf("%s is duplicated!",
707                                  defender_name(false).c_str());
708             }
709
710             // The player shouldn't get new permanent followers from cloning.
711             if (clone->attitude == ATT_FRIENDLY && !clone->is_summoned())
712                 clone->mark_summoned(6, true, MON_SUMM_CLONE);
713
714             // Monsters being cloned is interesting.
715             xom_is_stimulated(clone->friendly() ? 12 : 25);
716         }
717         break;
718     }
719
720     case CHAOS_POLY:
721         ASSERT(can_poly);
722         ASSERT(poly_chance > 0);
723         beam.flavour = BEAM_POLYMORPH;
724         break;
725
726     case CHAOS_POLY_UP:
727         ASSERT(can_poly);
728         ASSERT(poly_up_chance > 0);
729         ASSERT(defender->is_monster());
730
731         obvious_effect = you.can_see(defender);
732         monster_polymorph(mon, RANDOM_MONSTER, PPT_MORE);
733         break;
734
735     case CHAOS_MAKE_SHIFTER:
736     {
737         ASSERT(can_poly);
738         ASSERT(shifter_chance > 0);
739         ASSERT(!is_shifter);
740         ASSERT(defender->is_monster());
741
742         obvious_effect = you.can_see(defender);
743         mon->add_ench(one_chance_in(3) ? ENCH_GLOWING_SHAPESHIFTER
744                                        : ENCH_SHAPESHIFTER);
745         // Immediately polymorph monster, just to make the effect obvious.
746         monster_polymorph(mon, RANDOM_MONSTER);
747
748         // Xom loves it if this happens!
749         const int friend_factor = mon->friendly() ? 1 : 2;
750         const int glow_factor   = mon->has_ench(ENCH_SHAPESHIFTER) ? 1 : 2;
751         xom_is_stimulated(64 * friend_factor * glow_factor);
752         break;
753     }
754     case CHAOS_MISCAST:
755     {
756         int level = defender->get_hit_dice();
757
758         // At level == 27 there's a 13.9% chance of a level 3 miscast.
759         int level0_chance = level;
760         int level1_chance = max(0, level - 7);
761         int level2_chance = max(0, level - 12);
762         int level3_chance = max(0, level - 17);
763
764         level = random_choose_weighted(
765             level0_chance, 0,
766             level1_chance, 1,
767             level2_chance, 2,
768             level3_chance, 3,
769             0);
770
771         miscast_level  = level;
772         miscast_type   = SPTYP_RANDOM;
773         miscast_target = defender;
774         break;
775     }
776
777     case CHAOS_RAGE:
778         ASSERT(can_rage);
779         ASSERT(rage_chance > 0);
780         defender->go_berserk(false);
781         obvious_effect = you.can_see(defender);
782         break;
783
784     // For these, obvious_effect is computed during beam.fire() below.
785     case CHAOS_HEAL:
786         beam.flavour = BEAM_HEALING;
787         break;
788     case CHAOS_HASTE:
789         beam.flavour = BEAM_HASTE;
790         break;
791     case CHAOS_INVIS:
792         beam.flavour = BEAM_INVISIBILITY;
793         break;
794     case CHAOS_SLOW:
795         beam.flavour = BEAM_SLOW;
796         break;
797     case CHAOS_PARALYSIS:
798         beam.flavour = BEAM_PARALYSIS;
799         break;
800     case CHAOS_PETRIFY:
801         beam.flavour = BEAM_PETRIFY;
802         break;
803
804     default:
805         die("Invalid chaos effect type");
806         break;
807     }
808
809     if (beam.flavour != BEAM_NONE)
810     {
811         beam.glyph        = 0;
812         beam.range        = 0;
813         beam.colour       = BLACK;
814         beam.effect_known = false;
815         // Wielded brand is always known, but maybe this was from a call
816         // to chaos_affect_actor.
817         beam.effect_wanton = !fake_chaos_attack;
818
819         if (using_weapon() && you.can_see(attacker))
820         {
821             beam.name = wep_name(DESC_YOUR);
822             beam.item = weapon;
823         }
824         else
825             beam.name = atk_name(DESC_THE);
826
827         beam.thrower =
828             (attacker->is_player())           ? KILL_YOU
829             : attacker->as_monster()->confused_by_you() ? KILL_YOU_CONF
830                                                         : KILL_MON;
831
832         if (beam.thrower == KILL_YOU || attacker->as_monster()->friendly())
833             beam.attitude = ATT_FRIENDLY;
834
835         beam.source_id = attacker->mid;
836
837         beam.source = defender->pos();
838         beam.target = defender->pos();
839
840         beam.damage = dice_def(damage_done + special_damage + aux_damage, 1);
841
842         beam.ench_power = beam.damage.num;
843
844         const bool you_could_see = you.can_see(defender);
845         beam.fire();
846
847         if (you_could_see)
848             obvious_effect = beam.obvious_effect;
849     }
850
851     if (!you.can_see(attacker))
852         obvious_effect = false;
853 }
854
855 // NOTE: random_chaos_brand() and random_chaos_attack_flavour() should
856 // return a set of effects that are roughly the same, to make it easy
857 // for chaos_affects_defender() not to do duplicate effects caused
858 // by the non-chaos brands/flavours they return.
859 brand_type attack::random_chaos_brand()
860 {
861     brand_type brand = SPWPN_NORMAL;
862     // Assuming the chaos to be mildly intelligent, try to avoid brands
863     // that clash with the most basic resists of the defender,
864     // i.e. its holiness.
865     while (true)
866     {
867         brand = (random_choose_weighted(
868                      5, SPWPN_VORPAL,
869                     10, SPWPN_FLAMING,
870                     10, SPWPN_FREEZING,
871                     10, SPWPN_ELECTROCUTION,
872                     10, SPWPN_VENOM,
873                     10, SPWPN_CHAOS,
874                      5, SPWPN_DRAINING,
875                      5, SPWPN_VAMPIRISM,
876                      5, SPWPN_HOLY_WRATH,
877                      5, SPWPN_ANTIMAGIC,
878                      2, SPWPN_CONFUSE,
879                      2, SPWPN_DISTORTION,
880                      0));
881
882         if (one_chance_in(3))
883             break;
884
885         bool susceptible = true;
886         switch (brand)
887         {
888         case SPWPN_FLAMING:
889             if (defender->is_fiery())
890                 susceptible = false;
891             break;
892         case SPWPN_FREEZING:
893             if (defender->is_icy())
894                 susceptible = false;
895             break;
896         case SPWPN_VENOM:
897             if (defender->holiness() == MH_UNDEAD)
898                 susceptible = false;
899             break;
900         case SPWPN_VAMPIRISM:
901             if (defender->is_summoned())
902             {
903                 susceptible = false;
904                 break;
905             }
906             // intentional fall-through
907         case SPWPN_DRAINING:
908             if (defender->holiness() != MH_NATURAL)
909                 susceptible = false;
910             break;
911         case SPWPN_HOLY_WRATH:
912             if (!defender->holy_wrath_susceptible())
913                 susceptible = false;
914             break;
915         case SPWPN_CONFUSE:
916             if (defender->holiness() == MH_NONLIVING
917                 || defender->holiness() == MH_PLANT)
918             {
919                 susceptible = false;
920             }
921             break;
922         case SPWPN_ANTIMAGIC:
923             if (!defender->antimagic_susceptible())
924                 susceptible = false;
925             break;
926         default:
927             break;
928         }
929
930         if (susceptible)
931             break;
932     }
933 #ifdef NOTE_DEBUG_CHAOS_BRAND
934     string brand_name = "CHAOS brand: ";
935     switch (brand)
936     {
937     case SPWPN_NORMAL:          brand_name += "(plain)"; break;
938     case SPWPN_FLAMING:         brand_name += "flaming"; break;
939     case SPWPN_FREEZING:        brand_name += "freezing"; break;
940     case SPWPN_HOLY_WRATH:      brand_name += "holy wrath"; break;
941     case SPWPN_ELECTROCUTION:   brand_name += "electrocution"; break;
942     case SPWPN_VENOM:           brand_name += "venom"; break;
943     case SPWPN_DRAINING:        brand_name += "draining"; break;
944     case SPWPN_DISTORTION:      brand_name += "distortion"; break;
945     case SPWPN_VAMPIRISM:     brand_name += "vampirism"; break;
946     case SPWPN_VORPAL:          brand_name += "vorpal"; break;
947     case SPWPN_ANTIMAGIC:       brand_name += "antimagic"; break;
948
949     // both ranged and non-ranged
950     case SPWPN_CHAOS:           brand_name += "chaos"; break;
951     case SPWPN_CONFUSE:         brand_name += "confusion"; break;
952     default:                    brand_name += "(other)"; break;
953     }
954
955     // Pretty much duplicated by the chaos effect note,
956     // which will be much more informative.
957     if (brand != SPWPN_CHAOS)
958         take_note(Note(NOTE_MESSAGE, 0, 0, brand_name.c_str()), true);
959 #endif
960     return brand;
961 }
962
963 void attack::do_miscast()
964 {
965     if (miscast_level == -1)
966         return;
967
968     ASSERT(miscast_target != nullptr);
969     ASSERT_RANGE(miscast_level, 0, 4);
970     ASSERT(count_bits(miscast_type) == 1);
971
972     if (!miscast_target->alive())
973         return;
974
975     if (miscast_target->is_player() && you.banished)
976         return;
977
978     const bool chaos_brand =
979         using_weapon() && get_weapon_brand(*weapon) == SPWPN_CHAOS;
980
981     // If the miscast is happening on the attacker's side and is due to
982     // a chaos weapon then make smoke/sand/etc pour out of the weapon
983     // instead of the attacker's hands.
984     string hand_str;
985
986     string cause = atk_name(DESC_THE);
987
988     const int ignore_mask = ISFLAG_KNOW_CURSE | ISFLAG_KNOW_PLUSES;
989
990     if (attacker->is_player())
991     {
992         if (chaos_brand)
993         {
994             cause = "a chaos effect from ";
995             // Ignore a lot of item flags to make cause as short as possible,
996             // so it will (hopefully) fit onto a single line in the death
997             // cause screen.
998             cause += wep_name(DESC_YOUR, ignore_mask | ISFLAG_COSMETIC_MASK);
999
1000             if (miscast_target == attacker)
1001                 hand_str = wep_name(DESC_PLAIN, ignore_mask);
1002         }
1003     }
1004     else
1005     {
1006         if (chaos_brand && miscast_target == attacker
1007             && you.can_see(attacker))
1008         {
1009             hand_str = wep_name(DESC_PLAIN, ignore_mask);
1010         }
1011     }
1012
1013     MiscastEffect(miscast_target, attacker, MELEE_MISCAST,
1014                   (spschool_flag_type) miscast_type, miscast_level, cause,
1015                   NH_NEVER, 0, hand_str, false);
1016
1017     // Don't do miscast twice for one attack.
1018     miscast_level = -1;
1019 }
1020
1021 void attack::drain_defender()
1022 {
1023     if (defender->is_monster() && coinflip())
1024         return;
1025
1026     if (defender->holiness() != MH_NATURAL)
1027         return;
1028
1029     special_damage = resist_adjust_damage(defender, BEAM_NEG,
1030                                           (1 + random2(damage_done)) / 2);
1031
1032     if (defender->drain_exp(attacker, true, 20 + min(35, damage_done)))
1033     {
1034         if (defender->is_player())
1035             obvious_effect = true;
1036         else if (defender_visible)
1037         {
1038             special_damage_message =
1039                 make_stringf(
1040                     "%s %s %s!",
1041                     atk_name(DESC_THE).c_str(),
1042                     attacker->conj_verb("drain").c_str(),
1043                     defender_name(true).c_str());
1044         }
1045
1046         attacker->god_conduct(DID_NECROMANCY, 2);
1047     }
1048 }
1049
1050 void attack::drain_defender_speed()
1051 {
1052     if (!defender->res_negative_energy())
1053     {
1054         if (needs_message)
1055         {
1056             mprf("%s %s %s vigour!",
1057                  atk_name(DESC_THE).c_str(),
1058                  attacker->conj_verb("drain").c_str(),
1059                  def_name(DESC_ITS).c_str());
1060         }
1061
1062         special_damage = 1 + random2(damage_done) / 2;
1063         defender->slow_down(attacker, 5 + random2(7));
1064     }
1065 }
1066
1067 int attack::inflict_damage(int dam, beam_type flavour, bool clean)
1068 {
1069     if (flavour == NUM_BEAMS)
1070         flavour = special_damage_flavour;
1071     // Auxes temporarily clear damage_brand so we don't need to check
1072     if (damage_brand == SPWPN_REAPING ||
1073         damage_brand == SPWPN_CHAOS && one_chance_in(100))
1074     {
1075         defender->props["reaping_damage"].get_int() += dam;
1076         // With two reapers of different friendliness, the most recent one
1077         // gets the zombie. Too rare a case to care any more.
1078         defender->props["reaper"].get_int() = attacker->mid;
1079     }
1080     return defender->hurt(responsible, dam, flavour, kill_type,
1081                           "", aux_source.c_str(), clean);
1082 }
1083
1084 /* If debug, return formatted damage done
1085  *
1086  */
1087 string attack::debug_damage_number()
1088 {
1089 #ifdef DEBUG_DIAGNOSTICS
1090     return make_stringf(" for %d", damage_done);
1091 #else
1092     return "";
1093 #endif
1094 }
1095
1096 /* Returns standard attack punctuation
1097  *
1098  * Used in player / monster (both primary and aux) attacks
1099  */
1100 string attack::attack_strength_punctuation(int dmg)
1101 {
1102     if (dmg < HIT_WEAK)
1103         return ".";
1104     else if (dmg < HIT_MED)
1105         return "!";
1106     else if (dmg < HIT_STRONG)
1107         return "!!";
1108     else
1109     {
1110         string ret = "!!!";
1111         int tmpdamage = dmg;
1112         while (tmpdamage >= 2*HIT_STRONG)
1113         {
1114             ret += "!";
1115             tmpdamage >>= 1;
1116         }
1117         return ret;
1118     }
1119 }
1120
1121 /* Returns evasion adverb
1122  *
1123  */
1124 string attack::evasion_margin_adverb()
1125 {
1126     return (ev_margin <= -20) ? " completely" :
1127            (ev_margin <= -12) ? "" :
1128            (ev_margin <= -6)  ? " closely"
1129                               : " barely";
1130 }
1131
1132 void attack::stab_message()
1133 {
1134     defender->props["helpless"] = true;
1135
1136     switch (stab_bonus)
1137     {
1138     case 6:     // big melee, monster surrounded/not paying attention
1139         if (coinflip())
1140         {
1141             mprf("You %s %s from a blind spot!",
1142                   (you.species == SP_FELID) ? "pounce on" : "strike",
1143                   defender->name(DESC_THE).c_str());
1144         }
1145         else
1146         {
1147             mprf("You catch %s momentarily off-guard.",
1148                   defender->name(DESC_THE).c_str());
1149         }
1150         break;
1151     case 4:     // confused/fleeing
1152         if (!one_chance_in(3))
1153         {
1154             mprf("You catch %s completely off-guard!",
1155                   defender->name(DESC_THE).c_str());
1156         }
1157         else
1158         {
1159             mprf("You %s %s from behind!",
1160                   (you.species == SP_FELID) ? "pounce on" : "strike",
1161                   defender->name(DESC_THE).c_str());
1162         }
1163         break;
1164     case 2:
1165     case 1:
1166         if (you.species == SP_FELID && coinflip())
1167         {
1168             mprf("You pounce on the unaware %s!",
1169                  defender->name(DESC_PLAIN).c_str());
1170             break;
1171         }
1172         mprf("%s fails to defend %s.",
1173               defender->name(DESC_THE).c_str(),
1174               defender->pronoun(PRONOUN_REFLEXIVE).c_str());
1175         break;
1176     }
1177
1178     defender->props.erase("helpless");
1179 }
1180
1181 /* Returns the attacker's name
1182  *
1183  * Helper method to easily access the attacker's name
1184  */
1185 string attack::atk_name(description_level_type desc)
1186 {
1187     return actor_name(attacker, desc, attacker_visible);
1188 }
1189
1190 /* Returns the defender's name
1191  *
1192  * Helper method to easily access the defender's name
1193  */
1194 string attack::def_name(description_level_type desc)
1195 {
1196     return actor_name(defender, desc, defender_visible);
1197 }
1198
1199 /* Returns the attacking weapon's name
1200  *
1201  * Sets upthe appropriate descriptive level and obtains the name of a weapon
1202  * based on if the attacker is a player or non-player (non-players use a
1203  * plain name and a manually entered pronoun)
1204  */
1205 string attack::wep_name(description_level_type desc, iflags_t ignre_flags)
1206 {
1207     ASSERT(weapon != nullptr);
1208
1209     if (attacker->is_player())
1210         return weapon->name(desc, false, false, false, false, ignre_flags);
1211
1212     string name;
1213     bool possessive = false;
1214     if (desc == DESC_YOUR)
1215     {
1216         desc       = DESC_THE;
1217         possessive = true;
1218     }
1219
1220     if (possessive)
1221         name = apostrophise(atk_name(desc)) + " ";
1222
1223     name += weapon->name(DESC_PLAIN, false, false, false, false, ignre_flags);
1224
1225     return name;
1226 }
1227
1228 /* TODO: Remove this!
1229  * Removing it may not really be practical, in retrospect. Its only used
1230  * below, in calc_elemental_brand_damage, which is called for both frost and
1231  * flame brands for both players and monsters.
1232  */
1233 string attack::defender_name(bool allow_reflexive)
1234 {
1235     if (allow_reflexive && attacker == defender)
1236         return actor_pronoun(attacker, PRONOUN_REFLEXIVE, attacker_visible);
1237     else
1238         return def_name(DESC_THE);
1239 }
1240
1241 int attack::player_stat_modify_damage(int damage)
1242 {
1243     int dammod = 39;
1244     const int dam_stat_val = calc_stat_to_dam_base();
1245
1246     if (dam_stat_val > 11)
1247         dammod += (random2(dam_stat_val - 11) * 2);
1248     else if (dam_stat_val < 9)
1249         dammod -= (random2(9 - dam_stat_val) * 3);
1250
1251     damage *= dammod;
1252     damage /= 39;
1253
1254     return damage;
1255 }
1256
1257 int attack::player_apply_weapon_skill(int damage)
1258 {
1259     if (using_weapon())
1260     {
1261         damage *= 2500 + (random2(you.skill(wpn_skill, 100) + 1));
1262         damage /= 2500;
1263     }
1264
1265     return damage;
1266 }
1267
1268 int attack::player_apply_fighting_skill(int damage, bool aux)
1269 {
1270     const int base = aux? 40 : 30;
1271
1272     damage *= base * 100 + (random2(you.skill(SK_FIGHTING, 100) + 1));
1273     damage /= base * 100;
1274
1275     return damage;
1276 }
1277
1278 int attack::player_apply_misc_modifiers(int damage)
1279 {
1280     return damage;
1281 }
1282
1283 /**
1284  * Get the damage bonus from a weapon's enchantment.
1285  */
1286 int attack::get_weapon_plus()
1287 {
1288     if (weapon->base_type == OBJ_RODS)
1289         return weapon->special;
1290     if (weapon->base_type == OBJ_STAVES || weapon->sub_type == WPN_BLOWGUN)
1291         return 0;
1292     return weapon->plus;
1293 }
1294
1295 // Slaying and weapon enchantment. Apply this for slaying even if not
1296 // using a weapon to attack.
1297 int attack::player_apply_slaying_bonuses(int damage, bool aux)
1298 {
1299     int damage_plus = 0;
1300     if (!aux && using_weapon())
1301     {
1302         damage_plus = get_weapon_plus();
1303         if (you.duration[DUR_CORROSION])
1304             damage_plus -= 3 * you.props["corrosion_amount"].get_int();
1305     }
1306     damage_plus += slaying_bonus(!weapon && wpn_skill == SK_THROWING
1307                                  || (weapon && is_range_weapon(*weapon)
1308                                             && using_weapon()));
1309
1310     damage += (damage_plus > -1) ? (random2(1 + damage_plus))
1311                                  : (-random2(1 - damage_plus));
1312     return damage;
1313 }
1314
1315 int attack::player_apply_final_multipliers(int damage)
1316 {
1317     // Can't affect much of anything as a shadow.
1318     if (you.form == TRAN_SHADOW)
1319         damage = div_rand_round(damage, 2);
1320
1321     return damage;
1322 }
1323
1324 void attack::player_exercise_combat_skills()
1325 {
1326 }
1327
1328 /* Returns attacker base unarmed damage
1329  *
1330  * Scales for current mutations and unarmed effects
1331  * TODO: Complete symmetry for base_unarmed damage
1332  * between monsters and players.
1333  */
1334 int attack::calc_base_unarmed_damage()
1335 {
1336     // Should only get here if we're not wielding something that's a weapon.
1337     // If there's a non-weapon in hand, it has no base damage.
1338     if (weapon)
1339         return 0;
1340
1341     if (!attacker->is_player())
1342         return 0;
1343
1344     int damage = get_form()->get_base_unarmed_damage();
1345
1346     if (you.has_usable_claws())
1347     {
1348         // Claw damage only applies for bare hands.
1349         damage += you.has_claws(false) * 2;
1350         apply_bleeding = true;
1351     }
1352
1353     if (you.form_uses_xl())
1354         damage += you.experience_level;
1355     else if (you.form == TRAN_BAT || you.form == TRAN_PORCUPINE)
1356     {
1357         // Bats really don't do a lot of damage.
1358         damage += you.skill_rdiv(wpn_skill, 1, 5);
1359     }
1360     else
1361         damage += you.skill_rdiv(wpn_skill);
1362
1363     if (damage < 0)
1364         damage = 0;
1365
1366     return damage;
1367 }
1368
1369 // TODO: Potentially remove, consider integrating with other to-hit or stat
1370 // calculating methods
1371 // weighted average of strength and dex, between (str+dex)/2 and dex
1372 int attack::calc_stat_to_hit_base()
1373 {
1374     const int weight = weapon ? weapon_str_weight(*weapon) : 4;
1375
1376     // dex is modified by strength towards the average, by the
1377     // weighted amount weapon_str_weight() / 20.
1378     return you.dex() + (you.strength() - you.dex()) * weight / 20;
1379 }
1380
1381 // weighted average of strength and dex, between str and (str+dex)/2
1382 int attack::calc_stat_to_dam_base()
1383 {
1384     const int weight = weapon ? 10 - weapon_str_weight(*weapon) : 6;
1385     return you.strength() + (you.dex() - you.strength()) * weight / 20;
1386 }
1387
1388 int attack::calc_damage()
1389 {
1390     if (attacker->is_monster())
1391     {
1392         int damage = 0;
1393         int damage_max = 0;
1394         if (using_weapon() || wpn_skill == SK_THROWING)
1395         {
1396             damage_max = weapon_damage();
1397             damage += random2(damage_max);
1398
1399             int wpn_damage_plus = 0;
1400             if (weapon) // can be 0 for throwing projectiles
1401                 wpn_damage_plus = get_weapon_plus();
1402
1403             const int jewellery = attacker->as_monster()->inv[MSLOT_JEWELLERY];
1404             if (jewellery != NON_ITEM
1405                 && mitm[jewellery].is_type(OBJ_JEWELLERY, RING_SLAYING))
1406             {
1407                 wpn_damage_plus += mitm[jewellery].plus;
1408             }
1409
1410             wpn_damage_plus += attacker->scan_artefacts(ARTP_SLAYING);
1411
1412             if (wpn_damage_plus >= 0)
1413                 damage += random2(wpn_damage_plus);
1414             else
1415                 damage -= random2(1 - wpn_damage_plus);
1416
1417             damage -= 1 + random2(3);
1418         }
1419
1420         damage_max += attk_damage;
1421         damage     += 1 + random2(attk_damage);
1422
1423         damage = apply_damage_modifiers(damage, damage_max);
1424
1425         set_attack_verb(damage);
1426         return apply_defender_ac(damage, damage_max);
1427     }
1428     else
1429     {
1430         int potential_damage, damage;
1431
1432         potential_damage = using_weapon() || wpn_skill == SK_THROWING
1433             ? weapon_damage() : calc_base_unarmed_damage();
1434
1435         potential_damage = player_stat_modify_damage(potential_damage);
1436
1437         damage = random2(potential_damage+1);
1438
1439         damage = player_apply_weapon_skill(damage);
1440         damage = player_apply_fighting_skill(damage, false);
1441         damage = player_apply_misc_modifiers(damage);
1442         damage = player_apply_slaying_bonuses(damage, false);
1443         damage = player_stab(damage);
1444         // A failed stab may have awakened monsters, but that could have
1445         // caused the defender to cease to exist (spectral weapons with
1446         // missing summoners; or pacified monsters on a stair). FIXME:
1447         // The correct thing to do would be either to delay the call to
1448         // alert_nearby_monsters (currently in player_stab) until later
1449         // in the attack; or to avoid removing monsters in handle_behaviour.
1450         if (!defender->alive())
1451             return 0;
1452         damage = player_apply_final_multipliers(damage);
1453         damage = apply_defender_ac(damage);
1454
1455         damage = max(0, damage);
1456         set_attack_verb(damage);
1457
1458         return damage;
1459     }
1460
1461     return 0;
1462 }
1463
1464 int attack::test_hit(int to_land, int ev, bool randomise_ev)
1465 {
1466     int margin = AUTOMATIC_HIT;
1467     if (randomise_ev)
1468         ev = random2avg(2*ev, 2);
1469     if (to_land >= AUTOMATIC_HIT)
1470         return true;
1471     else if (x_chance_in_y(MIN_HIT_MISS_PERCENTAGE, 100))
1472         margin = (random2(2) ? 1 : -1) * AUTOMATIC_HIT;
1473     else
1474         margin = to_land - ev;
1475
1476 #ifdef DEBUG_DIAGNOSTICS
1477     dprf(DIAG_COMBAT, "to hit: %d; ev: %d; result: %s (%d)",
1478          to_hit, ev, (margin >= 0) ? "hit" : "miss", margin);
1479 #endif
1480
1481     return margin;
1482 }
1483
1484 int attack::apply_defender_ac(int damage, int damage_max) const
1485 {
1486     ASSERT(defender);
1487     int stab_bypass = 0;
1488     if (stab_bonus)
1489     {
1490         stab_bypass = you.skill(wpn_skill, 50) + you.skill(SK_STEALTH, 50);
1491         stab_bypass = random2(div_rand_round(stab_bypass, 100 * stab_bonus));
1492     }
1493     int after_ac = defender->apply_ac(damage, damage_max,
1494                                       AC_NORMAL, stab_bypass);
1495     dprf(DIAG_COMBAT, "AC: att: %s, def: %s, ac: %d, gdr: %d, dam: %d -> %d",
1496                  attacker->name(DESC_PLAIN, true).c_str(),
1497                  defender->name(DESC_PLAIN, true).c_str(),
1498                  defender->armour_class(),
1499                  defender->gdr_perc(),
1500                  damage,
1501                  after_ac);
1502
1503     return after_ac;
1504 }
1505
1506 bool attack::attack_warded_off()
1507 {
1508     const int WARDING_CHECK = 60;
1509
1510     if (defender->warding()
1511         && attacker->is_summoned()
1512         && attacker->as_monster()->check_res_magic(WARDING_CHECK) <= 0)
1513     {
1514         if (needs_message)
1515         {
1516             mprf("%s attack is warded away.",
1517                  attacker->name(DESC_ITS).c_str());
1518         }
1519         return true;
1520     }
1521
1522     return false;
1523 }
1524
1525 /* Determine whether a block occurred
1526  *
1527  * No blocks if defender is incapacitated, would be nice to eventually expand
1528  * this method to handle partial blocks as well as full blocks (although this
1529  * would serve as a nerf to shields and - while more realistic - may not be a
1530  * good mechanic for shields.
1531  *
1532  * Returns (block_occurred)
1533  */
1534 bool attack::attack_shield_blocked(bool verbose)
1535 {
1536     if (defender == attacker)
1537         return false; // You can't block your own attacks!
1538
1539     if (defender->incapacitated())
1540         return false;
1541
1542     const int con_block = random2(attacker->shield_bypass_ability(to_hit)
1543                                   + defender->shield_block_penalty());
1544     int pro_block = defender->shield_bonus();
1545
1546     if (!attacker->visible_to(defender))
1547         pro_block /= 3;
1548
1549     dprf(DIAG_COMBAT, "Defender: %s, Pro-block: %d, Con-block: %d",
1550          def_name(DESC_PLAIN).c_str(), pro_block, con_block);
1551
1552     if (pro_block >= con_block)
1553     {
1554         perceived_attack = true;
1555
1556         if (attack_ignores_shield(verbose))
1557             return false;
1558
1559         if (needs_message && verbose)
1560         {
1561             mprf("%s %s %s attack.",
1562                  defender_name(false).c_str(),
1563                  defender->conj_verb("block").c_str(),
1564                  attacker == defender ? "its own"
1565                                       : atk_name(DESC_ITS).c_str());
1566         }
1567
1568         defender->shield_block_succeeded(attacker);
1569
1570         return true;
1571     }
1572
1573     return false;
1574 }
1575
1576 attack_flavour attack::random_chaos_attack_flavour()
1577 {
1578     attack_flavour flavours[] =
1579         {AF_FIRE, AF_COLD, AF_ELEC, AF_POISON, AF_VAMPIRIC, AF_DISTORT,
1580          AF_CONFUSE, AF_CHAOS};
1581     return RANDOM_ELEMENT(flavours);
1582 }
1583
1584 bool attack::apply_damage_brand(const char *what)
1585 {
1586     bool brand_was_known = false;
1587     int brand = 0;
1588     bool ret = false;
1589
1590     if (using_weapon())
1591     {
1592         if (is_artefact(*weapon))
1593             brand_was_known = artefact_known_property(*weapon, ARTP_BRAND);
1594         else
1595             brand_was_known = item_type_known(*weapon);
1596     }
1597
1598     special_damage = 0;
1599     obvious_effect = false;
1600     brand = damage_brand == SPWPN_CHAOS ? random_chaos_brand() : damage_brand;
1601
1602     if (brand != SPWPN_FLAMING && brand != SPWPN_FREEZING
1603         && brand != SPWPN_ELECTROCUTION && brand != SPWPN_VAMPIRISM
1604         && !defender->alive())
1605     {
1606         // Most brands have no extra effects on just killed enemies, and the
1607         // effect would be often inappropriate.
1608         return false;
1609     }
1610
1611     if (!damage_done
1612         && (brand == SPWPN_FLAMING || brand == SPWPN_FREEZING
1613             || brand == SPWPN_HOLY_WRATH || brand == SPWPN_ANTIMAGIC
1614             || brand == SPWPN_VORPAL || brand == SPWPN_VAMPIRISM))
1615     {
1616         // These brands require some regular damage to function.
1617         return false;
1618     }
1619
1620     switch (brand)
1621     {
1622     case SPWPN_FLAMING:
1623         calc_elemental_brand_damage(BEAM_FIRE,
1624                                     defender->is_icy() ? "melt" : "burn",
1625                                     what);
1626         attacker->god_conduct(DID_FIRE, 1);
1627         break;
1628
1629     case SPWPN_FREEZING:
1630         calc_elemental_brand_damage(BEAM_COLD, "freeze", what);
1631         break;
1632
1633     case SPWPN_HOLY_WRATH:
1634         if (defender->holy_wrath_susceptible())
1635             special_damage = 1 + (random2(damage_done * 15) / 10);
1636
1637         if (special_damage && defender_visible)
1638         {
1639             special_damage_message =
1640                 make_stringf(
1641                     "%s %s%s",
1642                     defender_name(false).c_str(),
1643                     defender->conj_verb("convulse").c_str(),
1644                     attack_strength_punctuation(special_damage).c_str());
1645         }
1646         break;
1647
1648     case SPWPN_ELECTROCUTION:
1649         if (defender->res_elec() > 0)
1650             break;
1651         else if (one_chance_in(3))
1652         {
1653             special_damage_message =
1654                 defender->is_player()?
1655                    "You are electrocuted!"
1656                 :  "There is a sudden explosion of sparks!";
1657             special_damage = 8 + random2(13);
1658             special_damage_flavour = BEAM_ELECTRICITY;
1659         }
1660
1661         break;
1662
1663     case SPWPN_VENOM:
1664         if (!one_chance_in(4))
1665         {
1666             int old_poison;
1667
1668             if (defender->is_player())
1669                 old_poison = you.duration[DUR_POISONING];
1670             else
1671             {
1672                 old_poison =
1673                     (defender->as_monster()->get_ench(ENCH_POISON)).degree;
1674             }
1675
1676             defender->poison(attacker, 6 + random2(8) + random2(damage_done * 3 / 2));
1677
1678             if (defender->is_player()
1679                    && old_poison < you.duration[DUR_POISONING]
1680                 || !defender->is_player()
1681                    && old_poison <
1682                       (defender->as_monster()->get_ench(ENCH_POISON)).degree)
1683             {
1684                 obvious_effect = true;
1685             }
1686
1687         }
1688         break;
1689
1690     case SPWPN_DRAINING:
1691         drain_defender();
1692         break;
1693
1694     case SPWPN_VORPAL:
1695         special_damage = 1 + random2(damage_done) / 3;
1696         // Note: Leaving special_damage_message empty because there isn't one.
1697         break;
1698
1699     case SPWPN_VAMPIRISM:
1700     {
1701         if (!weapon
1702             || defender->holiness() != MH_NATURAL
1703             || defender->res_negative_energy()
1704             || damage_done < 1
1705             || attacker->stat_hp() == attacker->stat_maxhp()
1706             || !defender->is_player()
1707                && defender->as_monster()->is_summoned()
1708             || attacker->is_player() && you.duration[DUR_DEATHS_DOOR]
1709             || !attacker->is_player()
1710                && attacker->as_monster()->has_ench(ENCH_DEATHS_DOOR)
1711             || x_chance_in_y(2, 5) && !is_unrandom_artefact(*weapon, UNRAND_LEECH))
1712         {
1713             break;
1714         }
1715
1716         obvious_effect = true;
1717
1718         // Handle weapon effects.
1719         // We only get here if we've done base damage, so no
1720         // worries on that score.
1721         if (attacker->is_player())
1722             mpr("You feel better.");
1723         else if (attacker_visible)
1724         {
1725             if (defender->is_player())
1726             {
1727                 mprf("%s draws strength from your injuries!",
1728                      attacker->name(DESC_THE).c_str());
1729             }
1730             else
1731             {
1732                 mprf("%s is healed.",
1733                      attacker->name(DESC_THE).c_str());
1734             }
1735         }
1736
1737         int hp_boost = is_unrandom_artefact(*weapon, UNRAND_VAMPIRES_TOOTH)
1738                        ? damage_done : 1 + random2(damage_done);
1739
1740         dprf(DIAG_COMBAT, "Vampiric Healing: damage %d, healed %d",
1741              damage_done, hp_boost);
1742         attacker->heal(hp_boost);
1743
1744         attacker->god_conduct(DID_NECROMANCY, 2);
1745         break;
1746     }
1747     case SPWPN_PAIN:
1748         pain_affects_defender();
1749         break;
1750
1751     case SPWPN_DISTORTION:
1752         ret = distortion_affects_defender();
1753         break;
1754
1755     case SPWPN_CONFUSE:
1756     {
1757         // This was originally for confusing touch and it doesn't really
1758         // work on the player, but a monster with a chaos weapon will
1759         // occasionally come up with this brand. -cao
1760         if (defender->is_player())
1761             break;
1762
1763         // Also used for players in fungus form.
1764         if (attacker->is_player()
1765             && you.form == TRAN_FUNGUS
1766             && !you.duration[DUR_CONFUSING_TOUCH]
1767             && defender->is_unbreathing())
1768         {
1769             break;
1770         }
1771
1772         const int hdcheck =
1773             (defender->holiness() == MH_NATURAL ? random2(30) : random2(22));
1774
1775         if (hdcheck < defender->get_hit_dice()
1776             || one_chance_in(5)
1777             || defender->as_monster()->check_clarity(false))
1778         {
1779             break;
1780         }
1781
1782         // Declaring these just to pass to the enchant function.
1783         bolt beam_temp;
1784         beam_temp.thrower   = attacker->is_player() ? KILL_YOU : KILL_MON;
1785         beam_temp.flavour   = BEAM_CONFUSION;
1786         beam_temp.source_id = attacker->mid;
1787         beam_temp.apply_enchantment_to_monster(defender->as_monster());
1788         obvious_effect = beam_temp.obvious_effect;
1789
1790         if (attacker->is_player() && damage_brand == SPWPN_CONFUSE
1791             && you.duration[DUR_CONFUSING_TOUCH])
1792         {
1793             you.duration[DUR_CONFUSING_TOUCH] = 1;
1794             obvious_effect = false;
1795         }
1796         break;
1797     }
1798
1799     case SPWPN_CHAOS:
1800         chaos_affects_defender();
1801         break;
1802
1803     case SPWPN_ANTIMAGIC:
1804         antimagic_affects_defender(damage_done * 8);
1805         break;
1806
1807     default:
1808         if (using_weapon() && is_unrandom_artefact(*weapon, UNRAND_HELLFIRE))
1809         {
1810             calc_elemental_brand_damage(BEAM_HELLFIRE,
1811                                         defender->is_icy() ? "melt" : "burn",
1812                                         what);
1813             defender->expose_to_element(BEAM_HELLFIRE);
1814             attacker->god_conduct(DID_UNHOLY, 2 + random2(3));
1815             attacker->god_conduct(DID_FIRE, 10 + random2(5));
1816         }
1817         break;
1818     }
1819
1820     if (damage_brand == SPWPN_CHAOS)
1821     {
1822         if (brand != SPWPN_CHAOS && !ret
1823             && miscast_level == -1 && one_chance_in(20))
1824         {
1825             miscast_level  = 0;
1826             miscast_type   = SPTYP_RANDOM;
1827             miscast_target = coinflip() ? attacker : defender;
1828         }
1829
1830         if (responsible->is_player())
1831             did_god_conduct(DID_CHAOS, 2 + random2(3), brand_was_known);
1832     }
1833
1834     if (!obvious_effect)
1835         obvious_effect = !special_damage_message.empty();
1836
1837     if (needs_message && !special_damage_message.empty())
1838     {
1839         mpr(special_damage_message);
1840
1841         special_damage_message.clear();
1842         // Don't do message-only miscasts along with a special
1843         // damage message.
1844         if (miscast_level == 0)
1845             miscast_level = -1;
1846     }
1847
1848     if (special_damage > 0)
1849         inflict_damage(special_damage, special_damage_flavour);
1850
1851     if (defender->alive())
1852     {
1853         switch (brand)
1854         {
1855         case SPWPN_FLAMING:
1856             defender->expose_to_element(BEAM_FIRE);
1857             break;
1858
1859         case SPWPN_FREEZING:
1860             defender->expose_to_element(BEAM_COLD, 2);
1861             break;
1862         default:
1863             break;
1864         }
1865     }
1866
1867     if (obvious_effect && attacker_visible && using_weapon())
1868     {
1869         if (is_artefact(*weapon))
1870             artefact_learn_prop(*weapon, ARTP_BRAND);
1871         else
1872             set_ident_flags(*weapon, ISFLAG_KNOW_TYPE);
1873     }
1874
1875     return ret;
1876 }
1877
1878 /* Calculates special damage, prints appropriate combat text
1879  *
1880  * Applies a particular damage brand to the current attack, the setup and
1881  * calculation of base damage and other effects varies based on the type
1882  * of attack, but the calculation of elemental damage should be consistent.
1883  */
1884 void attack::calc_elemental_brand_damage(beam_type flavour,
1885                                          const char *verb,
1886                                          const char *what)
1887 {
1888     special_damage = resist_adjust_damage(defender, flavour,
1889                                           random2(damage_done) / 2 + 1);
1890
1891     if (needs_message && special_damage > 0 && verb)
1892     {
1893         // XXX: assumes "what" is singular
1894         special_damage_message = make_stringf(
1895             "%s %s %s%s",
1896             what ? what : atk_name(DESC_THE).c_str(),
1897             what ? conjugate_verb(verb, false).c_str()
1898                  : attacker->conj_verb(verb).c_str(),
1899             // Don't allow reflexive if the subject wasn't the attacker.
1900             defender_name(!what).c_str(),
1901             attack_strength_punctuation(special_damage).c_str());
1902     }
1903 }
1904
1905 int attack::player_stab_weapon_bonus(int damage)
1906 {
1907     int stab_skill = you.skill(wpn_skill, 50) + you.skill(SK_STEALTH, 50);
1908
1909     if (player_good_stab())
1910     {
1911         // We might be unarmed if we're using the boots of the Assassin.
1912         const bool extra_good = using_weapon() && weapon->sub_type == WPN_DAGGER;
1913         int bonus = you.dex() * (stab_skill + 100) / (extra_good ? 500 : 1000);
1914
1915         bonus   = stepdown_value(bonus, 10, 10, 30, 30);
1916         damage += bonus;
1917         damage *= 10 + div_rand_round(stab_skill, 100 * stab_bonus);
1918         damage /= 10;
1919     }
1920
1921     damage *= 12 + div_rand_round(stab_skill, 100 * stab_bonus);
1922     damage /= 12;
1923
1924     return damage;
1925 }
1926
1927 int attack::player_stab(int damage)
1928 {
1929     // The stabbing message looks better here:
1930     if (stab_attempt)
1931     {
1932         // Construct reasonable message.
1933         stab_message();
1934
1935         practise(EX_WILL_STAB);
1936     }
1937     else
1938     {
1939         stab_bonus = 0;
1940         // Ok.. if you didn't backstab, you wake up the neighborhood.
1941         // I can live with that.
1942         alert_nearby_monsters();
1943     }
1944
1945     if (stab_bonus)
1946     {
1947         // Let's make sure we have some damage to work with...
1948         damage = max(1, damage);
1949
1950         damage = player_stab_weapon_bonus(damage);
1951     }
1952
1953     return damage;
1954 }
1955
1956 /* Check for stab and prepare combat for stab-values
1957  *
1958  * Grant an automatic stab if paralyzed or sleeping (with highest damage value)
1959  * stab_bonus is used as the divisor in damage calculations, so lower values
1960  * will yield higher damage. Normal stab chance is (stab_skill + dex + 1 / roll)
1961  * This averages out to about 1/3 chance for a non extended-endgame stabber.
1962  */
1963 void attack::player_stab_check()
1964 {
1965     if (you.duration[DUR_CLUMSY] || you.confused())
1966     {
1967         stab_attempt = false;
1968         stab_bonus = 0;
1969         return;
1970     }
1971
1972     const stab_type st = find_stab_type(&you, defender);
1973     stab_attempt = (st != STAB_NO_STAB);
1974     const bool roll_needed = (st != STAB_SLEEPING && st != STAB_PARALYSED);
1975
1976     int roll = 100;
1977     if (st == STAB_INVISIBLE)
1978         roll -= 10;
1979
1980     switch (st)
1981     {
1982     case STAB_NO_STAB:
1983     case NUM_STAB:
1984         stab_bonus = 0;
1985         break;
1986     case STAB_SLEEPING:
1987     case STAB_PARALYSED:
1988         stab_bonus = 1;
1989         break;
1990     case STAB_HELD_IN_NET:
1991     case STAB_PETRIFYING:
1992     case STAB_PETRIFIED:
1993         stab_bonus = 2;
1994         break;
1995     case STAB_INVISIBLE:
1996     case STAB_CONFUSED:
1997     case STAB_FLEEING:
1998     case STAB_ALLY:
1999         stab_bonus = 4;
2000         break;
2001     case STAB_DISTRACTED:
2002         stab_bonus = 6;
2003         break;
2004     }
2005
2006     // See if we need to roll against dexterity / stabbing.
2007     if (stab_attempt && roll_needed)
2008     {
2009         stab_attempt = x_chance_in_y(you.skill_rdiv(wpn_skill, 1, 2)
2010                                      + you.skill_rdiv(SK_STEALTH, 1, 2)
2011                                      + you.dex() + 1,
2012                                      roll);
2013     }
2014
2015     if (stab_attempt)
2016         count_action(CACT_STAB, st);
2017 }