3 * Summary: Functions used during combat.
4 * Written by: Linley Henzell
10 #include "melee_attack.h"
21 #include "attitude-change.h"
32 #include "map_knowledge.h"
50 #include "mon-clone.h"
51 #include "mon-place.h"
53 #include "mgen_data.h"
55 #include "mon-stuff.h"
60 #include "random-var.h"
62 #include "godconduct.h"
66 #include "spl-clouds.h"
67 #include "spl-miscast.h"
68 #include "spl-summoning.h"
72 #include "transform.h"
80 #ifdef NOTE_DEBUG_CHAOS_BRAND
81 #define NOTE_DEBUG_CHAOS_EFFECTS
84 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
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
)
114 int margin
= AUTOMATIC_HIT
;
118 if (to_hit
>= AUTOMATIC_HIT
)
120 else if (r
[0].x_chance_in_y(MIN_HIT_MISS_PERCENTAGE
, 100))
121 margin
= (r
[1].random2(2) ? 1 : -1) * AUTOMATIC_HIT
;
124 roll
= r
[2].random2(to_hit
+ 1);
125 margin
= (roll
- r
[3].random2avg(ev
, 2));
128 #ifdef DEBUG_DIAGNOSTICS
132 miss
= 100.0 - MIN_HIT_MISS_PERCENTAGE
/ 2.0;
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
);
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
)
155 str_weight
= player_weapon_str_weight();
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)
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
))
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
)
185 if (you
.hunger_state
== HS_STARVING
)
187 else if (you
.hunger_state
< HS_SATIATED
)
191 else if (you
.species
!= SP_VAMPIRE
&& you
.hunger_state
== HS_STARVING
)
194 your_to_hit
+= slaying_bonus(PWPN_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
;
244 if (def
&& def
->friendly())
245 unchivalric
= UCAT_ALLY
;
248 if (def
&& mons_is_fleeing(def
))
249 unchivalric
= UCAT_FLEEING
;
252 if (attacker
&& !attacker
->visible_to(defender
))
253 unchivalric
= UCAT_INVISIBLE
;
256 if (def
&& def
->caught())
257 unchivalric
= UCAT_HELD_IN_NET
;
260 if (def
&& def
->petrifying())
261 unchivalric
= UCAT_PETRIFYING
;
264 if (defender
->petrified())
265 unchivalric
= UCAT_PETRIFIED
;
268 if (defender
->paralysed())
269 unchivalric
= UCAT_PARALYSED
;
272 if (defender
->asleep())
273 unchivalric
= UCAT_SLEEPING
;
275 return (unchivalric
);
278 //////////////////////////////////////////////////////////////////////////
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
)
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
;
320 hands
= hands_reqd(*weapon
, attacker
->body_size());
322 switch (single_damage_type(*weapon
))
337 shield
= attacker
->shield();
339 defender_shield
= defender
->shield();
341 hand_half_bonus
= (unarmed_ok
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
);
372 miscast_type
= SPTYP_NONE
;
373 miscast_target
= NULL
;
376 std::string
melee_attack::actor_name(const actor
*a
,
377 description_level_type desc
,
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
,
388 return (actor_visible
? a
->pronoun(pron
) : anon_pronoun(pron
));
391 std::string
melee_attack::anon_pronoun(pronoun_type pron
)
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
)
411 return (actor_invisible
? "It" : "Something");
414 case DESC_NOCAP_YOUR
:
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
);
446 bool possessive
= false;
447 if (desc
== DESC_CAP_YOUR
)
452 else if (desc
== DESC_NOCAP_YOUR
)
454 desc
= DESC_NOCAP_THE
;
459 name
= apostrophise(atk_name(desc
)) + " ";
461 name
+= weapon
->name(DESC_PLAIN
, false, false, false, false, ignore_flags
);
466 bool melee_attack::is_banished(const actor
*a
) const
468 if (!a
|| a
->alive())
471 if (a
->atype() == ACT_PLAYER
)
472 return (you
.banished
);
474 return (a
->as_monster()->flags
& MF_BANISHED
);
477 void melee_attack::check_autoberserk()
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
,
497 return (!defender
->alive());
503 void melee_attack::identify_mimic(actor
*act
)
506 && act
->atype() == ACT_MONSTER
507 && mons_is_mimic(act
->type
)
510 monster
* mon
= act
->as_monster();
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,
527 cancel_attack
= true;
532 if (attacker
!= defender
)
534 // Allow setting of your allies' target, etc.
535 attacker
->attacking(defender
);
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
552 if (attacker
== defender
)
553 xom_is_stimulated(255);
555 xom_is_stimulated(14);
557 if (damage_brand
== SPWPN_CHAOS
)
558 chaos_affects_attacker();
562 // Non-fumbled self-attacks due to confusion are still pretty funny,
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);
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.",
589 defender
->name(DESC_NOCAP_THE
).c_str());
593 mprf("You hit the %s.",
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(),
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(),
616 if (damage_brand
== SPWPN_CHAOS
)
617 chaos_affects_attacker();
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();
660 // This may invalidate both the attacker and defender.
661 fire_final_effects();
663 enable_attack_conducts(conducts
);
668 static int _modify_blood_amount(const int damage
, const int dam_type
)
670 int factor
= 0; // DVORP_NONE
674 case DVORP_CRUSHING
: // flails, also unarmed
675 case DVORP_TENTACLE
: // unarmed, tentacles
678 case DVORP_SLASHING
: // whips
681 case DVORP_PIERCING
: // pole-arms
684 case DVORP_STABBING
: // knives, daggers
687 case DVORP_SLICING
: // other short/long blades, also blade hands
690 case DVORP_CHOPPING
: // axes
693 case DVORP_CLAWING
: // unarmed, claws
698 return (damage
* factor
/ 10);
701 static bool _vamp_wants_blood_from_monster(const monster
* mon
)
703 if (you
.species
!= SP_VAMPIRE
)
706 if (you
.hunger_state
== HS_ENGORGED
)
709 if (mon
->is_summoned())
712 if (!mons_has_blood(mon
->type
))
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,
729 ASSERT(you
.species
== SP_VAMPIRE
);
731 if (!_vamp_wants_blood_from_monster(mon
))
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());
745 mprf("You draw %s blood!",
746 apostrophise(mon
->name(DESC_NOCAP_THE
, true)).c_str());
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())
762 if (heal
> 0 && !you
.duration
[DUR_DEATHS_DOOR
])
765 mprf("You feel %sbetter.", (you
.hp
== you
.hp_max
) ? "much " : "");
770 if (you
.hunger_state
!= HS_ENGORGED
)
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())
785 food_value
/= reduction
;
787 lessen_hunger(food_value
, false);
790 did_god_conduct(DID_DRINK_BLOOD
, 5 + random2(4));
795 bool melee_attack::player_attack()
802 player_apply_attack_delay();
805 coord_def where
= defender
->pos();
807 ev_margin
= player_hits_monster();
812 if (crawl_state
.game_is_hints())
813 Hints
.hints_melee_counter
++;
815 const bool shield_blocked
= attack_shield_blocked(true);
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.
832 slimify_monster(defender
->as_monster());
833 you
.duration
[DUR_SLIMIFY
] = 0;
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())
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)
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);
876 player_exercise_combat_skills();
878 if (player_check_monster_died())
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())
890 // ugh, inspecting attack_verb here is pretty ugly
891 if (damage_done
&& attack_verb
== "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(),
904 // At this point, pretend we didn't hit at all.
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);
921 if (player_monattk_hit_effects(false))
923 if (!defender
->alive())
927 const bool did_primary_hit
= did_hit
;
928 if (unarmed_ok
&& where
== defender
->pos() && player_aux_unarmed())
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
)
954 damage_brand
= SPWPN_NORMAL
;
960 aux_attack
= aux_verb
= "kick";
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())
972 // Max talon damage: 8.
973 aux_damage
+= player_mutation_level(MUT_TALONS
);
981 if (player_mutation_level(MUT_BEAK
)
982 && (!player_mutation_level(MUT_HORNS
) || coinflip()))
984 aux_attack
= aux_verb
= "peck";
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
))
999 if (get_helmet_desc(*helmet
) == THELM_DESC_SPIKED
1000 || get_helmet_desc(*helmet
) == THELM_DESC_HORNED
)
1009 aux_attack
= aux_verb
= "tail-slap";
1011 aux_damage
= 6 * you
.has_usable_tail();
1015 if (player_mutation_level(MUT_STINGER
) > 0)
1017 aux_damage
+= player_mutation_level(MUT_STINGER
) * 2 - 1;
1018 damage_brand
= SPWPN_VENOM
;
1024 aux_attack
= aux_verb
= "punch";
1025 aux_damage
= 5 + you
.skill(SK_UNARMED_COMBAT
) / 3;
1027 if (you
.form
== TRAN_BLADE_HANDS
)
1033 else if (you
.has_usable_claws())
1036 aux_damage
+= roll_dice(you
.has_claws(), 3);
1042 aux_attack
= aux_verb
= "bite";
1043 aux_damage
+= you
.has_usable_fangs() * 2
1044 + you
.skill(SK_UNARMED_COMBAT
) / 5;
1047 // prob of vampiric bite:
1048 // 1/4 when non-thirsty, 1/2 when thirsty, 100% when
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);
1066 case UNAT_PSEUDOPODS
:
1067 aux_attack
= aux_verb
= "slap";
1068 aux_damage
+= 4 * you
.has_usable_pseudopods();
1073 die("unknown aux attack type");
1078 static bool _tran_forbid_aux_attack(unarmed_attack_type atk
)
1085 return (you
.form
== TRAN_ICE_BEAST
1086 || you
.form
== TRAN_DRAGON
1087 || you
.form
== TRAN_SPIDER
1088 || you
.form
== TRAN_BAT
);
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
))
1106 return ((player_mutation_level(MUT_HOOVES
)
1107 || you
.has_usable_talons())
1111 return ((player_mutation_level(MUT_HORNS
)
1112 || player_mutation_level(MUT_BEAK
))
1113 && one_chance_in(3));
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));
1124 return ((you
.has_usable_fangs()
1125 || player_mutation_level(MUT_ACIDIC_BITE
))
1126 && one_chance_in(5));
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
,
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
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
));
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
)
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.",
1213 defender
->name(DESC_NOCAP_THE
).c_str(),
1214 defender
->pronoun(PRONOUN_NOCAP
).c_str());
1218 mprf("Your %s misses %s.", aux_attack
.c_str(),
1219 defender
->name(DESC_NOCAP_THE
).c_str());
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
;
1240 baseattack
= player_aux_choose_baseattack();
1242 for (int i
= UNAT_FIRST_ATTACK
; i
<= UNAT_LAST_ATTACK
; ++i
)
1244 if (!defender
->alive())
1247 unarmed_attack_type atk
= static_cast<unarmed_attack_type
>(i
);
1249 if (baseattack
!= atk
&& !_extra_aux_attack(atk
))
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())
1273 if (player_aux_test_hit())
1275 // Upset the monster.
1276 behaviour_event(defender
->as_monster(), ME_WHACK
, MHITYOU
);
1277 if (!defender
->alive())
1280 if (attack_shield_blocked(true))
1282 if (player_aux_apply(atk
))
1290 bool melee_attack::player_aux_apply(unarmed_attack_type atk
)
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.
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
;
1315 apply_bleeding
= true;
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
;
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);
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())))
1377 else // no damage was done
1379 mprf("You %s %s%s.",
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
);
1396 std::string
melee_attack::debug_damage_number()
1398 #ifdef DEBUG_DIAGNOSTICS
1399 return make_stringf(" for %d", damage_done
);
1405 std::string
melee_attack::special_attack_punctuation()
1407 if (special_damage
< 6)
1413 std::string
melee_attack::attack_strength_punctuation()
1415 if (attacker
->atype() == ACT_PLAYER
)
1417 if (damage_done
< HIT_WEAK
)
1419 else if (damage_done
< HIT_MED
)
1421 else if (damage_done
< HIT_STRONG
)
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"
1438 void melee_attack::player_announce_aux_hit()
1440 mprf("You %s %s%s%s",
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()
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 ";
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()
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
)
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
));
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
);
1530 if (to_hit
>= evasion
|| one_chance_in(20))
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
)
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);
1561 int melee_attack::player_aux_stat_modify_damage(int damage
)
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;
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));
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));
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);
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
;
1637 if (you
.piety
> 80 || coinflip())
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
);
1654 // Demonspawn get a damage bonus for demonic weapons.
1655 if (you
.species
== SP_DEMONSPAWN
&& is_demonic(*weapon
))
1656 damage
+= random2(3);
1662 void melee_attack::player_weapon_auto_id()
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());
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
))
1691 case SK_SHORT_BLADES
:
1693 int bonus
= (you
.dex() * (you
.skill(SK_STABBING
) + 1)) / 5;
1695 if (weapon
->sub_type
!= WPN_DAGGER
)
1698 bonus
= stepdown_value(bonus
, 10, 10, 30, 30);
1703 case SK_LONG_BLADES
:
1704 damage
*= 10 + you
.skill(SK_STABBING
) /
1705 (stab_bonus
+ (wpn_skill
== SK_SHORT_BLADES
? 0 : 2));
1709 damage
*= 12 + you
.skill(SK_STABBING
) / stab_bonus
;
1716 int melee_attack::player_stab(int damage
)
1718 // The stabbing message looks better here:
1721 // Construct reasonable message.
1722 stab_message(defender
, stab_bonus
);
1724 practise(EX_WILL_STAB
);
1726 did_god_conduct(DID_STABBING
, 4);
1731 // Ok.. if you didn't backstab, you wake up the neighborhood.
1732 // I can live with that.
1733 alert_nearby_monsters();
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
;
1751 defender
->as_monster()->speed_increment
= 0;
1755 damage
= player_stab_weapon_bonus(damage
);
1761 int melee_attack::player_apply_monster_ac(int damage
)
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
);
1772 damage
-= random2(1 + ac
);
1777 // Apply AC normally.
1778 if (defender
->armour_class() > 0)
1779 damage
-= random2(1 + defender
->armour_class());
1782 if (defender
->petrified())
1788 int melee_attack::player_weapon_type_modify(int damage
)
1790 int weap_type
= WPN_UNKNOWN
;
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";
1811 attack_verb
= "clumsily bash";
1816 // Take transformations into account, if no weapon is wielded.
1817 if (weap_type
== WPN_UNARMED
1818 && you
.form
!= TRAN_NONE
)
1825 if (damage
< HIT_WEAK
)
1826 attack_verb
= "hit";
1827 else if (damage
< HIT_STRONG
)
1828 attack_verb
= "bite";
1830 attack_verb
= "maul";
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";
1840 attack_verb
= "shred";
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";
1853 attack_verb
= "eviscerate";
1857 case TRAN_ICE_BEAST
:
1858 if (damage
< HIT_WEAK
)
1859 attack_verb
= "hit";
1860 else if (damage
< HIT_MED
)
1861 attack_verb
= "punch";
1863 attack_verb
= "pummel";
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";
1874 attack_verb
= "maul";
1876 attack_verb
= "trample";
1881 } // transformations
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
1889 switch (weapon
? single_damage_type(*weapon
) : -1)
1892 if (damage
< HIT_MED
)
1893 attack_verb
= "puncture";
1894 else if (damage
< HIT_STRONG
)
1895 attack_verb
= "impale";
1898 attack_verb
= "spit";
1899 if (defender
->atype() == ACT_MONSTER
1901 && mons_genus(defender
->as_monster()->type
) == MONS_HOG
)
1903 verb_degree
= " like the proverbial pig";
1906 verb_degree
= " like a pig";
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";
1922 attack_verb
= "open";
1923 verb_degree
= " like a pillowcase";
1928 if (damage
< HIT_MED
)
1929 attack_verb
= one_chance_in(4) ? "thump" : "sock";
1930 else if (damage
< HIT_STRONG
)
1931 attack_verb
= "bludgeon";
1934 attack_verb
= "crush";
1935 verb_degree
= " like a grape";
1940 if (damage
< HIT_MED
)
1941 attack_verb
= "whack";
1942 else if (damage
< HIT_STRONG
)
1943 attack_verb
= "thrash";
1946 switch(defender
->holiness())
1951 attack_verb
= "punish";
1952 verb_degree
= " causing immense pain";
1955 attack_verb
= "devastate";
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";
1970 attack_verb
= "eviscerate";
1974 if (damage
< HIT_MED
)
1975 attack_verb
= "punch";
1977 attack_verb
= "pummel";
1983 attack_verb
= "hit";
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
,
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(),
2026 // No further effects.
2028 else if (you
.species
== SP_VAMPIRE
2029 && damage_brand
== SPWPN_VAMPIRICISM
2031 && _player_vampire_draws_blood(defender
->as_monster(),
2033 (mondied
? 1 : 10)))
2035 // No further effects.
2037 // Vampiric *weapon* effects for the killing blow.
2038 else if (mondied
&& damage_brand
== SPWPN_VAMPIRICISM
2040 && you
.species
!= SP_VAMPIRE
) // vampires get their bonus elsewhere
2042 if (defender
->holiness() == MH_NATURAL
2043 && !defender
->is_summoned()
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",
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);
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())
2086 if (!no_damage_message
.empty())
2088 if (special_damage
> 0)
2089 emit_nodmg_hit_message();
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)
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
);
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
);
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());
2137 void melee_attack::_monster_die(monster
* mons
, killer_type killer
,
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
);
2155 chaos_killed_defender(def_copy
);
2161 mons_reaped(attacker
, def_copy
);
2166 static bool is_boolean_resist(beam_type flavour
)
2170 case BEAM_ELECTRICITY
:
2171 case BEAM_MIASMA
: // rotting
2173 case BEAM_WATER
: // water asphyxiation damage,
2174 // bypassed by being water inhabitant.
2181 // Gets the percentage of the total damage of this damage flavour that can
2183 static inline int get_resistible_fraction(beam_type flavour
)
2187 // Drowning damage from water is resistible by being a water thing, or
2188 // otherwise asphyx resistant.
2192 // Assume ice storm and throw icicle are mostly solid.
2199 case BEAM_POISON_ARROW
:
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
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
)
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
;
2230 if (mons
&& res
>= 3)
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.
2243 resistible
/= 1 + bonus_res
+ res
* res
;
2245 resistible
/= resist_fraction(res
, bonus_res
);
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
,
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(
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
)
2280 void melee_attack::drain_defender()
2282 if (defender
->atype() == ACT_MONSTER
&& one_chance_in(3))
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
=
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
=
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
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
2345 if (one_chance_in(3))
2347 if (defender_visible
)
2349 special_damage_message
=
2351 "Space bends around %s.",
2352 def_name(DESC_NOCAP_THE
).c_str());
2354 special_damage
+= 1 + random2avg(7, 2);
2358 if (one_chance_in(3))
2360 if (defender_visible
)
2362 special_damage_message
=
2364 "Space warps horribly around %s!",
2365 def_name(DESC_NOCAP_THE
).c_str());
2367 special_damage
+= 3 + random2avg(24, 2);
2371 if (one_chance_in(3))
2373 emit_nodmg_hit_message();
2374 if (defender_visible
)
2375 obvious_effect
= true;
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
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));
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
2405 if (is_artefact(*weapon
))
2406 artefact_wpn_learn_prop(*weapon
, ARTP_BRAND
);
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));
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
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()
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?
2465 // A chaos self-attack increases the chance of certain effects,
2466 // due to a short-circuit/feedback/resonance/whatever.
2467 if (attacker
== defender
)
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!");
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
2502 slowpara_chance
,// CHAOS_HASTE
2505 slowpara_chance
,// CHAOS_SLOW
2506 slowpara_chance
,// CHAOS_PARALYSIS
2507 slowpara_chance
,// CHAOS_PETRIFY
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: ";
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);
2536 switch (static_cast<chaos_type
>(choice
))
2540 ASSERT(can_clone
&& clone_chance
> 0);
2541 ASSERT(defender
->atype() == ACT_MONSTER
);
2543 int clone_idx
= clone_mons(defender
->as_monster(), true,
2545 if (clone_idx
!= NON_MONSTER
)
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);
2566 ASSERT(can_poly
&& poly_chance
> 0);
2567 beam
.flavour
= BEAM_POLYMORPH
;
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
);
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
);
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(
2614 miscast_level
= level
;
2615 miscast_type
= SPTYP_RANDOM
;
2616 miscast_target
= one_chance_in(3) ? attacker
: defender
;
2621 ASSERT(can_rage
&& rage_chance
> 0);
2622 defender
->go_berserk(false);
2623 obvious_effect
= you
.can_see(defender
);
2627 beam
.flavour
= BEAM_HEALING
;
2631 beam
.flavour
= BEAM_HASTE
;
2635 beam
.flavour
= BEAM_INVISIBILITY
;
2639 beam
.flavour
= BEAM_SLOW
;
2642 case CHAOS_PARALYSIS
:
2643 beam
.flavour
= BEAM_PARALYSIS
;
2647 beam
.flavour
= BEAM_PETRIFY
;
2651 die("Invalid chaos effect type");
2655 if (beam
.flavour
!= BEAM_NONE
)
2659 beam
.colour
= BLACK
;
2660 beam
.effect_known
= false;
2662 if (weapon
&& you
.can_see(attacker
))
2664 beam
.name
= wep_name(DESC_NOCAP_YOUR
);
2668 beam
.name
= atk_name(DESC_NOCAP_THE
);
2671 (attacker
->atype() == ACT_PLAYER
) ? KILL_YOU
2672 : attacker
->as_monster()->confused_by_you() ? KILL_YOU_CONF
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
;
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
)
2705 // The player can't use shops to escape, so don't bother.
2706 if (stair_feat
== DNGN_ENTER_SHOP
)
2709 // Don't move around notable terrain the player is aware of if it's
2711 if (is_notable_terrain(stair_feat
)
2712 && env
.map_knowledge(orig_pos
).known() && !you
.see_cell(orig_pos
))
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; \
2732 void melee_attack::chaos_affects_attacker()
2734 if (miscast_level
>= 1 || !attacker
->alive())
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);
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);
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,
2767 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
2768 take_note(Note(NOTE_MESSAGE
, 0, 0,
2769 "CHAOS affects attacker: smoke"), true);
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");
2783 msg
= "You hear " + noise
;
2787 msg
= getSpeakString("weapon_noises");
2788 std::string wepname
= wep_name(DESC_CAP_YOUR
);
2791 msg
= replace_all(msg
, "@Your_weapon@", wepname
);
2792 msg
= replace_all(msg
, "@The_weapon@", wepname
);
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);
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
)
2820 item_def
&item(mitm
[idx
]);
2822 if (!item
.defined() || item
.pos
!= mon
->pos())
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)
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
)
2850 // If it's a hydra the number of heads must match.
2851 if ((short) mon
->number
!= si
->props
[MONSTER_NUMBER
].get_short())
2855 corpse_index
= si
.link();
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
))
2876 // Good gods won't let their gifts/followers be raised as the
2878 if (is_good_god(mon
->god
))
2881 // First attempt to raise zombie fitted out with all its old
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)
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);
2933 simple_monster_message(zombie
, " appears from thin air!");
2934 autotoggle_autopickup(false);
2937 player_angers_monster(zombie
);
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())
2955 if (attacker
->atype() == ACT_PLAYER
&& you
.banished
)
2958 if (mon
->is_summoned() || (mon
->flags
& (MF_BANISHED
| MF_HARD_RESET
)))
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
,
2967 if (one_chance_in(100)
2968 && _make_zombie(mon
, corpse_class
, corpse_index
, fake_corpse
,
2971 #ifdef NOTE_DEBUG_CHAOS_EFFECTS
2972 take_note(Note(NOTE_MESSAGE
, 0, 0,
2973 "CHAOS killed defender: zombified monster"), true);
2979 void melee_attack::do_miscast()
2981 if (miscast_level
== -1)
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())
2991 if (miscast_target
->atype() == ACT_PLAYER
&& you
.banished
)
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
);
3005 const int ignore_mask
= ISFLAG_KNOW_CURSE
| ISFLAG_KNOW_PLUSES
;
3007 if (attacker
->atype() == ACT_PLAYER
)
3009 source
= NON_MONSTER
;
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
3016 cause
+= wep_name(DESC_NOCAP_YOUR
,
3018 | ISFLAG_COSMETIC_MASK
| ISFLAG_RACIAL_MASK
);
3020 if (miscast_target
== attacker
)
3021 hand_str
= wep_name(DESC_PLAIN
, ignore_mask
);
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.
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.
3054 brand
= (random_choose_weighted(
3058 10, SPWPN_ELECTROCUTION
,
3062 5, SPWPN_VAMPIRICISM
,
3063 5, SPWPN_HOLY_WRATH
,
3066 2, SPWPN_DISTORTION
,
3069 if (one_chance_in(3))
3072 bool susceptible
= true;
3076 if (defender
->is_fiery())
3077 susceptible
= false;
3079 case SPWPN_FREEZING
:
3080 if (defender
->is_icy())
3081 susceptible
= false;
3083 case SPWPN_ELECTROCUTION
:
3084 if (defender
->airborne())
3085 susceptible
= false;
3088 if (defender
->holiness() == MH_UNDEAD
)
3089 susceptible
= false;
3091 case SPWPN_VAMPIRICISM
:
3092 if (defender
->is_summoned())
3094 susceptible
= false;
3097 // intentional fall-through
3098 case SPWPN_DRAINING
:
3099 if (defender
->holiness() != MH_NATURAL
)
3100 susceptible
= false;
3102 case SPWPN_HOLY_WRATH
:
3103 if (defender
->holiness() != MH_UNDEAD
3104 && defender
->holiness() != MH_DEMONIC
)
3106 susceptible
= false;
3110 if (defender
->holiness() == MH_NONLIVING
3111 || defender
->holiness() == MH_PLANT
)
3113 susceptible
= false;
3116 case SPWPN_ANTIMAGIC
:
3117 if (defender
->as_monster() &&
3118 !defender
->as_monster()->can_use_spells())
3119 susceptible
= false;
3128 #ifdef NOTE_DEBUG_CHAOS_BRAND
3129 std::string brand_name
= "CHAOS 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);
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;
3175 if (is_artefact(*weapon
))
3176 brand_was_known
= artefact_known_wpn_property(*weapon
, ARTP_BRAND
);
3178 brand_was_known
= item_type_known(*weapon
);
3182 // Monster resistance to the brand.
3186 obvious_effect
= false;
3189 if (damage_brand
== SPWPN_CHAOS
)
3190 brand
= random_chaos_brand();
3192 brand
= damage_brand
;
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
);
3204 case SPWPN_FREEZING
:
3205 calc_elemental_brand_damage(BEAM_COLD
, defender
->res_cold(), "freeze");
3206 defender
->expose_to_element(BEAM_COLD
);
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
=
3218 def_name(DESC_CAP_THE
).c_str(),
3219 defender
->conj_verb("convulse").c_str(),
3220 special_attack_punctuation().c_str());
3224 case SPWPN_ELECTROCUTION
:
3226 if (defender
->airborne() || defender
->res_elec() > 0)
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
,
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
=
3260 defender
->name(DESC_CAP_THE
).c_str(),
3261 defender
->conj_verb("convulse").c_str(),
3262 special_attack_punctuation().c_str());
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
=
3276 defender
->name(DESC_CAP_THE
).c_str(),
3277 defender
->conj_verb("convulse").c_str(),
3278 special_attack_punctuation().c_str());
3284 if (!one_chance_in(4))
3288 if (defender
->atype() == ACT_PLAYER
)
3289 old_poison
= you
.duration
[DUR_POISONING
];
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
3307 (defender
->as_monster()->get_ench(ENCH_POISON
)).degree
)
3309 obvious_effect
= true;
3315 case SPWPN_DRAINING
:
3320 special_damage
= 1 + random2(damage_done
) / 4;
3321 // Note: Leaving special_damage_message empty because there isn't one.
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
);
3334 if (x_chance_in_y(defender
->res_negative_energy(), 3))
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))
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());
3364 mprf("%s is healed.",
3365 attacker
->name(DESC_CAP_THE
).c_str());
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
;
3378 hp_boost
= 1 + random2(damage_done
);
3380 attacker
->heal(hp_boost
);
3382 attacker
->god_conduct(DID_NECROMANCY
, 2);
3386 if (defender
->res_negative_energy())
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);
3404 case SPWPN_DISTORTION
:
3405 ret
= distortion_affects_defender();
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
)
3416 emit_nodmg_hit_message();
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.
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)
3440 if (you
.duration
[DUR_CONFUSING_TOUCH
] < 1)
3441 you
.duration
[DUR_CONFUSING_TOUCH
] = 1;
3442 obvious_effect
= false;
3448 chaos_affects_defender();
3451 case SPWPN_ANTIMAGIC
:
3452 if (defender
->atype() == ACT_PLAYER
)
3454 int mp_loss
= std::min(you
.magic_points
, random2(damage_done
* 2));
3457 mpr("You feel your power leaking away.", MSGCH_WARN
);
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;
3475 if (damage_brand
== SPWPN_CHAOS
&& brand
!= SPWPN_CHAOS
&& !ret
3476 && miscast_level
== -1 && one_chance_in(20))
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
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
);
3498 set_ident_flags(*weapon
, ISFLAG_KNOW_TYPE
);
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,
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
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
3526 void melee_attack::handle_noise(const coord_def
& pos
)
3528 // Successful stabs make no noise.
3536 int level
= (noise_factor
* damage_done
/ 100 / 4) + extra_noise
;
3538 if (noise_factor
> 0)
3539 level
= std::max(1, level
);
3542 noisy(level
, pos
, attacker
->mindex());
3548 // Returns true if the attack cut off a head *and* cauterized it.
3549 bool melee_attack::chop_hydra_head(int dam
,
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))
3558 if ((dam_type
== DVORP_SLICING
|| dam_type
== DVORP_CHOPPING
3559 || dam_type
== DVORP_CLAWING
)
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
);
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
);
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
)
3616 if (wpn_brand
== SPWPN_FLAMING
)
3618 if (defender_visible
)
3619 mpr("The flame cauterises the wound!");
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);
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
);
3651 void melee_attack::player_sustain_passive_damage()
3653 if (mons_class_flag(defender
->type
, M_ACID_SPLASH
))
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()
3675 if (!weapon
|| !item_is_staff(*weapon
))
3678 if (random2(15) > you
.skill(SK_EVOCATIONS
))
3681 switch (weapon
->sub_type
)
3684 if (damage_done
+ you
.skill(SK_AIR_MAGIC
) <= random2(20))
3688 resist_adjust_damage(defender
,
3690 defender
->res_elec(),
3691 player_staff_damage(SK_AIR_MAGIC
));
3695 special_damage_message
=
3696 make_stringf("%s is electrocuted!",
3697 defender
->name(DESC_CAP_THE
).c_str());
3698 special_damage_flavour
= BEAM_ELECTRICITY
;
3705 resist_adjust_damage(defender
,
3707 defender
->res_cold(),
3708 player_staff_damage(SK_ICE_MAGIC
));
3712 special_damage_message
=
3715 defender
->name(DESC_NOCAP_THE
).c_str());
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
=
3728 defender
->name(DESC_NOCAP_THE
).c_str());
3734 resist_adjust_damage(defender
,
3736 defender
->res_fire(),
3737 player_staff_damage(SK_FIRE_MAGIC
));
3741 special_damage_message
=
3744 defender
->name(DESC_NOCAP_THE
).c_str());
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
);
3761 if (defender
->res_negative_energy())
3764 if (x_chance_in_y(you
.skill(SK_NECROMANCY
) + 1, 8))
3766 special_damage
= player_staff_damage(SK_NECROMANCY
);
3770 special_damage_message
=
3772 "%s convulses in agony!",
3773 defender
->name(DESC_CAP_THE
).c_str());
3775 did_god_conduct(DID_NECROMANCY
, 4);
3781 case STAFF_SUMMONING
:
3782 case STAFF_CHANNELING
:
3783 case STAFF_CONJURATION
:
3784 case STAFF_ENCHANTMENT
:
3786 case STAFF_WIZARDRY
:
3790 mpr("You're wielding some staff I've never heard of! (fight.cc)",
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());
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
);
3826 void melee_attack::player_calc_hit_damage()
3828 int 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
)
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
);
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
)
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
);
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
);
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
;
3908 if (wearing_amulet(AMU_INACCURACY
))
3911 // If you can't see yourself, you're a little less accurate.
3912 if (!you
.visible_to(&you
))
3915 // fighting contribution
3916 your_to_hit
+= maybe_random2(1 + you
.skill(SK_FIGHTING
), random_factor
);
3918 // weapon skill contribution
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,
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
),
3938 // weapon bonus contribution
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())
3958 else if (weapon
->base_type
== OBJ_STAVES
)
3961 your_to_hit
+= property(*weapon
, PWPN_HIT
);
3963 if (item_is_rod(*weapon
))
3964 your_to_hit
+= (short)weapon
->props
["rod_enchantment"];
3969 your_to_hit
+= slaying_bonus(PWPN_HIT
);
3972 if (you
.hunger_state
== HS_STARVING
)
3976 your_to_hit
-= (player_armour_tohit_penalty
+ player_shield_tohit_penalty
);
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
;
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
);
3994 if (weapon
&& wpn_skill
== SK_SHORT_BLADES
&& you
.duration
[DUR_SURE_BLADE
])
3996 int turn_duration
= you
.duration
[DUR_SURE_BLADE
] / BASELINE_DELAY
;
3998 (random_factor
? random2limit(turn_duration
, 10) :
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
);
4014 your_to_hit
+= maybe_random2(10, random_factor
);
4017 your_to_hit
+= maybe_random2(12, random_factor
);
4019 case TRAN_ICE_BEAST
:
4020 your_to_hit
+= maybe_random2(10, random_factor
);
4022 case TRAN_BLADE_HANDS
:
4023 your_to_hit
+= maybe_random2(12, random_factor
);
4026 your_to_hit
+= maybe_random2(9, random_factor
);
4029 your_to_hit
+= maybe_random2(10, random_factor
);
4032 your_to_hit
+= maybe_random2(10, random_factor
);
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
))
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
]
4060 stab_attempt
= false;
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
);
4070 if (uat
== UCAT_INVISIBLE
&& !mons_sense_invis(defender
->as_monster()))
4075 case UCAT_NO_ATTACK
:
4079 case UCAT_PARALYSED
:
4082 case UCAT_HELD_IN_NET
:
4083 case UCAT_PETRIFYING
:
4084 case UCAT_PETRIFIED
:
4087 case UCAT_INVISIBLE
:
4093 case UCAT_DISTRACTED
:
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,
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
);
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;
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)
4165 // All weapons have min delay 7 or better
4169 // never go faster than speed 3 (ie 3 attacks per round)
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.
4197 if (you
.burden_state
== BS_UNENCUMBERED
)
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()
4213 if (you
.duration
[DUR_CONFUSING_TOUCH
])
4215 // No base hand damage while using this spell.
4225 damage
= (you
.species
== SP_VAMPIRE
? 2 : 1);
4227 case TRAN_ICE_BEAST
:
4230 case TRAN_BLADE_HANDS
:
4231 damage
= 12 + div_rand_round(you
.strength() + you
.dex(), 4);
4234 damage
= 12 + you
.strength();
4237 damage
= 20 + you
.strength();
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;
4261 damage
+= you
.skill(SK_UNARMED_COMBAT
);
4266 int melee_attack::player_calc_base_weapon_damage()
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
4275 if (shield
&& weapon
->base_type
== OBJ_WEAPONS
4276 && weapon_skill(*weapon
) == SK_STAVES
4277 && hands_reqd(*weapon
, you
.body_size()) == HANDS_HALF
)
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;
4317 // Non-friendly monsters should never violate sanctuary.
4320 dprf("Preventing hostile violation of sanctuary.");
4321 cancel_attack
= true;
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
)
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();
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());
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();
4375 && attacker
->is_summoned()
4376 && attacker
->as_monster()->check_res_magic(warding
) <= 0)
4380 mprf("%s tries to attack %s, but flinches away.",
4381 atk_name(DESC_CAP_THE
).c_str(),
4382 mons_defender_name().c_str());
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
)
4400 if (defender
->incapacitated())
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
))
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
);
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
);
4435 int melee_attack::mons_calc_damage(const mon_attack_def
&attk
)
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
4453 if (weapon
->plus2
>= 0)
4454 damage
+= random2(weapon
->plus2
);
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
;
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
);
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(),
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();
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);
4516 std::max(guaranteed_damage_reduction
, damage_reduction
);
4519 dprf("AC: at: %s, df: %s, dam: %d (max %d), DR: %d (GDR %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
[] =
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
))
4567 static const char *attack_types
[] =
4570 "hit", // including weapon attacks
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
))
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
)
4617 result
+= weapon
->name(DESC_NOCAP_A
);
4622 else if (attk
.flavour
== AF_REACH
4623 && grid_distance(attacker
->pos(), defender
->pos()) == 2)
4625 return " from afar";
4631 std::string
melee_attack::mons_defender_name()
4633 if (attacker
== defender
)
4634 return pronoun(attacker
, PRONOUN_REFLEXIVE
, attacker_visible
);
4636 return def_name(DESC_NOCAP_THE
);
4639 void melee_attack::mons_announce_hit(const mon_attack_def
&attk
)
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
)
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)
4675 if (attk
.flavour
== AF_POISON_NASTY
4676 || one_chance_in(15 + 5 * (attk
.flavour
== AF_POISON
? 1 : 0))
4678 && one_chance_in(attk
.flavour
== AF_POISON
? 4 : 3)))
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());
4694 mprf("%s poisons %s!",
4695 atk_name(DESC_CAP_THE
).c_str(),
4696 mons_defender_name().c_str());
4701 if (attk
.flavour
== AF_POISON_NASTY
)
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())
4717 if (one_chance_in(20) || (damage_done
> 2 && one_chance_in(3)))
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);
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
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
)
4751 if (defender
->res_poison() <= 0)
4753 if (one_chance_in(paralyse_roll
))
4754 defender
->paralyse(attacker
, roll_dice(1, 3));
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
);
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
);
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())
4800 // Cannot unequip player.
4801 // TODO: Allow stealing of the wielded weapon?
4802 // Needs to be unwielded properly and should never lead to
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
]))
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
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
;
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
;
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
))
4845 if (steal_what
== -1 || you
.gold
> 0 && one_chance_in(10))
4847 // Found no item worth stealing, try gold.
4850 if (silenced(mon
->pos()))
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
);
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
);
4873 mons_cast(mon
, beem
, SPELL_TELEPORT_SELF
);
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
;
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
)
4891 item_def
&new_item
= mitm
[idx
];
4892 new_item
.base_type
= OBJ_GOLD
;
4893 new_item
.sub_type
= 0;
4896 new_item
.special
= 0;
4898 new_item
.link
= NON_ITEM
;
4899 new_item
.quantity
= stolen_amount
;
4900 new_item
.pos
.reset();
4901 item_colour(new_item
);
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
);
4918 ASSERT(steal_what
!= -1);
4919 ASSERT(mslot
!= NUM_MONSTER_SLOTS
);
4920 ASSERT(mon
->inv
[mslot
] == NON_ITEM
);
4923 int index
= get_item_slot(10);
4924 if (index
== NON_ITEM
)
4927 item_def
&new_item
= mitm
[index
];
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());
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();
4966 if (one_chance_in(4))
4971 case AF_POISON_NASTY
:
4972 case AF_POISON_MEDIUM
:
4973 case AF_POISON_STRONG
:
4974 mons_do_poison(attk
);
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
4988 defender
->drain_stat(drained_stat
, 1, attacker
);
4994 if (one_chance_in(20) || (damage_done
> 2 && one_chance_in(3)))
4995 rot_defender(2 + random2(3), damage_done
> 5 ? 1 : 0);
4999 defender
->sicken(50 + random2(100));
5003 if (attacker
->type
== MONS_FIRE_VORTEX
)
5004 attacker
->as_monster()->hit_points
= -10;
5007 resist_adjust_damage(defender
,
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);
5026 resist_adjust_damage(defender
,
5028 defender
->res_cold(),
5029 attacker
->get_experience_level() +
5030 random2(2 * attacker
->get_experience_level()));
5032 if (needs_message
&& special_damage
)
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);
5047 resist_adjust_damage(
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
)
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
);
5071 // Only may bite non-vampiric monsters (or player) capable of bleeding.
5072 if (!defender
->can_bleed())
5075 // Disallow draining of summoned monsters since they can't bleed.
5076 // XXX: Is this too harsh?
5077 if (defender
->is_summoned())
5080 if (x_chance_in_y(defender
->res_negative_energy(), 3))
5083 if (defender
->stat_hp() < defender
->stat_maxhp())
5085 attacker
->heal(1 + random2(damage_done
), coinflip());
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
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);
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
5115 defender
->drain_stat(drained_stat
, 1, attacker
);
5120 if (defender
->holiness() == MH_UNDEAD
)
5123 if (one_chance_in(20) || (damage_done
> 0 && coinflip()))
5124 defender
->make_hungry(400, false);
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());
5140 if (attk
.type
== AT_SPORE
)
5142 if (defender
->res_poison() > 0)
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()));
5165 if (one_chance_in(30)
5166 || (damage_done
> 5 && coinflip())
5167 || (attk
.damage
== 0 && !one_chance_in(3)))
5174 // Only wasps at the moment.
5175 wasp_paralyse_defender();
5179 if (attacker
->type
== MONS_SPINY_WORM
&& defender
->res_poison() <= 0)
5180 defender
->poison(attacker
, 2 + random2(4));
5181 splash_defender_with_acid(3);
5185 distortion_affects_defender();
5189 if (!one_chance_in(3) || !defender
->can_go_berserk())
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);
5208 chaos_affects_defender();
5212 // Ignore monsters, for now.
5213 if (defender
->atype() != ACT_PLAYER
)
5216 _steal_item_from_player(attacker
->as_monster());
5221 // Monsters don't carry food.
5222 if (defender
->atype() != ACT_PLAYER
)
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(),
5232 mprf("%s devours some of your food!",
5233 atk_name(DESC_CAP_THE
).c_str());
5237 mprf("%s devours some of the food beneath you!",
5238 atk_name(DESC_CAP_THE
).c_str());
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());
5255 if (defender
->is_evil() || defender
->is_unholy())
5256 special_damage
= attk
.damage
* 0.75;
5258 if (needs_message
&& special_damage
)
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());
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)
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
);
5292 simple_monster_message(mon
, " is very cold.");
5295 flash_monster_colour(mon
, LIGHTBLUE
, 200);
5298 mon
->hurt(&you
, hurted
);
5302 mon
->expose_to_element(BEAM_COLD
, orig_hurted
);
5305 const int cold_res
= mon
->res_cold();
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
);
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
)
5353 simple_monster_message(attacker
->as_monster(),
5354 " dodges your spines.");
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
);
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
5387 attacker
->body_size(PSIZE_BODY
) - defender
->body_size(PSIZE_BODY
);
5388 if (!x_chance_in_y(size_diff
+ 3, 6))
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
))
5398 // don't trample into a monster - or do we want to cause a chain
5400 if (actor_at(new_pos
))
5403 defender
->move_to_pos(new_pos
);
5405 if (attacker
->is_habitable(old_pos
))
5406 attacker
->move_to_pos(old_pos
);
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
)
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());
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)
5452 if (!attacker
->alive())
5455 // Monster went away?
5456 if (!defender
->alive() || defender
->pos() != pos
)
5458 if (attacker
== defender
5459 || !attacker
->as_monster()->has_multitargeting())
5464 // Hydras can try and pick up a new monster to attack to
5465 // finish out their round. -cao
5467 for (adjacent_iterator
i(attacker
->pos()); i
; ++i
)
5470 && !mons_aligned(attacker
, &you
))
5472 attacker
->as_monster()->foe
= MHITYOU
;
5473 attacker
->as_monster()->target
= you
.pos();
5479 monster
* mons
= monster_at(*i
);
5480 if (mons
&& !mons_aligned(attacker
, mons
))
5489 // No adjacent hostiles.
5494 // Monsters hitting themselves get just one round.
5495 if (attack_number
> 0 && attacker
== defender
)
5500 mon_attack_def attk
= mons_attack_spec(attacker
->as_monster(),
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
;
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
);
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
;
5535 case AT_TENTACLE_SLAP
:
5568 // To prevent compiler warnings.
5572 die("Invalid attack flavour for noise_factor");
5576 die("Unhandled attack flavour for noise_factor");
5580 switch(attk
.flavour
)
5587 noise_factor
+= 100;
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(),
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
);
5646 if (defender_invisible
)
5648 // No evasion feedback if we don't know what we're
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)
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)
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;
5688 // Misses no matter what.
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());
5702 perceived_attack
= true;
5703 damage_done
= mons_calc_damage(attk
);
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.
5719 if (!attacker
->alive())
5724 if (check_unrand_effects())
5727 if (damage_done
< 1 && this_round_hit
&& !shield_blocked
)
5728 mons_announce_dud_hit(attk
);
5730 if (damage_done
> 0)
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
)))
5759 special_damage_message
.clear();
5760 special_damage_flavour
= BEAM_NONE
;
5762 if (attacker
!= defender
&& attk
.type
== AT_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
5777 if (is_banished(defender
))
5779 if (chaos_attack
&& attacker
->alive())
5780 chaos_affects_attacker();
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();
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
)
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
5822 if (miscast_level
== 0)
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();
5841 if (chaos_attack
&& attacker
->alive())
5842 chaos_affects_attacker();
5844 if (miscast_target
== defender
)
5847 // Yredelemnul's injury mirroring can kill the attacker.
5848 if (!attacker
->alive())
5851 if (miscast_target
== attacker
)
5854 // Miscast might have killed the attacker.
5855 if (!attacker
->alive())
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.
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;
5906 mons_perform_attack_rounds();
5911 void melee_attack::mons_check_attack_perceived()
5913 if (!perceived_attack
)
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();
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
;
5944 if (weapon
&& weapon
->base_type
== OBJ_WEAPONS
)
5945 mhit
+= weapon
->plus
+ property(*weapon
, PWPN_HIT
);
5947 if (attacker
->confused())
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)
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
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(),
5973 void melee_attack::chaos_affect_actor(actor
*victim
)
5975 melee_attack
attk(victim
, victim
);
5977 attk
.skip_chaos_message
= true;
5978 attk
.chaos_affects_defender();
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
)
5994 if (weapon
->base_type
!= OBJ_WEAPONS
)
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;
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
))
6028 std::string prompt
= "Really attack while ";
6029 if (unarmed_warning
)
6030 prompt
+= "being unarmed?";
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.
6040 you
.received_weapon_warning
= 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;
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;
6078 bool attack
= attk
.attack();
6081 // Attack was cancelled or unsuccessful...
6082 if (attk
.cancel_attack
)
6083 you
.turn_is_over
= false;
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)
6106 // speed adjustment for weapon using monsters
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);
6116 attacker
->speed_increment
-= delta
;
6120 bool monster_attack_actor(monster
* attacker
, actor
*defender
,
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(),
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
))
6139 // It's hard to attack from within a shell.
6140 if (attacker
->withdrawn())
6143 // In case the monster hasn't noticed you, bumping into it will
6145 behaviour_event(attacker
, ME_ALERT
, MHITYOU
);
6146 melee_attack
attk(attacker
, &you
, allow_unarmed
);
6152 // Two monsters fighting each other.
6153 bool monsters_fight(monster
* attacker
, monster
* defender
,
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
)
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).
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
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.
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
)
6217 else if (wpn_type
== WPN_QUICK_BLADE
) // high dex is very good for these
6220 if (hands
== HANDS_TWO
)
6223 // most weapons are capped at 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
)
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.
6248 int ret
= weapon_str_weight(weapon
->base_type
, weapon
->sub_type
);
6250 if (hands_reqd(*weapon
, you
.body_size()) == HANDS_HALF
&& !you
.shield())
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
)
6287 case 6: // big melee, monster surrounded/not paying attention
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());
6296 mprf("You catch %s momentarily off-guard.",
6297 defender
->name(DESC_NOCAP_THE
).c_str());
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());
6308 mprf("You %s %s from behind!",
6309 (you
.species
== SP_CAT
) ? "pounce on" : "strike",
6310 defender
->name(DESC_NOCAP_THE
).c_str());
6315 if (you
.species
== SP_CAT
&& coinflip())
6317 mprf("You pounce on the unaware %s!",
6318 defender
->name(DESC_PLAIN
).c_str());
6321 mprf("%s fails to defend %s.",
6322 defender
->name(DESC_CAP_THE
).c_str(),
6323 defender
->pronoun(PRONOUN_REFLEXIVE
).c_str());
6328 void chaos_affect_actor(actor
*victim
)
6330 melee_attack::chaos_affect_actor(victim
);