Activate Ash skill boost from 0* (mdonais)
[crawl.git] / crawl-ref / source / god-passive.cc
1 #include "AppHdr.h"
2
3 #include "god-passive.h"
4
5 #include <algorithm>
6 #include <cmath>
7
8 #include "act-iter.h"
9 #include "areas.h"
10 #include "artefact.h"
11 #include "art-enum.h"
12 #include "branch.h"
13 #include "chardump.h"
14 #include "cloud.h"
15 #include "coordit.h"
16 #include "directn.h"
17 #include "env.h"
18 #include "eq-type-flags.h"
19 #include "fight.h"
20 #include "files.h"
21 #include "fprop.h"
22 #include "god-abil.h"
23 #include "god-prayer.h"
24 #include "invent.h" // in_inventory
25 #include "item-name.h"
26 #include "item-prop.h"
27 #include "item-status-flag-type.h"
28 #include "items.h"
29 #include "libutil.h"
30 #include "map-knowledge.h"
31 #include "melee-attack.h"
32 #include "message.h"
33 #include "mon-cast.h"
34 #include "mon-place.h"
35 #include "mon-util.h"
36 #include "output.h"
37 #include "religion.h"
38 #include "shout.h"
39 #include "skills.h"
40 #include "spl-clouds.h"
41 #include "state.h"
42 #include "stringutil.h"
43 #include "tag-version.h"
44 #include "terrain.h"
45 #include "throw.h"
46 #include "unwind.h"
47
48 // TODO: template out the differences between this and god_power.
49 // TODO: use the display method rather than dummy powers in god_powers.
50 // TODO: finish using these for implementing passive abilities.
51 struct god_passive
52 {
53     // 1-6 means it unlocks at that many stars of piety;
54     // 0 means it is always present when in good standing with the god;
55     // -1 means it is present even under penance;
56     int rank;
57     passive_t pasv;
58     /** Message to be given on gain of this passive.
59      *
60      * If the string begins with an uppercase letter, it is treated as
61      * a complete sentence. Otherwise, if it contains the substring " NOW ",
62      * the string "You " is prepended. Otherwise, the string "You NOW " is
63      * prepended to the message, with the " NOW " then being replaced.
64      *
65      * The substring "GOD" is replaced with the name of the god.
66      * The substring " NOW " is replaced with " now " for messages about
67      * gaining the ability, or " " for messages about having the ability.
68      *
69      * Examples:
70      *   "have super powers"
71      *     => "You have super powers", "You now have super powers."
72      *   "are NOW powerful"
73      *     => "You are powerful", "You are now powerful."
74      *   "Your power is NOW great"
75      *     => "Your power is great", "Your power is now great"
76      *   "GOD NOW makes you powerful"
77      *     => "Moloch makes you powerful", "Moloch now makes you powerful"
78      *   "GOD grants you a boon"
79      *     => "Moloch grants you a boon" (in all situations)
80      *
81      * FIXME: This member is currently unused.
82      *
83      * @see god_passive::loss.
84      */
85     const char* gain;
86     /** Message to be given on loss of this passive.
87      *
88      * If empty, use the gain message. This makes sense only if the message
89      * contains " NOW ", either explicitly or implicitly through not
90      * beginning with a capital letter.
91      *
92      * The substring "GOD" is replaced with the name of the god.
93      * The substring " NOW " is replaced with " no longer ".
94      *
95      * Examples:
96      *   "have super powers"
97      *     => "You no longer have super powers"
98      *   "are NOW powerful"
99      *     => "You are no longer powerful"
100      *   "Your power is NOW great"
101      *     => "Your power is no longer great"
102      *   "GOD NOW makes you powerful"
103      *     => "Moloch no longer makes you powerful"
104      *   "GOD grants you a boon"
105      *     => "Moloch grants you a boon" (probably incorrect)
106      *
107      * FIXME: This member is currently unused.
108      *
109      * @see god_passive::gain.
110      */
111     const char* loss;
112
113     god_passive(int rank_, passive_t pasv_, const char* gain_,
114                 const char* loss_ = "")
115         : rank{rank_}, pasv{pasv_}, gain{gain_}, loss{*loss_ ? loss_ : gain_}
116     { }
117
118     god_passive(int rank_, const char* gain_, const char* loss_ = "")
119         : god_passive(rank_, passive_t::none, gain_, loss_)
120     { }
121
122     void display(bool gaining, const char* fmt) const
123     {
124         const char * const str = gaining ? gain : loss;
125         if (isupper(str[0]))
126             god_speaks(you.religion, str);
127         else
128             god_speaks(you.religion, make_stringf(fmt, str).c_str());
129     }
130 };
131
132 static const vector<god_passive> god_passives[] =
133 {
134     // no god
135     { },
136
137     // Zin
138     {
139         { -1, passive_t::protect_from_harm,
140               "GOD sometimes watches over you",
141               "GOD no longer watches over you"
142         },
143         { -1, passive_t::resist_mutation,
144               "GOD can shield you from mutations",
145               "GOD NOW shields you from mutations"
146         },
147         { -1, passive_t::resist_polymorph,
148               "GOD can protect you from unnatural transformations",
149               "GOD NOW protects you from unnatural transformations",
150         },
151         { -1, passive_t::resist_hell_effects,
152               "GOD can protect you from effects of Hell",
153               "GOD NOW protects you from effects of Hell"
154         },
155         { -1, passive_t::warn_shapeshifter,
156               "GOD will NOW warn you about shapeshifters"
157         },
158         {
159           6, passive_t::cleanse_mut_potions,
160               "GOD cleanses your potions of mutation",
161               "GOD no longer cleanses your potions of mutation",
162         }
163     },
164
165     // TSO
166     {
167         { -1, passive_t::protect_from_harm,
168               "GOD sometimes watches over you",
169               "GOD no longer watches over you"
170         },
171         { -1, passive_t::abjuration_protection_hd,
172               "GOD NOW protects your summons from abjuration" },
173         { -1, passive_t::bless_followers_vs_evil,
174               "GOD NOW blesses your followers when they kill evil beings" },
175         { -1, passive_t::restore_hp_mp_vs_evil,
176               "gain health and magic from killing evil beings" },
177         { -1, passive_t::no_stabbing,
178               "are NOW prevented from stabbing unaware creatures" },
179         {  0, passive_t::halo, "are NOW surrounded by divine halo" },
180     },
181
182     // Kikubaaqudgha
183     {
184         {  2, passive_t::miscast_protection_necromancy,
185               "GOD NOW protects you from necromancy miscasts"
186               " and mummy death curses"
187         },
188         {  4, passive_t::resist_torment,
189               "GOD NOW protects you from torment" },
190     },
191
192     // Yredelemnul
193     {
194         {  3, passive_t::nightvision, "can NOW see well in the dark" },
195     },
196
197     // Xom
198     { },
199
200     // Vehumet
201     {
202         { -1, passive_t::mp_on_kill,
203               "have a chance to gain magical power from killing" },
204         {  3, passive_t::spells_success,
205               "are NOW less likely to miscast destructive spells" },
206         {  4, passive_t::spells_range,
207               "can NOW cast destructive spells farther" },
208     },
209
210     // Okawaru
211     {
212         // None
213     },
214
215     // Makhleb
216     {
217         { -1, passive_t::restore_hp, "gain health from killing" },
218     },
219
220     // Sif Muna
221     { },
222
223     // Trog
224     {
225         { -1, passive_t::abjuration_protection,
226               "GOD NOW protects your allies from abjuration"
227         },
228         {  0, passive_t::extend_berserk,
229               "can NOW maintain berserk longer, and are less likely to pass out",
230               "can NOW maintain berserk as long, and are more likely to pass out"
231         },
232     },
233
234     // Nemelex
235     {
236         // None
237     },
238
239     // Elyvilon
240     {
241         { -1, passive_t::protect_from_harm,
242               "GOD sometimes watches over you",
243               "GOD no longer watches over you"
244         },
245         { -1, passive_t::protect_ally,
246               "GOD can protect the life of your allies",
247               "GOD NOW protects the life of your allies"
248         },
249     },
250
251     // Lugonu
252     {
253         { -1, passive_t::safe_distortion,
254               "are NOW protected from distortion unwield effects" },
255         { -1, passive_t::map_rot_res_abyss,
256               "remember the shape of the Abyss better" },
257         {  5, passive_t::attract_abyssal_rune,
258               "GOD will NOW help you find the Abyssal rune" },
259     },
260
261     // Beogh
262     {
263         { -1, passive_t::share_exp, "share experience with your followers" },
264         {  3, passive_t::convert_orcs, "inspire orcs to join your side" },
265         {  3, passive_t::bless_followers,
266               "GOD will bless your followers",
267               "GOD will no longer bless your followers"
268         },
269         {  5, passive_t::water_walk, "walk on water" },
270     },
271
272     // Jiyva
273     {
274         { -1, passive_t::neutral_slimes,
275               "Slimes and eye monsters are NOW neutral towards you" },
276         { -1, passive_t::jellies_army,
277               "GOD NOW summons jellies to protect you" },
278         { -1, passive_t::jelly_eating,
279               "GOD NOW allows jellies to devour items" },
280         { -1, passive_t::fluid_stats,
281               "GOD NOW adjusts your attributes periodically" },
282         {  0, passive_t::slime_wall_immune,
283               "are NOW immune to slime covered walls" },
284         {  2, passive_t::slime_feed,
285               "Your fellow slimes NOW consume items" },
286         {  3, passive_t::resist_corrosion,
287               "GOD NOW protects you from corrosion" },
288         {  4, passive_t::slime_mp,
289               "Items consumed by your fellow slimes NOW restore"
290               " your magical power"
291         },
292         {  5, passive_t::slime_hp,
293               "Items consumed by your fellow slimes NOW restore"
294               " your health"
295         },
296         {  6, passive_t::spawn_slimes_on_hit,
297               "spawn slimes when struck by massive blows" },
298         {  6, passive_t::unlock_slime_vaults,
299               "GOD NOW grants you access to the hidden treasures"
300               " of the Slime Pits"
301         },
302     },
303
304     // Fedhas
305     {
306         { -1, passive_t::pass_through_plants,
307               "can NOW walk through plants" },
308         { -1, passive_t::shoot_through_plants,
309               "can NOW safely fire through allied plants" },
310         {  0, passive_t::friendly_plants,
311               "Allied plants are NOW friendly towards you" },
312     },
313
314     // Cheibriados
315     {
316         { -1, passive_t::no_haste,
317               "are NOW protected from inadvertent hurry" },
318         { -1, passive_t::slowed, "move less quickly" },
319         {  0, passive_t::slow_orb_run,
320               "GOD will NOW aid your escape with the Orb of Zot",
321         },
322         {  0, passive_t::stat_boost,
323               "GOD NOW supports your attributes"
324         },
325         {  0, passive_t::slow_abyss,
326               "GOD will NOW slow the Abyss"
327         },
328         // TODO: this one should work regardless of penance, maybe?
329         {  0, passive_t::slow_zot,
330               "GOD will NOW slow Zot's hunt for you"
331         },
332         {  1, passive_t::slow_poison, "process poison slowly" },
333     },
334
335     // Ashenzari
336     {
337         { -1, passive_t::want_curses, "prefer cursed items" },
338         { -1, passive_t::detect_portals, "sense portals" },
339         { -1, passive_t::identify_items, "sense the properties of items" },
340         {  0, passive_t::auto_map, "have improved mapping abilities" },
341         {  0, passive_t::detect_montier, "sense threats" },
342         {  0, passive_t::detect_items, "sense items" },
343         {  0, passive_t::avoid_traps,
344               "avoid traps" },
345         {  0, passive_t::bondage_skill_boost,
346               "get a skill boost from cursed items" },
347         {  2, passive_t::sinv, "are NOW clear of vision" },
348         {  3, passive_t::clarity, "are NOW clear of mind" },
349         {  4, passive_t::xray_vision, "GOD NOW grants you astral sight" },
350     },
351
352     // Dithmenos
353     {
354         {  1, passive_t::nightvision, "can NOW see well in the dark" },
355         {  1, passive_t::umbra, "are NOW surrounded by an umbra" },
356         // TODO: this one should work regardless of penance.
357         {  3, passive_t::hit_smoke, "emit smoke when hit" },
358         {  4, passive_t::shadow_attacks,
359               "Your attacks are NOW mimicked by a shadow" },
360         {  4, passive_t::shadow_spells,
361               "Your attack spells are NOW mimicked by a shadow" },
362     },
363
364     // Gozag
365     {
366         { -1, passive_t::detect_gold, "detect gold" },
367         {  0, passive_t::goldify_corpses,
368               "GOD NOW turns all corpses to gold" },
369         {  0, passive_t::gold_aura, "have a gold aura" },
370     },
371
372     // Qazlal
373     {
374         {  0, passive_t::cloud_immunity, "and your divine allies are ADV immune to clouds" },
375         {  1, passive_t::storm_shield,
376               "generate elemental clouds to protect yourself" },
377         {  4, passive_t::upgraded_storm_shield,
378               "Your chances to be struck by projectiles are NOW reduced" },
379         {  5, passive_t::elemental_adaptation,
380               "Elemental attacks NOW leave you somewhat more resistant"
381               " to them"
382         }
383     },
384
385     // Ru
386     {
387         {  1, passive_t::aura_of_power,
388               "Your enemies will sometimes fail their attack or even hit themselves",
389               "Your enemies will NOW fail their attack or hit themselves"
390         },
391         {  2, passive_t::upgraded_aura_of_power,
392               "Enemies that inflict damage upon you will sometimes receive"
393               " a detrimental status effect",
394               "Enemies that inflict damage upon you will NOW receive"
395               " a detrimental status effect"
396         },
397     },
398
399 #if TAG_MAJOR_VERSION == 34
400     // Pakellas
401     {
402         { -1, passive_t::no_mp_regen,
403               "GOD NOW prevents you from regenerating your magical power" },
404         { -1, passive_t::mp_on_kill, "have a chance to gain magical power from"
405                                      " killing" },
406         {  1, passive_t::bottle_mp,
407               "GOD NOW collects and distills excess magic from your kills"
408         },
409     },
410 #endif
411
412     // Uskayaw
413     { },
414
415     // Hepliaklqana
416     {
417         { -1, passive_t::frail,
418               "GOD NOW siphons a part of your essence into your ancestor" },
419         {  5, passive_t::transfer_drain,
420               "drain nearby creatures when transferring your ancestor" },
421     },
422
423     // Wu Jian
424     {
425         { 0, passive_t::wu_jian_lunge, "perform damaging attacks by moving towards foes." },
426         { 1, passive_t::wu_jian_whirlwind, "lightly attack monsters by moving around them." },
427         { 2, passive_t::wu_jian_wall_jump, "perform airborne attacks in an area by jumping off a solid obstacle." },
428     },
429 };
430 COMPILE_CHECK(ARRAYSZ(god_passives) == NUM_GODS);
431
432 static bool _god_gives_passive_if(god_type god, passive_t passive,
433                                   function<bool(const god_passive&)> condition)
434 {
435     const auto &pasvec = god_passives[god];
436     return any_of(begin(pasvec), end(pasvec),
437                   [&] (const god_passive &p) -> bool
438                   { return p.pasv == passive && condition(p); });
439 }
440
441 bool god_gives_passive(god_type god, passive_t passive)
442 {
443   return _god_gives_passive_if(god, passive,
444                                [] (god_passive /*p*/) { return true; });
445 }
446
447 bool have_passive(passive_t passive)
448 {
449     return _god_gives_passive_if(you.religion, passive,
450                                  [] (god_passive p)
451                                  {
452                                      return piety_rank() >= p.rank
453                                          && (!player_under_penance()
454                                              || p.rank < 0);
455                                  });
456 }
457
458 bool will_have_passive(passive_t passive)
459 {
460   return _god_gives_passive_if(you.religion, passive,
461                                [] (god_passive/*p*/) { return true; });
462 }
463
464 // Returns a large number (10) if we will never get this passive.
465 int rank_for_passive(passive_t passive)
466 {
467     const auto &pasvec = god_passives[you.religion];
468     const auto found = find_if(begin(pasvec), end(pasvec),
469                               [passive] (const god_passive &p) -> bool
470                               {
471                                   return p.pasv == passive;
472                               });
473     return found == end(pasvec) ? 10 : found->rank;
474 }
475
476 int chei_stat_boost(int piety)
477 {
478     if (!have_passive(passive_t::stat_boost))
479         return 0;
480     if (piety < piety_breakpoint(0))  // Since you've already begun to slow down.
481         return 1;
482     if (piety >= piety_breakpoint(5))
483         return 15;
484     return (piety - 10) / 10;
485 }
486
487 // Eat from one random off-level item stack.
488 void jiyva_eat_offlevel_items()
489 {
490     // For wizard mode 'J' command
491     if (!have_passive(passive_t::jelly_eating))
492         return;
493
494     if (crawl_state.game_is_sprint())
495         return;
496
497     while (true)
498     {
499         if (one_chance_in(200))
500             break;
501
502         const int branch = random2(NUM_BRANCHES);
503
504         // Choose level based on main dungeon depth so that levels of
505         // short branches aren't picked more often.
506         ASSERT(brdepth[branch] <= MAX_BRANCH_DEPTH);
507         const int level = random2(MAX_BRANCH_DEPTH) + 1;
508
509         const level_id lid(static_cast<branch_type>(branch), level);
510
511         if (lid == level_id::current() || !you.level_visited(lid))
512             continue;
513
514         dprf("Checking %s", lid.describe().c_str());
515
516         level_excursion le;
517         le.go_to(lid);
518         while (true)
519         {
520             if (one_chance_in(200))
521                 break;
522
523             const coord_def p = random_in_bounds();
524
525             if (env.igrid(p) == NON_ITEM || testbits(env.pgrid(p), FPROP_NO_JIYVA))
526                 continue;
527
528             for (stack_iterator si(p); si; ++si)
529             {
530                 if (!item_is_jelly_edible(*si) || one_chance_in(4))
531                     continue;
532
533                 if (one_chance_in(4))
534                     break;
535
536                 dprf("Eating %s on %s",
537                      si->name(DESC_PLAIN).c_str(), lid.describe().c_str());
538
539                 // Needs a message now to explain possible hp or mp
540                 // gain from jiyva_slurp_bonus()
541                 mpr("You hear a distant slurping noise.");
542                 jiyva_slurp_item_stack(*si);
543                 item_was_destroyed(*si);
544                 destroy_item(si.index());
545             }
546             return;
547         }
548     }
549 }
550
551 int ash_scry_radius()
552 {
553     if (!have_passive(passive_t::xray_vision))
554         return 0;
555
556     // Radius 2 starting at 4* increasing to 4 at 6*
557     return piety_rank() - 2;
558 }
559
560 static bool _two_handed()
561 {
562     const item_def* wpn = you.slot_item(EQ_WEAPON, true);
563     if (!wpn)
564         return false;
565
566     hands_reqd_type wep_type = you.hands_reqd(*wpn, true);
567     return wep_type == HANDS_TWO;
568 }
569
570 static void _curse_boost_skills(const item_def &item)
571 {
572     if (!item.props.exists(CURSE_KNOWLEDGE_KEY))
573         return;
574
575     for (auto& curse : item.props[CURSE_KNOWLEDGE_KEY].get_vector())
576     {
577         skill_type sk = static_cast<skill_type>(curse.get_int());
578         if (you.skill_boost.count(sk))
579             you.skill_boost[sk]++;
580         else
581             you.skill_boost[sk] = 1;
582     }
583 }
584
585 // Checks bondage and sets ash piety
586 void ash_check_bondage()
587 {
588     if (!will_have_passive(passive_t::bondage_skill_boost))
589         return;
590
591     int num_cursed = 0, num_slots = 0;
592
593     you.skill_boost.clear();
594     for (int j = EQ_FIRST_EQUIP; j < NUM_EQUIP; j++)
595     {
596         const equipment_type i = static_cast<equipment_type>(j);
597         // Missing hands mean fewer rings
598         if (you.species != SP_OCTOPODE && i == EQ_LEFT_RING
599                  && you.get_mutation_level(MUT_MISSING_HAND))
600         {
601             continue;
602         }
603         // Octopodes don't count these slots:
604         else if (you.species == SP_OCTOPODE
605                  && ((i == EQ_LEFT_RING || i == EQ_RIGHT_RING)
606                      || (i == EQ_RING_EIGHT
607                          && you.get_mutation_level(MUT_MISSING_HAND))))
608         {
609             continue;
610         }
611         // *Only* octopodes count these slots:
612         else if (you.species != SP_OCTOPODE
613                  && i >= EQ_RING_ONE && i <= EQ_RING_EIGHT)
614         {
615             continue;
616         }
617         // The macabre finger necklace's extra slot does count if equipped.
618         else if (!player_equip_unrand(UNRAND_FINGER_AMULET)
619                  && i == EQ_RING_AMULET)
620         {
621             continue;
622         }
623
624         // transformed away slots are still considered to be possibly bound
625         if (you_can_wear(i))
626         {
627             num_slots++;
628             if (you.equip[i] != -1)
629             {
630                 const item_def& item = you.inv[you.equip[i]];
631                 if (item.cursed() && (i != EQ_WEAPON || is_weapon(item)))
632                 {
633                     if (i == EQ_WEAPON && _two_handed())
634                         num_cursed += 2;
635                     else
636                     {
637                         num_cursed++;
638                         if (i == EQ_BODY_ARMOUR && is_unrandom_artefact(item, UNRAND_LEAR))
639                             num_cursed += 3;
640                     }
641                     _curse_boost_skills(item);
642                 }
643             }
644         }
645     }
646
647     set_piety(ASHENZARI_BASE_PIETY
648               + (num_cursed * ASHENZARI_PIETY_SCALE) / num_slots);
649 }
650
651 bool god_id_item(item_def& item, bool silent)
652 {
653     iflags_t old_ided = item.flags & ISFLAG_IDENT_MASK;
654
655     if (have_passive(passive_t::identify_items))
656     {
657         // Don't identify runes or the orb, since this has no gameplay purpose
658         // and might mess up other things.
659         if (item.base_type == OBJ_RUNES || item_is_orb(item))
660             return false;
661
662         if ((item.base_type == OBJ_JEWELLERY || item.base_type == OBJ_STAVES)
663             && item_needs_autopickup(item))
664         {
665             item.props["needs_autopickup"] = true;
666         }
667         set_ident_type(item, true);
668         set_ident_flags(item, ISFLAG_IDENT_MASK);
669     }
670
671     iflags_t ided = item.flags & ISFLAG_IDENT_MASK;
672
673     if (ided & ~old_ided)
674     {
675         if (item.props.exists("needs_autopickup") && is_useless_item(item))
676             item.props.erase("needs_autopickup");
677
678         if (&item == you.weapon())
679             you.wield_change = true;
680
681         if (!silent)
682             mprf_nocap("%s", item.name(DESC_INVENTORY_EQUIP).c_str());
683
684         seen_item(item);
685         if (in_inventory(item))
686             auto_assign_item_slot(item);
687         return true;
688     }
689
690     // nothing new
691     return false;
692 }
693
694 static bool is_ash_portal(dungeon_feature_type feat)
695 {
696     if (feat_is_portal_entrance(feat))
697         return true;
698     switch (feat)
699     {
700     case DNGN_ENTER_HELL:
701     case DNGN_ENTER_ABYSS: // for completeness
702     case DNGN_EXIT_THROUGH_ABYSS:
703     case DNGN_EXIT_ABYSS:
704     case DNGN_ENTER_PANDEMONIUM:
705     case DNGN_EXIT_PANDEMONIUM:
706     // DNGN_TRANSIT_PANDEMONIUM is too mundane
707         return true;
708     default:
709         return false;
710     }
711 }
712
713 // Yay for rectangle_iterator and radius_iterator not sharing a base type
714 static bool _check_portal(coord_def where)
715 {
716     const dungeon_feature_type feat = env.grid(where);
717     if (feat != env.map_knowledge(where).feat() && is_ash_portal(feat))
718     {
719         env.map_knowledge(where).set_feature(feat);
720         set_terrain_mapped(where);
721
722         if (!testbits(env.pgrid(where), FPROP_SEEN_OR_NOEXP))
723         {
724             env.pgrid(where) |= FPROP_SEEN_OR_NOEXP;
725             if (!you.see_cell(where))
726                 return true;
727         }
728     }
729     return false;
730 }
731
732 int ash_detect_portals(bool all)
733 {
734     if (!have_passive(passive_t::detect_portals))
735         return 0;
736
737     int portals_found = 0;
738     const int map_radius = LOS_DEFAULT_RANGE + 1;
739
740     if (all)
741     {
742         for (rectangle_iterator ri(0); ri; ++ri)
743         {
744             if (_check_portal(*ri))
745                 portals_found++;
746         }
747     }
748     else
749     {
750         for (radius_iterator ri(you.pos(), map_radius, C_SQUARE); ri; ++ri)
751         {
752             if (_check_portal(*ri))
753                 portals_found++;
754         }
755     }
756
757     you.seen_portals += portals_found;
758     return portals_found;
759 }
760
761 monster_type ash_monster_tier(const monster *mon)
762 {
763     return monster_type(MONS_SENSED_TRIVIAL + monster_info(mon).threat);
764 }
765
766 /**
767  * Does the player have an ash skill boost for a particular skill?
768  */
769 bool ash_has_skill_boost(skill_type sk)
770 {
771     return have_passive(passive_t::bondage_skill_boost)
772            && you.skill_boost.count(sk) && you.skill_boost.find(sk)->second;
773 }
774
775 /**
776  * Calculate the ash skill point boost for skill sk.
777  *
778  * @param scaled_skill the skill level to calculate it for, scaled by 10.
779  *
780  * @return the skill point bonus to use.
781  */
782 unsigned int ash_skill_point_boost(skill_type sk, int scaled_skill)
783 {
784     unsigned int skill_points = 0;
785
786     skill_points += (you.skill_boost[sk] * 3 + 1) * (piety_rank() + 1)
787                     * max(scaled_skill, 1) * species_apt_factor(sk);
788     return skill_points;
789 }
790
791 int ash_skill_boost(skill_type sk, int scale)
792 {
793     // It gives a bonus to skill points. The formula is:
794     // ( curses * 3 + 1 ) * (piety_rank + 1) * skill_level
795
796     unsigned int skill_points = you.skill_points[sk]
797                   + get_crosstrain_points(sk)
798                   + ash_skill_point_boost(sk, you.skill(sk, 10, true));
799
800     int level = you.skills[sk];
801     while (level < MAX_SKILL_LEVEL && skill_points >= skill_exp_needed(level + 1, sk))
802         ++level;
803
804     level = level * scale + get_skill_progress(sk, level, skill_points, scale);
805
806     return min(level, MAX_SKILL_LEVEL * scale);
807 }
808
809 int gozag_gold_in_los(actor *whom)
810 {
811     if (!have_passive(passive_t::gold_aura))
812         return 0;
813
814     int gold_count = 0;
815
816     for (radius_iterator ri(whom->pos(), LOS_RADIUS, C_SQUARE, LOS_DEFAULT);
817          ri; ++ri)
818     {
819         for (stack_iterator j(*ri); j; ++j)
820         {
821             if (j->base_type == OBJ_GOLD)
822                 ++gold_count;
823         }
824     }
825
826     return gold_count;
827 }
828
829 void gozag_detect_level_gold(bool count)
830 {
831     ASSERT(you.on_current_level);
832     vector<item_def *> gold_piles;
833     vector<coord_def> gold_places;
834     int gold = 0;
835     for (rectangle_iterator ri(0); ri; ++ri)
836     {
837         for (stack_iterator j(*ri); j; ++j)
838         {
839             if (j->base_type == OBJ_GOLD && !(j->flags & ISFLAG_UNOBTAINABLE))
840             {
841                 gold += j->quantity;
842                 gold_piles.push_back(&(*j));
843                 gold_places.push_back(*ri);
844             }
845         }
846     }
847
848     if (!player_in_branch(BRANCH_ABYSS) && count)
849         you.attribute[ATTR_GOLD_GENERATED] += gold;
850
851     if (have_passive(passive_t::detect_gold))
852     {
853         for (unsigned int i = 0; i < gold_places.size(); i++)
854         {
855             int dummy = gold_piles[i]->index();
856             coord_def &pos = gold_places[i];
857             unlink_item(dummy);
858             move_item_to_grid(&dummy, pos, true);
859             if ((!env.map_knowledge(pos).item()
860                  || env.map_knowledge(pos).item()->base_type != OBJ_GOLD
861                  && you.visible_igrd(pos) != NON_ITEM))
862             {
863                 env.map_knowledge(pos).set_item(
864                         get_item_known_info(*gold_piles[i]),
865                         !!env.map_knowledge(pos).item());
866                 env.map_knowledge(pos).flags |= MAP_DETECTED_ITEM;
867 #ifdef USE_TILE
868                 // force an update for gold generated during Abyss shifts
869                 tiles.update_minimap(pos);
870 #endif
871             }
872         }
873     }
874 }
875
876 int qazlal_sh_boost(int piety)
877 {
878     if (!have_passive(passive_t::storm_shield))
879         return 0;
880
881     return min(piety, piety_breakpoint(5)) / 10;
882 }
883
884 // Not actually passive, but placing it here so that it can be easily compared
885 // with Qazlal's boost.
886 int tso_sh_boost()
887 {
888     if (!you.duration[DUR_DIVINE_SHIELD])
889         return 0;
890
891     return you.attribute[ATTR_DIVINE_SHIELD];
892 }
893
894 void qazlal_storm_clouds()
895 {
896     if (!have_passive(passive_t::storm_shield))
897         return;
898
899     // You are a *storm*. You are pretty loud!
900     noisy(min((int)you.piety, piety_breakpoint(5)) / 10, you.pos());
901
902     const int radius = you.piety >= piety_breakpoint(3) ? 2 : 1;
903
904     vector<coord_def> candidates;
905     for (radius_iterator ri(you.pos(), radius, C_SQUARE, LOS_SOLID, true);
906          ri; ++ri)
907     {
908         int count = 0;
909         if (cell_is_solid(*ri) || cloud_at(*ri))
910             continue;
911
912         // Don't create clouds over firewood
913         const monster * mon = monster_at(*ri);
914         if (mon != nullptr && mons_is_firewood(*mon))
915             continue;
916
917         // No clouds in corridors.
918         for (adjacent_iterator ai(*ri); ai; ++ai)
919             if (!cell_is_solid(*ai))
920                 count++;
921
922         if (count >= 5)
923             candidates.push_back(*ri);
924     }
925     const int count =
926         div_rand_round(min((int)you.piety, piety_breakpoint(5))
927                        * candidates.size() * you.time_taken,
928                        piety_breakpoint(5) * 7 * BASELINE_DELAY);
929     if (count < 0)
930         return;
931     shuffle_array(candidates);
932     int placed = 0;
933     for (unsigned int i = 0; placed < count && i < candidates.size(); i++)
934     {
935         bool water = false;
936         for (adjacent_iterator ai(candidates[i]); ai; ++ai)
937         {
938             if (feat_is_watery(env.grid(*ai)))
939                 water = true;
940         }
941
942         // No flame clouds over water to avoid steam generation.
943         cloud_type ctype;
944         do
945         {
946             ctype = random_choose(CLOUD_FIRE, CLOUD_COLD, CLOUD_STORM,
947                                   CLOUD_DUST);
948         } while (water && ctype == CLOUD_FIRE);
949
950         place_cloud(ctype, candidates[i], random_range(3, 5), &you);
951         placed++;
952     }
953 }
954
955 /**
956  * Handle Qazlal's elemental adaptation.
957  * This should be called (exactly once) for physical, fire, cold, and electrical damage.
958  * Right now, it is called only from expose_player_to_element. This may merit refactoring.
959  *
960  * @param flavour the beam type.
961  * @param strength The adaptations will trigger strength in (11 - piety_rank()) times. In practice, this is mostly called with a value of 2.
962  */
963 void qazlal_element_adapt(beam_type flavour, int strength)
964 {
965     if (strength <= 0
966         || !have_passive(passive_t::elemental_adaptation)
967         || !x_chance_in_y(strength, 11 - piety_rank()))
968     {
969         return;
970     }
971
972     beam_type what = BEAM_NONE;
973     duration_type dur = NUM_DURATIONS;
974     string descript = "";
975     switch (flavour)
976     {
977         case BEAM_FIRE:
978         case BEAM_LAVA:
979         case BEAM_STICKY_FLAME:
980         case BEAM_STEAM:
981             what = BEAM_FIRE;
982             dur = DUR_QAZLAL_FIRE_RES;
983             descript = "fire";
984             break;
985         case BEAM_COLD:
986         case BEAM_ICE:
987             what = BEAM_COLD;
988             dur = DUR_QAZLAL_COLD_RES;
989             descript = "cold";
990             break;
991         case BEAM_ELECTRICITY:
992             what = BEAM_ELECTRICITY;
993             dur = DUR_QAZLAL_ELEC_RES;
994             descript = "electricity";
995             break;
996         case BEAM_MMISSILE: // for LCS, iron shot
997         case BEAM_MISSILE:
998         case BEAM_FRAG:
999             what = BEAM_MISSILE;
1000             dur = DUR_QAZLAL_AC;
1001             descript = "physical attacks";
1002             break;
1003         default:
1004             return;
1005     }
1006
1007     if (what != BEAM_FIRE && you.duration[DUR_QAZLAL_FIRE_RES])
1008     {
1009         mprf(MSGCH_DURATION, "Your resistance to fire fades away.");
1010         you.duration[DUR_QAZLAL_FIRE_RES] = 0;
1011     }
1012
1013     if (what != BEAM_COLD && you.duration[DUR_QAZLAL_COLD_RES])
1014     {
1015         mprf(MSGCH_DURATION, "Your resistance to cold fades away.");
1016         you.duration[DUR_QAZLAL_COLD_RES] = 0;
1017     }
1018
1019     if (what != BEAM_ELECTRICITY && you.duration[DUR_QAZLAL_ELEC_RES])
1020     {
1021         mprf(MSGCH_DURATION, "Your resistance to electricity fades away.");
1022         you.duration[DUR_QAZLAL_ELEC_RES] = 0;
1023     }
1024
1025     if (what != BEAM_MISSILE && you.duration[DUR_QAZLAL_AC])
1026     {
1027         mprf(MSGCH_DURATION, "Your resistance to physical damage fades away.");
1028         you.duration[DUR_QAZLAL_AC] = 0;
1029         you.redraw_armour_class = true;
1030     }
1031
1032     mprf(MSGCH_GOD, "You feel %sprotected from %s.",
1033          you.duration[dur] > 0 ? "more " : "", descript.c_str());
1034
1035     // was scaled by 10 * strength. But the strength parameter is used so inconsistently that
1036     // it seems like a constant would be better, based on the typical value of 2.
1037     you.increase_duration(dur, 20, 80);
1038
1039     if (what == BEAM_MISSILE)
1040         you.redraw_armour_class = true;
1041 }
1042
1043 /**
1044  * Determine whether a Ru worshipper will attempt to interfere with an attack
1045  * against the player.
1046  *
1047  * @return bool Whether or not whether the worshipper will attempt to interfere.
1048  */
1049 bool does_ru_wanna_redirect(monster* mon)
1050 {
1051     return have_passive(passive_t::aura_of_power)
1052             && !mon->friendly()
1053             && you.see_cell_no_trans(mon->pos())
1054             && !mons_is_firewood(*mon)
1055             && !mon->submerged()
1056             && !mons_is_projectile(mon->type);
1057 }
1058
1059 /**
1060  * Determine which, if any, action Ru takes on a possible attack.
1061  *
1062  * @return ru_interference
1063  */
1064 ru_interference get_ru_attack_interference_level()
1065 {
1066     int r = random2(100);
1067     int chance = div_rand_round(you.piety, 16);
1068
1069     // 10% chance of stopping any attack at max piety
1070     if (r < chance)
1071         return DO_BLOCK_ATTACK;
1072
1073     // 5% chance of redirect at max piety
1074     else if (r < chance + div_rand_round(chance, 2))
1075         return DO_REDIRECT_ATTACK;
1076
1077     else
1078         return DO_NOTHING;
1079 }
1080
1081 static bool _shadow_acts(bool spell)
1082 {
1083     const passive_t pasv = spell ? passive_t::shadow_spells
1084                                  : passive_t::shadow_attacks;
1085     if (!have_passive(pasv))
1086         return false;
1087
1088     const int minpiety = piety_breakpoint(rank_for_passive(pasv) - 1);
1089
1090     // 10% chance at minimum piety; 50% chance at 200 piety.
1091     const int range = MAX_PIETY - minpiety;
1092     const int min   = range / 5;
1093     return x_chance_in_y(min + ((range - min)
1094                                 * (you.piety - minpiety)
1095                                 / (MAX_PIETY - minpiety)),
1096                          2 * range);
1097 }
1098
1099 monster* shadow_monster(bool equip)
1100 {
1101     if (monster_at(you.pos()))
1102         return nullptr;
1103
1104     int wpn_index  = NON_ITEM;
1105
1106     // Do a basic clone of the weapon.
1107     item_def* wpn = you.weapon();
1108     if (equip
1109         && wpn
1110         && is_weapon(*wpn))
1111     {
1112         wpn_index = get_mitm_slot(10);
1113         if (wpn_index == NON_ITEM)
1114             return nullptr;
1115         item_def& new_item = env.item[wpn_index];
1116         if (wpn->base_type == OBJ_STAVES)
1117         {
1118             new_item.base_type = OBJ_WEAPONS;
1119             new_item.sub_type  = WPN_STAFF;
1120         }
1121         else
1122         {
1123             new_item.base_type = wpn->base_type;
1124             new_item.sub_type  = wpn->sub_type;
1125         }
1126         new_item.quantity = 1;
1127         new_item.rnd = 1;
1128         new_item.flags   |= ISFLAG_SUMMONED;
1129     }
1130
1131     monster* mon = get_free_monster();
1132     if (!mon)
1133     {
1134         if (wpn_index)
1135             destroy_item(wpn_index);
1136         return nullptr;
1137     }
1138
1139     mon->type       = MONS_PLAYER_SHADOW;
1140     mon->behaviour  = BEH_SEEK;
1141     mon->attitude   = ATT_FRIENDLY;
1142     mon->flags      = MF_NO_REWARD | MF_JUST_SUMMONED | MF_SEEN
1143                     | MF_WAS_IN_VIEW | MF_HARD_RESET;
1144     mon->hit_points = you.hp;
1145     mon->set_hit_dice(min(27, max(1,
1146                                   you.skill_rdiv(wpn_index != NON_ITEM
1147                                                  ? item_attack_skill(env.item[wpn_index])
1148                                                  : SK_UNARMED_COMBAT, 10, 20)
1149                                   + you.skill_rdiv(SK_FIGHTING, 10, 20))));
1150     mon->set_position(you.pos());
1151     mon->mid        = MID_PLAYER;
1152     mon->inv[MSLOT_WEAPON]  = wpn_index;
1153     mon->inv[MSLOT_MISSILE] = NON_ITEM;
1154
1155     env.mgrid(you.pos()) = mon->mindex();
1156
1157     return mon;
1158 }
1159
1160 void shadow_monster_reset(monster *mon)
1161 {
1162     if (mon->inv[MSLOT_WEAPON] != NON_ITEM)
1163         destroy_item(mon->inv[MSLOT_WEAPON]);
1164     // in case the shadow unwields for some reason, e.g. you clumsily bash with
1165     // a ranged weapon:
1166     if (mon->inv[MSLOT_ALT_WEAPON] != NON_ITEM)
1167         destroy_item(mon->inv[MSLOT_ALT_WEAPON]);
1168     if (mon->inv[MSLOT_MISSILE] != NON_ITEM)
1169         destroy_item(mon->inv[MSLOT_MISSILE]);
1170
1171     mon->reset();
1172 }
1173
1174 /**
1175  * Check if the player is in melee range of the target.
1176  *
1177  * Certain effects, e.g. distortion blink, can cause monsters to leave melee
1178  * range between the initial hit & the shadow mimic.
1179  *
1180  * XXX: refactor this with attack/fight code!
1181  *
1182  * @param target    The creature to be struck.
1183  * @return          Whether the player is melee range of the target, using
1184  *                  their current weapon.
1185  */
1186 static bool _in_melee_range(actor* target)
1187 {
1188     const int dist = (you.pos() - target->pos()).rdist();
1189     return dist <= you.reach_range();
1190 }
1191
1192 void dithmenos_shadow_melee(actor* target)
1193 {
1194     if (!target
1195         || !target->alive()
1196         || !_in_melee_range(target)
1197         || !_shadow_acts(false))
1198     {
1199         return;
1200     }
1201
1202     monster* mon = shadow_monster();
1203     if (!mon)
1204         return;
1205
1206     mon->target     = target->pos();
1207     mon->foe        = target->mindex();
1208
1209     fight_melee(mon, target);
1210
1211     shadow_monster_reset(mon);
1212 }
1213
1214 void dithmenos_shadow_throw(const dist &d, const item_def &item)
1215 {
1216     ASSERT(d.isValid);
1217     if (!_shadow_acts(false))
1218         return;
1219
1220     monster* mon = shadow_monster();
1221     if (!mon)
1222         return;
1223
1224     int ammo_index = get_mitm_slot(10);
1225     if (ammo_index != NON_ITEM)
1226     {
1227         item_def& new_item = env.item[ammo_index];
1228         new_item.base_type = item.base_type;
1229         new_item.sub_type  = item.sub_type;
1230         new_item.quantity  = 1;
1231         new_item.rnd = 1;
1232         new_item.flags    |= ISFLAG_SUMMONED;
1233         mon->inv[MSLOT_MISSILE] = ammo_index;
1234
1235         mon->target = clamp_in_bounds(d.target);
1236
1237         bolt beem;
1238         beem.set_target(d);
1239         setup_monster_throw_beam(mon, beem);
1240         beem.item = &env.item[mon->inv[MSLOT_MISSILE]];
1241         mons_throw(mon, beem, mon->inv[MSLOT_MISSILE]);
1242     }
1243
1244     shadow_monster_reset(mon);
1245 }
1246
1247 void dithmenos_shadow_spell(bolt* orig_beam, spell_type spell)
1248 {
1249     if (!orig_beam)
1250         return;
1251
1252     const coord_def target = orig_beam->target;
1253
1254     if (orig_beam->target.origin()
1255         || (orig_beam->is_enchantment() && !is_valid_mon_spell(spell))
1256         || orig_beam->flavour == BEAM_CHARM
1257            && monster_at(target) && monster_at(target)->friendly()
1258         || !_shadow_acts(true))
1259     {
1260         return;
1261     }
1262
1263     monster* mon = shadow_monster();
1264     if (!mon)
1265         return;
1266
1267     // Don't let shadow spells get too powerful.
1268     mon->set_hit_dice(max(1,
1269                           min(3 * spell_difficulty(spell),
1270                               you.experience_level) / 2));
1271
1272     mon->target = clamp_in_bounds(target);
1273     if (actor_at(target))
1274         mon->foe = actor_at(target)->mindex();
1275
1276     spell_type shadow_spell = spell;
1277     if (!orig_beam->is_enchantment())
1278     {
1279         shadow_spell = (orig_beam->pierce) ? SPELL_SHADOW_BOLT
1280                                            : SPELL_SHADOW_SHARD;
1281     }
1282
1283     bolt beem;
1284     beem.target = target;
1285     beem.aimed_at_spot = orig_beam->aimed_at_spot;
1286
1287     mprf(MSGCH_FRIEND_SPELL, "%s mimicks your spell!",
1288          mon->name(DESC_THE).c_str());
1289     mons_cast(mon, beem, shadow_spell, MON_SPELL_WIZARD, false);
1290
1291     shadow_monster_reset(mon);
1292 }
1293
1294 static void _wu_jian_trigger_serpents_lash(const coord_def& old_pos,
1295                                            bool wall_jump)
1296 {
1297     if (you.attribute[ATTR_SERPENTS_LASH] == 0)
1298        return;
1299
1300     if (wall_jump && you.attribute[ATTR_SERPENTS_LASH] == 1)
1301     {
1302         // No turn manipulation, since we are only refunding half a wall jump's
1303         // time (the walk speed modifier for this special case is already
1304         // factored in main.cc)
1305         you.attribute[ATTR_SERPENTS_LASH] = 0;
1306     }
1307     else
1308     {
1309         you.turn_is_over = false;
1310         you.elapsed_time_at_last_input = you.elapsed_time;
1311         you.attribute[ATTR_SERPENTS_LASH] -= wall_jump ? 2 : 1;
1312         you.redraw_status_lights = true;
1313         update_turn_count();
1314     }
1315
1316     if (you.attribute[ATTR_SERPENTS_LASH] == 0)
1317     {
1318         you.increase_duration(DUR_EXHAUSTED, 12 + random2(5));
1319         mpr("Your supernatural speed expires.");
1320     }
1321
1322     if (!cell_is_solid(old_pos))
1323         check_place_cloud(CLOUD_DUST, old_pos, 2 + random2(3) , &you, 1, -1);
1324 }
1325
1326 static void _wu_jian_increment_heavenly_storm()
1327 {
1328     int storm = you.props[WU_JIAN_HEAVENLY_STORM_KEY].get_int();
1329     if (storm < WU_JIAN_HEAVENLY_STORM_MAX)
1330         you.props[WU_JIAN_HEAVENLY_STORM_KEY].get_int()++;
1331 }
1332
1333 void wu_jian_heaven_tick()
1334 {
1335     for (radius_iterator ai(you.pos(), 2, C_SQUARE, LOS_SOLID); ai; ++ai)
1336         if (!cell_is_solid(*ai))
1337             place_cloud(CLOUD_GOLD_DUST, *ai, 5 + random2(5), &you);
1338
1339     noisy(15, you.pos());
1340 }
1341
1342 void wu_jian_decrement_heavenly_storm()
1343 {
1344     int storm = you.props[WU_JIAN_HEAVENLY_STORM_KEY].get_int();
1345
1346     if (storm > 1)
1347     {
1348         you.props[WU_JIAN_HEAVENLY_STORM_KEY].get_int()--;
1349         you.set_duration(DUR_HEAVENLY_STORM, random_range(2, 3));
1350     }
1351     else
1352         wu_jian_end_heavenly_storm();
1353 }
1354
1355 // TODO: why isn't this implemented as a duration end effect?
1356 void wu_jian_end_heavenly_storm()
1357 {
1358     you.props.erase(WU_JIAN_HEAVENLY_STORM_KEY);
1359     you.duration[DUR_HEAVENLY_STORM] = 0;
1360     invalidate_agrid(true);
1361     mprf(MSGCH_GOD, "The heavenly storm settles.");
1362 }
1363
1364 bool wu_jian_has_momentum(wu_jian_attack_type attack_type)
1365 {
1366     return you.attribute[ATTR_SERPENTS_LASH]
1367            && attack_type != WU_JIAN_ATTACK_NONE
1368            && attack_type != WU_JIAN_ATTACK_TRIGGERED_AUX;
1369 }
1370
1371 static bool _can_attack_martial(const monster* mons)
1372 {
1373     return !(mons->wont_attack()
1374              || mons_is_firewood(*mons)
1375              || mons_is_projectile(mons->type)
1376              || !you.can_see(*mons));
1377 }
1378
1379 // A mismatch between attack speed and move speed may cause any particular
1380 // martial attack to be doubled, tripled, or not happen at all. Given enough
1381 // time moving, you would have made the same amount of attacks as tabbing.
1382 static int _wu_jian_number_of_attacks(bool wall_jump)
1383 {
1384     // Under the effect of serpent's lash, move delay is normalized to
1385     // 10 aut for every character, to avoid punishing fast races.
1386     const int move_delay = you.attribute[ATTR_SERPENTS_LASH]
1387                            ? 100
1388                            : player_movement_speed() * player_speed();
1389
1390     int attack_delay;
1391
1392     {
1393         // attack_delay() is dependent on you.time_taken, which won't be set
1394         // appropriately during a movement turn. This temporarily resets
1395         // you.time_taken to the initial value (see `_prep_input`) used for
1396         // basic, simple, melee attacks.
1397         // TODO: can `attack_delay` be changed to not depend on you.time_taken?
1398         unwind_var<int> reset_speed(you.time_taken, player_speed());
1399         attack_delay = you.attack_delay().roll();
1400     }
1401
1402     return div_rand_round(wall_jump ? 2 * move_delay : move_delay,
1403                           attack_delay * BASELINE_DELAY);
1404 }
1405
1406 static bool _wu_jian_lunge(const coord_def& old_pos)
1407 {
1408     coord_def lunge_direction = (you.pos() - old_pos).sgn();
1409     coord_def potential_target = you.pos() + lunge_direction;
1410     monster* mons = monster_at(potential_target);
1411
1412     if (!mons || !_can_attack_martial(mons) || !mons->alive())
1413         return false;
1414
1415     if (you.props.exists(WU_JIAN_HEAVENLY_STORM_KEY))
1416         _wu_jian_increment_heavenly_storm();
1417
1418     you.apply_berserk_penalty = false;
1419
1420     const int number_of_attacks = _wu_jian_number_of_attacks(false);
1421
1422     if (number_of_attacks == 0)
1423     {
1424         mprf("You lunge at %s, but your attack speed is too slow for a blow "
1425              "to land.", mons->name(DESC_THE).c_str());
1426         return false;
1427     }
1428     else
1429     {
1430         mprf("You lunge%s at %s%s.",
1431              wu_jian_has_momentum(WU_JIAN_ATTACK_LUNGE) ?
1432                  " with incredible momentum" : "",
1433              mons->name(DESC_THE).c_str(),
1434              number_of_attacks > 1 ? ", in a flurry of attacks" : "");
1435     }
1436
1437     count_action(CACT_INVOKE, ABIL_WU_JIAN_LUNGE);
1438
1439     for (int i = 0; i < number_of_attacks; i++)
1440     {
1441         if (!mons->alive())
1442             break;
1443         melee_attack lunge(&you, mons);
1444         lunge.wu_jian_attack = WU_JIAN_ATTACK_LUNGE;
1445         lunge.attack();
1446     }
1447
1448     return true;
1449 }
1450
1451 // Monsters adjacent to the given pos that are valid targets for whirlwind.
1452 static vector<monster*> _get_whirlwind_targets(coord_def pos)
1453 {
1454     vector<monster*> targets;
1455     for (adjacent_iterator ai(pos, true); ai; ++ai)
1456         if (monster_at(*ai) && _can_attack_martial(monster_at(*ai)))
1457             targets.push_back(monster_at(*ai));
1458     sort(targets.begin(), targets.end());
1459     return targets;
1460 }
1461
1462 static bool _wu_jian_whirlwind(const coord_def& old_pos)
1463 {
1464     bool did_at_least_one_attack = false;
1465
1466     const vector<monster*> targets = _get_whirlwind_targets(you.pos());
1467     if (targets.empty())
1468         return did_at_least_one_attack;
1469
1470     const vector<monster*> old_targets = _get_whirlwind_targets(old_pos);
1471     vector<monster*> common_targets;
1472     set_intersection(targets.begin(), targets.end(),
1473                      old_targets.begin(), old_targets.end(),
1474                      back_inserter(common_targets));
1475
1476     for (auto mons : common_targets)
1477     {
1478         if (!mons->alive())
1479             continue;
1480
1481         if (you.props.exists(WU_JIAN_HEAVENLY_STORM_KEY))
1482             _wu_jian_increment_heavenly_storm();
1483
1484         you.apply_berserk_penalty = false;
1485
1486         const int number_of_attacks = _wu_jian_number_of_attacks(false);
1487         if (number_of_attacks == 0)
1488         {
1489             mprf("You spin to attack %s, but your attack speed is too slow for "
1490                  "a blow to land.", mons->name(DESC_THE).c_str());
1491             continue;
1492         }
1493         else
1494         {
1495             mprf("You spin and attack %s%s%s.",
1496                  mons->name(DESC_THE).c_str(),
1497                  number_of_attacks > 1 ? " repeatedly" : "",
1498                  wu_jian_has_momentum(WU_JIAN_ATTACK_WHIRLWIND) ?
1499                      ", with incredible momentum" : "");
1500         }
1501
1502         count_action(CACT_INVOKE, ABIL_WU_JIAN_WHIRLWIND);
1503
1504         for (int i = 0; i < number_of_attacks; i++)
1505         {
1506             if (!mons->alive())
1507                 break;
1508             melee_attack whirlwind(&you, mons);
1509             whirlwind.wu_jian_attack = WU_JIAN_ATTACK_WHIRLWIND;
1510             whirlwind.wu_jian_number_of_targets = common_targets.size();
1511             whirlwind.attack();
1512             if (!did_at_least_one_attack)
1513               did_at_least_one_attack = true;
1514         }
1515     }
1516
1517     return did_at_least_one_attack;
1518 }
1519
1520 static bool _wu_jian_trigger_martial_arts(const coord_def& old_pos)
1521 {
1522     bool did_wu_jian_attacks = false;
1523
1524     if (you.pos() == old_pos || you.duration[DUR_CONF])
1525         return did_wu_jian_attacks;
1526
1527     if (have_passive(passive_t::wu_jian_lunge))
1528         did_wu_jian_attacks = _wu_jian_lunge(old_pos);
1529
1530     if (have_passive(passive_t::wu_jian_whirlwind))
1531         did_wu_jian_attacks |= _wu_jian_whirlwind(old_pos);
1532
1533     return did_wu_jian_attacks;
1534 }
1535
1536 void wu_jian_wall_jump_effects()
1537 {
1538     vector<monster*> targets;
1539     for (adjacent_iterator ai(you.pos(), true); ai; ++ai)
1540     {
1541         monster* target = monster_at(*ai);
1542         if (target && _can_attack_martial(target) && target->alive())
1543             targets.push_back(target);
1544
1545         if (!cell_is_solid(*ai))
1546             check_place_cloud(CLOUD_DUST, *ai, 1 + random2(3) , &you, 0, -1);
1547     }
1548
1549     for (auto target : targets)
1550     {
1551         if (!target->alive())
1552             continue;
1553
1554         if (you.props.exists(WU_JIAN_HEAVENLY_STORM_KEY))
1555             _wu_jian_increment_heavenly_storm();
1556
1557         you.apply_berserk_penalty = false;
1558
1559         // Twice the attacks as Wall Jump spends twice the time
1560         const int number_of_attacks = _wu_jian_number_of_attacks(true);
1561         if (number_of_attacks == 0)
1562         {
1563             mprf("You attack %s from above, but your attack speed is too slow"
1564                  " for a blow to land.", target->name(DESC_THE).c_str());
1565             continue;
1566         }
1567         else
1568         {
1569             mprf("You %sattack %s from above%s.",
1570                  number_of_attacks > 1 ? "repeatedly " : "",
1571                  target->name(DESC_THE).c_str(),
1572                  wu_jian_has_momentum(WU_JIAN_ATTACK_WALL_JUMP) ?
1573                      ", with incredible momentum" : "");
1574         }
1575
1576         for (int i = 0; i < number_of_attacks; i++)
1577         {
1578             if (!target->alive())
1579                 break;
1580
1581             melee_attack aerial(&you, target);
1582             aerial.wu_jian_attack = WU_JIAN_ATTACK_WALL_JUMP;
1583             aerial.wu_jian_number_of_targets = targets.size();
1584             aerial.attack();
1585         }
1586     }
1587 }
1588
1589 bool wu_jian_post_move_effects(bool did_wall_jump,
1590                                const coord_def& initial_position)
1591 {
1592     bool did_wu_jian_attacks = false;
1593
1594     if (!did_wall_jump)
1595         did_wu_jian_attacks = _wu_jian_trigger_martial_arts(initial_position);
1596
1597     if (you.turn_is_over)
1598         _wu_jian_trigger_serpents_lash(initial_position, did_wall_jump);
1599
1600     return did_wu_jian_attacks;
1601 }
1602
1603 /**
1604  * check if the monster in this cell exists and is a valid target for Uskayaw
1605  */
1606 static int _check_for_uskayaw_targets(coord_def where)
1607 {
1608     if (!cell_has_valid_target(where))
1609         return 0;
1610     monster* mons = monster_at(where);
1611     ASSERT(mons);
1612
1613     if (mons_is_firewood(*mons))
1614         return 0;
1615
1616     return 1;
1617 }
1618
1619 /**
1620  * Paralyse the monster in this cell, assuming one exists.
1621  *
1622  * Duration increases with invocations and experience level, and decreases
1623  * with target HD. The duration is pretty low, maxing out at 40 AUT.
1624  */
1625 static int _prepare_audience(coord_def where)
1626 {
1627     if (!_check_for_uskayaw_targets(where))
1628         return 0;
1629
1630     monster* mons = monster_at(where);
1631
1632     int power =  max(1, random2(1 + you.skill(SK_INVOCATIONS, 2))
1633                  + you.experience_level - mons->get_hit_dice());
1634     int duration = min(max(10, 5 + power), 40);
1635     mons->add_ench(mon_enchant(ENCH_PARALYSIS, 1, &you, duration));
1636
1637     return 1;
1638 }
1639
1640 /**
1641  * On hitting *** piety, all the monsters are paralysed by their appreciation
1642  * for your dance.
1643  */
1644 void uskayaw_prepares_audience()
1645 {
1646     int count = apply_area_visible(_check_for_uskayaw_targets, you.pos());
1647     if (count > 0)
1648     {
1649         simple_god_message(" prepares the audience for your solo!");
1650         apply_area_visible(_prepare_audience, you.pos());
1651
1652         // Increment a delay timer to prevent players from spamming this ability
1653         // via piety loss and gain. Timer is in AUT.
1654         you.props[USKAYAW_AUDIENCE_TIMER] = 300 + random2(201);
1655     }
1656     else // Reset the timer because we didn't actually execute.
1657         you.props[USKAYAW_AUDIENCE_TIMER] = 0;
1658 }
1659
1660 /**
1661  * Apply pain bond to the monster in this cell.
1662  */
1663 static int _bond_audience(coord_def where)
1664 {
1665     if (!_check_for_uskayaw_targets(where))
1666         return 0;
1667
1668     monster* mons = monster_at(where);
1669
1670     // Don't pain bond monsters that aren't invested in fighting the player
1671     if (mons->wont_attack())
1672         return 0;
1673
1674     int power = you.skill(SK_INVOCATIONS, 7) + you.experience_level
1675                  - mons->get_hit_dice();
1676     int duration = 20 + random2avg(power, 2);
1677     mons->add_ench(mon_enchant(ENCH_PAIN_BOND, 1, &you, duration));
1678
1679     return 1;
1680 }
1681
1682 /**
1683  * On hitting **** piety, all the monsters are pain bonded.
1684  */
1685 void uskayaw_bonds_audience()
1686 {
1687     int count = apply_area_visible(_check_for_uskayaw_targets, you.pos());
1688     if (count > 1)
1689     {
1690         simple_god_message(" links your audience in an emotional bond!");
1691         apply_area_visible(_bond_audience, you.pos());
1692
1693         // Increment a delay timer to prevent players from spamming this ability
1694         // via piety loss and gain. Timer is in AUT.
1695         you.props[USKAYAW_BOND_TIMER] = 300 + random2(201);
1696     }
1697     else // Reset the timer because we didn't actually execute.
1698         you.props[USKAYAW_BOND_TIMER] = 0;
1699 }