Code cleanup
[crawl.git] / crawl-ref / source / fight.cc
blobd9b31335bd9d9539ac9632c9b9e3b0e5ce124a30
1 /*
2 * File: fight.cc
3 * Summary: Functions used during combat.
4 * Written by: Linley Henzell
5 */
7 #include "AppHdr.h"
9 #include "fight.h"
10 #include "melee_attack.h"
12 #include <string.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <algorithm>
17 #include "externs.h"
19 #include "areas.h"
20 #include "artefact.h"
21 #include "attitude-change.h"
22 #include "beam.h"
23 #include "cloud.h"
24 #include "coordit.h"
25 #include "database.h"
26 #include "debug.h"
27 #include "delay.h"
28 #include "directn.h"
29 #include "effects.h"
30 #include "env.h"
31 #include "exercise.h"
32 #include "map_knowledge.h"
33 #include "feature.h"
34 #include "fineff.h"
35 #include "fprop.h"
36 #include "food.h"
37 #include "goditem.h"
38 #include "invent.h"
39 #include "items.h"
40 #include "itemname.h"
41 #include "itemprop.h"
42 #include "item_use.h"
43 #include "libutil.h"
44 #include "macro.h"
45 #include "makeitem.h"
46 #include "message.h"
47 #include "misc.h"
48 #include "mon-behv.h"
49 #include "mon-cast.h"
50 #include "mon-clone.h"
51 #include "mon-place.h"
52 #include "terrain.h"
53 #include "mgen_data.h"
54 #include "coord.h"
55 #include "mon-stuff.h"
56 #include "mon-util.h"
57 #include "mutation.h"
58 #include "ouch.h"
59 #include "player.h"
60 #include "random-var.h"
61 #include "religion.h"
62 #include "godconduct.h"
63 #include "shopping.h"
64 #include "skills.h"
65 #include "species.h"
66 #include "spl-clouds.h"
67 #include "spl-miscast.h"
68 #include "spl-summoning.h"
69 #include "spl-util.h"
70 #include "state.h"
71 #include "stuff.h"
72 #include "transform.h"
73 #include "traps.h"
74 #include "travel.h"
75 #include "hints.h"
76 #include "view.h"
77 #include "shout.h"
78 #include "xom.h"
80 #ifdef NOTE_DEBUG_CHAOS_BRAND
81 #define NOTE_DEBUG_CHAOS_EFFECTS
82 #endif
84 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
85 #include "notes.h"
86 #endif
88 const int HIT_WEAK = 7;
89 const int HIT_MED = 18;
90 const int HIT_STRONG = 36;
92 static void stab_message(actor *defender, int stab_bonus);
94 static inline int player_weapon_str_weight(void);
95 static inline int player_weapon_dex_weight(void);
97 static inline int calc_stat_to_hit_base(void);
98 static inline int calc_stat_to_dam_base(void);
99 static void mons_lose_attack_energy(monster* attacker, int wpn_speed,
100 int which_attack, int effective_attack);
103 **************************************************
105 * BEGIN PUBLIC FUNCTIONS *
107 **************************************************
110 // This function is only used when monsters are attacking.
111 int test_melee_hit(int to_hit, int ev, defer_rand& r)
113 int roll = -1;
114 int margin = AUTOMATIC_HIT;
116 ev *= 2;
118 if (to_hit >= AUTOMATIC_HIT)
119 return (true);
120 else if (r[0].x_chance_in_y(MIN_HIT_MISS_PERCENTAGE, 100))
121 margin = (r[1].random2(2) ? 1 : -1) * AUTOMATIC_HIT;
122 else
124 roll = r[2].random2(to_hit + 1);
125 margin = (roll - r[3].random2avg(ev, 2));
128 #ifdef DEBUG_DIAGNOSTICS
129 float miss;
131 if (to_hit < ev)
132 miss = 100.0 - MIN_HIT_MISS_PERCENTAGE / 2.0;
133 else
135 miss = MIN_HIT_MISS_PERCENTAGE / 2.0 +
136 ((100.0 - MIN_HIT_MISS_PERCENTAGE) * ev) / to_hit;
139 mprf(MSGCH_DIAGNOSTICS,
140 "to hit: %d; ev: %d; miss: %0.2f%%; roll: %d; result: %s%s (%d)",
141 to_hit, ev, miss, roll, (margin >= 0) ? "hit" : "miss",
142 (roll == -1) ? "!!!" : "", margin);
143 #endif
145 return (margin);
148 // This function returns the "extra" stats the player gets because of
149 // choice of weapon... it's used only for giving warnings when a player
150 // wields a less than ideal weapon.
151 int effective_stat_bonus(int wepType)
153 int str_weight;
154 if (wepType == -1)
155 str_weight = player_weapon_str_weight();
156 else
157 str_weight = weapon_str_weight(OBJ_WEAPONS, wepType);
159 return ((you.strength() - you.dex()) * (str_weight - 5) / 10);
162 // Returns the to-hit for your extra unarmed attacks.
163 // DOES NOT do the final roll (i.e., random2(your_to_hit)).
164 static int calc_your_to_hit_unarmed(int uattack = UNAT_NO_ATTACK,
165 bool vampiric = false)
167 int your_to_hit;
169 your_to_hit = 13 + you.dex() / 2 + you.skill(SK_UNARMED_COMBAT) / 2
170 + you.skill(SK_FIGHTING) / 5;
172 if (wearing_amulet(AMU_INACCURACY))
173 your_to_hit -= 5;
175 if (player_mutation_level(MUT_EYEBALLS))
176 your_to_hit += 2 * player_mutation_level(MUT_EYEBALLS) + 1;
178 // Vampires know how to bite and aim better when thirsty.
179 if (you.species == SP_VAMPIRE && uattack == UNAT_BITE)
181 your_to_hit += 1;
183 if (vampiric)
185 if (you.hunger_state == HS_STARVING)
186 your_to_hit += 2;
187 else if (you.hunger_state < HS_SATIATED)
188 your_to_hit += 1;
191 else if (you.species != SP_VAMPIRE && you.hunger_state == HS_STARVING)
192 your_to_hit -= 3;
194 your_to_hit += slaying_bonus(PWPN_HIT);
196 return your_to_hit;
199 // Calculates your to-hit roll. If random_factor is true, be stochastic;
200 // if false, determinstic (e.g. for chardumps).
201 int calc_your_to_hit(bool random_factor)
203 melee_attack attk(&you, NULL);
204 return attk.calc_to_hit(random_factor);
207 random_var calc_your_attack_delay()
209 melee_attack attk(&you, NULL);
210 return attk.player_calc_attack_delay();
213 static bool player_fights_well_unarmed(int heavy_armour_penalty)
215 return (you.burden_state == BS_UNENCUMBERED
216 && x_chance_in_y(you.skill(SK_UNARMED_COMBAT), 20)
217 && x_chance_in_y(2, 1 + heavy_armour_penalty));
220 unchivalric_attack_type is_unchivalric_attack(const actor *attacker,
221 const actor *defender)
223 const monster* def = defender->as_monster();
224 unchivalric_attack_type unchivalric = UCAT_NO_ATTACK;
226 // No unchivalric attacks on monsters that cannot fight (e.g.
227 // plants) or monsters the attacker can't see (either due to
228 // invisibility or being behind opaque clouds).
229 if (defender->cannot_fight() || (attacker && !attacker->can_see(defender)))
230 return (unchivalric);
232 // Distracted (but not batty); this only applies to players.
233 if (attacker && attacker->atype() == ACT_PLAYER
234 && def && def->foe != MHITYOU && !mons_is_batty(def))
236 unchivalric = UCAT_DISTRACTED;
239 // confused (but not perma-confused)
240 if (def && mons_is_confused(def, false))
241 unchivalric = UCAT_CONFUSED;
243 // allies
244 if (def && def->friendly())
245 unchivalric = UCAT_ALLY;
247 // fleeing
248 if (def && mons_is_fleeing(def))
249 unchivalric = UCAT_FLEEING;
251 // invisible
252 if (attacker && !attacker->visible_to(defender))
253 unchivalric = UCAT_INVISIBLE;
255 // held in a net
256 if (def && def->caught())
257 unchivalric = UCAT_HELD_IN_NET;
259 // petrifying
260 if (def && def->petrifying())
261 unchivalric = UCAT_PETRIFYING;
263 // petrified
264 if (defender->petrified())
265 unchivalric = UCAT_PETRIFIED;
267 // paralysed
268 if (defender->paralysed())
269 unchivalric = UCAT_PARALYSED;
271 // sleeping
272 if (defender->asleep())
273 unchivalric = UCAT_SLEEPING;
275 return (unchivalric);
278 //////////////////////////////////////////////////////////////////////////
279 // Melee attack
281 melee_attack::melee_attack(actor *attk, actor *defn,
282 bool allow_unarmed, int which_attack)
283 : attacker(attk), defender(defn), cancel_attack(false), did_hit(false),
284 perceived_attack(false), obvious_effect(false), needs_message(false),
285 attacker_visible(false), defender_visible(false),
286 attacker_invisible(false), defender_invisible(false),
287 unarmed_ok(allow_unarmed),
288 attack_number(which_attack), to_hit(0), damage_done(0), special_damage(0),
289 aux_damage(0), stab_attempt(false), stab_bonus(0), min_delay(0),
290 final_attack_delay(0), noise_factor(0), extra_noise(0), weapon(NULL),
291 damage_brand(SPWPN_NORMAL), wpn_skill(SK_UNARMED_COMBAT), hands(HANDS_ONE),
292 hand_half_bonus(false), skip_chaos_message(false), art_props(0),
293 unrand_entry(NULL), attack_verb("bug"), verb_degree(),
294 no_damage_message(), special_damage_message(), aux_attack(), aux_verb(),
295 special_damage_flavour(BEAM_NONE),
296 shield(NULL), defender_shield(NULL),
297 player_body_armour_penalty(0), player_shield_penalty(0),
298 player_armour_tohit_penalty(0), player_shield_tohit_penalty(0),
299 can_do_unarmed(false), apply_bleeding(false),
300 miscast_level(-1), miscast_type(SPTYP_NONE), miscast_target(NULL)
302 init_attack();
305 void melee_attack::init_attack()
307 weapon = attacker->weapon(attack_number);
308 damage_brand = attacker->damage_brand(attack_number);
310 if (weapon && weapon->base_type == OBJ_WEAPONS && is_artefact(*weapon))
312 artefact_wpn_properties(*weapon, art_props);
313 if (is_unrandom_artefact(*weapon))
314 unrand_entry = get_unrand_entry(weapon->special);
317 wpn_skill = weapon ? weapon_skill(*weapon) : SK_UNARMED_COMBAT;
318 if (weapon)
320 hands = hands_reqd(*weapon, attacker->body_size());
322 switch (single_damage_type(*weapon))
324 case DAM_BLUDGEON:
325 case DAM_WHIP:
326 noise_factor = 125;
327 break;
328 case DAM_SLICE:
329 noise_factor = 100;
330 break;
331 case DAM_PIERCE:
332 noise_factor = 75;
333 break;
337 shield = attacker->shield();
338 if (defender)
339 defender_shield = defender->shield();
341 hand_half_bonus = (unarmed_ok
342 && !shield
343 && weapon
344 && !weapon->cursed()
345 && hands == HANDS_HALF);
347 attacker_visible = attacker->observable();
348 attacker_invisible = (!attacker_visible && you.see_cell(attacker->pos()));
349 defender_visible = defender && defender->observable();
350 defender_invisible = (!defender_visible && defender
351 && you.see_cell(defender->pos()));
352 needs_message = (attacker_visible || defender_visible);
354 if (attacker && attacker->atype() == ACT_PLAYER)
356 player_body_armour_penalty =
357 player_adjusted_body_armour_evasion_penalty(1);
358 player_shield_penalty =
359 player_adjusted_shield_evasion_penalty(1);
360 dprf("Player body armour penalty: %d, shield penalty: %d",
361 player_body_armour_penalty, player_shield_penalty);
364 if (defender && defender->submerged())
366 // Unarmed attacks from tentacles are the only ones that can
367 // reach submerged monsters.
368 unarmed_ok = (attacker->damage_type() == DVORP_TENTACLE);
371 miscast_level = -1;
372 miscast_type = SPTYP_NONE;
373 miscast_target = NULL;
376 std::string melee_attack::actor_name(const actor *a,
377 description_level_type desc,
378 bool actor_visible,
379 bool actor_invisible)
381 return (actor_visible ? a->name(desc) : anon_name(desc, actor_invisible));
384 std::string melee_attack::pronoun(const actor *a,
385 pronoun_type pron,
386 bool actor_visible)
388 return (actor_visible ? a->pronoun(pron) : anon_pronoun(pron));
391 std::string melee_attack::anon_pronoun(pronoun_type pron)
393 switch (pron)
395 default:
396 case PRONOUN_CAP: return "It";
397 case PRONOUN_NOCAP: return "it";
398 case PRONOUN_CAP_POSSESSIVE: return "Its";
399 case PRONOUN_NOCAP_POSSESSIVE: return "its";
400 case PRONOUN_REFLEXIVE: return "itself";
404 std::string melee_attack::anon_name(description_level_type desc,
405 bool actor_invisible)
407 switch (desc)
409 case DESC_CAP_THE:
410 case DESC_CAP_A:
411 return (actor_invisible ? "It" : "Something");
412 case DESC_CAP_YOUR:
413 return ("Its");
414 case DESC_NOCAP_YOUR:
415 case DESC_NOCAP_ITS:
416 return ("its");
417 case DESC_NONE:
418 return ("");
419 case DESC_NOCAP_THE:
420 case DESC_NOCAP_A:
421 case DESC_PLAIN:
422 default:
423 return (actor_invisible? "it" : "something");
427 std::string melee_attack::atk_name(description_level_type desc) const
429 return actor_name(attacker, desc, attacker_visible, attacker_invisible);
432 std::string melee_attack::def_name(description_level_type desc) const
434 return actor_name(defender, desc, defender_visible, defender_invisible);
437 std::string melee_attack::wep_name(description_level_type desc,
438 iflags_t ignore_flags) const
440 ASSERT(weapon != NULL);
442 if (attacker->atype() == ACT_PLAYER)
443 return weapon->name(desc, false, false, false, false, ignore_flags);
445 std::string name;
446 bool possessive = false;
447 if (desc == DESC_CAP_YOUR)
449 desc = DESC_CAP_THE;
450 possessive = true;
452 else if (desc == DESC_NOCAP_YOUR)
454 desc = DESC_NOCAP_THE;
455 possessive = true;
458 if (possessive)
459 name = apostrophise(atk_name(desc)) + " ";
461 name += weapon->name(DESC_PLAIN, false, false, false, false, ignore_flags);
463 return (name);
466 bool melee_attack::is_banished(const actor *a) const
468 if (!a || a->alive())
469 return (false);
471 if (a->atype() == ACT_PLAYER)
472 return (you.banished);
473 else
474 return (a->as_monster()->flags & MF_BANISHED);
477 void melee_attack::check_autoberserk()
479 if (weapon
480 && art_props[ARTP_ANGRY] >= 1
481 && !one_chance_in(1 + art_props[ARTP_ANGRY]))
483 attacker->go_berserk(false);
487 bool melee_attack::check_unrand_effects(bool mondied)
489 // If bashing the defender with a wielded unrandart launcher, don't use
490 // unrand_entry->fight_func, since that's the function used for
491 // launched missiles.
492 if (unrand_entry && unrand_entry->fight_func.melee_effects
493 && weapon && fires_ammo_type(*weapon) == MI_NONE)
495 unrand_entry->fight_func.melee_effects(weapon, attacker, defender,
496 mondied);
497 return (!defender->alive());
500 return (false);
503 void melee_attack::identify_mimic(actor *act)
505 if (act
506 && act->atype() == ACT_MONSTER
507 && mons_is_mimic(act->type)
508 && you.can_see(act))
510 monster* mon = act->as_monster();
511 discover_mimic(mon);
515 bool melee_attack::attack()
517 // If a mimic is attacking or defending, it is thereafter known.
518 identify_mimic(attacker);
520 coord_def defender_pos = defender->pos();
522 if (attacker->atype() == ACT_PLAYER && defender->atype() == ACT_MONSTER)
524 if (stop_attack_prompt(defender->as_monster(), false,
525 attacker->pos()))
527 cancel_attack = true;
528 return (false);
532 if (attacker != defender)
534 // Allow setting of your allies' target, etc.
535 attacker->attacking(defender);
537 check_autoberserk();
540 // The attacker loses nutrition.
541 attacker->make_hungry(3, true);
543 // Xom thinks fumbles are funny...
544 if (attacker->fumbles_attack())
546 // Make sure the monster uses up some energy, even though
547 // it didn't actually attack.
548 attacker->lose_energy(EUT_ATTACK);
550 // ... and thinks fumbling when trying to hit yourself is just
551 // hilarious.
552 if (attacker == defender)
553 xom_is_stimulated(255);
554 else
555 xom_is_stimulated(14);
557 if (damage_brand == SPWPN_CHAOS)
558 chaos_affects_attacker();
560 return (false);
562 // Non-fumbled self-attacks due to confusion are still pretty funny,
563 // though.
564 else if (attacker == defender && attacker->confused())
566 // And is still hilarious if it's the player.
567 if (attacker->atype() == ACT_PLAYER)
568 xom_is_stimulated(255);
569 else
570 xom_is_stimulated(128);
573 // Defending monster protects itself from attacks using the wall
574 // it's in. Zotdef: allow a 5% chance of a hit anyway
575 if (defender->atype() == ACT_MONSTER && cell_is_solid(defender->pos())
576 && mons_wall_shielded(defender->as_monster())
577 && !one_chance_in(20))
579 std::string feat_name = raw_feature_description(grd(defender->pos()));
581 if (attacker->atype() == ACT_PLAYER)
583 player_apply_attack_delay();
585 if (you.can_see(defender))
587 mprf("The %s protects %s from harm.",
588 feat_name.c_str(),
589 defender->name(DESC_NOCAP_THE).c_str());
591 else
593 mprf("You hit the %s.",
594 feat_name.c_str());
597 else
599 // Make sure the monster uses up some energy, even though it
600 // didn't actually land a blow.
601 attacker->lose_energy(EUT_ATTACK);
603 if (!mons_near(defender->as_monster()))
605 simple_monster_message(attacker->as_monster(),
606 " hits something.");
608 else if (you.can_see(attacker))
610 mprf("%s tries to hit %s, but is blocked by the %s.",
611 attacker->name(DESC_CAP_THE).c_str(),
612 defender->name(DESC_NOCAP_THE).c_str(),
613 feat_name.c_str());
616 if (damage_brand == SPWPN_CHAOS)
617 chaos_affects_attacker();
619 return (true);
622 to_hit = calc_to_hit();
624 god_conduct_trigger conducts[3];
625 disable_attack_conducts(conducts);
627 if (attacker->atype() == ACT_PLAYER && attacker != defender)
628 set_attack_conducts(conducts, defender->as_monster());
630 // Trying to stay general beyond this point is a recipe for insanity.
631 // Maybe when Stone Soup hits 1.0... :-)
632 bool retval = ((attacker->atype() == ACT_PLAYER) ? player_attack() :
633 (defender->atype() == ACT_PLAYER) ? mons_attack_you()
634 : mons_attack_mons());
635 identify_mimic(defender);
637 if (env.sanctuary_time > 0 && retval && !cancel_attack
638 && attacker != defender && !attacker->confused())
640 if (is_sanctuary(attacker->pos()) || is_sanctuary(defender->pos()))
642 if (attacker->atype() == ACT_PLAYER
643 || attacker->as_monster()->friendly())
645 remove_sanctuary(true);
650 if (attacker->atype() == ACT_PLAYER)
652 handle_noise(defender_pos);
654 if (damage_brand == SPWPN_CHAOS)
655 chaos_affects_attacker();
657 do_miscast();
660 // This may invalidate both the attacker and defender.
661 fire_final_effects();
663 enable_attack_conducts(conducts);
665 return (retval);
668 static int _modify_blood_amount(const int damage, const int dam_type)
670 int factor = 0; // DVORP_NONE
672 switch (dam_type)
674 case DVORP_CRUSHING: // flails, also unarmed
675 case DVORP_TENTACLE: // unarmed, tentacles
676 factor = 2;
677 break;
678 case DVORP_SLASHING: // whips
679 factor = 4;
680 break;
681 case DVORP_PIERCING: // pole-arms
682 factor = 5;
683 break;
684 case DVORP_STABBING: // knives, daggers
685 factor = 8;
686 break;
687 case DVORP_SLICING: // other short/long blades, also blade hands
688 factor = 10;
689 break;
690 case DVORP_CHOPPING: // axes
691 factor = 17;
692 break;
693 case DVORP_CLAWING: // unarmed, claws
694 factor = 24;
695 break;
698 return (damage * factor / 10);
701 static bool _vamp_wants_blood_from_monster(const monster* mon)
703 if (you.species != SP_VAMPIRE)
704 return (false);
706 if (you.hunger_state == HS_ENGORGED)
707 return (false);
709 if (mon->is_summoned())
710 return (false);
712 if (!mons_has_blood(mon->type))
713 return (false);
715 const corpse_effect_type chunk_type = mons_corpse_effect(mon->type);
717 // Don't drink poisonous or mutagenic blood.
718 return (chunk_type == CE_CLEAN || chunk_type == CE_CONTAMINATED
719 || (chunk_is_poisonous(chunk_type) && player_res_poison()));
722 // Should life protection protect from this?
723 // Called when stabbing, for bite attacks, and vampires wielding vampiric weapons
724 // Returns true if blood was drawn.
725 static bool _player_vampire_draws_blood(const monster* mon, const int damage,
726 bool needs_bite_msg = false,
727 int reduction = 1)
729 ASSERT(you.species == SP_VAMPIRE);
731 if (!_vamp_wants_blood_from_monster(mon))
732 return (false);
734 const corpse_effect_type chunk_type = mons_corpse_effect(mon->type);
736 // Now print message, need biting unless already done (never for bat form!)
737 if (needs_bite_msg && !player_in_bat_form())
739 mprf("You bite %s, and draw %s blood!",
740 mon->name(DESC_NOCAP_THE, true).c_str(),
741 mon->pronoun(PRONOUN_NOCAP_POSSESSIVE).c_str());
743 else
745 mprf("You draw %s blood!",
746 apostrophise(mon->name(DESC_NOCAP_THE, true)).c_str());
749 // Regain hp.
750 if (you.hp < you.hp_max)
752 int heal = 1 + random2(damage);
753 if (chunk_type == CE_CLEAN)
754 heal += 1 + random2(damage);
755 if (heal > you.experience_level)
756 heal = you.experience_level;
758 // Decrease healing when done in bat form.
759 if (player_in_bat_form())
760 heal /= 2;
762 if (heal > 0 && !you.duration[DUR_DEATHS_DOOR])
764 inc_hp(heal, false);
765 mprf("You feel %sbetter.", (you.hp == you.hp_max) ? "much " : "");
769 // Gain nutrition.
770 if (you.hunger_state != HS_ENGORGED)
772 int food_value = 0;
773 if (chunk_type == CE_CLEAN)
774 food_value = 30 + random2avg(59, 2);
775 else if (chunk_type == CE_CONTAMINATED
776 || chunk_is_poisonous(chunk_type))
778 food_value = 15 + random2avg(29, 2);
781 // Bats get rather less nutrition out of it.
782 if (player_in_bat_form())
783 food_value /= 2;
785 food_value /= reduction;
787 lessen_hunger(food_value, false);
790 did_god_conduct(DID_DRINK_BLOOD, 5 + random2(4));
792 return (true);
795 bool melee_attack::player_attack()
797 if (cancel_attack)
798 return (false);
800 noise_factor = 100;
802 player_apply_attack_delay();
803 player_stab_check();
805 coord_def where = defender->pos();
807 ev_margin = player_hits_monster();
809 if (ev_margin >= 0)
811 did_hit = true;
812 if (crawl_state.game_is_hints())
813 Hints.hints_melee_counter++;
815 const bool shield_blocked = attack_shield_blocked(true);
817 if (shield_blocked)
818 damage_done = 0;
819 else
821 // This actually does more than calculate damage - it also
822 // sets up messages, etc.
823 player_calc_hit_damage();
826 if (you.duration[DUR_SLIMIFY]
827 && mon_can_be_slimified(defender->as_monster()))
829 // Bail out after sliming so we don't get aux unarmed and
830 // attack a fellow slime.
831 damage_done = 0;
832 slimify_monster(defender->as_monster());
833 you.duration[DUR_SLIMIFY] = 0;
834 return (true);
837 bool hit_woke_orc = false;
838 if (you.religion == GOD_BEOGH
839 && mons_genus(defender->mons_species()) == MONS_ORC
840 && !defender->is_summoned()
841 && !defender->as_monster()->is_shapeshifter()
842 && !player_under_penance() && you.piety >= piety_breakpoint(2)
843 && mons_near(defender->as_monster()) && defender->asleep())
845 hit_woke_orc = true;
849 if (damage_done > 0
850 && defender->can_bleed()
851 && !defender->is_summoned()
852 && !defender->submerged())
854 int blood = _modify_blood_amount(damage_done,
855 attacker->damage_type());
856 if (blood > defender->stat_hp())
857 blood = defender->stat_hp();
859 bleed_onto_floor(where, defender->type, blood, true);
862 if (damage_done > 0 || !defender_visible && !shield_blocked)
863 player_announce_hit();
864 else if (!shield_blocked && damage_done <= 0)
866 no_damage_message =
867 make_stringf("You %s %s.", attack_verb.c_str(),
868 defender->name(DESC_NOCAP_THE).c_str());
870 defender->as_monster()->del_ench(ENCH_HELPLESS);
872 damage_done = defender->hurt(&you, damage_done,
873 special_damage_flavour, false);
875 if (damage_done)
876 player_exercise_combat_skills();
878 if (player_check_monster_died())
879 return (true);
881 // Always upset monster regardless of damage.
882 // However, successful stabs inhibit shouting.
883 behaviour_event(defender->as_monster(), ME_WHACK, MHITYOU,
884 coord_def(), !stab_attempt);
886 // [ds] Monster may disappear after behaviour event.
887 if (!defender->alive())
888 return (true);
890 // ugh, inspecting attack_verb here is pretty ugly
891 if (damage_done && attack_verb == "trample")
892 do_trample();
894 player_sustain_passive_damage();
896 // Thirsty stabbing vampires get to draw blood.
897 if (you.species == SP_VAMPIRE && you.hunger_state < HS_SATIATED
898 && stab_attempt && stab_bonus > 0)
900 _player_vampire_draws_blood(defender->as_monster(),
901 damage_done, true);
904 // At this point, pretend we didn't hit at all.
905 if (shield_blocked)
906 did_hit = false;
908 if (hit_woke_orc)
910 // Call function of orcs first noticing you, but with
911 // beaten-up conversion messages (if applicable).
912 beogh_follower_convert(defender->as_monster(), true);
913 return (true);
916 else
917 player_warn_miss();
919 if (did_hit)
921 if (player_monattk_hit_effects(false))
922 return (true);
923 if (!defender->alive())
924 return (true);
927 const bool did_primary_hit = did_hit;
928 if (unarmed_ok && where == defender->pos() && player_aux_unarmed())
929 return (true);
931 if ((did_primary_hit || did_hit) && defender->alive()
932 && where == defender->pos())
934 print_wounds(defender->as_monster());
936 // Actually apply the bleeding effect, this can come from an aux claw
937 // or a main hand claw attack and up to now has not actually happened.
938 const int degree = you.has_claws();
939 if (apply_bleeding && defender->can_bleed()
940 && degree > 0 && damage_done > 0)
942 defender->as_monster()->bleed(3 + roll_dice(degree, 3), degree);
946 return (did_primary_hit || did_hit);
949 void melee_attack::player_aux_setup(unarmed_attack_type atk)
951 noise_factor = 100;
952 aux_attack.clear();
953 aux_verb.clear();
954 damage_brand = SPWPN_NORMAL;
955 aux_damage = 0;
957 switch (atk)
959 case UNAT_KICK:
960 aux_attack = aux_verb = "kick";
961 aux_damage = 5;
963 if (player_mutation_level(MUT_HOOVES))
965 // Max hoof damage: 10.
966 aux_damage += player_mutation_level(MUT_HOOVES) * 5 / 3;
968 else if (you.has_usable_talons())
970 aux_verb = "claw";
972 // Max talon damage: 8.
973 aux_damage += player_mutation_level(MUT_TALONS);
976 break;
978 case UNAT_HEADBUTT:
979 aux_damage = 5;
981 if (player_mutation_level(MUT_BEAK)
982 && (!player_mutation_level(MUT_HORNS) || coinflip()))
984 aux_attack = aux_verb = "peck";
985 aux_damage++;
986 noise_factor = 75;
988 else
990 aux_attack = aux_verb = "headbutt";
991 // Minotaurs used to get +5 damage here, now they get
992 // +6 because of the horns.
993 aux_damage += player_mutation_level(MUT_HORNS) * 3;
995 item_def* helmet = you.slot_item(EQ_HELMET);
996 if (helmet && is_hard_helmet(*helmet))
998 aux_damage += 2;
999 if (get_helmet_desc(*helmet) == THELM_DESC_SPIKED
1000 || get_helmet_desc(*helmet) == THELM_DESC_HORNED)
1002 aux_damage += 3;
1006 break;
1008 case UNAT_TAILSLAP:
1009 aux_attack = aux_verb = "tail-slap";
1011 aux_damage = 6 * you.has_usable_tail();
1013 noise_factor = 125;
1015 if (player_mutation_level(MUT_STINGER) > 0)
1017 aux_damage += player_mutation_level(MUT_STINGER) * 2 - 1;
1018 damage_brand = SPWPN_VENOM;
1021 break;
1023 case UNAT_PUNCH:
1024 aux_attack = aux_verb = "punch";
1025 aux_damage = 5 + you.skill(SK_UNARMED_COMBAT) / 3;
1027 if (you.form == TRAN_BLADE_HANDS)
1029 aux_verb = "slash";
1030 aux_damage += 6;
1031 noise_factor = 75;
1033 else if (you.has_usable_claws())
1035 aux_verb = "claw";
1036 aux_damage += roll_dice(you.has_claws(), 3);
1039 break;
1041 case UNAT_BITE:
1042 aux_attack = aux_verb = "bite";
1043 aux_damage += you.has_usable_fangs() * 2
1044 + you.skill(SK_UNARMED_COMBAT) / 5;
1045 noise_factor = 75;
1047 // prob of vampiric bite:
1048 // 1/4 when non-thirsty, 1/2 when thirsty, 100% when
1049 // bloodless
1050 if (_vamp_wants_blood_from_monster(defender->as_monster())
1051 && (you.hunger_state == HS_STARVING
1052 || you.hunger_state < HS_SATIATED && coinflip()
1053 || you.hunger_state >= HS_SATIATED && one_chance_in(4)))
1055 damage_brand = SPWPN_VAMPIRICISM;
1058 if (player_mutation_level(MUT_ACIDIC_BITE))
1060 damage_brand = SPWPN_ACID;
1061 aux_damage += roll_dice(2,4);
1064 break;
1066 case UNAT_PSEUDOPODS:
1067 aux_attack = aux_verb = "slap";
1068 aux_damage += 4 * you.has_usable_pseudopods();
1069 noise_factor = 125;
1070 break;
1072 default:
1073 die("unknown aux attack type");
1074 break;
1078 static bool _tran_forbid_aux_attack(unarmed_attack_type atk)
1080 switch (atk)
1082 case UNAT_KICK:
1083 case UNAT_HEADBUTT:
1084 case UNAT_PUNCH:
1085 return (you.form == TRAN_ICE_BEAST
1086 || you.form == TRAN_DRAGON
1087 || you.form == TRAN_SPIDER
1088 || you.form == TRAN_BAT);
1090 default:
1091 return (false);
1095 static bool _extra_aux_attack(unarmed_attack_type atk)
1097 // No extra unarmed attacks for disabled mutations.
1098 // XXX: It might be better to make player_mutation_level
1099 // aware of mutations that are disabled due to transformation.
1100 if (_tran_forbid_aux_attack(atk))
1101 return (false);
1103 switch (atk)
1105 case UNAT_KICK:
1106 return ((player_mutation_level(MUT_HOOVES)
1107 || you.has_usable_talons())
1108 && coinflip());
1110 case UNAT_HEADBUTT:
1111 return ((player_mutation_level(MUT_HORNS)
1112 || player_mutation_level(MUT_BEAK))
1113 && one_chance_in(3));
1115 case UNAT_TAILSLAP:
1116 return (you.has_usable_tail()
1117 && one_chance_in(4));
1119 case UNAT_PSEUDOPODS:
1120 return (you.has_usable_pseudopods()
1121 && one_chance_in(3));
1123 case UNAT_BITE:
1124 return ((you.has_usable_fangs()
1125 || player_mutation_level(MUT_ACIDIC_BITE))
1126 && one_chance_in(5));
1128 default:
1129 return (false);
1133 unarmed_attack_type melee_attack::player_aux_choose_baseattack()
1135 unarmed_attack_type baseattack = static_cast<unarmed_attack_type>
1136 (random_choose(UNAT_HEADBUTT, UNAT_KICK, UNAT_PUNCH, UNAT_NO_ATTACK,
1137 -1));
1139 // No punching with a shield or 2-handed wpn, except staves.
1140 if (baseattack == UNAT_PUNCH && !you.has_usable_offhand())
1141 baseattack = UNAT_NO_ATTACK;
1143 if (you.species == SP_NAGA && baseattack == UNAT_KICK)
1144 baseattack = UNAT_HEADBUTT;
1146 if (you.has_usable_tail()
1147 && (baseattack == UNAT_HEADBUTT || baseattack == UNAT_KICK)
1148 && one_chance_in(3))
1150 baseattack = UNAT_TAILSLAP;
1153 if (you.has_usable_pseudopods()
1154 && baseattack == UNAT_KICK && coinflip())
1156 baseattack = UNAT_PSEUDOPODS;
1159 // With fangs, replace head attacks with bites.
1160 if ((you.has_usable_fangs() || player_mutation_level(MUT_ACIDIC_BITE))
1161 && (baseattack == UNAT_HEADBUTT
1162 || baseattack == UNAT_KICK
1163 || _vamp_wants_blood_from_monster(defender->as_monster())
1164 && !one_chance_in(3)))
1166 baseattack = UNAT_BITE;
1169 if (_tran_forbid_aux_attack(baseattack))
1170 baseattack = UNAT_NO_ATTACK;
1172 return (baseattack);
1175 bool melee_attack::player_aux_test_hit()
1177 // XXX We're clobbering did_hit
1178 did_hit = false;
1180 const int evasion = defender->melee_evasion(attacker);
1181 const int helpful_evasion =
1182 defender->melee_evasion(attacker, EV_IGNORE_HELPLESS);
1184 if (you.religion != GOD_ELYVILON
1185 && you.penance[GOD_ELYVILON]
1186 && god_hates_your_god(GOD_ELYVILON, you.religion)
1187 && to_hit >= evasion
1188 && one_chance_in(20))
1190 simple_god_message(" blocks your attack.", GOD_ELYVILON);
1191 dec_penance(GOD_ELYVILON, 1 + random2(to_hit - evasion));
1192 return (false);
1195 bool auto_hit = one_chance_in(30);
1197 if (!auto_hit && to_hit >= evasion && helpful_evasion > evasion
1198 && defender_visible)
1200 defender->as_monster()->add_ench(ENCH_HELPLESS);
1203 if (to_hit >= evasion || auto_hit)
1204 return (true);
1206 const int phaseless_evasion =
1207 defender->melee_evasion(attacker, EV_IGNORE_PHASESHIFT);
1209 if (to_hit >= phaseless_evasion && defender_visible)
1211 mprf("Your %s passes through %s as %s momentarily phases out.",
1212 aux_attack.c_str(),
1213 defender->name(DESC_NOCAP_THE).c_str(),
1214 defender->pronoun(PRONOUN_NOCAP).c_str());
1216 else
1218 mprf("Your %s misses %s.", aux_attack.c_str(),
1219 defender->name(DESC_NOCAP_THE).c_str());
1222 return (false);
1225 // Returns true to end the attack round.
1226 bool melee_attack::player_aux_unarmed()
1228 unwind_var<int> save_brand(damage_brand);
1231 * baseattack is the auxiliary unarmed attack the player gets
1232 * for unarmed combat skill. Note that this can still be skipped,
1233 * e.g. UNAT_PUNCH with a shield.
1235 * Then, they can get extra attacks depending on mutations,
1236 * through _extra_aux_attack().
1238 unarmed_attack_type baseattack = UNAT_NO_ATTACK;
1239 if (can_do_unarmed)
1240 baseattack = player_aux_choose_baseattack();
1242 for (int i = UNAT_FIRST_ATTACK; i <= UNAT_LAST_ATTACK; ++i)
1244 if (!defender->alive())
1245 break;
1247 unarmed_attack_type atk = static_cast<unarmed_attack_type>(i);
1249 if (baseattack != atk && !_extra_aux_attack(atk))
1250 continue;
1252 // Determine and set damage and attack words.
1253 player_aux_setup(atk);
1255 to_hit = random2(calc_your_to_hit_unarmed(atk,
1256 damage_brand == SPWPN_VAMPIRICISM));
1258 make_hungry(2, true);
1260 handle_noise(defender->pos());
1261 alert_nearby_monsters();
1263 // [ds] kraken can flee when near death, causing the tentacle
1264 // the player was beating up to "die" and no longer be
1265 // available to answer questions beyond this point.
1266 // handle_noise stirs up all nearby monsters with a stick, so
1267 // the player may be beating up a tentacle, but the main body
1268 // of the kraken still gets a chance to act and submerge
1269 // tentacles before we get here.
1270 if (!defender->alive())
1271 return (true);
1273 if (player_aux_test_hit())
1275 // Upset the monster.
1276 behaviour_event(defender->as_monster(), ME_WHACK, MHITYOU);
1277 if (!defender->alive())
1278 return (true);
1280 if (attack_shield_blocked(true))
1281 continue;
1282 if (player_aux_apply(atk))
1283 return (true);
1287 return (false);
1290 bool melee_attack::player_aux_apply(unarmed_attack_type atk)
1292 did_hit = true;
1294 aux_damage = player_aux_stat_modify_damage(aux_damage);
1295 aux_damage += slaying_bonus(PWPN_DAMAGE);
1297 aux_damage = random2(aux_damage);
1299 aux_damage = player_apply_fighting_skill(aux_damage, true);
1300 aux_damage = player_apply_misc_modifiers(aux_damage);
1302 // Clear stab bonus which will be set for the primary weapon attack.
1303 stab_bonus = 0;
1305 const int pre_ac_dmg = aux_damage;
1306 const int post_ac_dmg = player_apply_monster_ac(aux_damage);
1308 aux_damage = post_ac_dmg;
1309 aux_damage = defender->hurt(&you, aux_damage, BEAM_MISSILE, false);
1310 damage_done = aux_damage;
1312 switch(atk)
1314 case UNAT_PUNCH:
1315 apply_bleeding = true;
1316 break;
1318 case UNAT_HEADBUTT:
1320 const int horns = player_mutation_level(MUT_HORNS);
1321 const int stun = bestroll(std::min(damage_done, 7), 1 + horns);
1323 defender->as_monster()->speed_increment -= stun;
1324 break;
1327 case UNAT_KICK:
1329 const int hooves = player_mutation_level(MUT_HOOVES);
1331 if (hooves && pre_ac_dmg > post_ac_dmg)
1333 const int dmg = bestroll(pre_ac_dmg - post_ac_dmg, hooves);
1334 // do some of the previously ignored damage in extra-damage
1335 damage_done += defender->hurt(&you, dmg, BEAM_MISSILE, false);
1338 break;
1341 default:
1342 break;
1345 if (damage_done > 0)
1347 // Clobber wpn_skill.
1348 wpn_skill = SK_UNARMED_COMBAT;
1349 player_exercise_combat_skills();
1351 player_announce_aux_hit();
1353 if (damage_brand == SPWPN_ACID)
1355 mprf("%s is splashed with acid.",
1356 defender->name(DESC_CAP_THE).c_str());
1357 corrode_monster(defender->as_monster());
1360 if (damage_brand == SPWPN_VENOM && coinflip())
1361 poison_monster(defender->as_monster(), KC_YOU);
1363 // Normal vampiric biting attack, not if already got stabbing special.
1364 if (damage_brand == SPWPN_VAMPIRICISM && you.species == SP_VAMPIRE
1365 && (!stab_attempt || stab_bonus <= 0))
1367 _player_vampire_draws_blood(defender->as_monster(), damage_done);
1370 if (atk == UNAT_TAILSLAP && you.species == SP_GREY_DRACONIAN
1371 && grd(you.pos()) == DNGN_DEEP_WATER
1372 && feat_is_water(grd(defender->as_monster()->pos())))
1374 do_trample();
1377 else // no damage was done
1379 mprf("You %s %s%s.",
1380 aux_verb.c_str(),
1381 defender->name(DESC_NOCAP_THE).c_str(),
1382 you.can_see(defender) ? ", but do no damage" : "");
1384 defender->as_monster()->del_ench(ENCH_HELPLESS);
1386 if (defender->as_monster()->hit_points < 1)
1388 _monster_die(defender->as_monster(), KILL_YOU, NON_MONSTER);
1390 return (true);
1393 return (false);
1396 std::string melee_attack::debug_damage_number()
1398 #ifdef DEBUG_DIAGNOSTICS
1399 return make_stringf(" for %d", damage_done);
1400 #else
1401 return ("");
1402 #endif
1405 std::string melee_attack::special_attack_punctuation()
1407 if (special_damage < 6)
1408 return ".";
1409 else
1410 return "!";
1413 std::string melee_attack::attack_strength_punctuation()
1415 if (attacker->atype() == ACT_PLAYER)
1417 if (damage_done < HIT_WEAK)
1418 return ".";
1419 else if (damage_done < HIT_MED)
1420 return "!";
1421 else if (damage_done < HIT_STRONG)
1422 return "!!";
1423 else
1424 return "!!!";
1426 else
1427 return (damage_done < HIT_WEAK ? "." : "!");
1430 std::string melee_attack::evasion_margin_adverb()
1432 return (ev_margin <= -20) ? " completely" :
1433 (ev_margin <= -12) ? "" :
1434 (ev_margin <= -6) ? " closely"
1435 : " barely";
1438 void melee_attack::player_announce_aux_hit()
1440 mprf("You %s %s%s%s",
1441 aux_verb.c_str(),
1442 defender->name(DESC_NOCAP_THE).c_str(),
1443 debug_damage_number().c_str(),
1444 attack_strength_punctuation().c_str());
1447 void melee_attack::player_announce_hit()
1449 if (!verb_degree.empty() && verb_degree[0] != ' ')
1450 verb_degree = " " + verb_degree;
1452 msg::stream << "You " << attack_verb << ' '
1453 << defender->name(DESC_NOCAP_THE)
1454 << verb_degree << debug_damage_number()
1455 << attack_strength_punctuation()
1456 << std::endl;
1459 std::string melee_attack::player_why_missed()
1461 const int ev = defender->melee_evasion(attacker);
1462 const int combined_penalty =
1463 player_armour_tohit_penalty + player_shield_tohit_penalty;
1464 if (to_hit < ev && to_hit + combined_penalty >= ev)
1466 const bool armour_miss =
1467 (player_armour_tohit_penalty
1468 && to_hit + player_armour_tohit_penalty >= ev);
1469 const bool shield_miss =
1470 (player_shield_tohit_penalty
1471 && to_hit + player_shield_tohit_penalty >= ev);
1473 const item_def *armour = you.slot_item(EQ_BODY_ARMOUR, false);
1474 const std::string armour_name =
1475 (armour? armour->name(DESC_BASENAME) : std::string("armour"));
1477 if (armour_miss && !shield_miss)
1478 return "Your " + armour_name + " prevents you from hitting ";
1479 else if (shield_miss && !armour_miss)
1480 return "Your shield prevents you from hitting ";
1481 else
1482 return ("Your shield and " + armour_name
1483 + " prevent you from hitting ");
1486 return ("You" + evasion_margin_adverb() + " miss ");
1489 void melee_attack::player_warn_miss()
1491 did_hit = false;
1492 // Upset only non-sleeping monsters if we missed.
1493 if (!defender->asleep())
1494 behaviour_event(defender->as_monster(), ME_WHACK, MHITYOU);
1496 msg::stream << player_why_missed()
1497 << defender->name(DESC_NOCAP_THE)
1498 << "."
1499 << std::endl;
1502 int melee_attack::player_hits_monster()
1504 const int evasion = defender->melee_evasion(attacker);
1505 const int helpful_evasion =
1506 defender->melee_evasion(attacker, EV_IGNORE_HELPLESS);
1507 dprf("your to-hit: %d; defender effective EV: %d", to_hit, evasion);
1509 if (you.religion != GOD_ELYVILON
1510 && you.penance[GOD_ELYVILON]
1511 && god_hates_your_god(GOD_ELYVILON, you.religion)
1512 && to_hit >= evasion
1513 && one_chance_in(20))
1515 simple_god_message(" blocks your attack.", GOD_ELYVILON);
1516 dec_penance(GOD_ELYVILON, 1 + random2(to_hit - evasion));
1517 return (false);
1520 if (to_hit >= evasion && helpful_evasion > evasion
1521 || ((defender->cannot_act() || defender->asleep())
1522 && !one_chance_in(10 + you.skill(SK_STABBING)))
1523 || defender->as_monster()->petrifying()
1524 && !one_chance_in(2 + you.skill(SK_STABBING)))
1526 defender->as_monster()->add_ench(ENCH_HELPLESS);
1527 return (1);
1530 if (to_hit >= evasion || one_chance_in(20))
1531 return (1);
1533 const int phaseless_evasion =
1534 defender->melee_evasion(attacker, EV_IGNORE_PHASESHIFT);
1536 if (to_hit >= phaseless_evasion && defender_visible)
1537 msg::stream << "Your attack passes through "
1538 << defender->name(DESC_NOCAP_THE) << " as "
1539 << defender->pronoun(PRONOUN_NOCAP)
1540 << " momentarily phases out." << std::endl;
1542 return (to_hit - helpful_evasion);
1545 int melee_attack::player_stat_modify_damage(int damage)
1547 int dammod = 78;
1548 const int dam_stat_val = calc_stat_to_dam_base();
1550 if (dam_stat_val > 11)
1551 dammod += (random2(dam_stat_val - 11) * 2);
1552 else if (dam_stat_val < 9)
1553 dammod -= (random2(9 - dam_stat_val) * 3);
1555 damage *= dammod;
1556 damage /= 78;
1558 return (damage);
1561 int melee_attack::player_aux_stat_modify_damage(int damage)
1563 int dammod = 10;
1564 const int dam_stat_val = calc_stat_to_dam_base();
1566 if (dam_stat_val > 11)
1567 dammod += random2(dam_stat_val - 11) / 3;
1568 else if (dam_stat_val < 9)
1569 dammod -= random2(9 - dam_stat_val) / 2;
1571 damage *= dammod;
1572 damage /= 10;
1574 return (damage);
1577 int melee_attack::player_apply_weapon_skill(int damage)
1579 if (weapon && (weapon->base_type == OBJ_WEAPONS
1580 || weapon->base_type == OBJ_STAVES))
1582 damage *= 25 + (random2(you.skill(wpn_skill) + 1));
1583 damage /= 25;
1586 return (damage);
1589 int melee_attack::player_apply_fighting_skill(int damage, bool aux)
1591 const int base = aux? 40 : 30;
1593 damage *= base + (random2(you.skill(SK_FIGHTING) + 1));
1594 damage /= base;
1596 return (damage);
1599 int melee_attack::player_apply_misc_modifiers(int damage)
1601 if (you.duration[DUR_MIGHT] || you.duration[DUR_BERSERK])
1602 damage += 1 + random2(10);
1604 if (you.species != SP_VAMPIRE && you.hunger_state == HS_STARVING)
1605 damage -= random2(5);
1607 return (damage);
1610 int melee_attack::player_apply_weapon_bonuses(int damage)
1612 if (weapon && (weapon->base_type == OBJ_WEAPONS
1613 || item_is_rod(*weapon)))
1615 int wpn_damage_plus = weapon->plus2;
1617 if (item_is_rod(*weapon))
1618 wpn_damage_plus = (short)weapon->props["rod_enchantment"];
1620 damage += (wpn_damage_plus > -1) ? (random2(1 + wpn_damage_plus))
1621 : -(1 + random2(-wpn_damage_plus));
1623 if (get_equip_race(*weapon) == ISFLAG_DWARVEN
1624 && player_genus(GENPC_DWARVEN))
1626 damage += random2(3);
1629 if (get_equip_race(*weapon) == ISFLAG_ORCISH
1630 && you.species == SP_HILL_ORC)
1632 if (you.religion == GOD_BEOGH && !player_under_penance())
1634 #ifdef DEBUG_DIAGNOSTICS
1635 const int orig_damage = damage;
1636 #endif
1637 if (you.piety > 80 || coinflip())
1638 damage++;
1640 damage +=
1641 random2avg(
1642 div_rand_round(
1643 std::min(static_cast<int>(you.piety), 180), 33), 2);
1644 #ifdef DEBUG_DIAGNOSTICS
1645 mprf(MSGCH_DIAGNOSTICS, "Damage: %d -> %d, Beogh bonus: %d",
1646 orig_damage, damage, damage - orig_damage);
1647 #endif
1650 if (coinflip())
1651 damage++;
1654 // Demonspawn get a damage bonus for demonic weapons.
1655 if (you.species == SP_DEMONSPAWN && is_demonic(*weapon))
1656 damage += random2(3);
1659 return (damage);
1662 void melee_attack::player_weapon_auto_id()
1664 if (weapon
1665 && weapon->base_type == OBJ_WEAPONS
1666 && !is_range_weapon(*weapon)
1667 && !item_ident(*weapon, ISFLAG_KNOW_PLUSES)
1668 && x_chance_in_y(you.skill(wpn_skill), 100))
1670 set_ident_flags(*weapon, ISFLAG_KNOW_PLUSES);
1671 mprf("You are wielding %s.", weapon->name(DESC_NOCAP_A).c_str());
1672 more();
1673 you.wield_change = true;
1677 int melee_attack::player_stab_weapon_bonus(int damage)
1679 if (weapon && weapon->base_type == OBJ_WEAPONS
1680 && (weapon->sub_type == WPN_CLUB
1681 || weapon->sub_type == WPN_SPEAR
1682 || weapon->sub_type == WPN_TRIDENT
1683 || weapon->sub_type == WPN_DEMON_TRIDENT
1684 || weapon->sub_type == WPN_TRISHULA))
1686 goto ok_weaps;
1689 switch (wpn_skill)
1691 case SK_SHORT_BLADES:
1693 int bonus = (you.dex() * (you.skill(SK_STABBING) + 1)) / 5;
1695 if (weapon->sub_type != WPN_DAGGER)
1696 bonus /= 2;
1698 bonus = stepdown_value(bonus, 10, 10, 30, 30);
1699 damage += bonus;
1701 // fall through
1702 ok_weaps:
1703 case SK_LONG_BLADES:
1704 damage *= 10 + you.skill(SK_STABBING) /
1705 (stab_bonus + (wpn_skill == SK_SHORT_BLADES ? 0 : 2));
1706 damage /= 10;
1707 // fall through
1708 default:
1709 damage *= 12 + you.skill(SK_STABBING) / stab_bonus;
1710 damage /= 12;
1713 return (damage);
1716 int melee_attack::player_stab(int damage)
1718 // The stabbing message looks better here:
1719 if (stab_attempt)
1721 // Construct reasonable message.
1722 stab_message(defender, stab_bonus);
1724 practise(EX_WILL_STAB);
1726 did_god_conduct(DID_STABBING, 4);
1728 else
1730 stab_bonus = 0;
1731 // Ok.. if you didn't backstab, you wake up the neighborhood.
1732 // I can live with that.
1733 alert_nearby_monsters();
1736 if (stab_bonus)
1738 // Let's make sure we have some damage to work with...
1739 damage = std::max(1, damage);
1741 if (defender->asleep())
1743 // Sleeping moster wakes up when stabbed but may be groggy.
1744 if (x_chance_in_y(you.skill(SK_STABBING) + you.dex() + 1, 200))
1746 int stun = random2(you.dex() + 1);
1748 if (defender->as_monster()->speed_increment > stun)
1749 defender->as_monster()->speed_increment -= stun;
1750 else
1751 defender->as_monster()->speed_increment = 0;
1755 damage = player_stab_weapon_bonus(damage);
1758 return (damage);
1761 int melee_attack::player_apply_monster_ac(int damage)
1763 if (stab_bonus)
1765 // When stabbing we can get by some of the armour.
1766 if (defender->armour_class() > 0)
1768 const int ac = defender->armour_class()
1769 - random2(you.skill(SK_STABBING) / stab_bonus);
1771 if (ac > 0)
1772 damage -= random2(1 + ac);
1775 else
1777 // Apply AC normally.
1778 if (defender->armour_class() > 0)
1779 damage -= random2(1 + defender->armour_class());
1782 if (defender->petrified())
1783 damage /= 3;
1785 return (damage);
1788 int melee_attack::player_weapon_type_modify(int damage)
1790 int weap_type = WPN_UNKNOWN;
1792 if (!weapon)
1793 weap_type = WPN_UNARMED;
1794 else if (item_is_staff(*weapon))
1795 weap_type = WPN_QUARTERSTAFF;
1796 else if (item_is_rod(*weapon))
1797 weap_type = WPN_CLUB;
1798 else if (weapon->base_type == OBJ_WEAPONS)
1799 weap_type = weapon->sub_type;
1801 // All weak hits look the same, except for when the player
1802 // has a non-weapon in hand. - bwr
1803 // Exception: vampire bats only bite to allow for drawing blood.
1804 if (damage < HIT_WEAK
1805 && (you.species != SP_VAMPIRE || !player_in_bat_form())
1806 && you.species != SP_CAT)
1808 if (weap_type != WPN_UNKNOWN)
1809 attack_verb = "hit";
1810 else
1811 attack_verb = "clumsily bash";
1813 return (damage);
1816 // Take transformations into account, if no weapon is wielded.
1817 if (weap_type == WPN_UNARMED
1818 && you.form != TRAN_NONE)
1820 switch (you.form)
1822 case TRAN_SPIDER:
1823 case TRAN_BAT:
1824 case TRAN_PIG:
1825 if (damage < HIT_WEAK)
1826 attack_verb = "hit";
1827 else if (damage < HIT_STRONG)
1828 attack_verb = "bite";
1829 else
1830 attack_verb = "maul";
1831 break;
1832 case TRAN_BLADE_HANDS:
1833 if (damage < HIT_WEAK)
1834 attack_verb = "hit";
1835 else if (damage < HIT_MED)
1836 attack_verb = "slash";
1837 else if (damage < HIT_STRONG)
1838 attack_verb = "slice";
1839 else
1840 attack_verb = "shred";
1841 break;
1842 case TRAN_STATUE:
1843 case TRAN_LICH:
1844 if (you.has_usable_claws())
1846 if (damage < HIT_WEAK)
1847 attack_verb = "scratch";
1848 else if (damage < HIT_MED)
1849 attack_verb = "claw";
1850 else if (damage < HIT_STRONG)
1851 attack_verb = "mangle";
1852 else
1853 attack_verb = "eviscerate";
1854 break;
1856 // or fall-through
1857 case TRAN_ICE_BEAST:
1858 if (damage < HIT_WEAK)
1859 attack_verb = "hit";
1860 else if (damage < HIT_MED)
1861 attack_verb = "punch";
1862 else
1863 attack_verb = "pummel";
1864 break;
1865 case TRAN_DRAGON:
1866 if (damage < HIT_WEAK)
1867 attack_verb = "hit";
1868 else if (damage < HIT_MED)
1869 attack_verb = "claw";
1870 else if (damage < HIT_STRONG)
1871 attack_verb = "bite";
1872 else
1874 attack_verb = "maul";
1875 if (coinflip())
1876 attack_verb = "trample";
1878 break;
1879 case TRAN_NONE:
1880 break;
1881 } // transformations
1883 return (damage);
1886 // Take normal hits into account. If the hit is from a weapon with
1887 // more than one damage type, randomly choose one damage type from
1888 // it.
1889 switch (weapon ? single_damage_type(*weapon) : -1)
1891 case DAM_PIERCE:
1892 if (damage < HIT_MED)
1893 attack_verb = "puncture";
1894 else if (damage < HIT_STRONG)
1895 attack_verb = "impale";
1896 else
1898 attack_verb = "spit";
1899 if (defender->atype() == ACT_MONSTER
1900 && defender_visible
1901 && mons_genus(defender->as_monster()->type) == MONS_HOG)
1903 verb_degree = " like the proverbial pig";
1905 else
1906 verb_degree = " like a pig";
1908 break;
1910 case DAM_SLICE:
1911 if (damage < HIT_MED)
1912 attack_verb = "slash";
1913 else if (damage < HIT_STRONG)
1914 attack_verb = "slice";
1915 else if (mons_genus(defender->as_monster()->type) == MONS_OGRE)
1917 attack_verb = "dice";
1918 verb_degree = " like an onion";
1920 else
1922 attack_verb = "open";
1923 verb_degree = " like a pillowcase";
1925 break;
1927 case DAM_BLUDGEON:
1928 if (damage < HIT_MED)
1929 attack_verb = one_chance_in(4) ? "thump" : "sock";
1930 else if (damage < HIT_STRONG)
1931 attack_verb = "bludgeon";
1932 else
1934 attack_verb = "crush";
1935 verb_degree = " like a grape";
1937 break;
1939 case DAM_WHIP:
1940 if (damage < HIT_MED)
1941 attack_verb = "whack";
1942 else if (damage < HIT_STRONG)
1943 attack_verb = "thrash";
1944 else
1946 switch(defender->holiness())
1948 case MH_HOLY:
1949 case MH_NATURAL:
1950 case MH_DEMONIC:
1951 attack_verb = "punish";
1952 verb_degree = " causing immense pain";
1953 break;
1954 default:
1955 attack_verb = "devastate";
1958 break;
1960 case -1: // unarmed
1961 if (you.damage_type() == DVORP_CLAWING)
1963 if (damage < HIT_WEAK)
1964 attack_verb = "scratch";
1965 else if (damage < HIT_MED)
1966 attack_verb = "claw";
1967 else if (damage < HIT_STRONG)
1968 attack_verb = "mangle";
1969 else
1970 attack_verb = "eviscerate";
1972 else
1974 if (damage < HIT_MED)
1975 attack_verb = "punch";
1976 else
1977 attack_verb = "pummel";
1979 break;
1981 case WPN_UNKNOWN:
1982 default:
1983 attack_verb = "hit";
1984 break;
1987 return (damage);
1990 void melee_attack::player_exercise_combat_skills()
1992 const bool helpless = defender->cannot_fight();
1993 practise(helpless ? EX_WILL_HIT_HELPLESS : EX_WILL_HIT,
1994 wpn_skill);
1997 void melee_attack::player_check_weapon_effects()
1999 if (weapon && weapon->base_type == OBJ_WEAPONS)
2001 if (is_holy_item(*weapon))
2002 did_god_conduct(DID_HOLY, 1);
2003 else if (is_demonic(*weapon))
2004 did_god_conduct(DID_UNHOLY, 1);
2005 else if (get_weapon_brand(*weapon) == SPWPN_SPEED
2006 || weapon->sub_type == WPN_QUICK_BLADE)
2008 did_god_conduct(DID_HASTY, 1);
2013 // Returns true if the combat round should end here.
2014 bool melee_attack::player_monattk_hit_effects(bool mondied)
2016 player_check_weapon_effects();
2018 mondied = check_unrand_effects(mondied) || mondied;
2020 // Thirsty vampires will try to use a stabbing situation to draw blood.
2021 if (you.species == SP_VAMPIRE && you.hunger_state < HS_SATIATED
2022 && mondied && stab_attempt && stab_bonus > 0
2023 && _player_vampire_draws_blood(defender->as_monster(),
2024 damage_done, true))
2026 // No further effects.
2028 else if (you.species == SP_VAMPIRE
2029 && damage_brand == SPWPN_VAMPIRICISM
2030 && you.weapon()
2031 && _player_vampire_draws_blood(defender->as_monster(),
2032 damage_done, false,
2033 (mondied ? 1 : 10)))
2035 // No further effects.
2037 // Vampiric *weapon* effects for the killing blow.
2038 else if (mondied && damage_brand == SPWPN_VAMPIRICISM
2039 && you.weapon()
2040 && you.species != SP_VAMPIRE) // vampires get their bonus elsewhere
2042 if (defender->holiness() == MH_NATURAL
2043 && !defender->is_summoned()
2044 && damage_done > 0
2045 && you.hp < you.hp_max
2046 && !one_chance_in(5)
2047 && !you.duration[DUR_DEATHS_DOOR])
2049 mpr("You feel better.");
2051 // More than if not killed.
2052 const int heal = 1 + random2(damage_done);
2054 dprf("Vampiric healing: damage %d, healed %d",
2055 damage_done, heal);
2056 inc_hp(heal, false);
2058 if (you.hunger_state != HS_ENGORGED)
2059 lessen_hunger(30 + random2avg(59, 2), false);
2061 did_god_conduct(DID_NECROMANCY, 2);
2065 if (mondied)
2066 return (true);
2068 // These effects apply only to monsters that are still alive:
2070 // Returns true if a head was cut off *and* the wound was cauterized,
2071 // in which case the cauterization was the ego effect, so don't burn
2072 // the hydra some more.
2074 // Also returns true if the hydra's last head was cut off, in which
2075 // case nothing more should be done to the hydra.
2076 if (decapitate_hydra(damage_done))
2077 return (!defender->alive());
2079 // These two (staff damage and damage brand) are mutually exclusive!
2080 player_apply_staff_damage();
2082 // Returns true if the monster croaked.
2083 if (!special_damage && apply_damage_brand())
2084 return (true);
2086 if (!no_damage_message.empty())
2088 if (special_damage > 0)
2089 emit_nodmg_hit_message();
2090 else
2092 mprf("You %s %s, but do no damage.",
2093 attack_verb.c_str(),
2094 defender->name(DESC_NOCAP_THE).c_str());
2098 if (needs_message && !special_damage_message.empty())
2100 mprf("%s", special_damage_message.c_str());
2101 // Don't do a message-only miscast right after a special damage.
2102 if (miscast_level == 0)
2103 miscast_level = -1;
2106 #ifdef DEBUG_DIAGNOSTICS
2107 mprf(MSGCH_DIAGNOSTICS, "Special damage to %s: %d, flavour: %d",
2108 defender->name(DESC_NOCAP_THE).c_str(),
2109 special_damage, special_damage_flavour);
2110 #endif
2112 special_damage = defender->hurt(&you, special_damage, special_damage_flavour, false);
2114 if (!defender->alive())
2116 _monster_die(defender->as_monster(), KILL_YOU, NON_MONSTER);
2118 return (true);
2121 if (stab_attempt && stab_bonus > 0 && weapon
2122 && weapon->base_type == OBJ_WEAPONS && weapon->sub_type == WPN_CLUB
2123 && damage_done + special_damage > random2(defender->get_experience_level())
2124 && !defender->as_monster()->has_ench(ENCH_CONFUSION)
2125 && mons_class_is_confusable(defender->type))
2127 if (defender->as_monster()->add_ench(mon_enchant(ENCH_CONFUSION, 0,
2128 KC_YOU, 20+random2(30)))) // 1-3 turns
2130 mprf("%s is stunned!", defender->name(DESC_CAP_THE).c_str());
2134 return (false);
2137 void melee_attack::_monster_die(monster* mons, killer_type killer,
2138 int killer_index)
2140 if (invalid_monster(mons))
2141 return; // Already died some other way.
2143 const bool chaos = (damage_brand == SPWPN_CHAOS);
2144 const bool reaping = (damage_brand == SPWPN_REAPING);
2146 // Copy defender before it gets reset by monster_die().
2147 monster* def_copy = NULL;
2148 if (chaos || reaping)
2149 def_copy = new monster(*mons);
2151 int corpse = monster_die(mons, killer, killer_index);
2153 if (chaos)
2155 chaos_killed_defender(def_copy);
2156 delete def_copy;
2158 else if (reaping)
2160 if (corpse != -1)
2161 mons_reaped(attacker, def_copy);
2162 delete def_copy;
2166 static bool is_boolean_resist(beam_type flavour)
2168 switch (flavour)
2170 case BEAM_ELECTRICITY:
2171 case BEAM_MIASMA: // rotting
2172 case BEAM_NAPALM:
2173 case BEAM_WATER: // water asphyxiation damage,
2174 // bypassed by being water inhabitant.
2175 return (true);
2176 default:
2177 return (false);
2181 // Gets the percentage of the total damage of this damage flavour that can
2182 // be resisted.
2183 static inline int get_resistible_fraction(beam_type flavour)
2185 switch (flavour)
2187 // Drowning damage from water is resistible by being a water thing, or
2188 // otherwise asphyx resistant.
2189 case BEAM_WATER:
2190 return (40);
2192 // Assume ice storm and throw icicle are mostly solid.
2193 case BEAM_ICE:
2194 return (25);
2196 case BEAM_LAVA:
2197 return (35);
2199 case BEAM_POISON_ARROW:
2200 return (70);
2202 default:
2203 return (100);
2207 // Adjusts damage for elemental resists, electricity and poison.
2209 // FIXME: Does not (yet) handle life draining, player acid damage
2210 // (does handle monster acid damage), miasma, and other exotic
2211 // attacks.
2213 // beam_type is just used to determine the damage flavour, it does not
2214 // necessarily imply that the attack is a beam attack.
2215 int resist_adjust_damage(actor *defender, beam_type flavour,
2216 int res, int rawdamage, bool ranged)
2218 if (!res)
2219 return (rawdamage);
2221 const bool mons = (defender->atype() == ACT_MONSTER);
2223 const int resistible_fraction = get_resistible_fraction(flavour);
2225 int resistible = rawdamage * resistible_fraction / 100;
2226 const int irresistible = rawdamage - resistible;
2228 if (res > 0)
2230 if (mons && res >= 3)
2231 resistible = 0;
2232 else
2234 // Check if this is a resist that pretends to be boolean for
2235 // damage purposes. Only electricity, miasma and sticky
2236 // flame (napalm) do this at the moment; raw poison damage
2237 // uses the normal formula.
2238 const int bonus_res = (is_boolean_resist(flavour) ? 1 : 0);
2240 // Use a new formula for players, but keep the old, more
2241 // effective one for monsters.
2242 if (mons)
2243 resistible /= 1 + bonus_res + res * res;
2244 else
2245 resistible /= resist_fraction(res, bonus_res);
2248 else if (res < 0)
2249 resistible = resistible * (ranged? 15 : 20) / 10;
2251 return std::max(resistible + irresistible, 0);
2254 void melee_attack::calc_elemental_brand_damage(beam_type flavour,
2255 int res,
2256 const char *verb)
2258 special_damage = resist_adjust_damage(defender, flavour, res,
2259 random2(damage_done) / 2 + 1);
2261 if (needs_message && special_damage > 0 && verb)
2263 special_damage_message = make_stringf(
2264 "%s %s %s%s",
2265 atk_name(DESC_CAP_THE).c_str(),
2266 attacker->conj_verb(verb).c_str(),
2267 mons_defender_name().c_str(),
2268 special_attack_punctuation().c_str());
2272 int melee_attack::fire_res_apply_cerebov_downgrade(int res)
2274 if (weapon && weapon->special == UNRAND_CEREBOV)
2275 --res;
2277 return (res);
2280 void melee_attack::drain_defender()
2282 if (defender->atype() == ACT_MONSTER && one_chance_in(3))
2283 return;
2285 special_damage = 1 + random2(damage_done)
2286 / (2 + defender->res_negative_energy());
2288 if (defender->drain_exp(attacker, true))
2290 if (defender->atype() == ACT_PLAYER)
2291 obvious_effect = true;
2292 else if (defender_visible)
2294 special_damage_message =
2295 make_stringf(
2296 "%s %s %s!",
2297 atk_name(DESC_CAP_THE).c_str(),
2298 attacker->conj_verb("drain").c_str(),
2299 mons_defender_name().c_str());
2302 attacker->god_conduct(DID_NECROMANCY, 2);
2306 void melee_attack::rot_defender(int amount, int immediate)
2308 if (defender->rot(attacker, amount, immediate, true))
2310 if (defender->atype() == ACT_MONSTER && defender_visible)
2312 special_damage_message =
2313 make_stringf(
2314 "%s %s!",
2315 def_name(DESC_CAP_THE).c_str(),
2316 amount > 0 ? "rots" : "looks less resilient");
2321 bool melee_attack::distortion_affects_defender()
2323 //jmf: blink frogs *like* distortion
2324 // I think could be amended to let blink frogs "grow" like
2325 // jellies do {dlb}
2326 if (defender->atype() == ACT_MONSTER
2327 && mons_genus(defender->as_monster()->type) == MONS_BLINK_FROG)
2329 if (one_chance_in(5))
2331 emit_nodmg_hit_message();
2332 if (defender_visible)
2334 special_damage_message =
2335 make_stringf("%s %s in the distortional energy.",
2336 def_name(DESC_CAP_THE).c_str(),
2337 defender->conj_verb("bask").c_str());
2340 defender->heal(1 + random2avg(7, 2), true); // heh heh
2342 return (false);
2345 if (one_chance_in(3))
2347 if (defender_visible)
2349 special_damage_message =
2350 make_stringf(
2351 "Space bends around %s.",
2352 def_name(DESC_NOCAP_THE).c_str());
2354 special_damage += 1 + random2avg(7, 2);
2355 return (false);
2358 if (one_chance_in(3))
2360 if (defender_visible)
2362 special_damage_message =
2363 make_stringf(
2364 "Space warps horribly around %s!",
2365 def_name(DESC_NOCAP_THE).c_str());
2367 special_damage += 3 + random2avg(24, 2);
2368 return (false);
2371 if (one_chance_in(3))
2373 emit_nodmg_hit_message();
2374 if (defender_visible)
2375 obvious_effect = true;
2376 defender->blink();
2377 return (false);
2380 // Used to be coinflip() || coinflip() for players, just coinflip()
2381 // for monsters; this is a compromise. Note that it makes banishment
2382 // a touch more likely for players, and a shade less likely for
2383 // monsters.
2384 if (!one_chance_in(3))
2386 emit_nodmg_hit_message();
2387 if (defender_visible)
2388 obvious_effect = true;
2389 defender->teleport(coinflip(), one_chance_in(5));
2390 return (false);
2393 if (you.level_type != LEVEL_ABYSS && coinflip())
2395 emit_nodmg_hit_message();
2397 if (defender->atype() == ACT_PLAYER && attacker_visible
2398 && weapon != NULL && !is_unrandom_artefact(*weapon)
2399 && !is_special_unrandom_artefact(*weapon))
2401 // If the player is being sent to the Abyss by being attacked
2402 // with a distortion weapon, then we have to ID it before
2403 // the player goes to Abyss, while the weapon object is
2404 // still in memory.
2405 if (is_artefact(*weapon))
2406 artefact_wpn_learn_prop(*weapon, ARTP_BRAND);
2407 else
2408 set_ident_flags(*weapon, ISFLAG_KNOW_TYPE);
2410 else if (defender_visible)
2411 obvious_effect = true;
2413 defender->banish(attacker->name(DESC_PLAIN, true));
2414 return (true);
2417 return (false);
2420 enum chaos_type
2422 CHAOS_CLONE,
2423 CHAOS_POLY,
2424 CHAOS_POLY_UP,
2425 CHAOS_MAKE_SHIFTER,
2426 CHAOS_MISCAST,
2427 CHAOS_RAGE,
2428 CHAOS_HEAL,
2429 CHAOS_HASTE,
2430 CHAOS_INVIS,
2431 CHAOS_SLOW,
2432 CHAOS_PARALYSIS,
2433 CHAOS_PETRIFY,
2434 NUM_CHAOS_TYPES
2437 // XXX: We might want to vary the probabilities for the various effects
2438 // based on whether the source is a weapon of chaos or a monster with
2439 // AF_CHAOS.
2440 void melee_attack::chaos_affects_defender()
2442 const bool mon = defender->atype() == ACT_MONSTER;
2443 const bool immune = mon && mons_immune_magic(defender->as_monster());
2444 const bool is_natural = mon && defender->holiness() == MH_NATURAL;
2445 const bool is_shifter = mon && defender->as_monster()->is_shapeshifter();
2446 const bool can_clone = mon && defender->is_holy()
2447 && mons_clonable(defender->as_monster(), true);
2448 const bool can_poly = is_shifter || (defender->can_safely_mutate()
2449 && !immune);
2450 const bool can_rage = defender->can_go_berserk();
2451 const bool can_slow = !mon || !mons_is_firewood(defender->as_monster());
2453 int clone_chance = can_clone ? 1 : 0;
2454 int poly_chance = can_poly ? 1 : 0;
2455 int poly_up_chance = can_poly && mon ? 1 : 0;
2456 int shifter_chance = can_poly && is_natural && mon ? 1 : 0;
2457 int rage_chance = can_rage ? 10 : 0;
2458 int miscast_chance = 10;
2459 int slowpara_chance= can_slow ? 10 : 0;
2461 // Already a shifter?
2462 if (is_shifter)
2463 shifter_chance = 0;
2465 // A chaos self-attack increases the chance of certain effects,
2466 // due to a short-circuit/feedback/resonance/whatever.
2467 if (attacker == defender)
2469 clone_chance *= 2;
2470 poly_chance *= 2;
2471 poly_up_chance *= 2;
2472 shifter_chance *= 2;
2473 miscast_chance *= 2;
2475 // Inform player that something is up.
2476 if (!skip_chaos_message && you.see_cell(defender->pos()))
2478 if (defender->atype() == ACT_PLAYER)
2479 mpr("You give off a flash of multicoloured light!");
2480 else if (you.can_see(defender))
2482 simple_monster_message(defender->as_monster(),
2483 " gives off a flash of "
2484 "multicoloured light!");
2486 else
2487 mpr("There is a flash of multicoloured light!");
2491 // NOTE: Must appear in exact same order as in chaos_type enumeration.
2492 int probs[NUM_CHAOS_TYPES] =
2494 clone_chance, // CHAOS_CLONE
2495 poly_chance, // CHAOS_POLY
2496 poly_up_chance, // CHAOS_POLY_UP
2497 shifter_chance, // CHAOS_MAKE_SHIFTER
2498 miscast_chance, // CHAOS_MISCAST
2499 rage_chance, // CHAOS_RAGE
2501 10, // CHAOS_HEAL
2502 slowpara_chance,// CHAOS_HASTE
2503 10, // CHAOS_INVIS
2505 slowpara_chance,// CHAOS_SLOW
2506 slowpara_chance,// CHAOS_PARALYSIS
2507 slowpara_chance,// CHAOS_PETRIFY
2510 bolt beam;
2511 beam.flavour = BEAM_NONE;
2513 int choice = choose_random_weighted(probs, probs + NUM_CHAOS_TYPES);
2514 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
2515 std::string chaos_effect = "CHAOS effect: ";
2516 switch (choice)
2518 case CHAOS_CLONE: chaos_effect += "clone"; break;
2519 case CHAOS_POLY: chaos_effect += "polymorph"; break;
2520 case CHAOS_POLY_UP: chaos_effect += "polymorph PPT_MORE"; break;
2521 case CHAOS_MAKE_SHIFTER: chaos_effect += "shifter"; break;
2522 case CHAOS_MISCAST: chaos_effect += "miscast"; break;
2523 case CHAOS_RAGE: chaos_effect += "berserk"; break;
2524 case CHAOS_HEAL: chaos_effect += "healing"; break;
2525 case CHAOS_HASTE: chaos_effect += "hasting"; break;
2526 case CHAOS_INVIS: chaos_effect += "invisible"; break;
2527 case CHAOS_SLOW: chaos_effect += "slowing"; break;
2528 case CHAOS_PARALYSIS: chaos_effect += "paralysis"; break;
2529 case CHAOS_PETRIFY: chaos_effect += "petrify"; break;
2530 default: chaos_effect += "(other)"; break;
2533 take_note(Note(NOTE_MESSAGE, 0, 0, chaos_effect.c_str()), true);
2534 #endif
2536 switch (static_cast<chaos_type>(choice))
2538 case CHAOS_CLONE:
2540 ASSERT(can_clone && clone_chance > 0);
2541 ASSERT(defender->atype() == ACT_MONSTER);
2543 int clone_idx = clone_mons(defender->as_monster(), true,
2544 &obvious_effect);
2545 if (clone_idx != NON_MONSTER)
2547 if (obvious_effect)
2549 special_damage_message =
2550 make_stringf("%s is duplicated!",
2551 def_name(DESC_NOCAP_THE).c_str());
2554 monster& clone(menv[clone_idx]);
2555 // The player shouldn't get new permanent followers from cloning.
2556 if (clone.attitude == ATT_FRIENDLY && !clone.is_summoned())
2557 clone.mark_summoned(6, true, MON_SUMM_CLONE);
2559 // Monsters being cloned is interesting.
2560 xom_is_stimulated(clone.friendly() ? 16 : 32);
2562 break;
2565 case CHAOS_POLY:
2566 ASSERT(can_poly && poly_chance > 0);
2567 beam.flavour = BEAM_POLYMORPH;
2568 break;
2570 case CHAOS_POLY_UP:
2571 ASSERT(can_poly && poly_up_chance > 0);
2572 ASSERT(defender->atype() == ACT_MONSTER);
2574 obvious_effect = you.can_see(defender);
2575 monster_polymorph(defender->as_monster(), RANDOM_MONSTER, PPT_MORE);
2576 break;
2578 case CHAOS_MAKE_SHIFTER:
2580 ASSERT(can_poly && shifter_chance > 0);
2581 ASSERT(!is_shifter);
2582 ASSERT(defender->atype() == ACT_MONSTER);
2584 obvious_effect = you.can_see(defender);
2585 defender->as_monster()->add_ench(one_chance_in(3) ?
2586 ENCH_GLOWING_SHAPESHIFTER : ENCH_SHAPESHIFTER);
2587 // Immediately polymorph monster, just to make the effect obvious.
2588 monster_polymorph(defender->as_monster(), RANDOM_MONSTER);
2590 // Xom loves it if this happens!
2591 const int friend_factor = defender->as_monster()->friendly() ? 1 : 2;
2592 const int glow_factor =
2593 (defender->as_monster()->has_ench(ENCH_SHAPESHIFTER) ? 1 : 2);
2594 xom_is_stimulated(64 * friend_factor * glow_factor);
2595 break;
2597 case CHAOS_MISCAST:
2599 int level = defender->get_experience_level();
2601 // At level == 27 there's a 13.9% chance of a level 3 miscast.
2602 int level0_chance = level;
2603 int level1_chance = std::max(0, level - 7);
2604 int level2_chance = std::max(0, level - 12);
2605 int level3_chance = std::max(0, level - 17);
2607 level = random_choose_weighted(
2608 level0_chance, 0,
2609 level1_chance, 1,
2610 level2_chance, 2,
2611 level3_chance, 3,
2614 miscast_level = level;
2615 miscast_type = SPTYP_RANDOM;
2616 miscast_target = one_chance_in(3) ? attacker : defender;
2617 break;
2620 case CHAOS_RAGE:
2621 ASSERT(can_rage && rage_chance > 0);
2622 defender->go_berserk(false);
2623 obvious_effect = you.can_see(defender);
2624 break;
2626 case CHAOS_HEAL:
2627 beam.flavour = BEAM_HEALING;
2628 break;
2630 case CHAOS_HASTE:
2631 beam.flavour = BEAM_HASTE;
2632 break;
2634 case CHAOS_INVIS:
2635 beam.flavour = BEAM_INVISIBILITY;
2636 break;
2638 case CHAOS_SLOW:
2639 beam.flavour = BEAM_SLOW;
2640 break;
2642 case CHAOS_PARALYSIS:
2643 beam.flavour = BEAM_PARALYSIS;
2644 break;
2646 case CHAOS_PETRIFY:
2647 beam.flavour = BEAM_PETRIFY;
2648 break;
2650 default:
2651 die("Invalid chaos effect type");
2652 break;
2655 if (beam.flavour != BEAM_NONE)
2657 beam.glyph = 0;
2658 beam.range = 0;
2659 beam.colour = BLACK;
2660 beam.effect_known = false;
2662 if (weapon && you.can_see(attacker))
2664 beam.name = wep_name(DESC_NOCAP_YOUR);
2665 beam.item = weapon;
2667 else
2668 beam.name = atk_name(DESC_NOCAP_THE);
2670 beam.thrower =
2671 (attacker->atype() == ACT_PLAYER) ? KILL_YOU
2672 : attacker->as_monster()->confused_by_you() ? KILL_YOU_CONF
2673 : KILL_MON;
2675 if (beam.thrower == KILL_YOU || attacker->as_monster()->friendly())
2676 beam.attitude = ATT_FRIENDLY;
2678 beam.beam_source = attacker->mindex();
2680 beam.source = defender->pos();
2681 beam.target = defender->pos();
2683 beam.damage = dice_def(damage_done + special_damage + aux_damage, 1);
2685 beam.ench_power = beam.damage.num;
2687 beam.fire();
2689 if (you.can_see(defender))
2690 obvious_effect = beam.obvious_effect;
2693 if (!you.can_see(attacker))
2694 obvious_effect = false;
2697 static bool _move_stairs(const actor* attacker, const actor* defender)
2699 const coord_def orig_pos = attacker->pos();
2700 const dungeon_feature_type stair_feat = grd(orig_pos);
2702 if (feat_stair_direction(stair_feat) == CMD_NO_CMD)
2703 return (false);
2705 // The player can't use shops to escape, so don't bother.
2706 if (stair_feat == DNGN_ENTER_SHOP)
2707 return (false);
2709 // Don't move around notable terrain the player is aware of if it's
2710 // out of sight.
2711 if (is_notable_terrain(stair_feat)
2712 && env.map_knowledge(orig_pos).known() && !you.see_cell(orig_pos))
2714 return (false);
2717 coord_def dest(-1, -1);
2718 // Prefer to send it under the defender.
2719 if (defender->alive() && defender->pos() != attacker->pos())
2720 dest = defender->pos();
2722 return slide_feature_over(attacker->pos(), dest);
2725 #define DID_AFFECT() \
2727 if (miscast_level == 0) \
2728 miscast_level = -1; \
2729 return; \
2732 void melee_attack::chaos_affects_attacker()
2734 if (miscast_level >= 1 || !attacker->alive())
2735 return;
2737 // Move stairs out from under the attacker.
2738 if (one_chance_in(100) && _move_stairs(attacker, defender))
2740 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
2741 take_note(Note(NOTE_MESSAGE, 0, 0,
2742 "CHAOS affects attacker: move stairs"), true);
2743 #endif
2744 DID_AFFECT();
2747 // Dump attacker or items under attacker to another level.
2748 if (is_valid_shaft_level()
2749 && (attacker->will_trigger_shaft()
2750 || igrd(attacker->pos()) != NON_ITEM)
2751 && one_chance_in(1000))
2753 (void) attacker->do_shaft();
2754 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
2755 take_note(Note(NOTE_MESSAGE, 0, 0,
2756 "CHAOS affects attacker: shaft effect"), true);
2757 #endif
2758 DID_AFFECT();
2761 // Create a colourful cloud.
2762 if (weapon && one_chance_in(1000))
2764 mprf("Smoke pours forth from %s!", wep_name(DESC_NOCAP_YOUR).c_str());
2765 big_cloud(random_smoke_type(), &you, attacker->pos(), 20,
2766 4 + random2(8));
2767 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
2768 take_note(Note(NOTE_MESSAGE, 0, 0,
2769 "CHAOS affects attacker: smoke"), true);
2770 #endif
2771 DID_AFFECT();
2774 // Make a loud noise.
2775 if (weapon && player_can_hear(attacker->pos())
2776 && one_chance_in(200))
2778 std::string msg = "";
2779 if (!you.can_see(attacker))
2781 std::string noise = getSpeakString("weapon_noise");
2782 if (!noise.empty())
2783 msg = "You hear " + noise;
2785 else
2787 msg = getSpeakString("weapon_noises");
2788 std::string wepname = wep_name(DESC_CAP_YOUR);
2789 if (!msg.empty())
2791 msg = replace_all(msg, "@Your_weapon@", wepname);
2792 msg = replace_all(msg, "@The_weapon@", wepname);
2796 if (!msg.empty())
2798 mpr(msg.c_str(), MSGCH_SOUND);
2799 noisy(15, attacker->pos(), attacker->mindex());
2800 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
2801 take_note(Note(NOTE_MESSAGE, 0, 0,
2802 "CHAOS affects attacker: noise"), true);
2803 #endif
2804 DID_AFFECT();
2809 static void _find_remains(monster* mon, int &corpse_class, int &corpse_index,
2810 item_def &fake_corpse, int &last_item,
2811 std::vector<int> items)
2813 for (int i = 0; i < NUM_MONSTER_SLOTS; ++i)
2815 const int idx = mon->inv[i];
2817 if (idx == NON_ITEM)
2818 continue;
2820 item_def &item(mitm[idx]);
2822 if (!item.defined() || item.pos != mon->pos())
2823 continue;
2825 items.push_back(idx);
2828 corpse_index = NON_ITEM;
2829 last_item = NON_ITEM;
2831 corpse_class = fill_out_corpse(mon, mon->type, fake_corpse, true);
2832 if (corpse_class == -1 || mons_weight(corpse_class) == 0)
2833 return;
2835 // Stop at first non-matching corpse, since the freshest corpse will
2836 // be at the top of the stack.
2837 for (stack_iterator si(mon->pos()); si; ++si)
2839 if (si->base_type == OBJ_CORPSES && si->sub_type == CORPSE_BODY)
2841 if (si->orig_monnum != fake_corpse.orig_monnum
2842 || si->plus != fake_corpse.plus
2843 || si->plus2 != fake_corpse.plus2
2844 || si->special != fake_corpse.special
2845 || si->flags != fake_corpse.flags)
2847 break;
2850 // If it's a hydra the number of heads must match.
2851 if ((short) mon->number != si->props[MONSTER_NUMBER].get_short())
2852 break;
2854 // Got it!
2855 corpse_index = si.link();
2856 break;
2858 else
2860 // Last item which we're sure belonged to the monster.
2861 for (unsigned int i = 0; i < items.size(); i++)
2862 if (items[i] == si.link())
2863 last_item = si.link();
2868 static bool _make_zombie(monster* mon, int corpse_class, int corpse_index,
2869 item_def &fake_corpse, int last_item)
2871 // If the monster dropped a corpse, then don't waste it by turning
2872 // it into a zombie.
2873 if (corpse_index != NON_ITEM || !mons_class_can_be_zombified(corpse_class))
2874 return (false);
2876 // Good gods won't let their gifts/followers be raised as the
2877 // undead.
2878 if (is_good_god(mon->god))
2879 return (false);
2881 // First attempt to raise zombie fitted out with all its old
2882 // equipment.
2883 int zombie_index = -1;
2884 int idx = get_item_slot(0);
2885 if (idx != NON_ITEM && last_item != NON_ITEM)
2887 mitm[idx] = fake_corpse;
2888 mitm[idx].pos = mon->pos();
2890 // Insert it in the item stack right after the monster's last
2891 // item, so it will be equipped with all the monster's items.
2892 mitm[idx].link = mitm[last_item].link;
2893 mitm[last_item].link = idx;
2895 animate_remains(mon->pos(), CORPSE_BODY, mon->behaviour,
2896 mon->foe, 0, "a chaos effect", mon->god,
2897 true, true, true, &zombie_index);
2900 // No equipment to get, or couldn't get it for some reason.
2901 if (zombie_index == -1)
2903 monster_type type = (mons_zombie_size(mon->type) == Z_SMALL) ?
2904 MONS_ZOMBIE_SMALL : MONS_ZOMBIE_LARGE;
2905 mgen_data mg(type, mon->behaviour, 0, 0, 0, mon->pos(),
2906 mon->foe, MG_FORCE_PLACE, mon->god,
2907 mon->type, mon->number);
2908 mg.non_actor_summoner = "a chaos effect";
2909 zombie_index = create_monster(mg);
2912 if (zombie_index == -1)
2913 return (false);
2915 monster* zombie = &menv[zombie_index];
2917 // Attempt to force zombie into exact same spot.
2918 if (zombie->pos() != mon->pos() && zombie->is_habitable(mon->pos()))
2919 zombie->move_to_pos(mon->pos());
2921 if (you.can_see(mon))
2923 if (you.can_see(zombie))
2924 simple_monster_message(mon, " turns into a zombie!");
2925 else if (last_item != NON_ITEM)
2927 simple_monster_message(mon, "'s equipment vanishes!");
2928 autotoggle_autopickup(true);
2931 else
2933 simple_monster_message(zombie, " appears from thin air!");
2934 autotoggle_autopickup(false);
2937 player_angers_monster(zombie);
2939 return (true);
2942 // mon is a copy of the monster from before monster_die() was called,
2943 // though its hitpoints may be non-positive.
2945 // NOTE: Isn't called if monster dies from poisoning caused by chaos.
2946 void melee_attack::chaos_killed_defender(monster* mon)
2948 ASSERT(mon->type != -1 && mon->type != MONS_NO_MONSTER);
2949 ASSERT(in_bounds(mon->pos()));
2950 ASSERT(!defender->alive());
2952 if (!attacker->alive())
2953 return;
2955 if (attacker->atype() == ACT_PLAYER && you.banished)
2956 return;
2958 if (mon->is_summoned() || (mon->flags & (MF_BANISHED | MF_HARD_RESET)))
2959 return;
2961 int corpse_class, corpse_index, last_item;
2962 item_def fake_corpse;
2963 std::vector<int> items;
2964 _find_remains(mon, corpse_class, corpse_index, fake_corpse, last_item,
2965 items);
2967 if (one_chance_in(100)
2968 && _make_zombie(mon, corpse_class, corpse_index, fake_corpse,
2969 last_item))
2971 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
2972 take_note(Note(NOTE_MESSAGE, 0, 0,
2973 "CHAOS killed defender: zombified monster"), true);
2974 #endif
2975 DID_AFFECT();
2979 void melee_attack::do_miscast()
2981 if (miscast_level == -1)
2982 return;
2984 ASSERT(miscast_target != NULL);
2985 ASSERT(miscast_level >= 0 && miscast_level <= 3);
2986 ASSERT(count_bits(miscast_type) == 1);
2988 if (!miscast_target->alive())
2989 return;
2991 if (miscast_target->atype() == ACT_PLAYER && you.banished)
2992 return;
2994 const bool chaos_brand =
2995 weapon && get_weapon_brand(*weapon) == SPWPN_CHAOS;
2997 // If the miscast is happening on the attacker's side and is due to
2998 // a chaos weapon then make smoke/sand/etc pour out of the weapon
2999 // instead of the attacker's hands.
3000 std::string hand_str;
3002 std::string cause = atk_name(DESC_NOCAP_THE);
3003 int source;
3005 const int ignore_mask = ISFLAG_KNOW_CURSE | ISFLAG_KNOW_PLUSES;
3007 if (attacker->atype() == ACT_PLAYER)
3009 source = NON_MONSTER;
3010 if (chaos_brand)
3012 cause = "a chaos effect from ";
3013 // Ignore a lot of item flags to make cause as short as possible,
3014 // so it will (hopefully) fit onto a single line in the death
3015 // cause screen.
3016 cause += wep_name(DESC_NOCAP_YOUR,
3017 ignore_mask
3018 | ISFLAG_COSMETIC_MASK | ISFLAG_RACIAL_MASK);
3020 if (miscast_target == attacker)
3021 hand_str = wep_name(DESC_PLAIN, ignore_mask);
3024 else
3026 source = attacker->mindex();
3028 if (chaos_brand && miscast_target == attacker
3029 && you.can_see(attacker))
3031 hand_str = wep_name(DESC_PLAIN, ignore_mask);
3035 MiscastEffect(miscast_target, source, (spschool_flag_type) miscast_type,
3036 miscast_level, cause, NH_NEVER, 0, hand_str, false);
3038 // Don't do miscast twice for one attack.
3039 miscast_level = -1;
3042 // NOTE: random_chaos_brand() and random_chaos_attack_flavour() should
3043 // return a set of effects that are roughly the same, to make it easy
3044 // for chaos_affects_defender() not to do duplicate effects caused
3045 // by the non-chaos brands/flavours they return.
3046 int melee_attack::random_chaos_brand()
3048 int brand = SPWPN_NORMAL;
3049 // Assuming the chaos to be mildly intelligent, try to avoid brands
3050 // that clash with the most basic resists of the defender,
3051 // i.e. its holiness.
3052 while (true)
3054 brand = (random_choose_weighted(
3055 5, SPWPN_VORPAL,
3056 10, SPWPN_FLAMING,
3057 10, SPWPN_FREEZING,
3058 10, SPWPN_ELECTROCUTION,
3059 10, SPWPN_VENOM,
3060 10, SPWPN_CHAOS,
3061 5, SPWPN_DRAINING,
3062 5, SPWPN_VAMPIRICISM,
3063 5, SPWPN_HOLY_WRATH,
3064 5, SPWPN_ANTIMAGIC,
3065 2, SPWPN_CONFUSE,
3066 2, SPWPN_DISTORTION,
3067 0));
3069 if (one_chance_in(3))
3070 break;
3072 bool susceptible = true;
3073 switch (brand)
3075 case SPWPN_FLAMING:
3076 if (defender->is_fiery())
3077 susceptible = false;
3078 break;
3079 case SPWPN_FREEZING:
3080 if (defender->is_icy())
3081 susceptible = false;
3082 break;
3083 case SPWPN_ELECTROCUTION:
3084 if (defender->airborne())
3085 susceptible = false;
3086 break;
3087 case SPWPN_VENOM:
3088 if (defender->holiness() == MH_UNDEAD)
3089 susceptible = false;
3090 break;
3091 case SPWPN_VAMPIRICISM:
3092 if (defender->is_summoned())
3094 susceptible = false;
3095 break;
3097 // intentional fall-through
3098 case SPWPN_DRAINING:
3099 if (defender->holiness() != MH_NATURAL)
3100 susceptible = false;
3101 break;
3102 case SPWPN_HOLY_WRATH:
3103 if (defender->holiness() != MH_UNDEAD
3104 && defender->holiness() != MH_DEMONIC)
3106 susceptible = false;
3108 break;
3109 case SPWPN_CONFUSE:
3110 if (defender->holiness() == MH_NONLIVING
3111 || defender->holiness() == MH_PLANT)
3113 susceptible = false;
3115 break;
3116 case SPWPN_ANTIMAGIC:
3117 if (defender->as_monster() &&
3118 !defender->as_monster()->can_use_spells())
3119 susceptible = false;
3120 break;
3121 default:
3122 break;
3125 if (susceptible)
3126 break;
3128 #ifdef NOTE_DEBUG_CHAOS_BRAND
3129 std::string brand_name = "CHAOS brand: ";
3130 switch (brand)
3132 case SPWPN_NORMAL: brand_name += "(plain)"; break;
3133 case SPWPN_FLAMING: brand_name += "flaming"; break;
3134 case SPWPN_FREEZING: brand_name += "freezing"; break;
3135 case SPWPN_HOLY_WRATH: brand_name += "holy wrath"; break;
3136 case SPWPN_ELECTROCUTION: brand_name += "electrocution"; break;
3137 case SPWPN_VENOM: brand_name += "venom"; break;
3138 case SPWPN_DRAINING: brand_name += "draining"; break;
3139 case SPWPN_DISTORTION: brand_name += "distortion"; break;
3140 case SPWPN_VAMPIRICISM: brand_name += "vampiricism"; break;
3141 case SPWPN_VORPAL: brand_name += "vorpal"; break;
3142 case SPWPN_ANTIMAGIC: brand_name += "anti-magic"; break;
3143 // ranged weapon brands
3144 case SPWPN_FLAME: brand_name += "flame"; break;
3145 case SPWPN_FROST: brand_name += "frost"; break;
3147 // both ranged and non-ranged
3148 case SPWPN_CHAOS: brand_name += "chaos"; break;
3149 case SPWPN_CONFUSE: brand_name += "confusion"; break;
3150 default: brand_name += "(other)"; break;
3153 // Pretty much duplicated by the chaos effect note,
3154 // which will be much more informative.
3155 if (brand != SPWPN_CHAOS)
3156 take_note(Note(NOTE_MESSAGE, 0, 0, brand_name.c_str()), true);
3157 #endif
3158 return (brand);
3161 mon_attack_flavour melee_attack::random_chaos_attack_flavour()
3163 mon_attack_flavour flavours[] =
3164 {AF_FIRE, AF_COLD, AF_ELEC, AF_POISON_NASTY, AF_VAMPIRIC, AF_DISTORT,
3165 AF_CONFUSE, AF_CHAOS};
3166 return (RANDOM_ELEMENT(flavours));
3169 bool melee_attack::apply_damage_brand()
3171 bool brand_was_known = false;
3173 if (weapon)
3175 if (is_artefact(*weapon))
3176 brand_was_known = artefact_known_wpn_property(*weapon, ARTP_BRAND);
3177 else
3178 brand_was_known = item_type_known(*weapon);
3180 bool ret = false;
3182 // Monster resistance to the brand.
3183 int res = 0;
3185 special_damage = 0;
3186 obvious_effect = false;
3188 int brand;
3189 if (damage_brand == SPWPN_CHAOS)
3190 brand = random_chaos_brand();
3191 else
3192 brand = damage_brand;
3194 switch (brand)
3196 case SPWPN_FLAMING:
3197 res = fire_res_apply_cerebov_downgrade(defender->res_fire());
3198 calc_elemental_brand_damage(BEAM_FIRE, res,
3199 defender->is_icy() ? "melt" : "burn");
3200 defender->expose_to_element(BEAM_FIRE);
3201 extra_noise += 1;
3202 break;
3204 case SPWPN_FREEZING:
3205 calc_elemental_brand_damage(BEAM_COLD, defender->res_cold(), "freeze");
3206 defender->expose_to_element(BEAM_COLD);
3207 break;
3209 case SPWPN_HOLY_WRATH:
3210 if (defender->undead_or_demonic())
3211 special_damage = 1 + (random2(damage_done * 15) / 10);
3213 if (special_damage && defender_visible)
3215 special_damage_message =
3216 make_stringf(
3217 "%s %s%s",
3218 def_name(DESC_CAP_THE).c_str(),
3219 defender->conj_verb("convulse").c_str(),
3220 special_attack_punctuation().c_str());
3222 break;
3224 case SPWPN_ELECTROCUTION:
3225 extra_noise += 2;
3226 if (defender->airborne() || defender->res_elec() > 0)
3227 break;
3228 else if (one_chance_in(3))
3230 special_damage_message =
3231 defender->atype() == ACT_PLAYER?
3232 "You are electrocuted!"
3233 : "There is a sudden explosion of sparks!";
3234 special_damage = 10 + random2(15);
3235 special_damage_flavour = BEAM_ELECTRICITY;
3237 // Check for arcing in water, and add the final effect.
3238 const coord_def& pos = defender->pos();
3240 // We know the defender is neither airborne nor electricity
3241 // resistant, from above, but is it on water?
3242 if (feat_is_water(grd(pos)))
3244 add_final_effect(FINEFF_LIGHTNING_DISCHARGE, attacker, defender,
3245 pos);
3249 break;
3251 case SPWPN_ORC_SLAYING:
3252 if (is_orckind(defender))
3254 special_damage = 1 + random2(3*damage_done/2);
3255 if (defender_visible)
3257 special_damage_message =
3258 make_stringf(
3259 "%s %s%s",
3260 defender->name(DESC_CAP_THE).c_str(),
3261 defender->conj_verb("convulse").c_str(),
3262 special_attack_punctuation().c_str());
3265 break;
3267 case SPWPN_DRAGON_SLAYING:
3268 if (is_dragonkind(defender))
3270 special_damage = 1 + random2(3*damage_done/2);
3271 if (defender_visible)
3273 special_damage_message =
3274 make_stringf(
3275 "%s %s%s",
3276 defender->name(DESC_CAP_THE).c_str(),
3277 defender->conj_verb("convulse").c_str(),
3278 special_attack_punctuation().c_str());
3281 break;
3283 case SPWPN_VENOM:
3284 if (!one_chance_in(4))
3286 int old_poison;
3288 if (defender->atype() == ACT_PLAYER)
3289 old_poison = you.duration[DUR_POISONING];
3290 else
3292 old_poison =
3293 (defender->as_monster()->get_ench(ENCH_POISON)).degree;
3296 // Poison monster message needs to arrive after hit message.
3297 emit_nodmg_hit_message();
3299 // Weapons of venom do two levels of poisoning to the player,
3300 // but only one level to monsters.
3301 defender->poison(attacker, 2);
3303 if (defender->atype() == ACT_PLAYER
3304 && old_poison < you.duration[DUR_POISONING]
3305 || defender->atype() != ACT_PLAYER
3306 && old_poison <
3307 (defender->as_monster()->get_ench(ENCH_POISON)).degree)
3309 obvious_effect = true;
3313 break;
3315 case SPWPN_DRAINING:
3316 drain_defender();
3317 break;
3319 case SPWPN_VORPAL:
3320 special_damage = 1 + random2(damage_done) / 4;
3321 // Note: Leaving special_damage_message empty because there isn't one.
3322 break;
3324 case SPWPN_VAMPIRICISM:
3326 // Vampire bat form -- why the special handling?
3327 if (attacker->atype() == ACT_PLAYER && you.species == SP_VAMPIRE
3328 && player_in_bat_form())
3330 _player_vampire_draws_blood(defender->as_monster(), damage_done);
3331 break;
3334 if (x_chance_in_y(defender->res_negative_energy(), 3))
3335 break;
3337 if (!weapon || defender->holiness() != MH_NATURAL || damage_done < 1
3338 || attacker->stat_hp() == attacker->stat_maxhp()
3339 || defender->atype() != ACT_PLAYER
3340 && defender->as_monster()->is_summoned()
3341 || attacker == &you && you.duration[DUR_DEATHS_DOOR]
3342 || one_chance_in(5))
3344 break;
3347 obvious_effect = true;
3349 // Handle weapon effects.
3350 // We only get here if we've done base damage, so no
3351 // worries on that score.
3353 if (attacker->atype() == ACT_PLAYER)
3354 mpr("You feel better.");
3355 else if (attacker_visible)
3357 if (defender->atype() == ACT_PLAYER)
3359 mprf("%s draws strength from your injuries!",
3360 attacker->name(DESC_CAP_THE).c_str());
3362 else
3364 mprf("%s is healed.",
3365 attacker->name(DESC_CAP_THE).c_str());
3369 int hp_boost = 0;
3371 // Thus is probably more valuable on larger weapons?
3372 if (weapon && is_unrandom_artefact(*weapon)
3373 && weapon->special == UNRAND_VAMPIRES_TOOTH)
3375 hp_boost = damage_done;
3377 else
3378 hp_boost = 1 + random2(damage_done);
3380 attacker->heal(hp_boost);
3382 attacker->god_conduct(DID_NECROMANCY, 2);
3383 break;
3385 case SPWPN_PAIN:
3386 if (defender->res_negative_energy())
3387 break;
3389 if (x_chance_in_y(attacker->skill(SK_NECROMANCY) + 1, 8))
3391 if (defender_visible)
3393 special_damage_message =
3394 make_stringf("%s %s in agony.",
3395 defender->name(DESC_CAP_THE).c_str(),
3396 defender->conj_verb("writhe").c_str());
3398 special_damage += random2(1 + attacker->skill(SK_NECROMANCY));
3401 attacker->god_conduct(DID_NECROMANCY, 4);
3402 break;
3404 case SPWPN_DISTORTION:
3405 ret = distortion_affects_defender();
3406 break;
3408 case SPWPN_CONFUSE:
3410 // This was originally for confusing touch and it doesn't really
3411 // work on the player, but a monster with a chaos weapon will
3412 // occassionally come up with this brand. -cao
3413 if (defender->atype() == ACT_PLAYER)
3414 break;
3416 emit_nodmg_hit_message();
3418 const int hdcheck =
3419 (defender->holiness() == MH_NATURAL ? random2(30) : random2(22));
3421 if (mons_class_is_confusable(defender->type)
3422 && hdcheck >= defender->get_experience_level())
3424 // Declaring these just to pass to the enchant function.
3425 bolt beam_temp;
3426 beam_temp.thrower =
3427 attacker->atype() == ACT_PLAYER? KILL_YOU : KILL_MON;
3428 beam_temp.flavour = BEAM_CONFUSION;
3429 beam_temp.beam_source = attacker->mindex();
3430 beam_temp.apply_enchantment_to_monster(defender->as_monster());
3431 obvious_effect = beam_temp.obvious_effect;
3434 if (attacker->atype() == ACT_PLAYER && damage_brand == SPWPN_CONFUSE)
3436 ASSERT(you.duration[DUR_CONFUSING_TOUCH]);
3437 you.duration[DUR_CONFUSING_TOUCH] -= roll_dice(3, 5)
3438 * BASELINE_DELAY;
3440 if (you.duration[DUR_CONFUSING_TOUCH] < 1)
3441 you.duration[DUR_CONFUSING_TOUCH] = 1;
3442 obvious_effect = false;
3444 break;
3447 case SPWPN_CHAOS:
3448 chaos_affects_defender();
3449 break;
3451 case SPWPN_ANTIMAGIC:
3452 if (defender->atype() == ACT_PLAYER)
3454 int mp_loss = std::min(you.magic_points, random2(damage_done * 2));
3455 if (!mp_loss)
3456 break;
3457 mpr("You feel your power leaking away.", MSGCH_WARN);
3458 dec_mp(mp_loss);
3459 obvious_effect = true;
3461 else if (defender->as_monster()->can_use_spells()
3462 && !mons_class_flag(defender->type, M_FAKE_SPELLS))
3464 defender->as_monster()->add_ench(mon_enchant(ENCH_ANTIMAGIC, 0,
3465 attacker->kill_alignment(), // doesn't matter
3466 random2(damage_done * 2) * BASELINE_DELAY));
3467 special_damage_message =
3468 apostrophise(defender->name(DESC_CAP_THE))
3469 + " magic leaks into the air.";
3470 obvious_effect = true;
3472 break;
3475 if (damage_brand == SPWPN_CHAOS && brand != SPWPN_CHAOS && !ret
3476 && miscast_level == -1 && one_chance_in(20))
3478 miscast_level = 0;
3479 miscast_type = SPTYP_RANDOM;
3480 miscast_target = coinflip() ? attacker : defender;
3483 if (attacker->atype() == ACT_PLAYER && damage_brand == SPWPN_CHAOS)
3485 // If your god objects to using chaos, then it makes the
3486 // brand obvious.
3487 if (did_god_conduct(DID_CHAOS, 2 + random2(3), brand_was_known))
3488 obvious_effect = true;
3490 if (!obvious_effect)
3491 obvious_effect = !special_damage_message.empty();
3493 if (obvious_effect && attacker_visible && weapon != NULL)
3495 if (is_artefact(*weapon))
3496 artefact_wpn_learn_prop(*weapon, ARTP_BRAND);
3497 else
3498 set_ident_flags(*weapon, ISFLAG_KNOW_TYPE);
3501 return (ret);
3505 // XXX:
3506 // * Noise should probably scale non-linearly with damage_done, and
3507 // maybe even non-linearly with noise_factor.
3509 // * Damage reduction via armour of the defender reduces noise,
3510 // but shouldn't.
3512 // * Damage reduction because of negative damage modifiers on the
3513 // weapon reduce noise, but probably shouldn't.
3515 // * Might want a different formula for noise generated by the
3516 // player.
3518 // Ideas:
3519 // * Each weapon type has a noise rating, like it does an accuracy
3520 // rating and base delay.
3522 // * For player, stealth skill and/or weapon skillr reducing noise.
3524 // * Randart property to make randart weapons louder or softer when
3525 // they hit.
3526 void melee_attack::handle_noise(const coord_def & pos)
3528 // Successful stabs make no noise.
3529 if (stab_attempt)
3531 noise_factor = 0;
3532 extra_noise = 0;
3533 return;
3536 int level = (noise_factor * damage_done / 100 / 4) + extra_noise;
3538 if (noise_factor > 0)
3539 level = std::max(1, level);
3541 if (level > 0)
3542 noisy(level, pos, attacker->mindex());
3544 noise_factor = 0;
3545 extra_noise = 0;
3548 // Returns true if the attack cut off a head *and* cauterized it.
3549 bool melee_attack::chop_hydra_head(int dam,
3550 int dam_type,
3551 int wpn_brand)
3553 // Monster attackers have only a 25% chance of making the
3554 // chop-check to prevent runaway head inflation.
3555 if (attacker->atype() == ACT_MONSTER && !one_chance_in(4))
3556 return (false);
3558 if ((dam_type == DVORP_SLICING || dam_type == DVORP_CHOPPING
3559 || dam_type == DVORP_CLAWING)
3560 && dam > 0
3561 && (dam >= 4 || wpn_brand == SPWPN_VORPAL || coinflip()))
3563 const char *verb = NULL;
3565 if (dam_type == DVORP_CLAWING)
3567 static const char *claw_verbs[] = { "rip", "tear", "claw" };
3568 verb = RANDOM_ELEMENT(claw_verbs);
3570 else
3572 static const char *slice_verbs[] =
3574 "slice", "lop", "chop", "hack"
3576 verb = RANDOM_ELEMENT(slice_verbs);
3579 if (defender->as_monster()->number == 1) // will be zero afterwards
3581 if (defender_visible)
3583 mprf("%s %s %s's last head off!",
3584 atk_name(DESC_CAP_THE).c_str(),
3585 attacker->conj_verb(verb).c_str(),
3586 def_name(DESC_NOCAP_THE).c_str());
3588 defender->as_monster()->number--;
3590 if (!defender->is_summoned())
3591 bleed_onto_floor(defender->pos(), defender->type,
3592 defender->as_monster()->hit_points, true);
3594 defender->hurt(attacker, defender->as_monster()->hit_points);
3596 return (true);
3598 else
3600 if (defender_visible)
3602 mprf("%s %s one of %s's heads off!",
3603 atk_name(DESC_CAP_THE).c_str(),
3604 attacker->conj_verb(verb).c_str(),
3605 def_name(DESC_NOCAP_THE).c_str());
3607 defender->as_monster()->number--;
3609 // Only living hydras get to regenerate heads.
3610 if (defender->holiness() == MH_NATURAL)
3612 unsigned int limit = 20;
3613 if (defender->type == MONS_LERNAEAN_HYDRA)
3614 limit = 27;
3616 if (wpn_brand == SPWPN_FLAMING)
3618 if (defender_visible)
3619 mpr("The flame cauterises the wound!");
3620 return (true);
3622 else if (defender->as_monster()->number < limit - 1)
3624 simple_monster_message(defender->as_monster(),
3625 " grows two more!");
3626 defender->as_monster()->number += 2;
3627 defender->heal(8 + random2(8), true);
3633 return (false);
3636 bool melee_attack::decapitate_hydra(int dam, int damage_type)
3638 if (defender->atype() == ACT_MONSTER
3639 && defender->as_monster()->has_hydra_multi_attack()
3640 && defender->type != MONS_SPECTRAL_THING)
3642 const int dam_type = (damage_type != -1) ? damage_type
3643 : attacker->damage_type();
3644 const int wpn_brand = attacker->damage_brand();
3646 return chop_hydra_head(dam, dam_type, wpn_brand);
3648 return (false);
3651 void melee_attack::player_sustain_passive_damage()
3653 if (mons_class_flag(defender->type, M_ACID_SPLASH))
3654 weapon_acid(5);
3657 int melee_attack::player_staff_damage(skill_type skill)
3659 return (random2(5*(you.skill(skill) + you.skill(SK_EVOCATIONS))/4));
3662 void melee_attack::emit_nodmg_hit_message()
3664 if (!no_damage_message.empty())
3666 mprf("%s", no_damage_message.c_str());
3667 no_damage_message.clear();
3671 void melee_attack::player_apply_staff_damage()
3673 special_damage = 0;
3675 if (!weapon || !item_is_staff(*weapon))
3676 return;
3678 if (random2(15) > you.skill(SK_EVOCATIONS))
3679 return;
3681 switch (weapon->sub_type)
3683 case STAFF_AIR:
3684 if (damage_done + you.skill(SK_AIR_MAGIC) <= random2(20))
3685 break;
3687 special_damage =
3688 resist_adjust_damage(defender,
3689 BEAM_ELECTRICITY,
3690 defender->res_elec(),
3691 player_staff_damage(SK_AIR_MAGIC));
3693 if (special_damage)
3695 special_damage_message =
3696 make_stringf("%s is electrocuted!",
3697 defender->name(DESC_CAP_THE).c_str());
3698 special_damage_flavour = BEAM_ELECTRICITY;
3701 break;
3703 case STAFF_COLD:
3704 special_damage =
3705 resist_adjust_damage(defender,
3706 BEAM_COLD,
3707 defender->res_cold(),
3708 player_staff_damage(SK_ICE_MAGIC));
3710 if (special_damage)
3712 special_damage_message =
3713 make_stringf(
3714 "You freeze %s!",
3715 defender->name(DESC_NOCAP_THE).c_str());
3717 break;
3719 case STAFF_EARTH:
3720 special_damage = player_staff_damage(SK_EARTH_MAGIC);
3721 special_damage = player_apply_monster_ac(special_damage);
3723 if (special_damage > 0)
3725 special_damage_message =
3726 make_stringf(
3727 "You crush %s!",
3728 defender->name(DESC_NOCAP_THE).c_str());
3730 break;
3732 case STAFF_FIRE:
3733 special_damage =
3734 resist_adjust_damage(defender,
3735 BEAM_FIRE,
3736 defender->res_fire(),
3737 player_staff_damage(SK_FIRE_MAGIC));
3739 if (special_damage)
3741 special_damage_message =
3742 make_stringf(
3743 "You burn %s!",
3744 defender->name(DESC_NOCAP_THE).c_str());
3746 break;
3748 case STAFF_POISON:
3750 // Base chance at 50% -- like mundane weapons.
3751 if (coinflip() || x_chance_in_y(you.skill(SK_POISON_MAGIC), 8))
3753 // Poison monster message needs to arrive after hit message.
3754 emit_nodmg_hit_message();
3755 poison_monster(defender->as_monster(), KC_YOU);
3757 break;
3760 case STAFF_DEATH:
3761 if (defender->res_negative_energy())
3762 break;
3764 if (x_chance_in_y(you.skill(SK_NECROMANCY) + 1, 8))
3766 special_damage = player_staff_damage(SK_NECROMANCY);
3768 if (special_damage)
3770 special_damage_message =
3771 make_stringf(
3772 "%s convulses in agony!",
3773 defender->name(DESC_CAP_THE).c_str());
3775 did_god_conduct(DID_NECROMANCY, 4);
3778 break;
3780 case STAFF_POWER:
3781 case STAFF_SUMMONING:
3782 case STAFF_CHANNELING:
3783 case STAFF_CONJURATION:
3784 case STAFF_ENCHANTMENT:
3785 case STAFF_ENERGY:
3786 case STAFF_WIZARDRY:
3787 break;
3789 default:
3790 mpr("You're wielding some staff I've never heard of! (fight.cc)",
3791 MSGCH_ERROR);
3792 break;
3795 if (special_damage > 0)
3797 if (!item_type_known(*weapon))
3799 set_ident_flags(*weapon, ISFLAG_KNOW_TYPE);
3800 set_ident_type(*weapon, ID_KNOWN_TYPE);
3802 mprf("You are wielding %s.", weapon->name(DESC_NOCAP_A).c_str());
3803 more();
3804 you.wield_change = true;
3809 bool melee_attack::player_check_monster_died()
3811 if (!defender->alive())
3813 // note: doesn't take account of special weapons, etc.
3814 dprf("Hit for %d.", damage_done);
3816 player_monattk_hit_effects(true);
3818 _monster_die(defender->as_monster(), KILL_YOU, NON_MONSTER);
3820 return (true);
3823 return (false);
3826 void melee_attack::player_calc_hit_damage()
3828 int potential_damage;
3830 potential_damage =
3831 !weapon ? player_calc_base_unarmed_damage()
3832 : player_calc_base_weapon_damage();
3834 // [0.6 AC/EV overhaul] Shields don't go well with hand-and-half weapons.
3835 if (weapon && hands == HANDS_HALF)
3837 potential_damage =
3838 std::max(1,
3839 potential_damage - roll_dice(1, player_shield_penalty));
3842 potential_damage = player_stat_modify_damage(potential_damage);
3844 // apply damage bonus from ring of slaying
3845 // (before randomization -- some of these rings
3846 // are stupidly powerful) -- GDL
3847 potential_damage += slaying_bonus(PWPN_DAMAGE);
3848 damage_done =
3849 potential_damage > 0 ? one_chance_in(3) + random2(potential_damage) : 0;
3850 damage_done = player_apply_weapon_skill(damage_done);
3851 damage_done = player_apply_fighting_skill(damage_done, false);
3852 damage_done = player_apply_misc_modifiers(damage_done);
3853 damage_done = player_apply_weapon_bonuses(damage_done);
3855 player_weapon_auto_id();
3857 damage_done = player_stab(damage_done);
3858 damage_done = player_apply_monster_ac(damage_done);
3860 // This doesn't actually modify damage. -- bwr
3861 // It only chooses the appropriate verb.
3862 damage_done = std::max(0, player_weapon_type_modify(damage_done));
3865 int melee_attack::calc_to_hit(bool random)
3867 return (attacker->atype() == ACT_PLAYER ? player_to_hit(random)
3868 : mons_to_hit());
3871 // Calculates your armour+shield penalty. If random_factor is true,
3872 // be stochastic; if false, deterministic (e.g. for chardumps.)
3873 void melee_attack::calc_player_armour_shield_tohit_penalty(bool random_factor)
3875 if (weapon && hands == HANDS_HALF)
3877 player_armour_tohit_penalty =
3878 maybe_roll_dice(1, player_body_armour_penalty, random_factor);
3879 player_shield_tohit_penalty =
3880 maybe_roll_dice(2, player_shield_penalty, random_factor);
3882 else
3884 player_armour_tohit_penalty =
3885 maybe_roll_dice(1, player_body_armour_penalty, random_factor);
3886 player_shield_tohit_penalty =
3887 maybe_roll_dice(1, player_shield_penalty, random_factor);
3891 int melee_attack::player_to_hit(bool random_factor)
3893 calc_player_armour_shield_tohit_penalty(random_factor);
3895 dprf("Armour/shield to-hit penalty: %d/%d",
3896 player_armour_tohit_penalty, player_shield_tohit_penalty);
3898 can_do_unarmed =
3899 player_fights_well_unarmed(player_armour_tohit_penalty
3900 + player_shield_tohit_penalty);
3902 int your_to_hit = 15 + (calc_stat_to_hit_base() / 2);
3904 #ifdef DEBUG_DIAGNOSTICS
3905 const int base_to_hit = your_to_hit;
3906 #endif
3908 if (wearing_amulet(AMU_INACCURACY))
3909 your_to_hit -= 5;
3911 // If you can't see yourself, you're a little less accurate.
3912 if (!you.visible_to(&you))
3913 your_to_hit -= 5;
3915 // fighting contribution
3916 your_to_hit += maybe_random2(1 + you.skill(SK_FIGHTING), random_factor);
3918 // weapon skill contribution
3919 if (weapon)
3921 if (wpn_skill != SK_FIGHTING)
3923 if (you.skill(wpn_skill) < 1 && player_in_a_dangerous_place())
3924 xom_is_stimulated(14); // Xom thinks that is mildly amusing.
3926 your_to_hit += maybe_random2(you.skill(wpn_skill) + 1,
3927 random_factor);
3930 else
3931 { // ...you must be unarmed
3932 your_to_hit += species_has_claws(you.species) ? 4 : 2;
3934 your_to_hit += maybe_random2(1 + you.skill(SK_UNARMED_COMBAT),
3935 random_factor);
3938 // weapon bonus contribution
3939 if (weapon)
3941 if (weapon->base_type == OBJ_WEAPONS)
3943 your_to_hit += weapon->plus;
3944 your_to_hit += property(*weapon, PWPN_HIT);
3946 if (get_equip_race(*weapon) == ISFLAG_ELVEN
3947 && player_genus(GENPC_ELVEN))
3949 your_to_hit += (random_factor && coinflip() ? 2 : 1);
3951 else if (get_equip_race(*weapon) == ISFLAG_ORCISH
3952 && you.religion == GOD_BEOGH && !player_under_penance())
3954 your_to_hit++;
3958 else if (weapon->base_type == OBJ_STAVES)
3960 // magical staff
3961 your_to_hit += property(*weapon, PWPN_HIT);
3963 if (item_is_rod(*weapon))
3964 your_to_hit += (short)weapon->props["rod_enchantment"];
3968 // slaying bonus
3969 your_to_hit += slaying_bonus(PWPN_HIT);
3971 // hunger penalty
3972 if (you.hunger_state == HS_STARVING)
3973 your_to_hit -= 3;
3975 // armour penalty
3976 your_to_hit -= (player_armour_tohit_penalty + player_shield_tohit_penalty);
3978 //mutation
3979 if (player_mutation_level(MUT_EYEBALLS))
3980 your_to_hit += 2 * player_mutation_level(MUT_EYEBALLS) + 1;
3982 #ifdef DEBUG_DIAGNOSTICS
3983 int roll_hit = your_to_hit;
3984 #endif
3986 // hit roll
3987 your_to_hit = maybe_random2(your_to_hit, random_factor);
3989 #ifdef DEBUG_DIAGNOSTICS
3990 dprf("to hit die: %d; rolled value: %d; base: %d",
3991 roll_hit, your_to_hit, base_to_hit);
3992 #endif
3994 if (weapon && wpn_skill == SK_SHORT_BLADES && you.duration[DUR_SURE_BLADE])
3996 int turn_duration = you.duration[DUR_SURE_BLADE] / BASELINE_DELAY;
3997 your_to_hit += 5 +
3998 (random_factor ? random2limit(turn_duration, 10) :
3999 turn_duration / 2);
4002 // other stuff
4003 if (!weapon)
4005 if (you.duration[DUR_CONFUSING_TOUCH])
4007 // Just trying to touch is easier that trying to damage.
4008 your_to_hit += maybe_random2(you.dex(), random_factor);
4011 switch (you.form)
4013 case TRAN_SPIDER:
4014 your_to_hit += maybe_random2(10, random_factor);
4015 break;
4016 case TRAN_BAT:
4017 your_to_hit += maybe_random2(12, random_factor);
4018 break;
4019 case TRAN_ICE_BEAST:
4020 your_to_hit += maybe_random2(10, random_factor);
4021 break;
4022 case TRAN_BLADE_HANDS:
4023 your_to_hit += maybe_random2(12, random_factor);
4024 break;
4025 case TRAN_STATUE:
4026 your_to_hit += maybe_random2(9, random_factor);
4027 break;
4028 case TRAN_DRAGON:
4029 your_to_hit += maybe_random2(10, random_factor);
4030 break;
4031 case TRAN_LICH:
4032 your_to_hit += maybe_random2(10, random_factor);
4033 break;
4034 case TRAN_PIG:
4035 case TRAN_NONE:
4036 break;
4040 // Check for backlight (Corona).
4041 if (defender && defender->atype() == ACT_MONSTER)
4043 if (defender->backlit(true, false))
4044 your_to_hit += 2 + random2(8);
4045 // Invisible monsters are hard to hit.
4046 else if (!defender->visible_to(&you))
4047 your_to_hit -= 6;
4050 return (your_to_hit);
4053 void melee_attack::player_stab_check()
4055 // Unknown mimics cannot be stabbed.
4056 // Confusion and having dex of 0 disallow stabs.
4057 if (mons_is_unknown_mimic(defender->as_monster()) || you.stat_zero[STAT_DEX]
4058 || you.confused())
4060 stab_attempt = false;
4061 stab_bonus = 0;
4062 return;
4065 const unchivalric_attack_type uat = is_unchivalric_attack(&you, defender);
4066 stab_attempt = (uat != UCAT_NO_ATTACK);
4067 const bool roll_needed = (uat != UCAT_SLEEPING && uat != UCAT_PARALYSED);
4069 int roll = 100;
4070 if (uat == UCAT_INVISIBLE && !mons_sense_invis(defender->as_monster()))
4071 roll -= 10;
4073 switch (uat)
4075 case UCAT_NO_ATTACK:
4076 stab_bonus = 0;
4077 break;
4078 case UCAT_SLEEPING:
4079 case UCAT_PARALYSED:
4080 stab_bonus = 1;
4081 break;
4082 case UCAT_HELD_IN_NET:
4083 case UCAT_PETRIFYING:
4084 case UCAT_PETRIFIED:
4085 stab_bonus = 2;
4086 break;
4087 case UCAT_INVISIBLE:
4088 case UCAT_CONFUSED:
4089 case UCAT_FLEEING:
4090 case UCAT_ALLY:
4091 stab_bonus = 4;
4092 break;
4093 case UCAT_DISTRACTED:
4094 stab_bonus = 6;
4095 break;
4098 // See if we need to roll against dexterity / stabbing.
4099 if (stab_attempt && roll_needed)
4101 stab_attempt = x_chance_in_y(you.skill(SK_STABBING) + you.dex() + 1,
4102 roll);
4106 random_var melee_attack::player_calc_attack_delay()
4108 random_var attack_delay = weapon ? player_weapon_speed()
4109 : player_unarmed_speed();
4111 if (player_shield_penalty)
4113 if (weapon && hands == HANDS_HALF)
4115 attack_delay += rv::roll_dice(1, player_shield_penalty);
4117 else
4119 attack_delay += rv::min(rv::random2(1 + player_shield_penalty),
4120 rv::random2(1 + player_shield_penalty));
4124 attack_delay = rv::max(attack_delay, constant(3));
4126 return (attack_delay);
4129 void melee_attack::player_apply_attack_delay()
4131 final_attack_delay = player_calc_attack_delay().roll();
4133 if (you.duration[DUR_FINESSE])
4135 ASSERT(!you.duration[DUR_BERSERK]);
4136 // Need to undo haste by hand.
4137 if (you.duration[DUR_HASTE])
4138 you.time_taken = haste_mul(you.time_taken);
4139 you.time_taken /= 2;
4142 you.time_taken =
4143 std::max(2, div_rand_round(you.time_taken * final_attack_delay, 10));
4145 dprf("Weapon speed: %d; min: %d; attack time: %d",
4146 final_attack_delay, min_delay, you.time_taken);
4149 random_var melee_attack::player_weapon_speed()
4151 random_var attack_delay = constant(15);
4153 if (weapon && (weapon->base_type == OBJ_WEAPONS
4154 || weapon->base_type == OBJ_STAVES))
4156 attack_delay = constant(property(*weapon, PWPN_SPEED));
4157 attack_delay -= constant(you.skill(wpn_skill) / 2);
4159 min_delay = property(*weapon, PWPN_SPEED) / 2;
4161 // Short blades can get up to at least unarmed speed.
4162 if (wpn_skill == SK_SHORT_BLADES && min_delay > 5)
4163 min_delay = 5;
4165 // All weapons have min delay 7 or better
4166 if (min_delay > 7)
4167 min_delay = 7;
4169 // never go faster than speed 3 (ie 3 attacks per round)
4170 if (min_delay < 3)
4171 min_delay = 3;
4173 // apply minimum to weapon skill modification
4174 attack_delay = rv::max(attack_delay, min_delay);
4176 if (weapon->base_type == OBJ_WEAPONS
4177 && damage_brand == SPWPN_SPEED)
4179 attack_delay = (attack_delay + constant(1)) / 2;
4183 return (attack_delay);
4186 random_var melee_attack::player_unarmed_speed()
4188 random_var unarmed_delay =
4189 rv::max(constant(10),
4190 (rv::roll_dice(1, 10) +
4191 rv::roll_dice(2, player_body_armour_penalty)));
4193 // Not even bats can attack faster than this.
4194 min_delay = 5;
4196 // Unarmed speed.
4197 if (you.burden_state == BS_UNENCUMBERED)
4199 unarmed_delay =
4200 rv::max(unarmed_delay
4201 - constant(you.skill(SK_UNARMED_COMBAT)
4202 / (player_in_bat_form() ? 3 : 5)),
4203 constant(min_delay));
4206 return (unarmed_delay);
4209 int melee_attack::player_calc_base_unarmed_damage()
4211 int damage = 3;
4213 if (you.duration[DUR_CONFUSING_TOUCH])
4215 // No base hand damage while using this spell.
4216 damage = 0;
4219 switch (you.form)
4221 case TRAN_SPIDER:
4222 damage = 5;
4223 break;
4224 case TRAN_BAT:
4225 damage = (you.species == SP_VAMPIRE ? 2 : 1);
4226 break;
4227 case TRAN_ICE_BEAST:
4228 damage = 12;
4229 break;
4230 case TRAN_BLADE_HANDS:
4231 damage = 12 + div_rand_round(you.strength() + you.dex(), 4);
4232 break;
4233 case TRAN_STATUE:
4234 damage = 12 + you.strength();
4235 break;
4236 case TRAN_DRAGON:
4237 damage = 20 + you.strength();
4238 break;
4239 case TRAN_LICH:
4240 damage = 5;
4241 break;
4242 case TRAN_PIG:
4243 break;
4244 case TRAN_NONE:
4245 break;
4248 if (you.has_usable_claws())
4250 // Claw damage only applies for bare hands.
4251 damage += you.has_claws(false) * 2;
4252 apply_bleeding = true;
4255 if (player_in_bat_form())
4257 // Bats really don't do a lot of damage.
4258 damage += you.skill(SK_UNARMED_COMBAT) / 5;
4260 else
4261 damage += you.skill(SK_UNARMED_COMBAT);
4263 return (damage);
4266 int melee_attack::player_calc_base_weapon_damage()
4268 int damage = 0;
4270 if (weapon->base_type == OBJ_WEAPONS || weapon->base_type == OBJ_STAVES)
4271 damage = property(*weapon, PWPN_DAMAGE);
4273 // Staves can be wielded with a worn shield, but are much less
4274 // effective.
4275 if (shield && weapon->base_type == OBJ_WEAPONS
4276 && weapon_skill(*weapon) == SK_STAVES
4277 && hands_reqd(*weapon, you.body_size()) == HANDS_HALF)
4279 damage /= 2;
4282 return (damage);
4285 ///////////////////////////////////////////////////////////////////////////
4287 bool melee_attack::mons_attack_mons()
4289 const coord_def atk_pos = attacker->pos();
4290 const coord_def def_pos = defender->pos();
4292 // Self-attacks never violate sanctuary.
4293 if ((is_sanctuary(atk_pos) || is_sanctuary(def_pos))
4294 && attacker != defender)
4296 // Friendly monsters should only violate sanctuary if explicitly
4297 // ordered to do so by the player.
4298 if (attacker->as_monster()->friendly())
4300 if (you.pet_target == MHITYOU || you.pet_target == MHITNOT)
4302 if (attacker->confused() && you.can_see(attacker))
4304 mpr("Zin prevents your ally from violating sanctuary "
4305 "in its confusion.", MSGCH_GOD);
4307 else if (attacker->berserk() && you.can_see(attacker))
4309 mpr("Zin prevents your ally from violating sanctuary "
4310 "in its berserker rage.", MSGCH_GOD);
4313 cancel_attack = true;
4314 return (false);
4317 // Non-friendly monsters should never violate sanctuary.
4318 else
4320 dprf("Preventing hostile violation of sanctuary.");
4321 cancel_attack = true;
4322 return (false);
4326 mons_perform_attack();
4328 // If a hydra was attacking it may have switched targets and started
4329 // hitting the player. -cao
4330 if (defender->atype() == ACT_PLAYER)
4331 return (did_hit);
4333 if (perceived_attack
4334 && (defender->as_monster()->foe == MHITNOT || one_chance_in(3))
4335 && attacker->alive() && defender->alive())
4337 behaviour_event(defender->as_monster(), ME_WHACK, attacker->mindex());
4340 // If an enemy attacked a friend, set the pet target if it isn't set
4341 // already, but not if sanctuary is in effect (pet target must be
4342 // set explicitly by the player during sanctuary).
4343 if (perceived_attack && attacker->alive()
4344 && defender->as_monster()->friendly()
4345 && !crawl_state.game_is_arena()
4346 && !attacker->as_monster()->wont_attack()
4347 && you.pet_target == MHITNOT
4348 && env.sanctuary_time <= 0)
4350 you.pet_target = attacker->mindex();
4353 return (did_hit);
4356 bool melee_attack::mons_self_destructs()
4358 if (attacker->type == MONS_GIANT_SPORE
4359 || attacker->type == MONS_BALL_LIGHTNING
4360 || attacker->type == MONS_ORB_OF_DESTRUCTION)
4362 attacker->as_monster()->hit_points = -1;
4363 // Do the explosion right now.
4364 monster_die(attacker->as_monster(), KILL_MON, attacker->mindex());
4365 return (true);
4367 return (false);
4370 bool melee_attack::mons_attack_warded_off()
4372 // [dshaligram] Note: warding is no longer a simple 50% chance.
4373 const int warding = defender->warding();
4374 if (warding
4375 && attacker->is_summoned()
4376 && attacker->as_monster()->check_res_magic(warding) <= 0)
4378 if (needs_message)
4380 mprf("%s tries to attack %s, but flinches away.",
4381 atk_name(DESC_CAP_THE).c_str(),
4382 mons_defender_name().c_str());
4384 return (true);
4387 return (false);
4390 int melee_attack::mons_attk_delay()
4392 return (weapon ? property(*weapon, PWPN_SPEED) : 0);
4395 bool melee_attack::attack_shield_blocked(bool verbose)
4397 if (!defender_shield && defender->atype() != ACT_PLAYER)
4398 return (false);
4400 if (defender->incapacitated())
4401 return (false);
4403 const int con_block = random2(attacker->shield_bypass_ability(to_hit)
4404 + defender->shield_block_penalty());
4405 int pro_block = defender->shield_bonus();
4407 if (!attacker->visible_to(defender))
4408 pro_block /= 3;
4410 #ifdef DEBUG_DIAGNOSTICS
4411 mprf(MSGCH_DIAGNOSTICS, "Defender: %s, Pro-block: %d, Con-block: %d",
4412 def_name(DESC_PLAIN).c_str(), pro_block, con_block);
4413 #endif
4415 if (pro_block >= con_block)
4417 perceived_attack = true;
4419 if (needs_message && verbose)
4421 mprf("%s %s %s attack.",
4422 def_name(DESC_CAP_THE).c_str(),
4423 defender->conj_verb("block").c_str(),
4424 atk_name(DESC_NOCAP_ITS).c_str());
4427 defender->shield_block_succeeded(attacker);
4429 return (true);
4432 return (false);
4435 int melee_attack::mons_calc_damage(const mon_attack_def &attk)
4437 int damage = 0;
4438 int damage_max = 0;
4439 if (weapon
4440 && weapon->base_type == OBJ_WEAPONS
4441 && !is_range_weapon(*weapon))
4443 damage_max = property(*weapon, PWPN_DAMAGE);
4444 damage += random2(damage_max);
4446 if (get_equip_race(*weapon) == ISFLAG_ORCISH
4447 && mons_genus(attacker->mons_species()) == MONS_ORC
4448 && coinflip())
4450 damage++;
4453 if (weapon->plus2 >= 0)
4454 damage += random2(weapon->plus2);
4455 else
4456 damage -= random2(1 - weapon->plus2);
4458 damage -= 1 + random2(3);
4461 damage_max += attk.damage;
4462 damage += 1 + random2(attk.damage);
4464 // Berserk/mighted/frenzied monsters get bonus damage.
4465 if (attacker->as_monster()->has_ench(ENCH_MIGHT)
4466 || attacker->as_monster()->has_ench(ENCH_BERSERK)
4467 || attacker->as_monster()->has_ench(ENCH_INSANE))
4469 damage = damage * 3 / 2;
4471 else if (attacker->as_monster()->has_ench(ENCH_BATTLE_FRENZY))
4473 const mon_enchant ench =
4474 attacker->as_monster()->get_ench(ENCH_BATTLE_FRENZY);
4476 #ifdef DEBUG_DIAGNOSTICS
4477 const int orig_damage = damage;
4478 #endif
4480 damage = damage * (115 + ench.degree * 15) / 100;
4482 #ifdef DEBUG_DIAGNOSTICS
4483 mprf(MSGCH_DIAGNOSTICS, "%s frenzy damage: %d->%d",
4484 attacker->name(DESC_PLAIN).c_str(), orig_damage, damage);
4485 #endif
4488 // If the defender is asleep, the attacker gets a stab.
4489 if (defender && defender->asleep())
4491 damage = damage * 5 / 2;
4492 #ifdef DEBUG_DIAGNOSTICS
4493 mprf(MSGCH_DIAGNOSTICS, "Stab damage vs %s: %d",
4494 defender->name(DESC_PLAIN).c_str(),
4495 damage);
4496 #endif
4499 return (mons_apply_defender_ac(damage, damage_max));
4502 int melee_attack::mons_apply_defender_ac(int damage, int damage_max)
4504 const int ac = defender->armour_class();
4505 if (ac > 0)
4507 int damage_reduction = random2(ac + 1);
4508 int guaranteed_damage_reduction = 0;
4510 if (defender->atype() == ACT_PLAYER)
4512 const int gdr_perc = defender->as_player()->gdr_perc();
4513 guaranteed_damage_reduction =
4514 std::min(damage_max * gdr_perc / 100, ac / 2);
4515 damage_reduction =
4516 std::max(guaranteed_damage_reduction, damage_reduction);
4519 dprf("AC: at: %s, df: %s, dam: %d (max %d), DR: %d (GDR %d), "
4520 "rdam: %d",
4521 attacker->name(DESC_PLAIN, true).c_str(),
4522 defender->name(DESC_PLAIN, true).c_str(),
4523 damage, damage_max, damage_reduction, guaranteed_damage_reduction,
4524 damage - damage_reduction);
4526 damage -= damage_reduction;
4528 return std::max(0, damage);
4531 static const char *klown_attack[] =
4533 "hit",
4534 "poke",
4535 "prod",
4536 "flog",
4537 "pound",
4538 "slap",
4539 "tickle",
4540 "defenestrate",
4541 "sucker-punch",
4542 "elbow",
4543 "pinch",
4544 "strangle-hug",
4545 "squeeze",
4546 "tease",
4547 "eye-gouge",
4548 "karate-kick",
4549 "headlock",
4550 "wrestle",
4551 "trip-wire",
4552 "kneecap"
4555 std::string melee_attack::mons_attack_verb(const mon_attack_def &attk)
4557 if (attacker->type == MONS_KILLER_KLOWN && attk.type == AT_HIT)
4558 return (RANDOM_ELEMENT(klown_attack));
4560 if (attk.type == AT_TENTACLE_SLAP
4561 && (attacker->type == MONS_KRAKEN_TENTACLE
4562 || attacker->type == MONS_ELDRITCH_TENTACLE))
4564 return ("slap");
4567 static const char *attack_types[] =
4570 "hit", // including weapon attacks
4571 "bite",
4572 "sting",
4574 // spore
4575 "hit",
4577 "touch",
4578 "engulf",
4579 "claw",
4580 "peck",
4581 "headbutt",
4582 "punch",
4583 "kick",
4584 "tentacle-slap",
4585 "tail-slap",
4586 "gore",
4587 "constrict",
4588 "trample",
4589 "trunk-slap",
4590 "snap closed at",
4591 "splash"
4594 ASSERT(attk.type <
4595 static_cast<int>(sizeof(attack_types) / sizeof(const char *)));
4596 return (attack_types[attk.type]);
4599 std::string melee_attack::mons_attack_desc(const mon_attack_def &attk)
4601 if (!you.can_see(attacker))
4602 return ("");
4604 if (weapon)
4606 std::string result = "";
4607 const item_def wpn = *weapon;
4608 if (get_weapon_brand(wpn) == SPWPN_REACHING)
4610 if (grid_distance(attacker->pos(), defender->pos()) == 2)
4611 result += " from afar";
4614 if (attacker->type != MONS_DANCING_WEAPON)
4616 result += " with ";
4617 result += weapon->name(DESC_NOCAP_A);
4620 return (result);
4622 else if (attk.flavour == AF_REACH
4623 && grid_distance(attacker->pos(), defender->pos()) == 2)
4625 return " from afar";
4628 return ("");
4631 std::string melee_attack::mons_defender_name()
4633 if (attacker == defender)
4634 return pronoun(attacker, PRONOUN_REFLEXIVE, attacker_visible);
4635 else
4636 return def_name(DESC_NOCAP_THE);
4639 void melee_attack::mons_announce_hit(const mon_attack_def &attk)
4641 if (needs_message)
4643 mprf("%s %s %s%s%s%s",
4644 atk_name(DESC_CAP_THE).c_str(),
4645 attacker->conj_verb(mons_attack_verb(attk)).c_str(),
4646 mons_defender_name().c_str(),
4647 debug_damage_number().c_str(),
4648 mons_attack_desc(attk).c_str(),
4649 attack_strength_punctuation().c_str());
4653 void melee_attack::mons_announce_dud_hit(const mon_attack_def &attk)
4655 if (needs_message)
4657 mprf("%s %s %s but does no damage.",
4658 atk_name(DESC_CAP_THE).c_str(),
4659 attacker->conj_verb(mons_attack_verb(attk)).c_str(),
4660 mons_defender_name().c_str());
4664 void melee_attack::mons_set_weapon(const mon_attack_def &attk)
4666 weapon = (attk.type == AT_HIT) ? attacker->weapon(attack_number) : NULL;
4667 damage_brand = attacker->damage_brand(attack_number);
4670 void melee_attack::mons_do_poison(const mon_attack_def &attk)
4672 if (defender->res_poison() > 0)
4673 return;
4675 if (attk.flavour == AF_POISON_NASTY
4676 || one_chance_in(15 + 5 * (attk.flavour == AF_POISON ? 1 : 0))
4677 || (damage_done > 1
4678 && one_chance_in(attk.flavour == AF_POISON ? 4 : 3)))
4680 if (needs_message)
4682 if (defender->atype() == ACT_PLAYER
4683 && (attk.type == AT_BITE || attk.type == AT_STING))
4685 if (attacker_visible)
4687 mprf("%s %s was poisonous!",
4688 apostrophise(attacker->name(DESC_CAP_THE)).c_str(),
4689 mons_attack_verb(attk).c_str());
4692 else
4694 mprf("%s poisons %s!",
4695 atk_name(DESC_CAP_THE).c_str(),
4696 mons_defender_name().c_str());
4700 int amount = 1;
4701 if (attk.flavour == AF_POISON_NASTY)
4702 amount++;
4703 else if (attk.flavour == AF_POISON_MEDIUM)
4704 amount += random2(3);
4705 else if (attk.flavour == AF_POISON_STRONG)
4706 amount += roll_dice(2, 5);
4708 defender->poison(attacker, amount);
4712 void melee_attack::mons_do_napalm()
4714 if (defender->res_sticky_flame())
4715 return;
4717 if (one_chance_in(20) || (damage_done > 2 && one_chance_in(3)))
4719 if (needs_message)
4721 mprf("%s %s covered in liquid flames%s",
4722 def_name(DESC_CAP_THE).c_str(),
4723 defender->conj_verb("are").c_str(),
4724 special_attack_punctuation().c_str());
4727 if (defender->atype() == ACT_PLAYER)
4728 napalm_player(random2avg(7, 3) + 1);
4729 else
4731 napalm_monster(
4732 defender->as_monster(),
4733 attacker->as_monster()->friendly() ?
4734 KC_FRIENDLY : KC_OTHER,
4735 std::min(4, 1 + random2(attacker->get_experience_level())/2));
4740 void melee_attack::wasp_paralyse_defender()
4742 // [dshaligram] Adopted 4.1.2's wasp mechanics, in slightly modified
4743 // form.
4744 if (attacker->type == MONS_RED_WASP || one_chance_in(3))
4745 defender->poison(attacker, coinflip() ? 2 : 1);
4747 int paralyse_roll = (damage_done > 4 ? 3 : 20);
4748 if (attacker->type == MONS_YELLOW_WASP)
4749 paralyse_roll += 3;
4751 if (defender->res_poison() <= 0)
4753 if (one_chance_in(paralyse_roll))
4754 defender->paralyse(attacker, roll_dice(1, 3));
4755 else
4756 defender->slow_down(attacker, roll_dice(1, 3));
4760 void melee_attack::splash_monster_with_acid(int strength)
4762 special_damage += roll_dice(2, 4);
4763 if (defender_visible)
4764 mprf("%s is splashed with acid.", defender->name(DESC_CAP_THE).c_str());
4765 corrode_monster(defender->as_monster());
4768 void melee_attack::splash_defender_with_acid(int strength)
4770 if (defender->atype() == ACT_PLAYER)
4772 mpr("You are splashed with acid!");
4773 splash_with_acid(strength);
4775 else
4776 splash_monster_with_acid(strength);
4779 static void _steal_item_from_player(monster* mon)
4781 if (mon->confused())
4783 std::string msg = getSpeakString("Maurice confused nonstealing");
4784 if (!msg.empty() && msg != "__NONE")
4786 msg = replace_all(msg, "@The_monster@", mon->name(DESC_CAP_THE));
4787 mpr(msg.c_str(), MSGCH_TALK);
4789 return;
4792 mon_inv_type mslot = NUM_MONSTER_SLOTS;
4793 int steal_what = -1;
4794 int total_value = 0;
4795 for (int m = 0; m < ENDOFPACK; ++m)
4797 if (!you.inv[m].defined())
4798 continue;
4800 // Cannot unequip player.
4801 // TODO: Allow stealing of the wielded weapon?
4802 // Needs to be unwielded properly and should never lead to
4803 // fatal stat loss.
4804 // 1KB: I'd say no, weapon is being held, it's different from pulling
4805 // a wand from your pocket.
4806 if (item_is_equipped(you.inv[m]))
4807 continue;
4809 mon_inv_type monslot = item_to_mslot(you.inv[m]);
4810 if (monslot == NUM_MONSTER_SLOTS)
4812 // Try a related slot instead to allow for stealing of other
4813 // valuable items.
4814 if (you.inv[m].base_type == OBJ_BOOKS)
4815 monslot = MSLOT_SCROLL;
4816 else if (you.inv[m].base_type == OBJ_JEWELLERY)
4817 monslot = MSLOT_MISCELLANY;
4818 else
4819 continue;
4822 // Only try to steal stuff we can still store somewhere.
4823 if (mon->inv[monslot] != NON_ITEM)
4825 if (monslot == MSLOT_WEAPON
4826 && mon->inv[MSLOT_ALT_WEAPON] == NON_ITEM)
4828 monslot = MSLOT_ALT_WEAPON;
4830 else
4831 continue;
4834 // Candidate for stealing.
4835 const int value = item_value(you.inv[m], true);
4836 total_value += value;
4838 if (x_chance_in_y(value, total_value))
4840 steal_what = m;
4841 mslot = monslot;
4845 if (steal_what == -1 || you.gold > 0 && one_chance_in(10))
4847 // Found no item worth stealing, try gold.
4848 if (you.gold == 0)
4850 if (silenced(mon->pos()))
4851 return;
4853 std::string complaint = getSpeakString("Maurice nonstealing");
4854 if (!complaint.empty())
4856 complaint = replace_all(complaint, "@The_monster@",
4857 mon->name(DESC_CAP_THE));
4858 mpr(complaint.c_str(), MSGCH_TALK);
4861 bolt beem;
4862 beem.source = mon->pos();
4863 beem.target = mon->pos();
4864 beem.beam_source = mon->mindex();
4866 // Try to teleport away.
4867 if (mon->has_ench(ENCH_TP))
4869 mons_cast_noise(mon, beem, SPELL_BLINK);
4870 monster_blink(mon);
4872 else
4873 mons_cast(mon, beem, SPELL_TELEPORT_SELF);
4875 return;
4878 const int stolen_amount = std::min(20 + random2(800), you.gold);
4879 if (mon->inv[MSLOT_GOLD] != NON_ITEM)
4881 // If Maurice already's got some gold, simply increase the amount.
4882 mitm[mon->inv[MSLOT_GOLD]].quantity += stolen_amount;
4884 else
4886 // Else create a new item for this pile of gold.
4887 const int idx = items(0, OBJ_GOLD, OBJ_RANDOM, true, 0, 0);
4888 if (idx == NON_ITEM)
4889 return;
4891 item_def &new_item = mitm[idx];
4892 new_item.base_type = OBJ_GOLD;
4893 new_item.sub_type = 0;
4894 new_item.plus = 0;
4895 new_item.plus2 = 0;
4896 new_item.special = 0;
4897 new_item.flags = 0;
4898 new_item.link = NON_ITEM;
4899 new_item.quantity = stolen_amount;
4900 new_item.pos.reset();
4901 item_colour(new_item);
4903 unlink_item(idx);
4905 mon->inv[MSLOT_GOLD] = idx;
4906 new_item.set_holding_monster(mon->mindex());
4908 mprf("%s steals %s your gold!",
4909 mon->name(DESC_CAP_THE).c_str(),
4910 stolen_amount == you.gold ? "all" : "some of");
4912 you.attribute[ATTR_GOLD_FOUND] -= stolen_amount;
4914 you.del_gold(stolen_amount);
4915 return;
4918 ASSERT(steal_what != -1);
4919 ASSERT(mslot != NUM_MONSTER_SLOTS);
4920 ASSERT(mon->inv[mslot] == NON_ITEM);
4922 // Create new item.
4923 int index = get_item_slot(10);
4924 if (index == NON_ITEM)
4925 return;
4927 item_def &new_item = mitm[index];
4929 // Copy item.
4930 new_item = you.inv[steal_what];
4932 // Set quantity, and set the item as unlinked.
4933 new_item.quantity -= random2(new_item.quantity);
4934 new_item.pos.reset();
4935 new_item.link = NON_ITEM;
4937 mprf("%s steals %s!",
4938 mon->name(DESC_CAP_THE).c_str(),
4939 new_item.name(DESC_NOCAP_YOUR).c_str());
4941 unlink_item(index);
4942 mon->inv[mslot] = index;
4943 new_item.set_holding_monster(mon->mindex());
4944 // You'll want to autopickup it after killing Maurice.
4945 new_item.flags |= ISFLAG_THROWN;
4946 mon->equip(new_item, mslot, true);
4948 // Item is gone from player's inventory.
4949 dec_inv_item_quantity(steal_what, new_item.quantity);
4952 void melee_attack::mons_apply_attack_flavour(const mon_attack_def &attk)
4954 // Most of this is from BWR 4.1.2.
4956 mon_attack_flavour flavour = attk.flavour;
4957 if (flavour == AF_CHAOS)
4958 flavour = random_chaos_attack_flavour();
4960 switch (flavour)
4962 default:
4963 break;
4965 case AF_MUTATE:
4966 if (one_chance_in(4))
4967 defender->mutate();
4968 break;
4970 case AF_POISON:
4971 case AF_POISON_NASTY:
4972 case AF_POISON_MEDIUM:
4973 case AF_POISON_STRONG:
4974 mons_do_poison(attk);
4975 break;
4977 case AF_POISON_STR:
4978 case AF_POISON_INT:
4979 case AF_POISON_DEX:
4980 if (defender->res_poison() <= 0)
4982 defender->poison(attacker, roll_dice(1, 3));
4983 if (one_chance_in(4))
4985 stat_type drained_stat = (flavour == AF_POISON_STR ? STAT_STR :
4986 flavour == AF_POISON_INT ? STAT_INT
4987 : STAT_DEX);
4988 defender->drain_stat(drained_stat, 1, attacker);
4991 break;
4993 case AF_ROT:
4994 if (one_chance_in(20) || (damage_done > 2 && one_chance_in(3)))
4995 rot_defender(2 + random2(3), damage_done > 5 ? 1 : 0);
4996 break;
4998 case AF_DISEASE:
4999 defender->sicken(50 + random2(100));
5000 break;
5002 case AF_FIRE:
5003 if (attacker->type == MONS_FIRE_VORTEX)
5004 attacker->as_monster()->hit_points = -10;
5006 special_damage =
5007 resist_adjust_damage(defender,
5008 BEAM_FIRE,
5009 defender->res_fire(),
5010 attacker->get_experience_level()
5011 + random2(attacker->get_experience_level()));
5013 if (needs_message && special_damage)
5015 mprf("%s %s engulfed in flames%s",
5016 def_name(DESC_CAP_THE).c_str(),
5017 defender->conj_verb("are").c_str(),
5018 special_attack_punctuation().c_str());
5021 defender->expose_to_element(BEAM_FIRE, 2);
5022 break;
5024 case AF_COLD:
5025 special_damage =
5026 resist_adjust_damage(defender,
5027 BEAM_COLD,
5028 defender->res_cold(),
5029 attacker->get_experience_level() +
5030 random2(2 * attacker->get_experience_level()));
5032 if (needs_message && special_damage)
5034 mprf("%s %s %s%s",
5035 atk_name(DESC_CAP_THE).c_str(),
5036 attacker->conj_verb("freeze").c_str(),
5037 mons_defender_name().c_str(),
5038 special_attack_punctuation().c_str());
5042 defender->expose_to_element(BEAM_COLD, 2);
5043 break;
5045 case AF_ELEC:
5046 special_damage =
5047 resist_adjust_damage(
5048 defender,
5049 BEAM_ELECTRICITY,
5050 defender->res_elec(),
5051 attacker->get_experience_level() +
5052 random2(attacker->get_experience_level() / 2));
5053 special_damage_flavour = BEAM_ELECTRICITY;
5055 if (defender->airborne())
5056 special_damage = special_damage * 2 / 3;
5058 if (needs_message && special_damage)
5060 mprf("%s %s %s%s",
5061 atk_name(DESC_CAP_THE).c_str(),
5062 attacker->conj_verb("shock").c_str(),
5063 mons_defender_name().c_str(),
5064 special_attack_punctuation().c_str());
5067 dprf("Shock damage: %d", special_damage);
5068 break;
5070 case AF_VAMPIRIC:
5071 // Only may bite non-vampiric monsters (or player) capable of bleeding.
5072 if (!defender->can_bleed())
5073 break;
5075 // Disallow draining of summoned monsters since they can't bleed.
5076 // XXX: Is this too harsh?
5077 if (defender->is_summoned())
5078 break;
5080 if (x_chance_in_y(defender->res_negative_energy(), 3))
5081 break;
5083 if (defender->stat_hp() < defender->stat_maxhp())
5085 attacker->heal(1 + random2(damage_done), coinflip());
5087 if (needs_message)
5089 mprf("%s %s strength from %s injuries!",
5090 atk_name(DESC_CAP_THE).c_str(),
5091 attacker->conj_verb("draw").c_str(),
5092 def_name(DESC_NOCAP_ITS).c_str());
5095 // 4.1.2 actually drains max hp; we're being nicer and just doing
5096 // a rot effect.
5097 if ((damage_done > 6 && one_chance_in(3)) || one_chance_in(20))
5099 if (defender->atype() == ACT_PLAYER)
5100 mprf("You feel less resilient.");
5101 rot_defender(0, coinflip() ? 2 : 1);
5104 break;
5106 case AF_DRAIN_STR:
5107 case AF_DRAIN_INT:
5108 case AF_DRAIN_DEX:
5109 if ((one_chance_in(20) || (damage_done > 0 && one_chance_in(3)))
5110 && defender->res_negative_energy() < random2(4))
5112 stat_type drained_stat = (flavour == AF_DRAIN_STR ? STAT_STR :
5113 flavour == AF_DRAIN_INT ? STAT_INT
5114 : STAT_DEX);
5115 defender->drain_stat(drained_stat, 1, attacker);
5117 break;
5119 case AF_HUNGER:
5120 if (defender->holiness() == MH_UNDEAD)
5121 break;
5123 if (one_chance_in(20) || (damage_done > 0 && coinflip()))
5124 defender->make_hungry(400, false);
5125 break;
5127 case AF_BLINK:
5128 if (one_chance_in(3))
5130 if (attacker_visible)
5132 mprf("%s %s!", attacker->name(DESC_CAP_THE).c_str(),
5133 attacker->conj_verb("blink").c_str());
5135 attacker->blink();
5137 break;
5139 case AF_CONFUSE:
5140 if (attk.type == AT_SPORE)
5142 if (defender->res_poison() > 0)
5143 break;
5145 if (--(attacker->as_monster()->hit_dice) <= 0)
5146 attacker->as_monster()->hit_points = -1;
5148 if (defender_visible)
5150 mprf("%s %s engulfed in a cloud of spores!",
5151 defender->name(DESC_CAP_THE).c_str(),
5152 defender->conj_verb("are").c_str());
5156 if (one_chance_in(10)
5157 || (damage_done > 2 && one_chance_in(3)))
5159 defender->confuse(attacker,
5160 1 + random2(3+attacker->get_experience_level()));
5162 break;
5164 case AF_DRAIN_XP:
5165 if (one_chance_in(30)
5166 || (damage_done > 5 && coinflip())
5167 || (attk.damage == 0 && !one_chance_in(3)))
5169 drain_defender();
5171 break;
5173 case AF_PARALYSE:
5174 // Only wasps at the moment.
5175 wasp_paralyse_defender();
5176 break;
5178 case AF_ACID:
5179 if (attacker->type == MONS_SPINY_WORM && defender->res_poison() <= 0)
5180 defender->poison(attacker, 2 + random2(4));
5181 splash_defender_with_acid(3);
5182 break;
5184 case AF_DISTORT:
5185 distortion_affects_defender();
5186 break;
5188 case AF_RAGE:
5189 if (!one_chance_in(3) || !defender->can_go_berserk())
5190 break;
5192 if (needs_message)
5194 mprf("%s %s %s!",
5195 atk_name(DESC_CAP_THE).c_str(),
5196 attacker->conj_verb("infuriate").c_str(),
5197 mons_defender_name().c_str());
5200 defender->go_berserk(false);
5201 break;
5203 case AF_NAPALM:
5204 mons_do_napalm();
5205 break;
5207 case AF_CHAOS:
5208 chaos_affects_defender();
5209 break;
5211 case AF_STEAL:
5212 // Ignore monsters, for now.
5213 if (defender->atype() != ACT_PLAYER)
5214 break;
5216 _steal_item_from_player(attacker->as_monster());
5217 break;
5219 case AF_STEAL_FOOD:
5221 // Monsters don't carry food.
5222 if (defender->atype() != ACT_PLAYER)
5223 break;
5225 const bool stolen = expose_player_to_element(BEAM_DEVOUR_FOOD, 10);
5226 const bool ground = expose_items_to_element(BEAM_DEVOUR_FOOD, you.pos(),
5227 10);
5228 if (needs_message)
5230 if (stolen)
5232 mprf("%s devours some of your food!",
5233 atk_name(DESC_CAP_THE).c_str());
5235 else if (ground)
5237 mprf("%s devours some of the food beneath you!",
5238 atk_name(DESC_CAP_THE).c_str());
5241 break;
5244 case AF_CRUSH:
5246 mprf("%s %s being crushed%s",
5247 def_name(DESC_CAP_THE).c_str(),
5248 defender->conj_verb("are").c_str(),
5249 special_attack_punctuation().c_str());
5251 break;
5253 case AF_HOLY:
5255 if (defender->is_evil() || defender->is_unholy())
5256 special_damage = attk.damage * 0.75;
5258 if (needs_message && special_damage)
5260 mprf("%s %s %s%s",
5261 atk_name(DESC_CAP_THE).c_str(),
5262 attacker->conj_verb("sear").c_str(),
5263 mons_defender_name().c_str(),
5264 special_attack_punctuation().c_str());
5268 break;
5274 void melee_attack::mons_do_passive_freeze()
5276 if (you.mutation[MUT_PASSIVE_FREEZE]
5277 && attacker->alive()
5278 && grid_distance(you.pos(), attacker->as_monster()->pos()) == 1)
5280 bolt beam;
5281 beam.flavour = BEAM_COLD;
5282 beam.thrower = KILL_YOU;
5284 monster* mon = attacker->as_monster();
5286 const int orig_hurted = random2(11);
5287 int hurted = mons_adjust_flavoured(mon, beam, orig_hurted);
5289 if (!hurted)
5290 return;
5292 simple_monster_message(mon, " is very cold.");
5294 #ifndef USE_TILE
5295 flash_monster_colour(mon, LIGHTBLUE, 200);
5296 #endif
5298 mon->hurt(&you, hurted);
5300 if (mon->alive())
5302 mon->expose_to_element(BEAM_COLD, orig_hurted);
5303 print_wounds(mon);
5305 const int cold_res = mon->res_cold();
5307 if (cold_res <= 0)
5309 const int stun = (1 - cold_res) * random2(7);
5310 mon->speed_increment -= stun;
5316 void melee_attack::mons_do_eyeball_confusion()
5318 if (you.mutation[MUT_EYEBALLS]
5319 && attacker->alive()
5320 && grid_distance(you.pos(), attacker->as_monster()->pos()) == 1
5321 && x_chance_in_y(player_mutation_level(MUT_EYEBALLS), 20))
5323 const int ench_pow = player_mutation_level(MUT_EYEBALLS) * 30;
5324 monster* mon = attacker->as_monster();
5326 if (mon->check_res_magic(ench_pow) <= 0
5327 && mons_class_is_confusable(mon->type))
5329 mprf("The eyeballs on your body gaze at %s.",
5330 mon->name(DESC_NOCAP_THE).c_str());
5332 mon->add_ench(mon_enchant(ENCH_CONFUSION, 0, KC_YOU,
5333 30 + random2(100)));
5338 void melee_attack::mons_do_spines()
5340 const item_def *body = you.slot_item(EQ_BODY_ARMOUR, false);
5341 const int mut = player_mutation_level(MUT_SPINY);
5342 int evp = 0;
5343 defer_rand r;
5345 if (body)
5346 evp = -property(*body, PARM_EVASION);
5348 if (you.mutation[MUT_SPINY] && attacker->alive() && one_chance_in(evp + 1))
5350 if (test_melee_hit(2 + 4 * mut, attacker->melee_evasion(defender), r)
5351 < 0)
5353 simple_monster_message(attacker->as_monster(),
5354 " dodges your spines.");
5355 return;
5358 int dmg = roll_dice(mut, 6);
5359 int ac = random2(1 + attacker->armour_class());
5360 int hurt = dmg - ac - evp;
5362 dprf("Spiny: dmg = %d ac = %d hurt = %d", dmg, ac, hurt);
5364 if (hurt <= 0)
5365 return;
5367 if (!defender_invisible)
5369 simple_monster_message(attacker->as_monster(),
5370 " is struck by your spines.");
5373 attacker->hurt(&you, hurt);
5377 bool melee_attack::do_trample()
5381 monster* def_monster = defender->as_monster();
5382 if (def_monster && mons_is_stationary(def_monster))
5383 // don't even print a message
5384 return false;
5386 int size_diff =
5387 attacker->body_size(PSIZE_BODY) - defender->body_size(PSIZE_BODY);
5388 if (!x_chance_in_y(size_diff + 3, 6))
5389 break;
5391 coord_def old_pos = defender->pos();
5392 coord_def new_pos = defender->pos() + defender->pos() - attacker->pos();
5394 // need a valid tile
5395 if (grd(new_pos) < DNGN_SHALLOW_WATER && !defender->is_habitable(new_pos))
5396 break;
5398 // don't trample into a monster - or do we want to cause a chain
5399 // reaction here?
5400 if (actor_at(new_pos))
5401 break;
5403 defender->move_to_pos(new_pos);
5405 if (attacker->is_habitable(old_pos))
5406 attacker->move_to_pos(old_pos);
5408 if (needs_message)
5410 mprf("%s %s backwards!",
5411 def_name(DESC_CAP_THE).c_str(),
5412 defender->conj_verb("stumble").c_str());
5415 // Interrupt stair travel and passwall.
5416 if (defender == &you)
5417 stop_delay(true);
5419 return true;
5420 } while (0);
5422 if (needs_message)
5424 mprf("%s %s %s ground!",
5425 def_name(DESC_CAP_THE).c_str(),
5426 defender->conj_verb("hold").c_str(),
5427 defender->pronoun(PRONOUN_NOCAP_POSSESSIVE).c_str());
5430 return false;
5433 void melee_attack::mons_perform_attack_rounds()
5435 const int nrounds = attacker->as_monster()->has_hydra_multi_attack() ?
5436 attacker->as_monster()->number : 4;
5437 coord_def pos = defender->pos();
5438 const bool was_delayed = you_are_delayed();
5440 // Melee combat, tell attacker to wield its melee weapon.
5441 attacker->as_monster()->wield_melee_weapon();
5443 monster* def_copy = NULL;
5444 int effective_attack_number = 0;
5445 for (attack_number = 0; attack_number < nrounds && attacker->alive();
5446 ++attack_number, ++effective_attack_number)
5448 // Handle noise from previous round.
5449 if (effective_attack_number > 0)
5450 handle_noise(pos);
5452 if (!attacker->alive())
5453 return;
5455 // Monster went away?
5456 if (!defender->alive() || defender->pos() != pos)
5458 if (attacker == defender
5459 || !attacker->as_monster()->has_multitargeting())
5461 break;
5464 // Hydras can try and pick up a new monster to attack to
5465 // finish out their round. -cao
5466 bool end = true;
5467 for (adjacent_iterator i(attacker->pos()); i; ++i)
5469 if (*i == you.pos()
5470 && !mons_aligned(attacker, &you))
5472 attacker->as_monster()->foe = MHITYOU;
5473 attacker->as_monster()->target = you.pos();
5474 defender = &you;
5475 end = false;
5476 break;
5479 monster* mons = monster_at(*i);
5480 if (mons && !mons_aligned(attacker, mons))
5482 defender = mons;
5483 end = false;
5484 pos = mons->pos();
5485 break;
5489 // No adjacent hostiles.
5490 if (end)
5491 break;
5494 // Monsters hitting themselves get just one round.
5495 if (attack_number > 0 && attacker == defender)
5496 break;
5498 init_attack();
5500 mon_attack_def attk = mons_attack_spec(attacker->as_monster(),
5501 attack_number);
5502 if (attk.type == AT_WEAP_ONLY)
5504 int weap = attacker->as_monster()->inv[MSLOT_WEAPON];
5505 if (weap == NON_ITEM)
5506 attk.type = AT_NONE;
5507 else if (is_range_weapon(mitm[weap]))
5508 attk.type = AT_SHOOT;
5509 else
5510 attk.type = AT_HIT;
5513 if (attk.type == AT_NONE)
5515 // Make sure the monster uses up some energy, even though it
5516 // didn't actually attack.
5517 if (effective_attack_number == 0)
5518 attacker->as_monster()->lose_energy(EUT_ATTACK);
5519 break;
5522 // Skip dummy attacks.
5523 if ((!unarmed_ok && attk.type != AT_HIT && attk.flavour != AF_REACH)
5524 || attk.type == AT_SHOOT)
5526 --effective_attack_number;
5527 continue;
5530 if (weapon == NULL)
5532 switch (attk.type)
5534 case AT_HEADBUTT:
5535 case AT_TENTACLE_SLAP:
5536 case AT_TAIL_SLAP:
5537 case AT_TRAMPLE:
5538 case AT_TRUNK_SLAP:
5539 noise_factor = 150;
5540 break;
5542 case AT_HIT:
5543 case AT_PUNCH:
5544 case AT_KICK:
5545 case AT_CLAW:
5546 case AT_GORE:
5547 case AT_SNAP:
5548 case AT_SPLASH:
5549 noise_factor = 125;
5550 break;
5552 case AT_BITE:
5553 case AT_PECK:
5554 case AT_CONSTRICT:
5555 noise_factor = 100;
5556 break;
5558 case AT_STING:
5559 case AT_SPORE:
5560 case AT_ENGULF:
5561 noise_factor = 75;
5562 break;
5564 case AT_TOUCH:
5565 noise_factor = 0;
5566 break;
5568 // To prevent compiler warnings.
5569 case AT_NONE:
5570 case AT_RANDOM:
5571 case AT_SHOOT:
5572 die("Invalid attack flavour for noise_factor");
5573 break;
5575 default:
5576 die("Unhandled attack flavour for noise_factor");
5577 break;
5580 switch(attk.flavour)
5582 case AF_FIRE:
5583 noise_factor += 50;
5584 break;
5586 case AF_ELEC:
5587 noise_factor += 100;
5588 break;
5590 default:
5591 break;
5595 damage_done = 0;
5596 mons_set_weapon(attk);
5597 to_hit = mons_to_hit();
5599 const bool chaos_attack = damage_brand == SPWPN_CHAOS
5600 || (attk.flavour == AF_CHAOS
5601 && attacker != defender);
5603 // Make copy of monster before monster_die() resets it.
5604 if (chaos_attack && defender->atype() == ACT_MONSTER && !def_copy)
5605 def_copy = new monster(*defender->as_monster());
5607 final_attack_delay = mons_attk_delay();
5608 if (damage_brand == SPWPN_SPEED)
5609 final_attack_delay = final_attack_delay / 2 + 1;
5611 mons_lose_attack_energy(attacker->as_monster(),
5612 final_attack_delay,
5613 attack_number,
5614 effective_attack_number);
5616 bool shield_blocked = false;
5617 bool this_round_hit = false;
5619 if (attacker != defender)
5621 if (attack_shield_blocked(true))
5623 shield_blocked = true;
5624 perceived_attack = true;
5625 this_round_hit = did_hit = true;
5627 // XXX: what is the chance for here?
5628 else if (attacker_visible && one_chance_in(3))
5630 perceived_attack = true;
5631 if (defender == &you)
5632 practise(EX_MONSTER_MAY_HIT);
5636 if (!shield_blocked)
5638 const int defender_evasion = defender->melee_evasion(attacker);
5639 int defender_evasion_help
5640 = defender->melee_evasion(attacker, EV_IGNORE_HELPLESS);
5641 int defender_evasion_nophase
5642 = defender->melee_evasion(attacker, EV_IGNORE_PHASESHIFT);
5644 defer_rand r;
5646 if (defender_invisible)
5648 // No evasion feedback if we don't know what we're
5649 // fighting.
5650 defender_evasion_help = defender_evasion;
5651 defender_evasion_nophase = defender_evasion;
5654 ev_margin = test_melee_hit(to_hit, defender_evasion_help, r);
5656 if (attacker == defender || ev_margin >= 0)
5658 // Will hit no matter what.
5659 this_round_hit = true;
5661 else if (test_melee_hit(to_hit, defender_evasion, r) >= 0)
5663 if (needs_message)
5665 mprf("Helpless, %s %s to dodge %s attack.",
5666 mons_defender_name().c_str(),
5667 defender->conj_verb("fail").c_str(),
5668 atk_name(DESC_NOCAP_ITS).c_str());
5670 this_round_hit = true;
5672 else if (test_melee_hit(to_hit, defender_evasion_nophase, r) >= 0)
5674 if (needs_message)
5676 mprf("%s momentarily %s out as %s "
5677 "attack passes through %s%s",
5678 defender->name(DESC_CAP_THE).c_str(),
5679 defender->conj_verb("phase").c_str(),
5680 atk_name(DESC_NOCAP_ITS).c_str(),
5681 defender->pronoun(PRONOUN_OBJECTIVE).c_str(),
5682 attack_strength_punctuation().c_str());
5684 this_round_hit = false;
5686 else
5688 // Misses no matter what.
5689 if (needs_message)
5691 mprf("%s%s misses %s%s",
5692 atk_name(DESC_CAP_THE).c_str(),
5693 evasion_margin_adverb().c_str(),
5694 mons_defender_name().c_str(),
5695 attack_strength_punctuation().c_str());
5699 if (this_round_hit)
5701 did_hit = true;
5702 perceived_attack = true;
5703 damage_done = mons_calc_damage(attk);
5705 else
5707 perceived_attack = perceived_attack || attacker_visible;
5710 if (attacker != defender &&
5711 defender->atype() == ACT_PLAYER &&
5712 (grid_distance(you.pos(), attacker->as_monster()->pos()) == 1
5713 || attk.flavour == AF_REACH))
5715 // Check for spiny mutation.
5716 mons_do_spines();
5718 // Spines can kill!
5719 if (!attacker->alive())
5720 break;
5724 if (check_unrand_effects())
5725 break;
5727 if (damage_done < 1 && this_round_hit && !shield_blocked)
5728 mons_announce_dud_hit(attk);
5730 if (damage_done > 0)
5732 if (shield_blocked)
5733 dprf("ERROR: Non-zero damage after shield block!");
5734 mons_announce_hit(attk);
5736 if (defender == &you)
5737 practise(EX_MONSTER_WILL_HIT);
5739 if (defender->can_bleed()
5740 && !defender->is_summoned()
5741 && !defender->submerged())
5743 int blood = _modify_blood_amount(damage_done,
5744 attacker->damage_type());
5746 if (blood > defender->stat_hp())
5747 blood = defender->stat_hp();
5749 bleed_onto_floor(pos, defender->type, blood, true);
5752 if (decapitate_hydra(damage_done,
5753 attacker->damage_type(attack_number)))
5755 continue;
5758 special_damage = 0;
5759 special_damage_message.clear();
5760 special_damage_flavour = BEAM_NONE;
5762 if (attacker != defender && attk.type == AT_TRAMPLE)
5763 do_trample();
5765 // Monsters attacking themselves don't get attack flavour.
5766 // The message sequences look too weird. Also, stealing
5767 // attacks aren't handled until after the damage msg.
5768 if (attacker != defender && attk.flavour != AF_STEAL)
5769 mons_apply_attack_flavour(attk);
5771 if (needs_message && !special_damage_message.empty())
5772 mprf("%s", special_damage_message.c_str());
5774 // Defender banished. Bail before chaos_killed_defender()
5775 // is called, since the defender is still alive in the
5776 // Abyss.
5777 if (is_banished(defender))
5779 if (chaos_attack && attacker->alive())
5780 chaos_affects_attacker();
5782 do_miscast();
5783 break;
5786 defender->hurt(attacker, damage_done + special_damage,
5787 special_damage_flavour);
5789 if (!defender->alive())
5791 if (chaos_attack && defender->atype() == ACT_MONSTER)
5792 chaos_killed_defender(def_copy);
5794 if (chaos_attack && attacker->alive())
5795 chaos_affects_attacker();
5797 do_miscast();
5798 continue;
5801 // Yredelemnul's injury mirroring can kill the attacker.
5802 // Also, bail if the monster is attacking itself without a
5803 // weapon, since intrinsic monster attack flavours aren't
5804 // applied for self-attacks.
5805 if (!attacker->alive() || (attacker == defender && !weapon))
5807 if (miscast_target == defender)
5808 do_miscast();
5809 break;
5812 special_damage = 0;
5813 special_damage_message.clear();
5814 special_damage_flavour = BEAM_NONE;
5815 apply_damage_brand();
5817 if (needs_message && !special_damage_message.empty())
5819 mprf("%s", special_damage_message.c_str());
5820 // Don't do message-only miscasts along with a special
5821 // damage message.
5822 if (miscast_level == 0)
5823 miscast_level = -1;
5826 if (special_damage > 0)
5827 defender->hurt(attacker, special_damage, special_damage_flavour);
5829 if (!defender->alive())
5831 if (chaos_attack && defender->atype() == ACT_MONSTER)
5832 chaos_killed_defender(def_copy);
5834 if (chaos_attack && attacker->alive())
5835 chaos_affects_attacker();
5837 do_miscast();
5838 continue;
5841 if (chaos_attack && attacker->alive())
5842 chaos_affects_attacker();
5844 if (miscast_target == defender)
5845 do_miscast();
5847 // Yredelemnul's injury mirroring can kill the attacker.
5848 if (!attacker->alive())
5849 break;
5851 if (miscast_target == attacker)
5852 do_miscast();
5854 // Miscast might have killed the attacker.
5855 if (!attacker->alive())
5856 break;
5858 if (attk.flavour == AF_STEAL)
5859 mons_apply_attack_flavour(attk);
5862 item_def *weap = attacker->as_monster()->mslot_item(MSLOT_WEAPON);
5863 if (weap && you.can_see(attacker) && weap->cursed()
5864 && is_range_weapon(*weap))
5866 set_ident_flags(*weap, ISFLAG_KNOW_CURSE);
5870 // Check for passive freeze or eyeball mutation.
5871 if (defender->atype() == ACT_PLAYER && defender->alive()
5872 && attacker != defender)
5874 mons_do_eyeball_confusion();
5875 mons_do_passive_freeze();
5878 // Handle noise from last round.
5879 handle_noise(pos);
5881 if (def_copy)
5882 delete def_copy;
5884 // Invisible monster might have interrupted butchering.
5885 if (was_delayed && defender->atype() == ACT_PLAYER && perceived_attack
5886 && !attacker_visible)
5888 handle_interrupted_swap(false, true);
5892 bool melee_attack::mons_perform_attack()
5894 if (attacker != defender && mons_self_destructs())
5895 return (did_hit = perceived_attack = true);
5897 if (attacker != defender && mons_attack_warded_off())
5899 // A warded-off attack takes half the normal energy.
5900 attacker->lose_energy(EUT_ATTACK, 2);
5902 perceived_attack = true;
5903 return (false);
5906 mons_perform_attack_rounds();
5908 return (did_hit);
5911 void melee_attack::mons_check_attack_perceived()
5913 if (!perceived_attack)
5914 return;
5916 if (attacker->alive() && defender->atype() == ACT_PLAYER)
5918 interrupt_activity(AI_MONSTER_ATTACKS, attacker->as_monster());
5920 // If a friend wants to help, they can attack the attacking
5921 // monster, unless sanctuary is in effect since pet_target can
5922 // only be changed explicitly by the player during sanctuary.
5923 if (you.pet_target == MHITNOT && env.sanctuary_time <= 0)
5924 you.pet_target = attacker->mindex();
5928 bool melee_attack::mons_attack_you()
5930 mons_perform_attack();
5931 mons_check_attack_perceived();
5932 return (did_hit);
5935 int melee_attack::mons_to_hit()
5937 const int hd_mult = mons_class_flag(attacker->type, M_FIGHTER)? 25 : 15;
5938 int mhit = 18 + attacker->get_experience_level() * hd_mult / 10;
5940 #ifdef DEBUG_DIAGNOSTICS
5941 const int base_hit = mhit;
5942 #endif
5944 if (weapon && weapon->base_type == OBJ_WEAPONS)
5945 mhit += weapon->plus + property(*weapon, PWPN_HIT);
5947 if (attacker->confused())
5948 mhit -= 5;
5950 if (defender->backlit(true, false))
5951 mhit += 2 + random2(8);
5953 if (defender->atype() == ACT_PLAYER
5954 && player_mutation_level(MUT_TRANSLUCENT_SKIN) >= 3)
5955 mhit -= 5;
5957 // Invisible defender is hard to hit if you can't see invis. Note
5958 // that this applies only to monsters vs monster and monster vs
5959 // player. Does not apply to a player fighting an invisible
5960 // monster.
5961 if (!defender->visible_to(attacker))
5962 mhit = mhit * 65 / 100;
5964 #ifdef DEBUG_DIAGNOSTICS
5965 mprf(MSGCH_DIAGNOSTICS, "%s: Base to-hit: %d, Final to-hit: %d",
5966 attacker->name(DESC_PLAIN).c_str(),
5967 base_hit, mhit);
5968 #endif
5970 return (mhit);
5973 void melee_attack::chaos_affect_actor(actor *victim)
5975 melee_attack attk(victim, victim);
5976 attk.weapon = NULL;
5977 attk.skip_chaos_message = true;
5978 attk.chaos_affects_defender();
5979 attk.do_miscast();
5980 if (!attk.special_damage_message.empty()
5981 && you.can_see(victim))
5983 mprf("%s", attk.special_damage_message.c_str());
5987 ///////////////////////////////////////////////////////////////////////////
5989 static bool _is_melee_weapon(const item_def *weapon)
5991 if (weapon->base_type == OBJ_STAVES)
5992 return (true);
5994 if (weapon->base_type != OBJ_WEAPONS)
5995 return (false);
5997 return (!is_range_weapon(*weapon));
6000 bool wielded_weapon_check(item_def *weapon, bool no_message)
6002 bool weapon_warning = false;
6003 bool unarmed_warning = false;
6005 if (weapon)
6007 if (needs_handle_warning(*weapon, OPER_ATTACK)
6008 || !_is_melee_weapon(weapon))
6010 weapon_warning = true;
6013 else if (you.attribute[ATTR_WEAPON_SWAP_INTERRUPTED]
6014 && you_tran_can_wear(EQ_WEAPON))
6016 const int weap = you.attribute[ATTR_WEAPON_SWAP_INTERRUPTED] - 1;
6017 const item_def &wpn = you.inv[weap];
6018 if (_is_melee_weapon(&wpn))
6019 unarmed_warning = true;
6022 if (!you.received_weapon_warning && !you.confused()
6023 && (weapon_warning || unarmed_warning))
6025 if (no_message)
6026 return (false);
6028 std::string prompt = "Really attack while ";
6029 if (unarmed_warning)
6030 prompt += "being unarmed?";
6031 else
6032 prompt += "wielding " + weapon->name(DESC_NOCAP_YOUR) + "? ";
6034 const bool result = yesno(prompt.c_str(), true, 'n');
6036 learned_something_new(HINT_WIELD_WEAPON); // for hints mode Rangers
6038 // Don't warn again if you decide to continue your attack.
6039 if (result)
6040 you.received_weapon_warning = true;
6042 return (result);
6045 return (true);
6048 // Returns true if you hit the monster.
6049 bool you_attack(int monster_attacked, bool unarmed_attacks)
6051 ASSERT(!crawl_state.game_is_arena());
6053 monster* defender = &menv[monster_attacked];
6055 // Can't damage orbs or boulders this way.
6056 if (mons_is_projectile(defender->type) && !you.confused())
6058 you.turn_is_over = false;
6059 return (false);
6062 melee_attack attk(&you, defender, unarmed_attacks);
6064 // We're trying to hit a monster, break out of travel/explore now.
6065 if (!travel_kill_monster(defender->type))
6066 interrupt_activity(AI_HIT_MONSTER, defender);
6068 // Check if the player is fighting with something unsuitable,
6069 // or someone unsuitable.
6070 if (you.can_see(defender)
6071 && !mons_is_unknown_mimic(defender)
6072 && !wielded_weapon_check(attk.weapon))
6074 you.turn_is_over = false;
6075 return (false);
6078 bool attack = attk.attack();
6079 if (!attack)
6081 // Attack was cancelled or unsuccessful...
6082 if (attk.cancel_attack)
6083 you.turn_is_over = false;
6084 return (false);
6087 return (true);
6090 // Lose attack energy for attacking with a weapon. which_attack is the actual
6091 // attack number, effective_attack is the attack number excluding synthetic
6092 // attacks (i.e. excluding M_ARCHER monsters' AT_SHOOT attacks).
6093 static void mons_lose_attack_energy(monster* attacker, int wpn_speed,
6094 int which_attack, int effective_attack)
6096 // Initial attack causes energy to be used for all attacks. No
6097 // additional energy is used for unarmed attacks.
6098 if (effective_attack == 0)
6099 attacker->lose_energy(EUT_ATTACK);
6101 // Monsters lose additional energy only for the first two weapon
6102 // attacks; subsequent hits are free.
6103 if (effective_attack > 1)
6104 return;
6106 // speed adjustment for weapon using monsters
6107 if (wpn_speed > 0)
6109 const int atk_speed = attacker->action_energy(EUT_ATTACK);
6110 // only get one third penalty/bonus for second weapons.
6111 if (effective_attack > 0)
6112 wpn_speed = div_rand_round((2 * atk_speed + wpn_speed), 3);
6114 int delta = div_rand_round((wpn_speed - 10 + (atk_speed - 10)), 2);
6115 if (delta > 0)
6116 attacker->speed_increment -= delta;
6120 bool monster_attack_actor(monster* attacker, actor *defender,
6121 bool allow_unarmed)
6123 ASSERT(defender == &you || defender->atype() == ACT_MONSTER);
6124 return (defender->atype() == ACT_PLAYER ?
6125 monster_attack(attacker, allow_unarmed)
6126 : monsters_fight(attacker, defender->as_monster(),
6127 allow_unarmed));
6130 // A monster attacking the player.
6131 bool monster_attack(monster* attacker, bool allow_unarmed)
6133 ASSERT(!crawl_state.game_is_arena());
6135 // Friendly and good neutral monsters won't attack unless confused.
6136 if (attacker->wont_attack() && !mons_is_confused(attacker))
6137 return (false);
6139 // It's hard to attack from within a shell.
6140 if (attacker->withdrawn())
6141 return (false);
6143 // In case the monster hasn't noticed you, bumping into it will
6144 // change that.
6145 behaviour_event(attacker, ME_ALERT, MHITYOU);
6146 melee_attack attk(attacker, &you, allow_unarmed);
6147 attk.attack();
6149 return (true);
6152 // Two monsters fighting each other.
6153 bool monsters_fight(monster* attacker, monster* defender,
6154 bool allow_unarmed)
6156 melee_attack attk(attacker, defender, allow_unarmed);
6157 return (attk.attack());
6162 **************************************************
6164 * END PUBLIC FUNCTIONS *
6166 **************************************************
6169 // Returns a value between 0 and 10 representing the weight given to str
6170 int weapon_str_weight(object_class_type wpn_class, int wpn_type)
6172 int ret;
6174 const int wpn_skill = weapon_skill(wpn_class, wpn_type);
6175 const int hands = hands_reqd(wpn_class, wpn_type, you.body_size());
6177 // These are low values, because we'll be adding some bonus to the
6178 // larger weapons later. Remember also that 1-1/2-hand weapons get
6179 // a bonus in player_weapon_str_weight() as well (can't be done
6180 // here because this function is used for cases where the weapon
6181 // isn't being used by the player).
6183 // Reasonings:
6184 // - Short Blades are the best for the dexterous... although they
6185 // are very limited in damage potential
6186 // - Long Swords are better for the dexterous, the two-handed
6187 // swords are a 50/50 split; bastard swords are in between.
6188 // - Staves: didn't want to punish the mages who want to use
6189 // these... made it a 50/50 split after the 2-hnd bonus
6190 // - Polearms: Spears and tridents are the only ones that can
6191 // be used one handed and are poking weapons, which requires
6192 // more agility than strength. The other ones also require a
6193 // fair amount of agility so they end up at 50/50 (most can poke
6194 // as well as slash... although slashing is not their strong
6195 // point).
6196 // - Axes are weighted and edged and so require mostly strength,
6197 // but not as much as Maces and Flails, which are typically
6198 // blunt and spiked weapons.
6199 switch (wpn_skill)
6201 case SK_SHORT_BLADES: ret = 2; break;
6202 case SK_LONG_BLADES: ret = 3; break;
6203 case SK_STAVES: ret = 3; break; // == 5 after 2-hand bonus
6204 case SK_POLEARMS: ret = 3; break; // most are +2 for 2-hands
6205 case SK_AXES: ret = 6; break;
6206 case SK_MACES_FLAILS: ret = 7; break;
6207 default: ret = 5; break;
6210 // whips are special cased (because they are not much like maces)
6211 if (wpn_type == WPN_WHIP
6212 || wpn_type == WPN_DEMON_WHIP
6213 || wpn_type == WPN_SACRED_SCOURGE)
6215 ret = 2;
6217 else if (wpn_type == WPN_QUICK_BLADE) // high dex is very good for these
6218 ret = 1;
6220 if (hands == HANDS_TWO)
6221 ret += 2;
6223 // most weapons are capped at 8
6224 if (ret > 8)
6226 // these weapons are huge, so strength plays a larger role
6227 if (wpn_type == WPN_GIANT_CLUB || wpn_type == WPN_GIANT_SPIKED_CLUB)
6228 ret = 9;
6229 else
6230 ret = 8;
6233 return (ret);
6236 // Returns a value from 0 to 10 representing the weight of strength to
6237 // dexterity for the players currently wielded weapon.
6238 static inline int player_weapon_str_weight()
6240 const item_def* weapon = you.weapon();
6242 // Unarmed, weighted slightly towards dex -- would have been more,
6243 // but then we'd be punishing Trolls and Ghouls who are strong and
6244 // get special unarmed bonuses.
6245 if (!weapon)
6246 return (4);
6248 int ret = weapon_str_weight(weapon->base_type, weapon->sub_type);
6250 if (hands_reqd(*weapon, you.body_size()) == HANDS_HALF && !you.shield())
6251 ret += 1;
6253 return (ret);
6256 // weapon_dex_weight() + weapon_str_weight == 10, so we only need to
6257 // define one of these.
6258 static inline int player_weapon_dex_weight(void)
6260 return (10 - player_weapon_str_weight());
6263 // weighted average of strength and dex, between (str+dex)/2 and dex
6264 static inline int calc_stat_to_hit_base(void)
6266 // towards_str_avg is a variable, whose sign points towards strength,
6267 // and the magnitude is half the difference (thus, when added directly
6268 // to you.dex() it gives the average of the two.
6269 const signed int towards_str_avg = (you.strength() - you.dex()) / 2;
6271 // dex is modified by strength towards the average, by the
6272 // weighted amount weapon_str_weight() / 10.
6273 return (you.dex() + towards_str_avg * player_weapon_str_weight() / 10);
6276 // weighted average of strength and dex, between str and (str+dex)/2
6277 static inline int calc_stat_to_dam_base(void)
6279 const signed int towards_dex_avg = (you.dex() - you.strength()) / 2;
6280 return (you.strength() + towards_dex_avg * player_weapon_dex_weight() / 10);
6283 static void stab_message(actor *defender, int stab_bonus)
6285 switch (stab_bonus)
6287 case 6: // big melee, monster surrounded/not paying attention
6288 if (coinflip())
6290 mprf("You %s %s from a blind spot!",
6291 (you.species == SP_CAT) ? "pounce on" : "strike",
6292 defender->name(DESC_NOCAP_THE).c_str());
6294 else
6296 mprf("You catch %s momentarily off-guard.",
6297 defender->name(DESC_NOCAP_THE).c_str());
6299 break;
6300 case 4: // confused/fleeing
6301 if (!one_chance_in(3))
6303 mprf("You catch %s completely off-guard!",
6304 defender->name(DESC_NOCAP_THE).c_str());
6306 else
6308 mprf("You %s %s from behind!",
6309 (you.species == SP_CAT) ? "pounce on" : "strike",
6310 defender->name(DESC_NOCAP_THE).c_str());
6312 break;
6313 case 2:
6314 case 1:
6315 if (you.species == SP_CAT && coinflip())
6317 mprf("You pounce on the unaware %s!",
6318 defender->name(DESC_PLAIN).c_str());
6319 break;
6321 mprf("%s fails to defend %s.",
6322 defender->name(DESC_CAP_THE).c_str(),
6323 defender->pronoun(PRONOUN_REFLEXIVE).c_str());
6324 break;
6328 void chaos_affect_actor(actor *victim)
6330 melee_attack::chaos_affect_actor(victim);