Use cell_is_solid where appropriate.
[crawl:crawl.git] / crawl-ref / source / fight.cc
1 /**
2  * @file
3  * @brief functions used during combat
4  */
5
6 #include "AppHdr.h"
7
8 #include "fight.h"
9
10 #include <string.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <algorithm>
14
15 #include "art-enum.h"
16 #include "cloud.h"
17 #include "coordit.h"
18 #include "delay.h"
19 #include "env.h"
20 #include "fineff.h"
21 #include "fprop.h"
22 #include "hints.h"
23 #include "invent.h"
24 #include "itemprop.h"
25 #include "melee_attack.h"
26 #include "mgen_data.h"
27 #include "misc.h"
28 #include "mon-behv.h"
29 #include "mon-cast.h"
30 #include "mon-place.h"
31 #include "mon-util.h"
32 #include "ouch.h"
33 #include "player.h"
34 #include "random-var.h"
35 #include "shopping.h"
36 #include "spl-miscast.h"
37 #include "spl-summoning.h"
38 #include "state.h"
39 #include "stuff.h"
40 #include "target.h"
41 #include "terrain.h"
42 #include "travel.h"
43 #include "traps.h"
44
45 /* Handles melee combat between attacker and defender
46  *
47  * Works using the new fight rewrite. For a monster attacking, this method
48  * loops through all their available attacks, instantiating a new melee_attack
49  * for each attack. Combat effects should not go here, if at all possible. This
50  * is merely a wrapper function which is used to start combat.
51  */
52 bool fight_melee(actor *attacker, actor *defender, bool *did_hit, bool simu)
53 {
54     if (defender->is_player())
55     {
56         ASSERT(!crawl_state.game_is_arena());
57         // Friendly and good neutral monsters won't attack unless confused.
58         if (attacker->as_monster()->wont_attack()
59             && !mons_is_confused(attacker->as_monster())
60             && !attacker->as_monster()->has_ench(ENCH_INSANE))
61         {
62             return false;
63         }
64
65         // It's hard to attack from within a shell.
66         if (attacker->as_monster()->withdrawn())
67             return false;
68
69         // Boulders can't melee while they're rolling past you
70         if (attacker->as_monster()->rolling())
71             return false;
72
73         // In case the monster hasn't noticed you, bumping into it will
74         // change that.
75         behaviour_event(attacker->as_monster(), ME_ALERT, defender);
76     }
77     else if (attacker->is_player())
78     {
79         ASSERT(!crawl_state.game_is_arena());
80         // Can't damage orbs this way.
81         if (mons_is_projectile(defender->type) && !you.confused())
82         {
83             you.turn_is_over = false;
84             return false;
85         }
86
87         melee_attack attk(&you, defender);
88
89         if (simu)
90             attk.simu = true;
91
92         // We're trying to hit a monster, break out of travel/explore now.
93         if (!travel_kill_monster(defender->type))
94             interrupt_activity(AI_HIT_MONSTER, defender->as_monster());
95
96         // Check if the player is fighting with something unsuitable,
97         // or someone unsuitable.
98         if (you.can_see(defender)
99             && !wielded_weapon_check(attk.weapon))
100         {
101             you.turn_is_over = false;
102             return false;
103         }
104
105         if (!attk.attack())
106         {
107             // Attack was cancelled or unsuccessful...
108             if (attk.cancel_attack)
109                 you.turn_is_over = false;
110             return false;
111         }
112
113         if (did_hit)
114             *did_hit = attk.did_hit;
115
116         // A spectral weapon attacks whenever the player does
117         if (!simu && you.props.exists("spectral_weapon"))
118             trigger_spectral_weapon(&you, defender);
119
120         return true;
121     }
122
123     // If execution gets here, attacker != Player, so we can safely continue
124     // with processing the number of attacks a monster has without worrying
125     // about unpredictable or weird results from players.
126
127     // If this is a spectral weapon check if it can attack
128     if (attacker->as_monster()->type == MONS_SPECTRAL_WEAPON
129         && !confirm_attack_spectral_weapon(attacker->as_monster(), defender))
130     {
131         // Pretend an attack happened,
132         // so the weapon doesn't advance unecessarily.
133         return true;
134     }
135
136     const int nrounds = attacker->as_monster()->has_hydra_multi_attack() ?
137         attacker->as_monster()->number : 4;
138     coord_def pos    = defender->pos();
139
140     // Melee combat, tell attacker to wield its melee weapon.
141     attacker->as_monster()->wield_melee_weapon();
142
143     int effective_attack_number = 0;
144     int attack_number;
145     for (attack_number = 0; attack_number < nrounds && attacker->alive();
146          ++attack_number, ++effective_attack_number)
147     {
148         if (!attacker->alive())
149             return false;
150
151         // Monster went away?
152         if (!defender->alive()
153             || defender->pos() != pos
154             || defender->is_banished())
155         {
156             if (attacker == defender
157                || !attacker->as_monster()->has_multitargetting())
158             {
159                 break;
160             }
161
162             // Hydras can try and pick up a new monster to attack to
163             // finish out their round. -cao
164             bool end = true;
165             for (adjacent_iterator i(attacker->pos()); i; ++i)
166             {
167                 if (*i == you.pos()
168                     && !mons_aligned(attacker, &you))
169                 {
170                     attacker->as_monster()->foe = MHITYOU;
171                     attacker->as_monster()->target = you.pos();
172                     defender = &you;
173                     end = false;
174                     break;
175                 }
176
177                 monster* mons = monster_at(*i);
178                 if (mons && !mons_aligned(attacker, mons))
179                 {
180                     defender = mons;
181                     end = false;
182                     pos = mons->pos();
183                     break;
184                 }
185             }
186
187             // No adjacent hostiles.
188             if (end)
189                 break;
190         }
191
192         melee_attack melee_attk(attacker, defender, attack_number,
193                                 effective_attack_number);
194
195         if (simu)
196             melee_attk.simu = true;
197
198         // If the attack fails out, keep effective_attack_number up to
199         // date so that we don't cause excess energy loss in monsters
200         if (!melee_attk.attack())
201             effective_attack_number = melee_attk.effective_attack_number;
202         else if (did_hit && !(*did_hit))
203             *did_hit = melee_attk.did_hit;
204
205         fire_final_effects();
206     }
207
208     // A spectral weapon attacks whenever the player does
209     if (!simu && attacker->props.exists("spectral_weapon"))
210         trigger_spectral_weapon(attacker, defender);
211
212     return true;
213 }
214
215 // Handles jump attack between attacker and defender.  We need attack_pos since
216 // defender may not exist.
217 bool fight_jump(actor *attacker, actor *defender, coord_def attack_pos,
218                 coord_def landing_pos, set<coord_def> landing_sites,
219                 bool jump_blocked, bool *did_hit)
220 {
221     set<coord_def>::const_iterator site;
222
223     ASSERT(!crawl_state.game_is_arena());
224
225     melee_attack first_attk(attacker, defender, -1, -1, false, true,
226                             jump_blocked, landing_pos);
227
228     // Do player warnings for electrocution and sanctuary based on possible
229     // landing sites.
230     if (attacker->is_player())
231     {
232         bool conduct_prompted, zot_trap_prompted, trap_prompted,
233             exclusion_prompted, cloud_prompted, terrain_prompted;
234         bool defender_vuln = !defender
235             || (feat_is_water(grd(defender->pos()))
236                 && (!you.can_see(defender) || defender->ground_level()));
237         bool check_landing_only = false;
238         string prompt;
239         item_def *weapon = attacker->weapon(-1);
240
241         conduct_prompted = zot_trap_prompted = trap_prompted
242             = exclusion_prompted = cloud_prompted = terrain_prompted = false;
243
244         // Can't damage orbs this way.
245         if (defender && mons_is_projectile(defender->type) && !you.confused())
246         {
247             you.turn_is_over = false;
248             return false;
249         }
250
251         // Check if the player is fighting with something unsuitable,
252         // or someone unsuitable.
253         if (defender && you.can_see(defender)
254             && !wielded_weapon_check(first_attk.weapon))
255         {
256             you.turn_is_over = false;
257             return false;
258         }
259         else if (!defender || !you.can_see(defender))
260         {
261             prompt = "Really jump-attack where there is no visible monster?";
262             if (!yesno(prompt.c_str(), true, 'n'))
263             {
264                 canned_msg(MSG_OK);
265                 you.turn_is_over = false;
266                 return false;
267             }
268         }
269
270         for (site = landing_sites.begin(); site != landing_sites.end(); site++)
271         {
272             bool ground_level = !you.airborne() && !you.can_cling_to(*site)
273                 && you.species != SP_DJINNI;
274             if (attacker->damage_brand(-1) == SPWPN_ELECTROCUTION
275                 && !you.received_weapon_warning
276                 && (feat_is_water(grd(*site)) && ground_level)
277                 && !attacker->res_elec()
278                 && defender_vuln
279                 && adjacent(*site, defender->pos()))
280             {
281                 prompt = "Really jump-attack with ";
282                 if (weapon)
283                     prompt += weapon->name(DESC_YOUR);
284                 else
285                     prompt += "your electric unarmed attack";
286                 prompt += " when you might land in water? ";
287                 if (yesno(prompt.c_str(), true, 'n'))
288                     you.received_weapon_warning = true;
289                 else
290                 {
291                     canned_msg(MSG_OK);
292                     you.turn_is_over = false;
293                     return false;
294                 }
295             }
296             // If we have no defender or have one we can't see and are attacking
297             // from within or at a sanctuary position , prompt.
298             if (!conduct_prompted && (!defender || !you.can_see(defender)))
299             {
300                 prompt = "";
301                 if (is_sanctuary(attack_pos))
302                     prompt = "Really jump-attack in your sanctuary?";
303                 else if (is_sanctuary(*site))
304                     prompt = "Really jump-attack when you might land in your "
305                         "sanctuary?";
306                 if (prompt != "")
307                 {
308                     if (yesno(prompt.c_str(), true, 'n'))
309                         conduct_prompted = true;
310                     else
311                     {
312                         canned_msg(MSG_OK);
313                         you.turn_is_over = false;
314                         return false;
315                     }
316                 }
317             }
318             // If we have a defender that we can see, check the attack on the
319             // defender in general for conduct and check the landing site for
320             // sanctuary; on subsequent sites check only the landing site for a
321             // sanctuary if necessary.
322             if (defender && !conduct_prompted)
323             {
324                 if (stop_attack_prompt(defender->as_monster(), false, *site,
325                                        false, &conduct_prompted, *site,
326                                        check_landing_only))
327                 {
328                     you.turn_is_over = false;
329                     return false;
330                 }
331             }
332
333             // On the first landing site, check the hit function for elec or
334             // devastator for conduct
335             if (!check_landing_only && !conduct_prompted
336                 && (attacker->damage_brand(-1) == SPWPN_ELECTROCUTION
337                     || weapon && is_unrandom_artefact(*weapon)
338                     && weapon->special == UNRAND_DEVASTATOR))
339             {
340                 string verb = "jump-attack", junk1, junk2;
341                 if (defender)
342                 {
343                     verb = (bad_attack(defender->as_monster(),
344                                        junk1, junk2)
345                             ? "jump-attack" : "jump-attack near");
346                 }
347
348                 bool (*aff_func)(const coord_def &) = 0;
349                 if (attacker->damage_brand(-1) == SPWPN_ELECTROCUTION)
350                     aff_func = conduction_affected;
351
352                 targetter_smite hitfunc(attacker, 1, 1, 1, false, aff_func);
353                 hitfunc.set_aim(attack_pos);
354                 hitfunc.origin = *site;
355
356                 if (stop_attack_prompt(hitfunc, verb, nullptr,
357                                        &conduct_prompted))
358                 {
359                     you.turn_is_over = false;
360                     return false;
361                 }
362             }
363
364             // Check landing in dangerous clouds
365             if (!cloud_prompted
366                 && !check_moveto_cloud(*site, "jump-attack", &cloud_prompted))
367             {
368                 you.turn_is_over = false;
369                 return false;
370             }
371
372             //  Check landing on traps, continuing to check for zot traps even
373             //  if we've prompted about other kinds of traps.
374             if (!zot_trap_prompted)
375             {
376                 trap_def* trap = find_trap(*site);
377                 if (trap && env.grid(*site) != DNGN_UNDISCOVERED_TRAP
378                     && trap->type == TRAP_ZOT)
379                 {
380                     if (!check_moveto_trap(*site, "jump-attack",
381                                            &trap_prompted))
382                     {
383                         you.turn_is_over = false;
384                         return false;
385                     }
386                     zot_trap_prompted = true;
387                 }
388                 else if (!trap_prompted
389                          && !check_moveto_trap(*site, "jump-attack",
390                                                &trap_prompted))
391                 {
392                     you.turn_is_over = false;
393                     return false;
394                 }
395             }
396
397             // Check landing in exclusions
398             if (!exclusion_prompted
399                 && !check_moveto_exclusion(*site, "jump-attack",
400                                            &exclusion_prompted))
401             {
402                 you.turn_is_over = false;
403                 return false;
404             }
405
406             // Check landing over dangerous terrain while flying or transformed
407             // with expiring status.
408             if (!terrain_prompted
409                 && !check_moveto_terrain(*site, "jump-attack", "",
410                                          &terrain_prompted))
411             {
412                 you.turn_is_over = false;
413                 return false;
414             }
415             check_landing_only = true;
416         }
417     }
418     if (!first_attk.attack() && first_attk.cancel_attack)
419     {
420         you.turn_is_over = false;
421         return false;
422     }
423     if (did_hit)
424         *did_hit = first_attk.did_hit;
425     return true;
426 }
427
428 unchivalric_attack_type is_unchivalric_attack(const actor *attacker,
429                                               const actor *defender)
430 {
431     const monster* def = defender->as_monster();
432     unchivalric_attack_type unchivalric = UCAT_NO_ATTACK;
433
434     // No unchivalric attacks on monsters that cannot fight (e.g.
435     // plants) or monsters the attacker can't see (either due to
436     // invisibility or being behind opaque clouds).
437     if (defender->cannot_fight() || (attacker && !attacker->can_see(defender)))
438         return unchivalric;
439
440     // Distracted (but not batty); this only applies to players.
441     if (attacker && attacker->is_player()
442         && def && def->foe != MHITYOU && !mons_is_batty(def))
443     {
444         unchivalric = UCAT_DISTRACTED;
445     }
446
447     // confused (but not perma-confused)
448     if (def && mons_is_confused(def, false))
449         unchivalric = UCAT_CONFUSED;
450
451     // allies
452     if (def && def->friendly())
453         unchivalric = UCAT_ALLY;
454
455     // fleeing
456     if (def && mons_is_fleeing(def))
457         unchivalric = UCAT_FLEEING;
458
459     // invisible
460     if (attacker && !attacker->visible_to(defender))
461         unchivalric = UCAT_INVISIBLE;
462
463     // held in a net
464     if (def && def->caught())
465         unchivalric = UCAT_HELD_IN_NET;
466
467     // petrifying
468     if (def && def->petrifying())
469         unchivalric = UCAT_PETRIFYING;
470
471     // petrified
472     if (defender->petrified())
473         unchivalric = UCAT_PETRIFIED;
474
475     // paralysed
476     if (defender->paralysed())
477         unchivalric = UCAT_PARALYSED;
478
479     // sleeping
480     if (defender->asleep())
481         unchivalric = UCAT_SLEEPING;
482
483     return unchivalric;
484 }
485
486 static bool is_boolean_resist(beam_type flavour)
487 {
488     switch (flavour)
489     {
490     case BEAM_ELECTRICITY:
491     case BEAM_MIASMA: // rotting
492     case BEAM_NAPALM:
493     case BEAM_WATER:  // water asphyxiation damage,
494                       // bypassed by being water inhabitant.
495         return true;
496     default:
497         return false;
498     }
499 }
500
501 // Gets the percentage of the total damage of this damage flavour that can
502 // be resisted.
503 static inline int get_resistible_fraction(beam_type flavour)
504 {
505     switch (flavour)
506     {
507     // Drowning damage from water is resistible by being a water thing, or
508     // otherwise asphyx resistant.
509     case BEAM_WATER:
510         return 40;
511
512     // Assume ice storm and throw icicle are mostly solid.
513     case BEAM_ICE:
514         return 40;
515
516     case BEAM_LAVA:
517         return 55;
518
519     case BEAM_POISON_ARROW:
520     case BEAM_GHOSTLY_FLAME:
521         return 70;
522
523     default:
524         return 100;
525     }
526 }
527
528 // Adjusts damage for elemental resists, electricity and poison.
529 //
530 // FIXME: Does not (yet) handle life draining, player acid damage
531 // (does handle monster acid damage), miasma, and other exotic
532 // attacks.
533 //
534 // beam_type is just used to determine the damage flavour, it does not
535 // necessarily imply that the attack is a beam attack.
536 int resist_adjust_damage(actor *defender, beam_type flavour,
537                          int res, int rawdamage, bool ranged)
538 {
539     if (!res)
540         return rawdamage;
541
542     const bool mons = (defender->is_monster());
543
544     const int resistible_fraction = get_resistible_fraction(flavour);
545
546     int resistible = rawdamage * resistible_fraction / 100;
547     const int irresistible = rawdamage - resistible;
548
549     if (res > 0)
550     {
551         if (((mons || flavour == BEAM_NEG) && res >= 3) || res > 3)
552             resistible = 0;
553         else
554         {
555             // Check if this is a resist that pretends to be boolean for
556             // damage purposes.  Only electricity, miasma and sticky
557             // flame (napalm) do this at the moment; raw poison damage
558             // uses the normal formula.
559             const int bonus_res = (is_boolean_resist(flavour) ? 1 : 0);
560
561             // Use a new formula for players, but keep the old, more
562             // effective one for monsters.
563             if (mons)
564                 resistible /= 1 + bonus_res + res * res;
565             else if (flavour == BEAM_NEG)
566                 resistible /= res * 2;
567             else
568                 resistible /= resist_fraction(res, bonus_res);
569         }
570     }
571     else if (res < 0)
572         resistible = resistible * (ranged? 15 : 20) / 10;
573
574     return max(resistible + irresistible, 0);
575 }
576
577 ///////////////////////////////////////////////////////////////////////////
578
579 bool wielded_weapon_check(item_def *weapon, bool no_message)
580 {
581     bool weapon_warning  = false;
582     bool unarmed_warning = false;
583
584     if (weapon)
585     {
586         if (needs_handle_warning(*weapon, OPER_ATTACK)
587             || !is_melee_weapon(*weapon))
588         {
589             weapon_warning = true;
590         }
591     }
592     else if (you.attribute[ATTR_WEAPON_SWAP_INTERRUPTED]
593              && you_tran_can_wear(EQ_WEAPON))
594     {
595         const int weap = you.attribute[ATTR_WEAPON_SWAP_INTERRUPTED] - 1;
596         const item_def &wpn = you.inv[weap];
597         if (is_melee_weapon(wpn)
598             && you.skill(weapon_skill(wpn)) > you.skill(SK_UNARMED_COMBAT))
599         {
600             unarmed_warning = true;
601         }
602     }
603
604     if (!you.received_weapon_warning && !you.confused()
605         && (weapon_warning || unarmed_warning))
606     {
607         if (no_message)
608             return false;
609
610         string prompt  = "Really attack while ";
611         if (unarmed_warning)
612             prompt += "unarmed?";
613         else
614             prompt += "wielding " + weapon->name(DESC_YOUR) + "? ";
615
616         const bool result = yesno(prompt.c_str(), true, 'n');
617
618         learned_something_new(HINT_WIELD_WEAPON); // for hints mode Rangers
619
620         // Don't warn again if you decide to continue your attack.
621         if (result)
622             you.received_weapon_warning = true;
623
624         return result;
625     }
626
627     return true;
628 }
629
630 // Used by cleave and jump attack to determine if multi-hit targets will be
631 // attacked.
632 static bool _dont_harm(const actor* attacker, const actor* defender)
633 {
634     return (mons_aligned(attacker, defender)
635             || attacker == &you && defender->wont_attack()
636             || defender == &you && attacker->wont_attack());
637 }
638 // Put the potential cleave targets into a list. Up to 3, taken in order by
639 // rotating from the def position and stopping at the first solid feature.
640 void get_cleave_targets(const actor* attacker, const coord_def& def, int dir,
641                         list<actor*> &targets)
642 {
643     // Prevent scanning invalid coordinates if the attacker dies partway through
644     // a cleave (due to hitting explosive creatures, or perhaps other things)
645     if (!attacker->alive())
646         return;
647
648     const coord_def atk = attacker->pos();
649     coord_def atk_vector = def - atk;
650
651     for (int i = 0; i < 3; ++i)
652     {
653         atk_vector = rotate_adjacent(atk_vector, dir);
654         if (cell_is_solid(atk + atk_vector))
655             break;
656
657         actor * target = actor_at(atk + atk_vector);
658         if (target && !_dont_harm(attacker, target))
659             targets.push_back(target);
660     }
661 }
662
663 void get_all_cleave_targets(const actor* attacker, const coord_def& def,
664                             list<actor*> &targets)
665 {
666     if (cell_is_solid(def))
667         return;
668
669     int dir = coinflip() ? -1 : 1;
670     get_cleave_targets(attacker, def, dir, targets);
671     targets.reverse();
672     if (actor_at(def))
673         targets.push_back(actor_at(def));
674     get_cleave_targets(attacker, def, -dir, targets);
675 }
676
677 void attack_cleave_targets(actor* attacker, list<actor*> &targets,
678                            int attack_number, int effective_attack_number)
679 {
680     while (!targets.empty())
681     {
682         actor* def = targets.front();
683         if (attacker->alive() && def && def->alive()
684             && !_dont_harm(attacker, def))
685         {
686             melee_attack attck(attacker, def, attack_number,
687                                ++effective_attack_number, true);
688             attck.attack();
689         }
690         targets.pop_front();
691     }
692 }
693
694 int finesse_adjust_delay(int delay)
695 {
696     if (you.duration[DUR_FINESSE])
697     {
698         ASSERT(!you.duration[DUR_BERSERK]);
699         // Need to undo haste by hand.
700         if (you.duration[DUR_HASTE])
701             delay = haste_mul(delay);
702         delay = div_rand_round(delay, 2);
703     }
704     return delay;
705 }
706
707 bool conduction_affected(const coord_def &pos)
708 {
709     const actor *act = actor_at(pos);
710
711     // Don't check rElec to avoid leaking information about armour etc.
712     return feat_is_water(grd(pos)) && act && act->ground_level();
713 }