1 /**
2  * @file
3  * @brief God-granted abilities.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "god-abil.h"
9 
10 #include <cmath>
11 #include <numeric>
12 #include <sstream>
13 
14 #include "act-iter.h"
15 #include "areas.h"
16 #include "artefact.h"
17 #include "art-enum.h"
18 #include "attitude-change.h"
19 #include "bloodspatter.h"
20 #include "branch.h"
21 #include "chardump.h"
22 #include "cloud.h"
23 #include "colour.h"
24 #include "coordit.h"
25 #include "curse-type.h"
26 #include "dactions.h"
27 #include "database.h"
28 #include "describe.h"
29 #include "dgn-overview.h"
30 #include "directn.h"
31 #include "dungeon.h"
32 #include "english.h"
33 #include "fight.h"
34 #include "files.h"
35 #include "fineff.h"
36 #include "format.h" // formatted_string
37 #include "god-blessing.h"
38 #include "god-companions.h"
39 #include "god-item.h"
40 #include "god-passive.h"
41 #include "hints.h"
42 #include "hiscores.h"
43 #include "invent.h"
44 #include "item-prop.h"
45 #include "item-status-flag-type.h"
46 #include "items.h"
47 #include "item-use.h"
48 #include "libutil.h"
49 #include "losglobal.h"
50 #include "macro.h"
51 #include "mapmark.h"
52 #include "maps.h"
53 #include "message.h"
54 #include "mon-act.h"
55 #include "mon-behv.h"
56 #include "mon-death.h"
57 #include "mon-gear.h" // H: give_weapon()/give_armour()
58 #include "mon-place.h"
59 #include "mon-poly.h"
60 #include "mon-tentacle.h"
61 #include "mon-util.h"
62 #include "movement.h"
63 #include "mutation.h"
64 #include "notes.h"
65 #include "ouch.h"
66 #include "output.h"
67 #include "place.h"
68 #include "player-equip.h"
69 #include "player-stats.h"
70 #include "potion.h"
71 #include "prompt.h"
72 #include "religion.h"
73 #include "shout.h"
74 #include "skill-menu.h"
75 #include "spl-book.h"
76 #include "spl-goditem.h"
77 #include "spl-monench.h"
78 #include "spl-transloc.h"
79 #include "spl-util.h"
80 #include "spl-wpnench.h"
81 #include "sprint.h"
82 #include "state.h"
83 #include "stringutil.h"
84 #include "tag-version.h"
85 #include "target.h"
86 #include "teleport.h" // monster_teleport
87 #include "terrain.h"
88 #ifdef USE_TILE
89  #include "rltiles/tiledef-main.h"
90 #endif
91 #include "timed-effects.h"
92 #include "traps.h"
93 #include "viewchar.h"
94 #include "view.h"
95 
96 static bool _player_sacrificed_arcana();
97 
98 // Load the sacrifice_def definition and the sac_data array.
99 #include "sacrifice-data.h"
100 
101 /** Would a god currently allow using a one-time six-star ability?
102  * Does not check whether the god actually grants such an ability.
103  */
can_do_capstone_ability(god_type god)104 bool can_do_capstone_ability(god_type god)
105 {
106    return in_good_standing(god, 5) && !you.one_time_ability_used[god];
107 }
108 
_god_blessing_description(god_type god)109 static const char *_god_blessing_description(god_type god)
110 {
111     switch (god)
112     {
113     case GOD_SHINING_ONE:
114         return "blessed by the Shining One";
115     case GOD_LUGONU:
116         return "corrupted by Lugonu";
117     case GOD_KIKUBAAQUDGHA:
118         return "bloodied by Kikubaaqudgha";
119     default:
120         return "touched by the gods";
121     }
122 }
123 
124 /**
125  * Perform a capstone god ability that blesses a weapon with the god's
126  * brand.
127 
128  * @param god    The god performing the blessing.
129  * @param brand  The brand being granted.
130  * @param colour The colour to flash when the weapon is branded.
131  * @returns True if the weapon was successfully branded, false otherwise.
132 */
bless_weapon(god_type god,brand_type brand,colour_t colour)133 bool bless_weapon(god_type god, brand_type brand, colour_t colour)
134 {
135     ASSERT(can_do_capstone_ability(god));
136 
137     int item_slot = prompt_invent_item("Brand which weapon?",
138                                        menu_type::invlist,
139                                        OSEL_BLESSABLE_WEAPON, OPER_ANY,
140                                        invprompt_flag::escape_only);
141 
142     if (item_slot == PROMPT_NOTHING || item_slot == PROMPT_ABORT)
143     {
144         canned_msg(MSG_OK);
145         return false;
146     }
147 
148     item_def& wpn(you.inv[item_slot]);
149     // Only TSO allows blessing ranged weapons.
150     if (!is_brandable_weapon(wpn, brand == SPWPN_HOLY_WRATH, true))
151         return false;
152 
153     string prompt = "Do you wish to have " + wpn.name(DESC_YOUR)
154                        + " ";
155     if (brand == SPWPN_PAIN)
156         prompt += "bloodied with pain";
157     else if (brand == SPWPN_DISTORTION)
158         prompt += "corrupted with distortion";
159     else
160         prompt += "blessed with holy wrath";
161     prompt += "?";
162     if (!yesno(prompt.c_str(), true, 'n'))
163     {
164         canned_msg(MSG_OK);
165         return false;
166     }
167 
168     if (you.duration[DUR_EXCRUCIATING_WOUNDS]) // just in case
169     {
170         ASSERT(you.weapon());
171         end_weapon_brand(*you.weapon());
172     }
173 
174     string old_name = wpn.name(DESC_A);
175     set_equip_desc(wpn, ISFLAG_GLOWING);
176     set_item_ego_type(wpn, OBJ_WEAPONS, brand);
177     enchant_weapon(wpn, true);
178     enchant_weapon(wpn, true);
179 
180     if (god == GOD_SHINING_ONE)
181     {
182         convert2good(wpn);
183 
184         if (is_blessed_convertible(wpn))
185             origin_acquired(wpn, GOD_SHINING_ONE);
186     }
187     else if (is_evil_god(god))
188         convert2bad(wpn);
189 
190     you.wield_change = true;
191     you.one_time_ability_used.set(god);
192     calc_mp(); // in case the old brand was antimagic,
193     you.redraw_armour_class = true; // protection,
194     you.redraw_evasion = true;      // or evasion
195     const string desc = old_name + " " + _god_blessing_description(god);
196     take_note(Note(NOTE_ID_ITEM, 0, 0, wpn.name(DESC_A), desc));
197     wpn.flags |= ISFLAG_NOTED_ID;
198     wpn.props[FORCED_ITEM_COLOUR_KEY] = colour;
199 
200     mprf(MSGCH_GOD, "Your %s shines brightly!", wpn.name(DESC_QUALNAME).c_str());
201     flash_view(UA_PLAYER, colour);
202     simple_god_message(" booms: Use this gift wisely!");
203     you.one_time_ability_used.set(you.religion);
204     take_note(Note(NOTE_GOD_GIFT, you.religion));
205 
206     if (god == GOD_SHINING_ONE)
207     {
208         holy_word(100, HOLY_WORD_TSO, you.pos(), true);
209         // Un-bloodify surrounding squares.
210         for (radius_iterator ri(you.pos(), 3, C_SQUARE, LOS_SOLID); ri; ++ri)
211             if (is_bloodcovered(*ri))
212                 env.pgrid(*ri) &= ~FPROP_BLOODY;
213     }
214     else if (god == GOD_KIKUBAAQUDGHA)
215     {
216         you.gift_timeout = 1; // no protection during pain branding weapon
217         torment(&you, TORMENT_KIKUBAAQUDGHA, you.pos());
218         you.gift_timeout = 0; // protection after pain branding weapon
219         // Bloodify surrounding squares (75% chance).
220         for (radius_iterator ri(you.pos(), 2, C_SQUARE, LOS_SOLID); ri; ++ri)
221             if (!one_chance_in(4))
222                 maybe_bloodify_square(*ri);
223     }
224 
225 #ifndef USE_TILE_LOCAL
226     // Allow extra time for the flash to linger.
227     scaled_delay(1000);
228 #endif
229     return true;
230 }
231 
_gold_to_donation(int gold)232 static int _gold_to_donation(int gold)
233 {
234     return static_cast<int>((gold * log((float)gold)) / MAX_PIETY);
235 }
236 
237 // donate gold to gain piety distributed over time
zin_donate_gold()238 bool zin_donate_gold()
239 {
240     if (you.gold == 0)
241     {
242         mpr("You don't have anything to sacrifice.");
243         return false;
244     }
245 
246     if (!yesno("Do you wish to donate half of your money?", true, 'n'))
247     {
248         canned_msg(MSG_OK);
249         return false;
250     }
251 
252     const int donation_cost = (you.gold / 2) + 1;
253     const int donation = _gold_to_donation(donation_cost);
254 
255 #if defined(DEBUG_DIAGNOSTICS) || defined(DEBUG_SACRIFICE) || defined(DEBUG_PIETY)
256     mprf(MSGCH_DIAGNOSTICS, "A donation of $%d amounts to an "
257          "increase of piety by %d.", donation_cost, donation);
258 #endif
259     // Take a note of the donation.
260     take_note(Note(NOTE_DONATE_MONEY, donation_cost));
261 
262     you.attribute[ATTR_DONATIONS] += donation_cost;
263 
264     you.del_gold(donation_cost);
265 
266     if (donation < 1)
267     {
268         simple_god_message(" finds your generosity lacking.");
269         return false;
270     }
271 
272     you.duration[DUR_PIETY_POOL] += donation;
273     if (you.duration[DUR_PIETY_POOL] > 30000)
274         you.duration[DUR_PIETY_POOL] = 30000;
275 
276     const int estimated_piety =
277         min(MAX_PENANCE + MAX_PIETY, you.piety + you.duration[DUR_PIETY_POOL]);
278 
279     if (player_under_penance())
280     {
281         if (estimated_piety >= you.penance[GOD_ZIN])
282             mpr("You feel that you will soon be absolved of all your sins.");
283         else
284             mpr("You feel that your burden of sins will soon be lighter.");
285     }
286     else
287     {
288         string result = "You feel that " + god_name(GOD_ZIN) + " will soon be ";
289         result +=
290             (estimated_piety >= piety_breakpoint(5)) ? "exalted by your worship" :
291             (estimated_piety >= piety_breakpoint(4)) ? "extremely pleased with you" :
292             (estimated_piety >= piety_breakpoint(3)) ? "greatly pleased with you" :
293             (estimated_piety >= piety_breakpoint(2)) ? "most pleased with you" :
294             (estimated_piety >= piety_breakpoint(1)) ? "pleased with you" :
295             (estimated_piety >= piety_breakpoint(0)) ? "aware of your devotion"
296                                                      : "noncommittal";
297         result += (donation >= 30 && you.piety < piety_breakpoint(5)) ? "!" : ".";
298 
299         mpr(result);
300     }
301 
302     return true;
303 }
304 
305 static void _zin_saltify(monster* mon);
306 
307 static const char * const book_of_zin[][3] =
308 {
309     {
310         "It was the word of Zin that there would not be @sin_noun@...",
311         "...and did the people not suffer until they had @smitten@...",
312         "...the @sinners@, after which all was well?",
313     },
314 
315     {
316         "The voice of Zin, pure and clear, did say that the @sinners@...",
317         "...were not @virtuous@! And hearing this, the people rose up...",
318         "...and embraced @virtue@, for they feared Zin's wrath.",
319     },
320 
321     {
322         "Zin spoke of the doctrine of @virtue@, and...",
323         "...saw the @sinners@ filled with fear, for they were...",
324         "...@sin_adj@ and knew Zin's wrath would come for them.",
325     },
326 
327     {
328         "And so Zin bade the @sinners@ to come before...",
329         "...the altar, that judgement might be passed...",
330         "...upon those who were not @virtuous@.",
331     },
332 
333     {
334         "To the devout, Zin provideth. From the rest...",
335         "...ye @sinners@, ye guilty...",
336         "...of @sin_noun@, Zin taketh.",
337     },
338 
339     {
340         "Zin saw the @sin_noun@ of the @sinners@, and...",
341         "...was displeased, for did the law not say that...",
342         "...those who did not become @virtuous@ would be @smitten@?",
343     },
344 
345     {
346         "Zin said that @virtue@ shall be the law of the land, and...",
347         "...those who turn to @sin_noun@ will be @smitten@. This was fair...",
348         "...and just, and not a voice dissented.",
349     },
350 
351     {
352         "Damned, damned be the @sinners@ and...",
353         "...all else who abandon @virtue@! Let them...",
354         "...be @smitten@ by the jurisprudence of Zin!",
355     },
356 
357     {
358 
359         "And Zin said to all in attendance, 'Which of ye...",
360         "...number among the @sinners@? Come before me, that...",
361         "...I may @smite@ you now for your @sin_noun@!'",
362     },
363 
364     {
365         "Yea, I say unto thee, bring forth...",
366         "...the @sinners@ that they may know...",
367         "...the wrath of Zin, and thus be @smitten@!",
368     },
369 
370     {
371         "In a great set of silver scales are weighed the...",
372         "...souls of the @sinners@, and with their @sin_adj@...",
373         "...ways, the balance hath tipped against them!",
374     },
375 
376     {
377         "It is just that the @sinners@ shall be @smitten@...",
378         "...in due time, for @virtue@ is what Zin has declared...",
379         "...the law of the land, and Zin's word is law!",
380     },
381 
382     {
383         "Thus the people made the covenant of @virtue@ with...",
384         "...Zin, and all was good, for they knew that the...",
385         "...@sinners@ would trouble them no longer.",
386     },
387 
388     {
389         "What of the @sinners@? @Smitten@ for their...",
390         "...@sin_noun@ they shall be! Zin will @smite@ them again...",
391         "...and again, and again!",
392     },
393 
394     {
395         "And lo, the wrath of Zin did find...",
396         "...them wherever they hid, and the @sinners@...",
397         "...were @smitten@ for their @sin_noun@!",
398     },
399 
400     {
401         "Zin looked out upon the remains of the @sinners@...",
402         "...and declared it good that they had been...",
403         "...@smitten@. And thus justice was done.",
404     },
405 
406     {
407         "The law of Zin demands thee...",
408         "...be @virtuous@, and that the punishment for @sin_noun@...",
409         "...shall be swift and harsh!",
410     },
411 
412     {
413         "It was then that Zin bade them...",
414         "...not to stray from @virtue@, lest...",
415         "...they become as damned as the @sinners@.",
416     },
417 
418     {
419         "Only the @virtuous@ shall be judged worthy, and...",
420         "...all the @sinners@ will be found wanting. Such is...",
421         "...the word of Zin, and such is the law!",
422     },
423 
424     {
425         "To those who would swear an oath of @virtue@ on my altar...",
426         "...I bring ye salvation. To the rest, ye @sinners@...",
427         "...and the @sin_adj@, the name of Zin shall be thy damnation.",
428     },
429 
430     {
431         "And Zin decreed that the people would be...",
432         "...protected from @sin_noun@ in all its forms, and...",
433         "...preserved in their @virtue@ for all the days to come.",
434     },
435 
436     {
437         "For those who would enter Zin's holy bosom...",
438         "...and live in @virtue@, Zin provideth. Such is...",
439         "...the covenant, and such is the way of things.",
440     },
441 
442     {
443         "Zin hath not damned the @sinners@, but it is they...",
444         "...that have damned themselves for their @sin_noun@, for...",
445         "...did Zin not decree that to be @sin_adj@ was wrong?",
446     },
447 
448     {
449         "And Zin, furious at their @sin_noun@, held...",
450         "...aloft a silver sceptre! The @sinners@...",
451         "...were @smitten@, and thus the way of things was maintained.",
452     },
453 
454     {
455         "When the law of the land faltered, Zin rose...",
456         "...from the silver throne, and the @sinners@ were...",
457         "...@smitten@. And it was thus that the law was made good.",
458     },
459 
460     {
461         "Zin descended from on high in a silver chariot...",
462         "...to @smite@ the @sinners@ for their...",
463         "...@sin_noun@, and thus judgement was rendered.",
464     },
465 
466     {
467         "The @sinners@ stood before Zin, and in that instant...",
468         "...they knew they would be found guilty of @sin_noun@...",
469         "...for that is the word of Zin, and Zin's word is law.",
470     },
471 };
472 
473 static const char * const sinner_text[] =
474 {
475     "hordes of the Abyss",
476     "bastard children of Xom",
477     "amorphous wretches",
478     "fetid masses",
479     "agents of filth",
480     "squalid dregs",
481     "unbelievers",
482     "heretics",
483     "guilty",
484     "legions of the damned",
485     "servants of Hell",
486     "forces of darkness",
487 };
488 
489 // First column is adjective, then noun.
490 static const char * const sin_text[][2] =
491 {
492     { "chaotic",      "chaos" },
493     { "discordant",   "discord" },
494     { "anarchic",     "anarchy" },
495     { "unclean",      "uncleanliness" },
496     { "impure",       "impurity" },
497     { "contaminated", "contamination" },
498     { "unfaithful",   "unfaithfulness" },
499     { "disloyal",     "disloyalty" },
500     { "doubting",     "doubt" },
501     { "profane",      "profanity" },
502     { "blasphemous",  "blasphemy" },
503     { "sacrilegious", "sacrilege" },
504 };
505 
506 // First column is adjective, then noun.
507 static const char * const virtue_text[][2] =
508 {
509     { "ordered",   "order" },
510     { "harmonic",  "harmony" },
511     { "lawful",    "lawfulness" },
512     { "clean",     "cleanliness" },
513     { "pure",      "purity" },
514     { "hygienic",  "hygiene" },
515     { "faithful",  "faithfulness" },
516     { "loyal",     "loyalty" },
517     { "believing", "belief" },
518     { "reverent",  "reverence" },
519     { "pious",     "piety" },
520     { "obedient",  "obedience" },
521 };
522 
523 // First column is infinitive, then gerund.
524 static const char * const smite_text[][2] =
525 {
526     { "purify",      "purified" },
527     { "censure",     "censured" },
528     { "condemn",     "condemned" },
529     { "strike down", "struck down" },
530     { "expel",       "expelled" },
531     { "oust",        "ousted" },
532     { "smite",       "smitten" },
533     { "castigate",   "castigated" },
534     { "rebuke",      "rebuked" },
535 };
536 
537 /** Get the verse to recite this turn.
538  *
539  *  @param seed       The seed to keep the book coherent between turns.
540  *  @param prayertype One of the four recite types.
541  *  @param step       -1: We're either starting or stopping, so we just want the passage name.
542  *                    2/1/0: That many rounds are left. So, if step = 2, we want to show the passage #1/3.
543  *  @returns the verse to be said this turn, or if step == -1, which verse it is.
544  */
zin_recite_text(const int seed,const int prayertype,int step)545 string zin_recite_text(const int seed, const int prayertype, int step)
546 {
547     // To have deterministic passages we extract portions of the seed.
548     // We use trits: "digits" in the base-3 expansion of seed.
549 
550     COMPILE_CHECK(ARRAYSZ(book_of_zin) == 27);
551     const int chapter = seed % 27;
552     const int verse = (seed/27) % 81;
553 
554     // Change step to turn 1, turn 2, or turn 3.
555     if (step > -1)
556     {
557         step = abs(step-3);
558         if (step > 3)
559             step = 1;
560     }
561     else
562     {
563         const string bookname = (prayertype == RECITE_CHAOTIC)  ?  "Abominations" :
564                                 (prayertype == RECITE_IMPURE)   ?  "Ablutions"    :
565                                 (prayertype == RECITE_HERETIC)  ?  "Apostates"    :
566                                 (prayertype == RECITE_UNHOLY)   ?  "Anathema"     :
567                                                                    "Bugginess";
568         ostringstream out;
569         out << bookname << ' ' << (chapter + 1) << ':' << (verse + 1);
570         return out.str();
571     }
572 
573     // These mad-libs are deterministically derived from the verse number
574     // and prayer type. Sins and virtues are paired, so use the same sub-
575     // seed. Sinners, sins, and smites are uncorrelated so do not share
576     // trits.
577     COMPILE_CHECK(ARRAYSZ(sinner_text) == 12);
578     COMPILE_CHECK(ARRAYSZ(sin_text) == 12);
579     COMPILE_CHECK(ARRAYSZ(virtue_text) == 12);
580     const int sinner_seed = verse % 3 + prayertype * 3;
581     const int sin_seed = (verse/27) % 3 + prayertype * 3;
582     const int virtue_seed = sin_seed;
583 
584     COMPILE_CHECK(ARRAYSZ(smite_text) == 9);
585     const int smite_seed = (verse/3) % 9;
586 
587     string recite = book_of_zin[chapter][step-1];
588 
589     const map<string, string> replacements =
590     {
591         { "sinners", sinner_text[sinner_seed] },
592 
593         { "sin_adj",  sin_text[sin_seed][0] },
594         { "sin_noun", sin_text[sin_seed][1] },
595 
596         { "virtuous", virtue_text[virtue_seed][0] },
597         { "virtue",   virtue_text[virtue_seed][1] },
598 
599         { "smite",   smite_text[smite_seed][0] },
600         { "smitten", smite_text[smite_seed][1] },
601         { "Smitten", uppercase_first(smite_text[smite_seed][1]) },
602     };
603 
604     return replace_keys(recite, replacements);
605 }
606 
607 /** How vulnerable to RECITE_HERETIC is this monster?
608  *
609  * @param[in] mon The monster to check.
610  * @returns the susceptibility.
611  */
_heretic_recite_weakness(const monster * mon)612 static int _heretic_recite_weakness(const monster *mon)
613 {
614     int degree = 0;
615 
616     // Sleeping or paralyzed monsters will wake up or still perceive their
617     // surroundings, respectively. So, you can still recite to them.
618     if (mons_intel(*mon) >= I_HUMAN
619         && !(mon->has_ench(ENCH_DUMB) || mons_is_confused(*mon)))
620     {
621         // In the eyes of Zin, everyone is a sinner until proven otherwise!
622         degree++;
623 
624         // Any priest is a heretic...
625         if (mon->is_priest())
626             degree++;
627 
628         // Or those who believe in themselves...
629         if (mon->type == MONS_DEMIGOD)
630             degree++;
631 
632         // ...but evil gods are worse.
633         if (is_evil_god(mon->god) || is_unknown_god(mon->god))
634             degree++;
635 
636         // (The above mean that worshipers will be treated as
637         // priests for reciting, even if they aren't actually.)
638 
639 
640         // Sanity check: monsters that won't attack you, and aren't
641         // priests/evil, don't get recited against.
642         if (mon->wont_attack() && degree <= 1)
643             degree = 0;
644 
645         // Sanity check: monsters that are holy, know holy spells, or worship
646         // holy gods aren't heretics.
647         if (mon->is_holy() || is_good_god(mon->god))
648             degree = 0;
649     }
650 
651     return degree;
652 }
653 
654 /** Check whether this monster might be influenced by Recite.
655  *
656  * @param[in] mon The monster to check.
657  * @param[out] eligibility A vector, indexed by recite_type, that indicates
658  *             which recitation types the monster is affected by, if any:
659  *             eligibility[RECITE_FOO] is nonzero if the monster is affected
660  *             by RECITE_FOO.
661  * @param quiet[in]     Whether to suppress messaging.
662  * @returns Whether the monster is eligible for recite. If the monster can be
663  *          recited to, the eligibility vector indicates the valid types of
664  *          recite.
665  */
zin_check_recite_to_single_monster(const monster * mon,recite_counts & eligibility,bool quiet)666 recite_eligibility zin_check_recite_to_single_monster(const monster *mon,
667                                                   recite_counts &eligibility,
668                                                   bool quiet)
669 {
670     ASSERT(mon);
671 
672     // Can't recite anyway if they were recently recited to.
673     if (mon->has_ench(ENCH_RECITE_TIMER))
674         return RE_RECITE_TIMER;
675 
676     const mon_holy_type holiness = mon->holiness();
677     eligibility.init(0);
678 
679     // Anti-chaos prayer: Hits things vulnerable to silver, or with chaotic spells/gods.
680     eligibility[RECITE_CHAOTIC] = mon->how_chaotic(true);
681 
682     // Anti-impure prayer: Hits things that Zin hates in general.
683     // Don't look at the monster's god; that's what RECITE_HERETIC is for.
684     eligibility[RECITE_IMPURE] = mon->how_unclean(false);
685 
686     // Anti-unholy prayer: Hits demons and incorporeal undead.
687     if (holiness & MH_UNDEAD && mon->is_insubstantial()
688         || holiness & MH_DEMONIC)
689     {
690         eligibility[RECITE_UNHOLY]++;
691     }
692 
693     // Anti-heretic prayer: Hits intelligent monsters, especially priests.
694     eligibility[RECITE_HERETIC] = _heretic_recite_weakness(mon);
695 
696 #ifdef DEBUG_DIAGNOSTICS
697     if (!quiet)
698     {
699         string elig;
700         for (int i = 0; i < NUM_RECITE_TYPES; i++)
701             elig += '0' + eligibility[i];
702         dprf("Eligibility: %s", elig.c_str());
703     }
704 #else
705     UNUSED(quiet);
706 #endif
707 
708     bool maybe_eligible = false;
709     // Checking to see whether they were eligible for anything at all.
710     for (int i = 0; i < NUM_RECITE_TYPES; i++)
711         if (eligibility[i] > 0)
712             maybe_eligible = true;
713 
714     if (maybe_eligible)
715     {
716         // Too high HD to be affected at current power.
717         if (mon->get_hit_dice() >= zin_recite_power())
718             return RE_TOO_STRONG;
719         return RE_ELIGIBLE;
720     }
721 
722     return RE_INELIGIBLE;
723 }
724 
zin_recite_power()725 int zin_recite_power()
726 {
727     // Resistance is now based on HD.
728     // Anything at or above (30+30)/2 = 30 'power' (HD) is completely immune.
729     const int power_mult = 10;
730     const int invo_power = you.skill_rdiv(SK_INVOCATIONS, power_mult)
731                            + 3 * power_mult;
732     const int piety_power = you.piety * 3 / 2;
733     return (invo_power + piety_power) / 2 / power_mult;
734 }
735 
zin_check_able_to_recite(bool quiet)736 bool zin_check_able_to_recite(bool quiet)
737 {
738     if (you.duration[DUR_RECITE])
739     {
740         if (!quiet)
741             mpr("Finish your current sermon first, please.");
742         return false;
743     }
744 
745     if (you.duration[DUR_RECITE_COOLDOWN])
746     {
747         if (!quiet)
748             mpr("You're not ready to recite again yet.");
749         return false;
750     }
751 
752     return true;
753 }
754 
755 /**
756  * Check whether there are monsters who might be influenced by Recite.
757  * If prayertype is null, we're just checking whether we can.
758  * Otherwise we're actually reciting, and may need to present a menu.
759  *
760  * @param quiet     Whether to suppress messages.
761  * @return  0 if no eligible monsters were found.
762  * @return  1 if an eligible audience was found.
763  * @return  -1 if the only monsters found cannot currently be affected (either
764  *          due to lack of recite power, or already having been affected)
765  *
766  */
zin_check_recite_to_monsters(bool quiet)767 int zin_check_recite_to_monsters(bool quiet)
768 {
769     bool found_temp_ineligible = false;
770     bool found_eligible = false;
771 
772     for (radius_iterator ri(you.pos(), LOS_DEFAULT); ri; ++ri)
773     {
774         const monster *mon = monster_at(*ri);
775         if (!mon || !you.can_see(*mon))
776             continue;
777 
778         recite_counts retval;
779         switch (zin_check_recite_to_single_monster(mon, retval, quiet))
780         {
781         case RE_TOO_STRONG:
782         case RE_RECITE_TIMER:
783             found_temp_ineligible = true;
784         // Intentional fallthrough
785         case RE_INELIGIBLE:
786             continue;
787 
788         case RE_ELIGIBLE:
789             found_eligible = true;
790         }
791     }
792 
793     if (!found_eligible && !found_temp_ineligible)
794     {
795         if (!quiet)
796             dprf("No audience found!");
797         return 0;
798     }
799     else if (!found_eligible && found_temp_ineligible)
800     {
801         if (!quiet)
802             dprf("No sensible audience found!");
803         return -1;
804     }
805     else
806         return 1; // We just recite against everything.
807 }
808 
809 enum class zin_eff
810 {
811     nothing,
812     daze,
813     confuse,
814     paralyse,
815     smite,
816     blind,
817     silver_corona,
818     antimagic,
819     mute,
820     mad,
821     dumb,
822     ignite_chaos,
823     saltify,
824     holy_word,
825 };
826 
zin_recite_to_single_monster(const coord_def & where)827 bool zin_recite_to_single_monster(const coord_def& where)
828 {
829     // That's a pretty good sanity check, I guess.
830     ASSERT(you_worship(GOD_ZIN));
831 
832     monster* mon = monster_at(where);
833 
834     // Once you're already reciting, invis is ok.
835     if (!mon || !cell_see_cell(where, you.pos(), LOS_DEFAULT))
836         return false;
837 
838     recite_counts eligibility;
839     bool affected = false;
840 
841     if (zin_check_recite_to_single_monster(mon, eligibility) != RE_ELIGIBLE)
842         return false;
843 
844     recite_type prayertype = RECITE_HERETIC;
845     for (int i = NUM_RECITE_TYPES - 1; i >= RECITE_HERETIC; i--)
846     {
847         if (eligibility[i] > 0)
848         {
849             prayertype = static_cast <recite_type>(i);
850             break;
851         }
852     }
853 
854     // Second check: because this affects the whole screen over several turns,
855     // its effects are staggered. There's a 50% chance per monster, per turn,
856     // that nothing will happen - so the cumulative odds of nothing happening
857     // are one in eight, since you recite three times.
858     if (coinflip())
859         return false;
860 
861     const int power = zin_recite_power();
862     // Old recite was mostly deterministic, which is bad.
863     const int resist = mon->get_hit_dice() + random2(6);
864     const int check = power - resist;
865 
866     // We abort if we didn't *beat* their HD - but first we might get a cute message.
867     if (mon->can_speak() && one_chance_in(5))
868     {
869         if (check < -10)
870             simple_monster_message(*mon, " guffaws at your puny god.");
871         else if (check < -5)
872             simple_monster_message(*mon, " sneers at your recitation.");
873     }
874 
875     if (check <= 0)
876         return false;
877 
878     // To what degree are they eligible for this prayertype?
879     const int degree = eligibility[prayertype];
880     const bool minor = degree <= (prayertype == RECITE_HERETIC ? 2 : 1);
881     const int spellpower = power * 2 + degree * 20;
882     zin_eff effect = zin_eff::nothing;
883 
884     switch (prayertype)
885     {
886     case RECITE_HERETIC:
887         if (degree == 1)
888         {
889             if (mon->asleep())
890                 break;
891             // This is the path for 'conversion' effects.
892             // Their degree is only 1 if they weren't a priest,
893             // a worshiper of an evil or chaotic god, etc.
894 
895             // Right now, it only has the 'failed conversion' effects, though.
896             // This branch can't hit sleeping monsters - until they wake up.
897 
898             if (check < 5)
899                 effect = zin_eff::daze;
900             else if (check < 10)
901             {
902                 if (coinflip())
903                     effect = zin_eff::confuse;
904                 else
905                     effect = zin_eff::daze;
906             }
907             else if (check < 15)
908                 effect = zin_eff::confuse;
909             else
910             {
911                 if (one_chance_in(3))
912                     effect = zin_eff::paralyse;
913                 else
914                     effect = zin_eff::confuse;
915             }
916         }
917         else
918         {
919             // This is the path for 'smiting' effects.
920             // Their degree is only greater than 1 if
921             // they're unable to be redeemed.
922             if (check < 5)
923             {
924                 if (coinflip())
925                     effect = zin_eff::confuse;
926                 else
927                     effect = zin_eff::smite;
928             }
929             else if (check < 10)
930             {
931                 if (one_chance_in(3))
932                     effect = zin_eff::blind;
933                 else if (mon->antimagic_susceptible())
934                     effect = zin_eff::antimagic;
935                 else
936                     effect = zin_eff::silver_corona;
937             }
938             else if (check < 15)
939             {
940                 if (one_chance_in(3))
941                     effect = zin_eff::blind;
942                 else if (coinflip())
943                     effect = zin_eff::paralyse;
944                 else
945                     effect = zin_eff::mute;
946             }
947             else
948             {
949                 if (coinflip())
950                     effect = zin_eff::mad;
951                 else
952                     effect = zin_eff::dumb;
953             }
954         }
955         break;
956 
957     case RECITE_CHAOTIC:
958         if (check < 5)
959             effect = zin_eff::smite;
960         else if (check < 10)
961             effect = zin_eff::silver_corona;
962         else if (check < 15)
963             effect = zin_eff::ignite_chaos;
964         else
965             effect = zin_eff::saltify;
966         break;
967 
968     case RECITE_IMPURE:
969         if (check < 5)
970             effect = zin_eff::smite;
971         else if (check < 10)
972             effect = zin_eff::silver_corona;
973         else if (check < 15)
974         {
975             if (mon->undead_or_demonic() && coinflip())
976                 effect = zin_eff::holy_word;
977             else
978                 effect = zin_eff::silver_corona;
979         }
980         else
981             effect = zin_eff::saltify;
982         break;
983 
984     case RECITE_UNHOLY:
985         if (check < 5)
986         {
987             if (coinflip())
988                 effect = zin_eff::daze;
989             else
990                 effect = zin_eff::confuse;
991         }
992         else if (check < 10)
993         {
994             if (coinflip())
995                 effect = zin_eff::confuse;
996             else
997                 effect = zin_eff::silver_corona;
998         }
999         // Half of the time, the anti-unholy prayer will be capped at this
1000         // level of effect.
1001         else if (check < 15 || coinflip())
1002         {
1003             if (coinflip())
1004                 effect = zin_eff::holy_word;
1005             else
1006                 effect = zin_eff::silver_corona;
1007         }
1008         else
1009             effect = zin_eff::saltify;
1010         break;
1011 
1012     case NUM_RECITE_TYPES:
1013         die("invalid recite type");
1014     }
1015 
1016     // And the actual effects...
1017     switch (effect)
1018     {
1019     case zin_eff::nothing:
1020         break;
1021 
1022     case zin_eff::daze:
1023         if (mon->add_ench(mon_enchant(ENCH_DAZED, degree, &you,
1024                           (degree + random2(spellpower)) * BASELINE_DELAY)))
1025         {
1026             simple_monster_message(*mon, " is dazed by your recitation.");
1027             affected = true;
1028         }
1029         break;
1030 
1031     case zin_eff::confuse:
1032         if (!mon->clarity()
1033             && mon->add_ench(mon_enchant(ENCH_CONFUSION, degree, &you,
1034                              (degree + random2(spellpower)) * BASELINE_DELAY)))
1035         {
1036             if (prayertype == RECITE_HERETIC)
1037                 simple_monster_message(*mon, " is confused by your recitation.");
1038             else
1039                 simple_monster_message(*mon, " stumbles about in disarray.");
1040             affected = true;
1041         }
1042         break;
1043 
1044     case zin_eff::paralyse:
1045         if (mon->add_ench(mon_enchant(ENCH_PARALYSIS, 0, &you,
1046                           (degree + random2(spellpower)) * BASELINE_DELAY)))
1047         {
1048             simple_monster_message(*mon,
1049                 minor ? " is awed by your recitation."
1050                       : " is aghast at the heresy of your recitation.");
1051             affected = true;
1052         }
1053         break;
1054 
1055     case zin_eff::smite:
1056         if (minor)
1057             simple_monster_message(*mon, " is smitten by the wrath of Zin.");
1058         else
1059             simple_monster_message(*mon, " is blasted by the fury of Zin!");
1060         // XXX: This duplicates code in cast_smiting().
1061         mon->hurt(&you, 7 + (random2(spellpower) * 33 / 191));
1062         if (mon->alive())
1063             print_wounds(*mon);
1064         affected = true;
1065         break;
1066 
1067     case zin_eff::blind:
1068         if (mon->add_ench(mon_enchant(ENCH_BLIND, degree, &you, INFINITE_DURATION)))
1069         {
1070             simple_monster_message(*mon, " is struck blind by the wrath of Zin!");
1071             affected = true;
1072         }
1073         break;
1074 
1075     case zin_eff::silver_corona:
1076         if (mon->add_ench(mon_enchant(ENCH_SILVER_CORONA, degree, &you,
1077                           (degree + random2(spellpower)) * BASELINE_DELAY)))
1078         {
1079             simple_monster_message(*mon, " is limned with silver light.");
1080             affected = true;
1081         }
1082         break;
1083 
1084     case zin_eff::antimagic:
1085         ASSERT(prayertype == RECITE_HERETIC);
1086         if (mon->add_ench(mon_enchant(ENCH_ANTIMAGIC, degree, &you,
1087                           (degree + random2(spellpower)) * BASELINE_DELAY)))
1088         {
1089             simple_monster_message(*mon,
1090                 minor ? " quails at your recitation."
1091                       : " looks feeble and powerless before your recitation.");
1092             affected = true;
1093         }
1094         break;
1095 
1096     case zin_eff::mute:
1097         if (mon->add_ench(mon_enchant(ENCH_MUTE, degree, &you, INFINITE_DURATION)))
1098         {
1099             simple_monster_message(*mon, " is struck mute by the wrath of Zin!");
1100             affected = true;
1101         }
1102         break;
1103 
1104     case zin_eff::mad:
1105         if (mon->add_ench(mon_enchant(ENCH_MAD, degree, &you, INFINITE_DURATION)))
1106         {
1107             simple_monster_message(*mon, " is driven mad by the wrath of Zin!");
1108             affected = true;
1109         }
1110         break;
1111 
1112     case zin_eff::dumb:
1113         if (mon->add_ench(mon_enchant(ENCH_DUMB, degree, &you, INFINITE_DURATION)))
1114         {
1115             simple_monster_message(*mon, " is left stupefied by the wrath of Zin!");
1116             affected = true;
1117         }
1118         break;
1119 
1120     case zin_eff::ignite_chaos:
1121         ASSERT(prayertype == RECITE_CHAOTIC);
1122         {
1123             bolt beam;
1124             dice_def dam_dice(0, 5 + spellpower/7);  // Dice added below if applicable.
1125             dam_dice.num = degree;
1126 
1127             int damage = dam_dice.roll();
1128             if (damage > 0)
1129             {
1130                 mon->hurt(&you, damage, BEAM_MISSILE, KILLED_BY_BEAM,
1131                           "", "", false);
1132 
1133                 if (mon->alive())
1134                 {
1135                     simple_monster_message(*mon,
1136                       (damage < 25) ? "'s chaotic flesh sizzles and spatters!" :
1137                       (damage < 50) ? "'s chaotic flesh bubbles and boils."
1138                                     : "'s chaotic flesh runs like molten wax.");
1139 
1140                     print_wounds(*mon);
1141                     behaviour_event(mon, ME_WHACK, &you);
1142                     affected = true;
1143                 }
1144                 else
1145                 {
1146                     simple_monster_message(*mon,
1147                         " melts away into a sizzling puddle of chaotic flesh.");
1148                     monster_die(*mon, KILL_YOU, NON_MONSTER);
1149                 }
1150             }
1151         }
1152         break;
1153 
1154     case zin_eff::saltify:
1155         _zin_saltify(mon);
1156         break;
1157 
1158     case zin_eff::holy_word:
1159         holy_word_monsters(where, spellpower, HOLY_WORD_ZIN, &you);
1160         affected = true;
1161         break;
1162     }
1163 
1164     // Recite time, to prevent monsters from being recited against
1165     // more than once in a given recite instance.
1166     if (affected)
1167         mon->add_ench(mon_enchant(ENCH_RECITE_TIMER, degree, &you, 40));
1168 
1169     // Monsters that have been affected may shout.
1170     if (affected
1171         && one_chance_in(3)
1172         && mon->alive()
1173         && mons_can_shout(mon->type))
1174     {
1175         monster_attempt_shout(*mon);
1176     }
1177 
1178     return true;
1179 }
1180 
_zin_saltify(monster * mon)1181 static void _zin_saltify(monster* mon)
1182 {
1183     const coord_def where = mon->pos();
1184     const monster_type pillar_type =
1185         mons_is_zombified(*mon) ? mons_zombie_base(*mon)
1186                                 : mons_species(mon->type);
1187     const int hd = mon->get_hit_dice();
1188 
1189     simple_monster_message(*mon, " is turned into a pillar of salt by the wrath of Zin!");
1190 
1191     // If the monster leaves a corpse when it dies, destroy the corpse.
1192     item_def* corpse = monster_die(*mon, KILL_YOU, NON_MONSTER);
1193     if (corpse)
1194         destroy_item(corpse->index());
1195 
1196     if (monster *pillar = create_monster(
1197                         mgen_data(MONS_PILLAR_OF_SALT,
1198                                   BEH_HOSTILE,
1199                                   where,
1200                                   MHITNOT,
1201                                   MG_FORCE_PLACE).set_base(pillar_type),
1202                                   false))
1203     {
1204         // Enemies with more HD leave longer-lasting pillars of salt.
1205         int time_left = (random2(8) + hd) * BASELINE_DELAY;
1206         mon_enchant temp_en(ENCH_SLOWLY_DYING, 1, 0, time_left);
1207         pillar->update_ench(temp_en);
1208     }
1209 }
1210 
zin_vitalisation()1211 bool zin_vitalisation()
1212 {
1213     simple_god_message(" grants you divine stamina.");
1214 
1215     // Add divine stamina.
1216     const int stamina_amt =
1217         max(1, you.skill_rdiv(SK_INVOCATIONS, 1, 3));
1218     you.attribute[ATTR_DIVINE_STAMINA] = stamina_amt;
1219     you.set_duration(DUR_DIVINE_STAMINA, 60 + roll_dice(2, 10));
1220 
1221     notify_stat_change(STAT_STR, stamina_amt, true);
1222     notify_stat_change(STAT_INT, stamina_amt, true);
1223     notify_stat_change(STAT_DEX, stamina_amt, true);
1224 
1225     return true;
1226 }
1227 
zin_remove_divine_stamina()1228 void zin_remove_divine_stamina()
1229 {
1230     mprf(MSGCH_DURATION, "Your divine stamina fades away.");
1231     notify_stat_change(STAT_STR, -you.attribute[ATTR_DIVINE_STAMINA], true);
1232     notify_stat_change(STAT_INT, -you.attribute[ATTR_DIVINE_STAMINA], true);
1233     notify_stat_change(STAT_DEX, -you.attribute[ATTR_DIVINE_STAMINA], true);
1234     you.duration[DUR_DIVINE_STAMINA] = 0;
1235     you.attribute[ATTR_DIVINE_STAMINA] = 0;
1236 }
1237 
zin_remove_all_mutations()1238 bool zin_remove_all_mutations()
1239 {
1240     ASSERT(you.how_mutated());
1241     ASSERT(can_do_capstone_ability(you.religion));
1242 
1243     if (!yesno("Do you wish to cure all of your mutations?", true, 'n'))
1244     {
1245         canned_msg(MSG_OK);
1246         return false;
1247     }
1248     flash_view(UA_PLAYER, WHITE);
1249 #ifndef USE_TILE_LOCAL
1250     // Allow extra time for the flash to linger.
1251     scaled_delay(1000);
1252 #endif
1253 
1254     you.one_time_ability_used.set(GOD_ZIN);
1255     take_note(Note(NOTE_GOD_GIFT, you.religion));
1256     simple_god_message(" draws all chaos from your body!");
1257     delete_all_mutations("Zin's power");
1258     return true;
1259 }
1260 
zin_sanctuary()1261 void zin_sanctuary()
1262 {
1263     ASSERT(!env.sanctuary_time);
1264 
1265     // Yes, shamelessly stolen from NetHack...
1266     if (!silenced(you.pos())) // How did you manage that?
1267         mprf(MSGCH_SOUND, "You hear a choir sing!");
1268     else
1269         mpr("You are suddenly bathed in radiance!");
1270 
1271     flash_view(UA_PLAYER, WHITE);
1272 #ifndef USE_TILE_LOCAL
1273     // Allow extra time for the flash to linger.
1274     scaled_delay(1000);
1275 #endif
1276 
1277     // Pets stop attacking and converge on you.
1278     you.pet_target = MHITYOU;
1279     create_sanctuary(you.pos(), 7 + you.skill_rdiv(SK_INVOCATIONS) / 2);
1280 }
1281 
1282 // shield bonus = attribute for duration turns, then decreasing by 1
1283 //                every two out of three turns
1284 // overall shield duration = duration + attribute
1285 // recasting simply resets those two values (to better values, presumably)
tso_divine_shield()1286 void tso_divine_shield()
1287 {
1288     if (!you.duration[DUR_DIVINE_SHIELD])
1289     {
1290         if (you.shield())
1291         {
1292             mprf("Your shield is strengthened by %s divine power.",
1293                  apostrophise(god_name(GOD_SHINING_ONE)).c_str());
1294         }
1295         else
1296             mpr("A divine shield forms around you!");
1297     }
1298     else
1299         mpr("Your divine shield is renewed.");
1300 
1301     // Duration from 35-80 turns.
1302     you.set_duration(DUR_DIVINE_SHIELD,
1303                      35 + you.skill_rdiv(SK_INVOCATIONS, 5, 3));
1304 
1305     // Size of SH bonus.
1306     you.attribute[ATTR_DIVINE_SHIELD] =
1307         12 + you.skill_rdiv(SK_INVOCATIONS, 4, 5);
1308 
1309     you.redraw_armour_class = true;
1310 }
1311 
tso_remove_divine_shield()1312 void tso_remove_divine_shield()
1313 {
1314     mprf(MSGCH_DURATION, "Your divine shield fades away.");
1315     you.duration[DUR_DIVINE_SHIELD] = 0;
1316     you.attribute[ATTR_DIVINE_SHIELD] = 0;
1317     you.redraw_armour_class = true;
1318 }
1319 
elyvilon_purification()1320 void elyvilon_purification()
1321 {
1322     mpr("You feel purified!");
1323 
1324     you.duration[DUR_SICKNESS] = 0;
1325     you.duration[DUR_POISONING] = 0;
1326     you.duration[DUR_CONF] = 0;
1327     you.duration[DUR_SLOW] = 0;
1328     you.duration[DUR_PETRIFYING] = 0;
1329     you.duration[DUR_WEAK] = 0;
1330     restore_stat(STAT_ALL, 0, false);
1331     undrain_hp(9999);
1332     you.redraw_evasion = true;
1333 }
1334 
elyvilon_divine_vigour()1335 bool elyvilon_divine_vigour()
1336 {
1337     bool success = false;
1338 
1339     if (!you.duration[DUR_DIVINE_VIGOUR])
1340     {
1341         mprf("%s grants you divine vigour.",
1342              god_name(GOD_ELYVILON).c_str());
1343 
1344         const int vigour_amt = 1 + you.skill_rdiv(SK_INVOCATIONS, 1, 3);
1345         const int old_hp_max = you.hp_max;
1346         const int old_mp_max = you.max_magic_points;
1347         you.attribute[ATTR_DIVINE_VIGOUR] = vigour_amt;
1348         you.set_duration(DUR_DIVINE_VIGOUR,
1349                          40 + you.skill_rdiv(SK_INVOCATIONS, 5, 2));
1350 
1351         calc_hp();
1352         inc_hp((you.hp_max * you.hp + old_hp_max - 1)/old_hp_max - you.hp);
1353         calc_mp();
1354         if (old_mp_max > 0)
1355         {
1356             inc_mp((you.max_magic_points * you.magic_points + old_mp_max - 1)
1357                      / old_mp_max
1358                    - you.magic_points);
1359         }
1360 
1361         success = true;
1362     }
1363     else
1364         canned_msg(MSG_NOTHING_HAPPENS);
1365 
1366     return success;
1367 }
1368 
elyvilon_remove_divine_vigour()1369 void elyvilon_remove_divine_vigour()
1370 {
1371     mprf(MSGCH_DURATION, "Your divine vigour fades away.");
1372     you.duration[DUR_DIVINE_VIGOUR] = 0;
1373     you.attribute[ATTR_DIVINE_VIGOUR] = 0;
1374     calc_hp();
1375     calc_mp();
1376 }
1377 
vehumet_supports_spell(spell_type spell)1378 bool vehumet_supports_spell(spell_type spell)
1379 {
1380     if (spell_typematch(spell, spschool::conjuration))
1381         return true;
1382 
1383     // Conjurations work by conjuring up a chunk of short-lived matter and
1384     // propelling it towards the victim. This is the most popular way, but
1385     // by no means it has a monopoly for being destructive.
1386     // Vehumet loves all direct physical destruction.
1387     if (spell == SPELL_SHATTER
1388         || spell == SPELL_LRD
1389         || spell == SPELL_SANDBLAST
1390         || spell == SPELL_AIRSTRIKE
1391         || spell == SPELL_POLAR_VORTEX
1392         || spell == SPELL_FREEZE
1393         || spell == SPELL_IGNITE_POISON
1394         || spell == SPELL_OZOCUBUS_REFRIGERATION
1395         || spell == SPELL_OLGREBS_TOXIC_RADIANCE
1396         || spell == SPELL_VIOLENT_UNRAVELLING
1397         || spell == SPELL_INNER_FLAME
1398         || spell == SPELL_IGNITION
1399         || spell == SPELL_FROZEN_RAMPARTS
1400         || spell == SPELL_MAXWELLS_COUPLING
1401         || spell == SPELL_NOXIOUS_BOG
1402         || spell == SPELL_POISONOUS_VAPOURS)
1403     {
1404         return true;
1405     }
1406 
1407     return false;
1408 }
1409 
trog_do_trogs_hand(int pow)1410 void trog_do_trogs_hand(int pow)
1411 {
1412     you.increase_duration(DUR_TROGS_HAND,
1413                           5 + roll_dice(2, pow / 3 + 1), 100,
1414                           "Your skin crawls.");
1415     mprf(MSGCH_DURATION, "You feel strong-willed.");
1416 }
1417 
trog_remove_trogs_hand()1418 void trog_remove_trogs_hand()
1419 {
1420     mprf(MSGCH_DURATION, "Your skin stops crawling.");
1421     mprf(MSGCH_DURATION, "You feel less strong-willed.");
1422     you.duration[DUR_TROGS_HAND] = 0;
1423 }
1424 
1425 /**
1426  * Has the monster been given a Beogh gift?
1427  *
1428  * @param mon the orc in question.
1429  * @returns whether you have given the monster a Beogh gift before now.
1430  */
given_gift(const monster * mon)1431 bool given_gift(const monster* mon)
1432 {
1433     return mon->props.exists(BEOGH_RANGE_WPN_GIFT_KEY)
1434             || mon->props.exists(BEOGH_MELEE_WPN_GIFT_KEY)
1435             || mon->props.exists(BEOGH_ARM_GIFT_KEY)
1436             || mon->props.exists(BEOGH_SH_GIFT_KEY);
1437 }
1438 
1439 /**
1440  * Checks whether the target monster is a valid target for beogh item-gifts.
1441  *
1442  * @param mons[in]  The monster to consider giving an item to.
1443  * @param quiet     Whether to print messages if the target is invalid.
1444  * @return          Whether the player can give an item to the monster.
1445  */
beogh_can_gift_items_to(const monster * mons,bool quiet)1446 bool beogh_can_gift_items_to(const monster* mons, bool quiet)
1447 {
1448     if (!mons || !mons->visible_to(&you))
1449     {
1450         if (!quiet)
1451             canned_msg(MSG_NOTHING_THERE);
1452         return false;
1453     }
1454 
1455     if (!is_orcish_follower(*mons) || mons_genus(mons->type) != MONS_ORC)
1456     {
1457         if (!quiet)
1458             mpr("That's not an orcish ally!");
1459         return false;
1460     }
1461 
1462     if (!mons->is_named())
1463     {
1464         if (!quiet)
1465             mpr("That orc has not proved itself worthy of your gift.");
1466         return false;
1467     }
1468 
1469     if (given_gift(mons))
1470     {
1471         if (!quiet)
1472         {
1473             mprf("%s has already been given a gift.",
1474                  mons->name(DESC_THE, false).c_str());
1475         }
1476         return false;
1477     }
1478 
1479     return true;
1480 }
1481 
1482 /**
1483  * Checks whether there are any valid targets for beogh gifts in LOS.
1484  */
_valid_beogh_gift_targets_in_sight()1485 static bool _valid_beogh_gift_targets_in_sight()
1486 {
1487     for (monster_near_iterator rad(you.pos(), LOS_NO_TRANS); rad; ++rad)
1488         if (beogh_can_gift_items_to(*rad))
1489             return true;
1490     return false;
1491 }
1492 
1493 /**
1494  * Allow the player to give an item to a named orcish ally that hasn't
1495  * been given a gift before
1496  *
1497  * @returns whether an item was given.
1498  */
beogh_gift_item()1499 bool beogh_gift_item()
1500 {
1501     if (!_valid_beogh_gift_targets_in_sight())
1502     {
1503         mpr("No worthy followers in sight.");
1504         return false;
1505     }
1506 
1507     dist spd;
1508 
1509     direction_chooser_args args;
1510     args.restricts = DIR_TARGET;
1511     args.mode = TARG_BEOGH_GIFTABLE;
1512     args.range = LOS_RADIUS;
1513     args.needs_path = false;
1514     args.self = confirm_prompt_type::cancel;
1515     args.show_floor_desc = true;
1516     args.top_prompt = "Select a follower to give a gift to.";
1517 
1518     direction(spd, args);
1519 
1520     if (!spd.isValid)
1521         return false;
1522 
1523     monster* mons = monster_at(spd.target);
1524     if (!beogh_can_gift_items_to(mons, false))
1525         return false;
1526 
1527     int item_slot = prompt_invent_item("Give which item?",
1528                                        menu_type::invlist, OSEL_BEOGH_GIFT);
1529 
1530     if (item_slot == PROMPT_ABORT || item_slot == PROMPT_NOTHING)
1531     {
1532         canned_msg(MSG_OK);
1533         return false;
1534     }
1535 
1536     item_def& gift = you.inv[item_slot];
1537 
1538     const bool shield = is_shield(gift);
1539     const bool body_armour = gift.base_type == OBJ_ARMOUR
1540                              && get_armour_slot(gift) == EQ_BODY_ARMOUR;
1541     const bool weapon = gift.base_type == OBJ_WEAPONS;
1542     const bool range_weapon = weapon && is_range_weapon(gift);
1543     const item_def* mons_weapon = mons->weapon();
1544     const item_def* mons_alt_weapon = mons->mslot_item(MSLOT_ALT_WEAPON);
1545 
1546     if (weapon && !mons->could_wield(gift)
1547         || body_armour && !check_armour_size(gift, mons->body_size())
1548         || !item_is_selected(gift, OSEL_BEOGH_GIFT))
1549     {
1550         mprf("You can't give that to %s.", mons->name(DESC_THE, false).c_str());
1551 
1552         return false;
1553     }
1554     else if (shield
1555              && (mons_weapon && mons->hands_reqd(*mons_weapon) == HANDS_TWO
1556                  || mons_alt_weapon
1557                     && mons->hands_reqd(*mons_alt_weapon) == HANDS_TWO))
1558     {
1559         mprf("%s can't equip that with a two-handed weapon.",
1560              mons->name(DESC_THE, false).c_str());
1561         return false;
1562     }
1563 
1564     // if we're giving a ranged weapon to an orc holding a melee weapon in
1565     // their hands, or vice versa, put it in their carried slot instead.
1566     // this will of course drop anything that's there.
1567     const bool use_alt_slot = weapon && mons_weapon
1568                               && is_range_weapon(gift) !=
1569                                  is_range_weapon(*mons_weapon);
1570 
1571     const auto mslot = body_armour ? MSLOT_ARMOUR :
1572                                     shield ? MSLOT_SHIELD :
1573                               use_alt_slot ? MSLOT_ALT_WEAPON :
1574                                              MSLOT_WEAPON;
1575 
1576     item_def *floor_item = mons->take_item(item_slot, mslot);
1577     if (!floor_item)
1578     {
1579         // this probably means move_to_grid in drop_item failed?
1580         mprf(MSGCH_ERROR, "Gift failed: %s is unable to take %s.",
1581                                         mons->name(DESC_THE, false).c_str(),
1582                                         gift.name(DESC_THE, false).c_str());
1583         return false;
1584     }
1585     if (use_alt_slot)
1586         mons->swap_weapons();
1587 
1588     dprf("is_ranged weap: %d", range_weapon);
1589     if (range_weapon)
1590         gift_ammo_to_orc(mons);
1591 
1592     if (shield)
1593         mons->props[BEOGH_SH_GIFT_KEY] = true;
1594     else if (body_armour)
1595         mons->props[BEOGH_ARM_GIFT_KEY] = true;
1596     else if (range_weapon)
1597         mons->props[BEOGH_RANGE_WPN_GIFT_KEY] = true;
1598     else
1599         mons->props[BEOGH_MELEE_WPN_GIFT_KEY] = true;
1600 
1601     return true;
1602 }
1603 
beogh_resurrect()1604 bool beogh_resurrect()
1605 {
1606     item_def* corpse = nullptr;
1607     bool found_any = false;
1608     for (stack_iterator si(you.pos()); si; ++si)
1609         if (si->props.exists(ORC_CORPSE_KEY))
1610         {
1611             found_any = true;
1612             if (yesno(("Resurrect "
1613                        + si->props[ORC_CORPSE_KEY].get_monster().full_name(DESC_THE)
1614                        + "?").c_str(), true, 'n'))
1615             {
1616                 corpse = &*si;
1617                 break;
1618             }
1619         }
1620     if (!corpse)
1621     {
1622         mprf("There's nobody %shere you can resurrect.",
1623              found_any ? "else " : "");
1624         return false;
1625     }
1626 
1627     coord_def pos;
1628     ASSERT(corpse->props.exists(ORC_CORPSE_KEY));
1629     for (fair_adjacent_iterator ai(you.pos()); ai; ++ai)
1630     {
1631         if (!actor_at(*ai)
1632             && corpse->props[ORC_CORPSE_KEY].get_monster().is_location_safe(*ai))
1633         {
1634             pos = *ai;
1635         }
1636     }
1637     if (pos.origin())
1638     {
1639         mpr("There's no room!");
1640         return false;
1641     }
1642 
1643     monster* mon = get_free_monster();
1644     *mon = corpse->props[ORC_CORPSE_KEY];
1645     destroy_item(corpse->index());
1646     env.mid_cache[mon->mid] = mon->mindex();
1647     mon->hit_points = mon->max_hit_points;
1648     mon->inv.init(NON_ITEM);
1649     for (stack_iterator si(you.pos()); si; ++si)
1650     {
1651         if (!si->props.exists(DROPPER_MID_KEY)
1652             || si->props[DROPPER_MID_KEY].get_int() != int(mon->mid))
1653         {
1654             continue;
1655         }
1656         unwind_var<int> save_speedinc(mon->speed_increment);
1657         mon->pickup_item(*si, false, true);
1658     }
1659     mon->move_to_pos(pos);
1660     mon->timeout_enchantments(100);
1661     beogh_convert_orc(mon, conv_t::resurrection);
1662 
1663     return true;
1664 }
1665 
jiyva_remove_bad_mutation()1666 bool jiyva_remove_bad_mutation()
1667 {
1668     if (!you.how_mutated())
1669     {
1670         mpr("You have no bad mutations to be cured!");
1671         return false;
1672     }
1673 
1674     // Ensure that only bad mutations are removed.
1675     if (!delete_mutation(RANDOM_BAD_MUTATION, "Jiyva's power", true, false, true, true))
1676     {
1677         canned_msg(MSG_NOTHING_HAPPENS);
1678         return false;
1679     }
1680 
1681     mpr("You feel cleansed.");
1682     return true;
1683 }
1684 
yred_injury_mirror()1685 bool yred_injury_mirror()
1686 {
1687     return in_good_standing(GOD_YREDELEMNUL, 1)
1688            && you.duration[DUR_MIRROR_DAMAGE]
1689            && crawl_state.which_god_acting() != GOD_YREDELEMNUL;
1690 }
1691 
yred_can_enslave_soul(monster * mon)1692 bool yred_can_enslave_soul(monster* mon)
1693 {
1694     return (mon->holiness() & MH_NATURAL
1695             || mon->holiness() & MH_DEMONIC
1696             || mon->holiness() & MH_HOLY)
1697            && !mon->is_summoned()
1698            && !mons_enslaved_body_and_soul(*mon)
1699            && mon->attitude != ATT_FRIENDLY
1700            && mons_intel(*mon) >= I_HUMAN
1701            && mon->type != MONS_PANDEMONIUM_LORD;
1702 }
1703 
yred_make_enslaved_soul(monster * mon,bool force_hostile)1704 void yred_make_enslaved_soul(monster* mon, bool force_hostile)
1705 {
1706     ASSERT(mon); // XXX: change to monster &mon
1707     ASSERT(mons_enslaved_body_and_soul(*mon));
1708 
1709     add_daction(DACT_OLD_CHARMD_SOULS_POOF);
1710     remove_enslaved_soul_companion();
1711 
1712     const string whose = you.can_see(*mon) ? apostrophise(mon->name(DESC_THE))
1713                                            : mon->pronoun(PRONOUN_POSSESSIVE);
1714 
1715     // Remove the monster's soul-enslaving enchantment, as it's no
1716     // longer needed.
1717     mon->del_ench(ENCH_SOUL_RIPE, false, false);
1718 
1719     // Remove the monster's invisibility enchantment. If we don't do
1720     // this, it'll stay invisible after being remade as a spectral thing
1721     // below.
1722     mon->del_ench(ENCH_INVIS, false, false);
1723 
1724     // If the monster's held in a net, get it out.
1725     mons_clear_trapping_net(mon);
1726 
1727     // Rebrand or drop any holy equipment, and keep wielding the rest. Also
1728     // remove any active avatars.
1729     for (int slot = MSLOT_WEAPON; slot <= MSLOT_ALT_WEAPON; slot++)
1730     {
1731         item_def *wpn = mon->mslot_item(static_cast<mon_inv_type>(slot));
1732         if (wpn && get_weapon_brand(*wpn) == SPWPN_HOLY_WRATH)
1733         {
1734             set_item_ego_type(*wpn, OBJ_WEAPONS, SPWPN_DRAINING);
1735             convert2bad(*wpn);
1736         }
1737     }
1738     monster_drop_things(mon, false, [](const item_def& item)
1739                                     { return is_holy_item(item); });
1740     mon->remove_summons();
1741 
1742     const monster orig = *mon;
1743 
1744     // Use the original monster type as the zombified type here, to get
1745     // the proper stats from it.
1746     define_zombie(mon, mon->type, MONS_SPECTRAL_THING);
1747 
1748     // If the original monster has been levelled up, its HD might be different
1749     // from its class HD, in which case its HP should be rerolled to match.
1750     if (mon->get_experience_level() != orig.get_experience_level())
1751     {
1752         mon->set_hit_dice(max(orig.get_experience_level(), 1));
1753         roll_zombie_hp(mon);
1754     }
1755 
1756     mon->colour = ETC_UNHOLY;
1757 
1758     mon->flags |= MF_NO_REWARD;
1759     mon->flags |= MF_ENSLAVED_SOUL;
1760 
1761     // If the original monster type has melee abilities, make sure
1762     // its spectral thing has them as well.
1763     mon->flags |= orig.flags & MF_MELEE_MASK;
1764     monster_spells spl = orig.spells;
1765     for (const mon_spell_slot &slot : spl)
1766         if (!(get_spell_flags(slot.spell) & spflag::holy))
1767             mon->spells.push_back(slot);
1768     if (mon->spells.size())
1769         mon->props[CUSTOM_SPELLS_KEY] = true;
1770 
1771     name_zombie(*mon, orig);
1772 
1773     mons_make_god_gift(*mon, GOD_YREDELEMNUL);
1774     add_companion(mon);
1775 
1776     mon->attitude = !force_hostile ? ATT_FRIENDLY : ATT_HOSTILE;
1777     behaviour_event(mon, ME_ALERT, force_hostile ? &you : 0);
1778 
1779     mon->stop_constricting_all();
1780     mon->stop_being_constricted();
1781 
1782     if (orig.halo_radius()
1783         || orig.umbra_radius()
1784         || orig.silence_radius())
1785     {
1786         invalidate_agrid();
1787     }
1788 
1789     mprf("%s soul %s.", whose.c_str(),
1790          !force_hostile ? "is now yours" : "fights you");
1791 }
1792 
kiku_receive_corpses(int pow)1793 bool kiku_receive_corpses(int pow)
1794 {
1795     // pow = necromancy * 4, ranges from 0 to 108
1796     dprf("kiku_receive_corpses() power: %d", pow);
1797 
1798     // Kiku gives branch-appropriate corpses (like shadow creatures).
1799     // 1d2 at 0 Nec, up to 8 at 27 Nec.
1800     int expected_extra_corpses = 1 + random2(2) + random2(pow / 18);
1801     int corpse_delivery_radius = 1;
1802 
1803     // We should get the same number of corpses
1804     // in a hallway as in an open room.
1805     int spaces_for_corpses = 0;
1806     for (radius_iterator ri(you.pos(), corpse_delivery_radius, C_SQUARE,
1807                             LOS_NO_TRANS, true); ri; ++ri)
1808     {
1809         if (mons_class_can_pass(MONS_HUMAN, env.grid(*ri)))
1810             spaces_for_corpses++;
1811     }
1812     // floating over lava, heavy tomb abuse, etc
1813     if (!spaces_for_corpses)
1814         spaces_for_corpses++;
1815 
1816     int percent_chance_a_square_receives_extra_corpse = // can be > 100
1817         int(float(expected_extra_corpses) / float(spaces_for_corpses) * 100.0);
1818 
1819     int corpses_created = 0;
1820 
1821     for (radius_iterator ri(you.pos(), corpse_delivery_radius, C_SQUARE,
1822                             LOS_NO_TRANS); ri; ++ri)
1823     {
1824         bool square_is_walkable = mons_class_can_pass(MONS_HUMAN, env.grid(*ri));
1825         bool square_is_player_square = (*ri == you.pos());
1826         bool square_gets_corpse =
1827             random2(100) < percent_chance_a_square_receives_extra_corpse
1828             || square_is_player_square && random2(100) < 97;
1829 
1830         if (!square_is_walkable || !square_gets_corpse)
1831             continue;
1832 
1833         corpses_created++;
1834 
1835         // Find an appropriate monster corpse for level and power.
1836         const int adjusted_power = min(pow / 4, random2(random2(pow)));
1837         // Pick a place based on the power. This may be below the branch's
1838         // start, that's ok.
1839         const level_id lev(you.where_are_you, adjusted_power
1840                            - absdungeon_depth(you.where_are_you, 0));
1841         const monster_type mon_type = pick_local_corpsey_monster(lev);
1842         ASSERT(mons_class_can_be_zombified(mons_species(mon_type)));
1843 
1844         // Create corpse object.
1845         monster dummy;
1846         dummy.type = mon_type;
1847         define_monster(dummy);
1848         dummy.position = *ri;
1849 
1850         item_def* corpse = place_monster_corpse(dummy, true);
1851         if (!corpse)
1852             continue;
1853 
1854         // Higher piety means fresher corpses.
1855         int rottedness = 200 -
1856             (!one_chance_in(10) ? random2(200 - you.piety)
1857                                 : random2(100 + random2(75)));
1858         corpse->freshness = rottedness;
1859     }
1860 
1861     if (corpses_created)
1862     {
1863         if (you_worship(GOD_KIKUBAAQUDGHA))
1864         {
1865             simple_god_message(corpses_created > 1 ? " delivers you corpses!"
1866                                                    : " delivers you a corpse!");
1867         }
1868         maybe_update_stashes();
1869         return true;
1870     }
1871     else
1872     {
1873         if (you_worship(GOD_KIKUBAAQUDGHA))
1874             simple_god_message(" can find no cadavers for you!");
1875         return false;
1876     }
1877 }
1878 
1879 /**
1880  * Destroy a corpse at the player's location
1881  *
1882  * @return  True if a corpse was destroyed, false otherwise.
1883 */
kiku_take_corpse()1884 bool kiku_take_corpse()
1885 {
1886     for (int i = you.visible_igrd(you.pos()); i != NON_ITEM; i = env.item[i].link)
1887     {
1888         item_def &item(env.item[i]);
1889 
1890         if (item.base_type != OBJ_CORPSES || item.sub_type != CORPSE_BODY)
1891             continue;
1892         item_was_destroyed(item);
1893         destroy_item(i);
1894         return true;
1895     }
1896 
1897     return false;
1898 }
1899 
kiku_gift_capstone_spells()1900 bool kiku_gift_capstone_spells()
1901 {
1902     ASSERT(can_do_capstone_ability(you.religion));
1903 
1904     vector<spell_type> spells = { SPELL_HAUNT,
1905                                   SPELL_BORGNJORS_REVIVIFICATION,
1906                                   SPELL_INFESTATION,
1907                                   SPELL_NECROMUTATION,
1908                                   SPELL_DEATHS_DOOR };
1909 
1910     string msg = "Do you wish to receive knowledge of "
1911                  + comma_separated_fn(spells.begin(), spells.end(), spell_title)
1912                  + "?";
1913 
1914     if (!yesno(msg.c_str(), true, 'n'))
1915     {
1916         canned_msg(MSG_OK);
1917         return false;
1918     }
1919 
1920     simple_god_message(" grants you forbidden knowledge!");
1921     library_add_spells(spells);
1922     flash_view(UA_PLAYER, RED);
1923 #ifndef USE_TILE_LOCAL
1924     // Allow extra time for the flash to linger.
1925     scaled_delay(1000);
1926 #endif
1927     more();
1928     you.one_time_ability_used.set(you.religion);
1929     take_note(Note(NOTE_GOD_GIFT, you.religion));
1930     return true;
1931 }
1932 
fedhas_passthrough_class(const monster_type mc)1933 bool fedhas_passthrough_class(const monster_type mc)
1934 {
1935     return have_passive(passive_t::pass_through_plants)
1936            && mons_class_is_plant(mc)
1937            && mons_class_is_stationary(mc)
1938            && mc != MONS_SNAPLASHER_VINE
1939            && mc != MONS_SNAPLASHER_VINE_SEGMENT;
1940 }
1941 
1942 // Fedhas allows worshipers to walk on top of stationary plants and
1943 // fungi.
fedhas_passthrough(const monster * target)1944 bool fedhas_passthrough(const monster* target)
1945 {
1946     return target
1947            && fedhas_passthrough_class(target->type)
1948            && (mons_species(target->type) != MONS_OKLOB_PLANT
1949                || target->attitude != ATT_HOSTILE);
1950 }
1951 
fedhas_passthrough(const monster_info * target)1952 bool fedhas_passthrough(const monster_info* target)
1953 {
1954     return target
1955            && fedhas_passthrough_class(target->type)
1956            && (mons_species(target->type) != MONS_OKLOB_PLANT
1957                || target->attitude != ATT_HOSTILE);
1958 }
1959 
_lugonu_warp_monster(monster & mon,int pow)1960 static bool _lugonu_warp_monster(monster& mon, int pow)
1961 {
1962     if (coinflip())
1963         return false;
1964 
1965     if (!mon.friendly())
1966         behaviour_event(&mon, ME_ANNOY, &you);
1967 
1968     mon.hurt(&you, 1 + random2(pow / 6));
1969 
1970     if (mon.alive() && !mon.no_tele(true, false))
1971         mon.blink();
1972 
1973     return true;
1974 }
1975 
_lugonu_warp_area(int pow)1976 static void _lugonu_warp_area(int pow)
1977 {
1978     apply_monsters_around_square([pow] (monster& mon) {
1979         return _lugonu_warp_monster(mon, pow);
1980     }, you.pos());
1981 }
1982 
lugonu_bend_space()1983 void lugonu_bend_space()
1984 {
1985     const int pow = 4 + skill_bump(SK_INVOCATIONS);
1986     const bool area_warp = random2(pow) > 9;
1987 
1988     mprf("Space bends %saround you!", area_warp ? "sharply " : "");
1989 
1990     if (area_warp)
1991         _lugonu_warp_area(pow);
1992 
1993     uncontrolled_blink(true);
1994 }
1995 
cheibriados_time_bend(int pow)1996 void cheibriados_time_bend(int pow)
1997 {
1998     mpr("The flow of time bends around you.");
1999 
2000     for (adjacent_iterator ai(you.pos()); ai; ++ai)
2001     {
2002         monster* mon = monster_at(*ai);
2003         if (mon && !mon->is_stationary())
2004         {
2005             int res_margin = roll_dice(mon->get_hit_dice(), 3);
2006             res_margin -= random2avg(pow, 2);
2007             if (res_margin > 0)
2008             {
2009                 mprf("%s%s",
2010                      mon->name(DESC_THE).c_str(),
2011                      mon->resist_margin_phrase(res_margin).c_str());
2012                 continue;
2013             }
2014 
2015             simple_god_message(
2016                 make_stringf(" rebukes %s.",
2017                              mon->name(DESC_THE).c_str()).c_str(),
2018                              GOD_CHEIBRIADOS);
2019             do_slow_monster(*mon, &you);
2020         }
2021     }
2022 }
2023 
_slouch_damage(monster * mon)2024 static int _slouch_damage(monster *mon)
2025 {
2026     // Please change handle_monster_move in mon-act.cc to match.
2027     const int jerk_num = mon->type == MONS_SIXFIRHY ? 8
2028                        : mon->type == MONS_JIANGSHI ? 48
2029                                                     : 1;
2030 
2031     const int jerk_denom = mon->type == MONS_SIXFIRHY ? 24
2032                          : mon->type == MONS_JIANGSHI ? 90
2033                                                       : 1;
2034 
2035     const int player_numer = BASELINE_DELAY * BASELINE_DELAY * BASELINE_DELAY;
2036     return 4 * (mon->speed * BASELINE_DELAY * jerk_num
2037                            / mon->action_energy(EUT_MOVE) / jerk_denom
2038                 - player_numer / player_movement_speed() / player_speed());
2039 }
2040 
_slouchable(coord_def where)2041 static bool _slouchable(coord_def where)
2042 {
2043     monster* mon = monster_at(where);
2044     if (mon == nullptr || mon->is_stationary() || mon->cannot_move()
2045         || mons_is_projectile(mon->type)
2046         || mon->asleep() && !mons_is_confused(*mon))
2047     {
2048         return false;
2049     }
2050 
2051     return _slouch_damage(mon) > 0;
2052 }
2053 
_act_slouchable(const actor * act)2054 static bool _act_slouchable(const actor *act)
2055 {
2056     if (act->is_player())
2057         return false;  // too slow-witted
2058     return _slouchable(act->pos());
2059 }
2060 
_slouch_monsters(coord_def where)2061 static int _slouch_monsters(coord_def where)
2062 {
2063     if (!_slouchable(where))
2064         return 0;
2065 
2066     monster* mon = monster_at(where);
2067     ASSERT(mon);
2068 
2069     // Between 1/2 and 3/2 of _slouch_damage(), but weighted strongly
2070     // towards the middle.
2071     const int dmg = roll_dice(_slouch_damage(mon), 3) / 2;
2072 
2073     mon->hurt(&you, dmg, BEAM_MMISSILE, KILLED_BY_BEAM, "", "", true);
2074     return 1;
2075 }
2076 
cheibriados_slouch()2077 bool cheibriados_slouch()
2078 {
2079     int count = apply_area_visible(_slouchable, you.pos());
2080     if (!count)
2081         if (!yesno("There's no one hasty visible. Invoke Slouch anyway?",
2082                    true, 'n'))
2083         {
2084             canned_msg(MSG_OK);
2085             return false;
2086         }
2087 
2088     targeter_radius hitfunc(&you, LOS_DEFAULT);
2089     if (stop_attack_prompt(hitfunc, "harm", _act_slouchable))
2090         return false;
2091 
2092     mpr("You can feel time thicken for a moment.");
2093     dprf("your speed is %d", player_movement_speed());
2094 
2095     apply_area_visible(_slouch_monsters, you.pos());
2096     return true;
2097 }
2098 
_run_time_step()2099 static void _run_time_step()
2100 {
2101     ASSERT(you.duration[DUR_TIME_STEP] > 0);
2102     do
2103     {
2104         run_environment_effects();
2105         handle_monsters();
2106         manage_clouds();
2107     }
2108     while (--you.duration[DUR_TIME_STEP] > 0);
2109 }
2110 
2111 // A low-duration step from time, allowing monsters to get closer
2112 // to the player safely.
cheibriados_temporal_distortion()2113 void cheibriados_temporal_distortion()
2114 {
2115     const coord_def old_pos = you.pos();
2116 
2117     you.duration[DUR_TIME_STEP] = 3 + random2(3);
2118     you.moveto(coord_def(0, 0));
2119 
2120     _run_time_step();
2121 
2122     you.los_noise_level = 0;
2123     you.los_noise_last_turn = 0;
2124 
2125     if (monster *mon = monster_at(old_pos))
2126     {
2127         mon->props[FAKE_BLINK_KEY].get_bool() = true;
2128         mon->blink();
2129         mon->props.erase(FAKE_BLINK_KEY);
2130         if (monster *stubborn = monster_at(old_pos))
2131             monster_teleport(stubborn, true, true);
2132     }
2133 
2134     you.moveto(old_pos);
2135     you.duration[DUR_TIME_STEP] = 0;
2136 
2137     mpr("You warp the flow of time around you!");
2138 }
2139 
cheibriados_time_step(int pow)2140 void cheibriados_time_step(int pow) // pow is the number of turns to skip
2141 {
2142     const coord_def old_pos = you.pos();
2143 
2144     mpr("You step out of the flow of time.");
2145     flash_view(UA_PLAYER, LIGHTBLUE);
2146     you.duration[DUR_TIME_STEP] = pow;
2147     you.moveto(coord_def(0, 0));
2148 
2149     you.time_taken = 10;
2150     _run_time_step();
2151     // Update corpses, etc. This does also shift monsters, but only by
2152     // a tiny bit.
2153     update_level(pow * 10);
2154 
2155 #ifndef USE_TILE_LOCAL
2156     scaled_delay(1000);
2157 #endif
2158 
2159     if (monster *mon = monster_at(old_pos))
2160     {
2161         mon->props[FAKE_BLINK_KEY].get_bool() = true;
2162         mon->blink();
2163         mon->props.erase(FAKE_BLINK_KEY);
2164         if (monster *stubborn = monster_at(old_pos))
2165             monster_teleport(stubborn, true, true);
2166     }
2167 
2168     you.moveto(old_pos);
2169     you.duration[DUR_TIME_STEP] = 0;
2170 
2171     flash_view(UA_PLAYER, 0);
2172     mpr("You return to the normal time flow.");
2173 }
2174 
2175 struct curse_data
2176 {
2177     string name;
2178     string abbr;
2179     vector<skill_type> boosted;
2180 };
2181 
2182 static map<curse_type, curse_data> _ashenzari_curses =
2183 {
2184     { CURSE_MELEE, {
2185         "Melee Combat", "Melee",
2186         { SK_SHORT_BLADES, SK_LONG_BLADES, SK_AXES, SK_MACES_FLAILS,
2187             SK_POLEARMS, SK_STAVES, SK_UNARMED_COMBAT },
2188     } },
2189     { CURSE_RANGED, {
2190         "Ranged Combat", "Range",
2191         { SK_SLINGS, SK_BOWS, SK_CROSSBOWS, SK_THROWING },
2192     } },
2193     { CURSE_ELEMENTS, {
2194         "Elements", "Elem",
2195         { SK_FIRE_MAGIC, SK_ICE_MAGIC, SK_AIR_MAGIC, SK_EARTH_MAGIC },
2196     } },
2197     { CURSE_ALCHEMY, {
2198         "Alchemy", "Alch",
2199         { SK_POISON_MAGIC, SK_TRANSMUTATIONS },
2200     } },
2201     { CURSE_COMPANIONS, {
2202         "Companions", "Comp",
2203         { SK_SUMMONINGS, SK_NECROMANCY },
2204     } },
2205     { CURSE_BEGUILING, {
2206         "Beguiling", "Bglg",
2207         { SK_CONJURATIONS, SK_HEXES, SK_TRANSLOCATIONS },
2208     } },
2209     { CURSE_SELF, {
2210         "Introspection", "Self",
2211         { SK_FIGHTING, SK_SPELLCASTING },
2212     } },
2213     { CURSE_FORTITUDE, {
2214         "Fortitude", "Fort",
2215         { SK_ARMOUR, SK_SHIELDS },
2216     } },
2217     { CURSE_CUNNING, {
2218         "Cunning", "Cun",
2219         { SK_DODGING, SK_STEALTH },
2220     } },
2221     { CURSE_EVOCATIONS, {
2222         "Evocations", "Evo",
2223         { SK_EVOCATIONS },
2224     } },
2225 };
2226 
_can_use_curse(const curse_data & c)2227 static bool _can_use_curse(const curse_data& c)
2228 {
2229     for (skill_type sk : c.boosted)
2230         if (you.can_currently_train[sk])
2231             return true;
2232 
2233     return false;
2234 }
2235 
curse_name(const CrawlStoreValue & c)2236 string curse_name(const CrawlStoreValue& c)
2237 {
2238     return _ashenzari_curses[static_cast<curse_type>(c.get_int())].name;
2239 }
2240 
curse_abbr(const CrawlStoreValue & curse)2241 string curse_abbr(const CrawlStoreValue& curse)
2242 {
2243     const curse_data& c =
2244         _ashenzari_curses[static_cast<curse_type>(curse.get_int())];
2245 
2246     return c.abbr;
2247 }
2248 
curse_skills(const CrawlStoreValue & curse)2249 const vector<skill_type>& curse_skills(const CrawlStoreValue& curse)
2250 {
2251     const curse_data& c =
2252         _ashenzari_curses[static_cast<curse_type>(curse.get_int())];
2253 
2254     return c.boosted;
2255 }
2256 
ashenzari_curse_knowledge_list()2257 static string ashenzari_curse_knowledge_list()
2258 {
2259     if (!you_worship(GOD_ASHENZARI))
2260         return "";
2261 
2262     const CrawlVector& curses = you.props[CURSE_KNOWLEDGE_KEY].get_vector();
2263 
2264     return lowercase_string(comma_separated_fn(curses.begin(), curses.end(),
2265                               curse_name));
2266 }
2267 
desc_curse_skills(const CrawlStoreValue & curse)2268 string desc_curse_skills(const CrawlStoreValue& curse)
2269 {
2270     const curse_data& c =
2271         _ashenzari_curses[static_cast<curse_type>(curse.get_int())];
2272 
2273     vector<skill_type> trainable;
2274 
2275     for (skill_type sk : c.boosted)
2276         if (you.can_currently_train[sk])
2277             trainable.push_back(sk);
2278 
2279     return c.name + ": "
2280            + comma_separated_fn(trainable.begin(), trainable.end(), skill_name);
2281 }
2282 
2283 /**
2284  * Choose skills to boost accompanying the current curse.
2285  */
_choose_curse_knowledge()2286 static void _choose_curse_knowledge()
2287 {
2288     // This loop choses two available skills without replacement,
2289     // it is a two element version of a reservoir sampling algorithm.
2290     //
2291     // If Ashenzari curses need some fancier weighting this is the
2292     // place to do that weighting.
2293     curse_type first_choice = NUM_CURSES;
2294     curse_type second_choice = NUM_CURSES;
2295     int valid_curses = 0;
2296     for (auto const& curse : _ashenzari_curses)
2297     {
2298         if (_can_use_curse(curse.second))
2299         {
2300             ++valid_curses;
2301             if (valid_curses == 1)
2302                 first_choice = curse.first;
2303             else if (valid_curses == 2)
2304             {
2305                 second_choice = curse.first;
2306                 if (coinflip())
2307                     swap(first_choice, second_choice);
2308             }
2309             else if (one_chance_in(valid_curses))
2310                 first_choice = curse.first;
2311             else if (one_chance_in(valid_curses - 1))
2312                 second_choice = curse.first;
2313         }
2314     }
2315 
2316     you.props.erase(CURSE_KNOWLEDGE_KEY);
2317     CrawlVector &curses = you.props[CURSE_KNOWLEDGE_KEY].get_vector();
2318 
2319     if (first_choice != NUM_CURSES)
2320         curses.push_back(first_choice);
2321     if (second_choice != NUM_CURSES)
2322         curses.push_back(second_choice);
2323 
2324     // It's not an error for this to be empty, curses are still useful for
2325     // piety alone
2326 }
2327 
2328 /**
2329  * Offer a new curse to the player, letting them know their new curse is
2330  * available.
2331  */
ashenzari_offer_new_curse()2332 void ashenzari_offer_new_curse()
2333 {
2334     // No curse at full piety, since shattering resets the curse timer anyway
2335     if (piety_rank() > 5)
2336         return;
2337 
2338     _choose_curse_knowledge();
2339 
2340     you.props[AVAILABLE_CURSE_KEY] = true;
2341     you.props[ASHENZARI_CURSE_PROGRESS_KEY] = 0;
2342     const string curse_names = ashenzari_curse_knowledge_list();
2343     const string offer_string = curse_names.empty() ? "" :
2344                                 (" of " + curse_names);
2345 
2346     mprf(MSGCH_GOD, "Ashenzari invites you to partake of a vision"
2347                     " and a curse%s.", offer_string.c_str());
2348 }
2349 
_do_curse_item(item_def & item)2350 static void _do_curse_item(item_def &item)
2351 {
2352     mprf("Your %s glows black for a moment.", item.name(DESC_PLAIN).c_str());
2353     item.flags |= ISFLAG_CURSED;
2354 
2355     if (you.equip[EQ_WEAPON] == item.link)
2356     {
2357         // Redraw the weapon.
2358         you.wield_change = true;
2359     }
2360 
2361     for (auto & curse : you.props[CURSE_KNOWLEDGE_KEY].get_vector())
2362     {
2363         add_inscription(item,
2364                 curse_abbr(static_cast<curse_type>(curse.get_int())));
2365         item.props[CURSE_KNOWLEDGE_KEY].get_vector().push_back(curse);
2366     }
2367 }
2368 
2369 /**
2370  * Give a prompt to curse an item.
2371  *
2372  * This is the core logic behind Ash's Curse Item ability.
2373  * Player can abort without penalty.
2374  * Player can curse only worn items.
2375  *
2376  * @return       Whether the player cursed anything.
2377  */
ashenzari_curse_item()2378 bool ashenzari_curse_item()
2379 {
2380     const string prompt_msg = make_stringf("Curse which item? (Esc to abort)");
2381     const int item_slot = prompt_invent_item(prompt_msg.c_str(),
2382                                              menu_type::invlist,
2383                                              OSEL_CURSABLE, OPER_ANY,
2384                                              invprompt_flag::escape_only);
2385     if (prompt_failed(item_slot))
2386         return false;
2387 
2388     item_def& item(you.inv[item_slot]);
2389 
2390     if (!item_is_cursable(item))
2391     {
2392         mpr("You can't curse that!");
2393         return false;
2394     }
2395 
2396     _do_curse_item(item);
2397     make_ashenzari_randart(item);
2398     ash_check_bondage();
2399 
2400     you.props.erase(CURSE_KNOWLEDGE_KEY);
2401     you.props.erase(AVAILABLE_CURSE_KEY);
2402 
2403     return true;
2404 }
2405 
2406 /**
2407  * Give a prompt to uncurse (and destroy an item).
2408  *
2409  * Player can abort without penalty.
2410  *
2411  * @return      Whether the player uncursed anything.
2412  */
ashenzari_uncurse_item()2413 bool ashenzari_uncurse_item()
2414 {
2415     int item_slot = prompt_invent_item("Uncurse and destroy which item?",
2416                                        menu_type::invlist,
2417                                        OSEL_CURSED_WORN, OPER_ANY,
2418                                        invprompt_flag::escape_only);
2419     if (prompt_failed(item_slot))
2420         return false;
2421 
2422     item_def& item(you.inv[item_slot]);
2423 
2424     if (is_unrandom_artefact(item, UNRAND_FINGER_AMULET)
2425         && you.equip[EQ_RING_AMULET] != -1)
2426     {
2427         mprf(MSGCH_PROMPT, "You must shatter the curse binding the ring to "
2428                            "the amulet's finger first!");
2429         return false;
2430     }
2431 
2432     if (item_is_melded(item))
2433     {
2434         mprf(MSGCH_PROMPT, "You cannot shatter the curse on %s while it is "
2435                            "melded with your body!",
2436              item.name(DESC_THE).c_str());
2437         return false;
2438     }
2439 
2440     if (!yesno(make_stringf("Really remove and destroy %s?%s",
2441                             item.name(DESC_THE).c_str(),
2442                             you.props.exists(AVAILABLE_CURSE_KEY) ?
2443                                 " Ashenzari will withdraw the offered vision "
2444                                 "and curse!"
2445                                 : "").c_str(),
2446                             false, 'n'))
2447     {
2448         canned_msg(MSG_OK);
2449         return false;
2450     }
2451 
2452     mprf("You shatter the curse binding %s!", item.name(DESC_THE).c_str());
2453     unequip_item(item_equip_slot(you.inv[item_slot]));
2454     ash_check_bondage();
2455 
2456     you.props[ASHENZARI_CURSE_PROGRESS_KEY] = 0;
2457     if (you.props.exists(AVAILABLE_CURSE_KEY))
2458     {
2459         simple_god_message(" withdraws the vision and curse.");
2460         you.props.erase(AVAILABLE_CURSE_KEY);
2461     }
2462 
2463     return true;
2464 }
2465 
can_convert_to_beogh()2466 bool can_convert_to_beogh()
2467 {
2468     if (silenced(you.pos()))
2469         return false;
2470 
2471     for (monster* m : monster_near_iterator(you.pos(), LOS_NO_TRANS))
2472         if (mons_allows_beogh_now(*m))
2473             return true;
2474 
2475     return false;
2476 }
2477 
spare_beogh_convert()2478 void spare_beogh_convert()
2479 {
2480     if (you.one_time_ability_used[GOD_BEOGH])
2481     {
2482         // You still get to convert, but orcs will remain hostile.
2483         mprf(MSGCH_TALK, "%s", getSpeakString("orc_priest_apostate").c_str());
2484         return;
2485     }
2486 
2487     set<mid_t> witnesses;
2488 
2489     you.religion = GOD_NO_GOD;
2490     for (radius_iterator ri(you.pos(), LOS_DEFAULT); ri; ++ri)
2491     {
2492         const monster *mon = monster_at(*ri);
2493         // An invis player converting is ok, for simplicity.
2494         if (!mon || !cell_see_cell(you.pos(), *ri, LOS_DEFAULT))
2495             continue;
2496         if (mon->attitude != ATT_HOSTILE)
2497             continue;
2498         if (mons_genus(mon->type) != MONS_ORC)
2499             continue;
2500         witnesses.insert(mon->mid);
2501 
2502         // Anyone who has seen the priest perform the ceremony will spare you
2503         // as well.
2504         if (mons_allows_beogh(*mon))
2505         {
2506             for (radius_iterator pi(you.pos(), LOS_DEFAULT); pi; ++pi)
2507             {
2508                 const monster *orc = monster_at(*pi);
2509                 if (!orc || !cell_see_cell(*ri, *pi, LOS_DEFAULT))
2510                     continue;
2511                 if (mons_genus(orc->type) != MONS_ORC)
2512                     continue;
2513                 if (mon->attitude != ATT_HOSTILE)
2514                     continue;
2515                 witnesses.insert(orc->mid);
2516             }
2517         }
2518     }
2519 
2520     int witc = 0;
2521     for (auto wit : witnesses)
2522     {
2523         monster *orc = monster_by_mid(wit);
2524         if (!orc || !orc->alive())
2525             continue;
2526 
2527         ++witc;
2528         orc->del_ench(ENCH_CHARM);
2529         mons_pacify(*orc, ATT_GOOD_NEUTRAL, true);
2530     }
2531 
2532     you.religion = GOD_BEOGH;
2533     you.one_time_ability_used.set(GOD_BEOGH);
2534 
2535     if (witc == 1)
2536         mpr("The priest welcomes you and lets you live.");
2537     else
2538     {
2539         mpr("With a roar of approval, the orcs welcome you as one of their own,"
2540             " and spare your life.");
2541     }
2542 }
2543 
dithmenos_shadow_step()2544 bool dithmenos_shadow_step()
2545 {
2546     // You can shadow-step anywhere within your umbra.
2547     ASSERT(you.umbra_radius() > -1);
2548     const int range = you.umbra_radius();
2549 
2550     targeter_shadow_step tgt(&you, you.umbra_radius());
2551     direction_chooser_args args;
2552     args.hitfunc = &tgt;
2553     args.restricts = DIR_SHADOW_STEP;
2554     args.mode = TARG_HOSTILE;
2555     args.range = range;
2556     args.just_looking = false;
2557     args.needs_path = false;
2558     args.top_prompt = "Aiming: <white>Shadow Step</white>";
2559     dist sdirect;
2560     direction(sdirect, args);
2561     if (!sdirect.isValid || tgt.landing_site.origin())
2562         return false;
2563 
2564     // Check for hazards.
2565     bool zot_trap_prompted = false,
2566          trap_prompted = false,
2567          exclusion_prompted = false,
2568          cloud_prompted = false,
2569          terrain_prompted = false;
2570 
2571     for (auto site : tgt.additional_sites)
2572     {
2573         if (!cloud_prompted
2574             && !check_moveto_cloud(site, "shadow step", &cloud_prompted))
2575         {
2576             return false;
2577         }
2578 
2579         if (!zot_trap_prompted)
2580         {
2581             trap_def* trap = trap_at(site);
2582             if (trap && trap->type == TRAP_ZOT)
2583             {
2584                 if (!check_moveto_trap(site, "shadow step",
2585                                        &trap_prompted))
2586                 {
2587                     you.turn_is_over = false;
2588                     return false;
2589                 }
2590                 zot_trap_prompted = true;
2591             }
2592             else if (!trap_prompted
2593                      && !check_moveto_trap(site, "shadow step",
2594                                            &trap_prompted))
2595             {
2596                 you.turn_is_over = false;
2597                 return false;
2598             }
2599         }
2600 
2601         if (!exclusion_prompted
2602             && !check_moveto_exclusion(site, "shadow step",
2603                                        &exclusion_prompted))
2604         {
2605             return false;
2606         }
2607 
2608         if (!terrain_prompted
2609             && !check_moveto_terrain(site, "shadow step", "",
2610                                      &terrain_prompted))
2611         {
2612             return false;
2613         }
2614     }
2615 
2616     const coord_def old_pos = you.pos();
2617     // XXX: This only ever fails if something's on the landing site;
2618     // perhaps this should be handled more gracefully.
2619     if (!you.move_to_pos(tgt.landing_site))
2620     {
2621         mpr("Something blocks your shadow step.");
2622         return true;
2623     }
2624 
2625     const actor *victim = actor_at(sdirect.target);
2626     mprf("You step into %s shadow.",
2627          apostrophise(victim->name(DESC_THE)).c_str());
2628     // Using 'stepped = true' here because it's Shadow *Step*.
2629     // This helps to evade splash upon landing on water.
2630     moveto_location_effects(env.grid(old_pos), true, old_pos);
2631 
2632     return true;
2633 }
2634 
2635 static potion_type _gozag_potion_list[][4] =
2636 {
2637     { POT_HEAL_WOUNDS, NUM_POTIONS, NUM_POTIONS, NUM_POTIONS },
2638     { POT_HEAL_WOUNDS, POT_CURING, NUM_POTIONS, NUM_POTIONS },
2639     { POT_HEAL_WOUNDS, POT_MAGIC, NUM_POTIONS, NUM_POTIONS },
2640     { POT_CURING, POT_MAGIC, NUM_POTIONS, NUM_POTIONS },
2641     { POT_HEAL_WOUNDS, POT_BERSERK_RAGE, NUM_POTIONS, NUM_POTIONS },
2642     { POT_HASTE, NUM_POTIONS, NUM_POTIONS, NUM_POTIONS },
2643     { POT_HASTE, POT_HEAL_WOUNDS, NUM_POTIONS, NUM_POTIONS },
2644     { POT_HASTE, POT_BRILLIANCE, NUM_POTIONS, NUM_POTIONS },
2645     { POT_HASTE, POT_RESISTANCE, NUM_POTIONS, NUM_POTIONS },
2646     { POT_BRILLIANCE, POT_MAGIC, NUM_POTIONS, NUM_POTIONS },
2647     { POT_INVISIBILITY, POT_MIGHT, NUM_POTIONS, NUM_POTIONS },
2648     { POT_HEAL_WOUNDS, POT_CURING, POT_MAGIC, NUM_POTIONS },
2649     { POT_HEAL_WOUNDS, POT_CURING, POT_BERSERK_RAGE, NUM_POTIONS },
2650     { POT_MIGHT, POT_BRILLIANCE, NUM_POTIONS, NUM_POTIONS },
2651     { POT_RESISTANCE, NUM_POTIONS, NUM_POTIONS, NUM_POTIONS },
2652     { POT_RESISTANCE, POT_MIGHT, NUM_POTIONS, NUM_POTIONS },
2653     { POT_RESISTANCE, POT_MIGHT, POT_HASTE, NUM_POTIONS },
2654     { POT_RESISTANCE, POT_INVISIBILITY, NUM_POTIONS, NUM_POTIONS },
2655     { POT_LIGNIFY, POT_MIGHT, POT_RESISTANCE, NUM_POTIONS },
2656 };
2657 
_gozag_add_potions(CrawlVector & vec,potion_type * which)2658 static void _gozag_add_potions(CrawlVector &vec, potion_type *which)
2659 {
2660     for (; *which != NUM_POTIONS; which++)
2661     {
2662         // Check cases where a potion is permanently useless to the player
2663         // species - temporarily useless potions can still be offered.
2664         if (*which == POT_BERSERK_RAGE
2665             && !you.can_go_berserk(true, false, true, nullptr, false))
2666         {
2667             continue;
2668         }
2669         if (*which == POT_HASTE && you.stasis())
2670             continue;
2671         if (*which == POT_MAGIC && you.has_mutation(MUT_HP_CASTING))
2672             continue;
2673         if (*which == POT_LIGNIFY && you.undead_state(false) == US_UNDEAD)
2674             continue;
2675 
2676         // Don't add potions which are already in the list
2677         bool dup = false;
2678         for (unsigned int i = 0; i < vec.size(); i++)
2679             if (vec[i].get_int() == *which)
2680             {
2681                 dup = true;
2682                 break;
2683             }
2684         if (!dup)
2685             vec.push_back(*which);
2686     }
2687 }
2688 
2689 #define ADD_POTIONS(a,b) _gozag_add_potions(a, b[random2(ARRAYSZ(b))])
2690 
gozag_setup_potion_petition(bool quiet)2691 bool gozag_setup_potion_petition(bool quiet)
2692 {
2693     if (you.gold < GOZAG_POTION_PETITION_AMOUNT)
2694     {
2695         if (!quiet)
2696         {
2697             mprf("You need at least %d gold to purchase potions right now!",
2698                  GOZAG_POTION_PETITION_AMOUNT);
2699         }
2700         return false;
2701     }
2702 
2703     return true;
2704 }
2705 
gozag_potion_petition()2706 bool gozag_potion_petition()
2707 {
2708     CrawlVector *pots[GOZAG_MAX_POTIONS];
2709     int prices[GOZAG_MAX_POTIONS];
2710 
2711     item_def dummy;
2712     dummy.base_type = OBJ_POTIONS;
2713     dummy.quantity = 1;
2714 
2715     if (!you.props.exists(make_stringf(GOZAG_POTIONS_KEY, 0)))
2716     {
2717         bool affordable_potions = false;
2718         while (!affordable_potions)
2719         {
2720             for (int i = 0; i < GOZAG_MAX_POTIONS; i++)
2721             {
2722                 prices[i] = 0;
2723                 const int multiplier = random_range(20, 30); // arbitrary
2724 
2725                 string key = make_stringf(GOZAG_POTIONS_KEY, i);
2726                 you.props.erase(key);
2727                 you.props[key].new_vector(SV_INT, SFLAG_CONST_TYPE);
2728                 pots[i] = &you.props[key].get_vector();
2729 
2730                 do
2731                 {
2732                     ADD_POTIONS(*pots[i], _gozag_potion_list);
2733                     if (coinflip())
2734                         ADD_POTIONS(*pots[i], _gozag_potion_list);
2735                 }
2736                 while (pots[i]->empty());
2737 
2738                 for (const CrawlStoreValue& store : *pots[i])
2739                 {
2740                     dummy.sub_type = store.get_int();
2741                     prices[i] += item_value(dummy, true);
2742                     dprf("%d", item_value(dummy, true));
2743                 }
2744                 dprf("pre: %d", prices[i]);
2745                 prices[i] *= multiplier;
2746                 dprf("mid: %d", prices[i]);
2747                 prices[i] /= 10;
2748                 dprf("post: %d", prices[i]);
2749                 key = make_stringf(GOZAG_PRICE_KEY, i);
2750                 you.props[key].get_int() = prices[i];
2751 
2752                 if (prices[i] <= GOZAG_POTION_PETITION_AMOUNT)
2753                     affordable_potions = true;
2754             }
2755         }
2756     }
2757     else
2758     {
2759         for (int i = 0; i < GOZAG_MAX_POTIONS; i++)
2760         {
2761             string key = make_stringf(GOZAG_POTIONS_KEY, i);
2762             pots[i] = &you.props[key].get_vector();
2763             key = make_stringf(GOZAG_PRICE_KEY, i);
2764             prices[i] = you.props[key].get_int();
2765         }
2766     }
2767 
2768     int keyin = 0;
2769 
2770     while (true)
2771     {
2772         if (crawl_state.seen_hups)
2773             return false;
2774 
2775         clear_messages();
2776         for (int i = 0; i < GOZAG_MAX_POTIONS; i++)
2777         {
2778             string line = make_stringf("  [%c] - %d gold - ", i + 'a',
2779                                        prices[i]);
2780             vector<string> pot_names;
2781             for (const CrawlStoreValue& store : *pots[i])
2782                 pot_names.emplace_back(potion_type_name(store.get_int()));
2783             line += comma_separated_line(pot_names.begin(), pot_names.end());
2784             mpr_nojoin(MSGCH_PLAIN, line);
2785         }
2786         mprf(MSGCH_PROMPT, "Purchase which effect?");
2787         keyin = toalower(get_ch()) - 'a';
2788         if (keyin < 0 || keyin > GOZAG_MAX_POTIONS - 1)
2789             continue;
2790 
2791         if (you.gold < prices[keyin])
2792         {
2793             mpr("You don't have enough gold for that!");
2794             more();
2795             continue;
2796         }
2797 
2798         break;
2799     }
2800 
2801     ASSERT(you.gold >= prices[keyin]);
2802     you.del_gold(prices[keyin]);
2803     you.attribute[ATTR_GOZAG_GOLD_USED] += prices[keyin];
2804 
2805     for (auto pot : *pots[keyin])
2806         potionlike_effect(static_cast<potion_type>(pot.get_int()), 40);
2807 
2808     for (int i = 0; i < GOZAG_MAX_POTIONS; i++)
2809     {
2810         string key = make_stringf(GOZAG_POTIONS_KEY, i);
2811         you.props.erase(key);
2812         key = make_stringf(GOZAG_PRICE_KEY, i);
2813         you.props.erase(key);
2814     }
2815 
2816     return true;
2817 }
2818 
2819 /**
2820  * The price to order a merchant from Gozag. Doesn't depend on the shop's
2821  * type or contents. The maximum possible price is used as the minimum amount
2822  * of gold you need to use the ability.
2823  */
gozag_price_for_shop(bool max)2824 int gozag_price_for_shop(bool max)
2825 {
2826     // This value probably needs tweaking.
2827     const int max_base = 800;
2828     const int base = max ? max_base : random_range(max_base/2, max_base);
2829     const int price = base
2830                       * (GOZAG_SHOP_BASE_MULTIPLIER
2831                          + GOZAG_SHOP_MOD_MULTIPLIER
2832                            * you.attribute[ATTR_GOZAG_SHOPS])
2833                       / GOZAG_SHOP_BASE_MULTIPLIER;
2834     return price;
2835 }
2836 
gozag_setup_call_merchant(bool quiet)2837 bool gozag_setup_call_merchant(bool quiet)
2838 {
2839     const int gold_min = gozag_price_for_shop(true);
2840     if (you.gold < gold_min)
2841     {
2842         if (!quiet)
2843         {
2844             mprf("You currently need %d gold to open negotiations with a "
2845                  "merchant.", gold_min);
2846         }
2847         return false;
2848     }
2849     if (!is_connected_branch(level_id::current().branch))
2850     {
2851         if (!quiet)
2852         {
2853             mprf("No merchants are willing to come to this location.");
2854             return false;
2855         }
2856     }
2857     if (env.grid(you.pos()) != DNGN_FLOOR)
2858     {
2859         if (!quiet)
2860         {
2861             mprf("You need to be standing on open floor to call a merchant.");
2862             return false;
2863         }
2864     }
2865 
2866     return true;
2867 }
2868 
2869 /**
2870  * Is the given index within the valid range for gozag shop offers?
2871  */
_gozag_valid_shop_index(int index)2872 static bool _gozag_valid_shop_index(int index)
2873 {
2874     return index >= 0 && index < GOZAG_MAX_SHOPS;
2875 }
2876 
2877 /**
2878  * What is the type of shop that gozag is offering at the given index?
2879  */
_gozag_shop_type(int index)2880 static shop_type _gozag_shop_type(int index)
2881 {
2882     ASSERT(_gozag_valid_shop_index(index));
2883     const int type =
2884         you.props[make_stringf(GOZAG_SHOP_TYPE_KEY, index)].get_int();
2885     return static_cast<shop_type>(type);
2886 }
2887 
2888 /**
2889  * What is the price of calling the shop that gozag is offering at the given
2890  * index?
2891  */
_gozag_shop_price(int index)2892 static int _gozag_shop_price(int index)
2893 {
2894     ASSERT(_gozag_valid_shop_index(index));
2895 
2896     return you.props[make_stringf(GOZAG_SHOP_COST_KEY, index)].get_int();
2897 }
2898 
2899 /**
2900  * Initialize the set of shops currently offered to the player through Call
2901  * Merchant.
2902  *
2903  * @param index       The index of the shop offer to be defined.
2904  * @param valid_shops Vector of acceptable shop types based on the player and
2905  *                    previous choices for this merchant call.
2906 */
_setup_gozag_shop(int index,vector<shop_type> & valid_shops)2907 static void _setup_gozag_shop(int index, vector<shop_type> &valid_shops)
2908 {
2909     ASSERT(!you.props.exists(make_stringf(GOZAG_SHOPKEEPER_NAME_KEY, index)));
2910 
2911     shop_type type = NUM_SHOPS;
2912     int choice = random2(valid_shops.size());
2913     type = valid_shops[choice];
2914     // Don't choose this shop type again for this merchant call.
2915     valid_shops.erase(valid_shops.begin() + choice);
2916     you.props[make_stringf(GOZAG_SHOP_TYPE_KEY, index)].get_int() = type;
2917 
2918     you.props[make_stringf(GOZAG_SHOPKEEPER_NAME_KEY, index)].get_string()
2919                                     = make_name();
2920 
2921     const bool need_suffix = type != SHOP_GENERAL
2922                              && type != SHOP_GENERAL_ANTIQUE
2923                              && type != SHOP_DISTILLERY;
2924     you.props[make_stringf(GOZAG_SHOP_SUFFIX_KEY, index)].get_string()
2925                                     = need_suffix
2926                                       ? random_choose("Shoppe", "Boutique",
2927                                                       "Emporium", "Shop")
2928                                       : "";
2929 
2930     you.props[make_stringf(GOZAG_SHOP_COST_KEY, index)].get_int()
2931         = gozag_price_for_shop();
2932 }
2933 
2934 /**
2935  * Build a string describing the name, price & type of the shop being offered
2936  * at the given index.
2937  *
2938  * @param index     The index of the shop to be described.
2939  * @return          The shop description.
2940  *                  E.g. "[a]   973 gold - Cranius' Magic Scroll Boutique"
2941  */
_describe_gozag_shop(int index)2942 static string _describe_gozag_shop(int index)
2943 {
2944     const int cost = _gozag_shop_price(index);
2945 
2946     const char offer_letter = 'a' + index;
2947     const string shop_name =
2948         apostrophise(you.props[make_stringf(GOZAG_SHOPKEEPER_NAME_KEY,
2949                                             index)].get_string());
2950     const shop_type type = _gozag_shop_type(index);
2951     const string type_name = shop_type_name(type);
2952     const string suffix =
2953         you.props[make_stringf(GOZAG_SHOP_SUFFIX_KEY, index)].get_string();
2954 
2955     return make_stringf("  [%c] %5d gold - %s %s %s",
2956                         offer_letter,
2957                         cost,
2958                         shop_name.c_str(),
2959                         type_name.c_str(),
2960                         suffix.c_str());
2961 }
2962 
2963 /**
2964  * Let the player choose from the currently available merchants to call.
2965  *
2966  * @param   The index of the chosen shop; -1 if none was chosen (due to e.g.
2967  *          a seen_hup).
2968  */
_gozag_choose_shop()2969 static int _gozag_choose_shop()
2970 {
2971     if (crawl_state.seen_hups)
2972         return -1;
2973 
2974     clear_messages();
2975     for (int i = 0; i < GOZAG_MAX_SHOPS; i++)
2976         mpr_nojoin(MSGCH_PLAIN, _describe_gozag_shop(i).c_str());
2977 
2978     mprf(MSGCH_PROMPT, "Fund which merchant?");
2979     const int shop_index = toalower(get_ch()) - 'a';
2980     if (shop_index < 0 || shop_index > GOZAG_MAX_SHOPS - 1)
2981         return _gozag_choose_shop(); // tail recurse
2982 
2983     if (you.gold < _gozag_shop_price(shop_index))
2984     {
2985         mpr("You don't have enough gold to fund that merchant!");
2986         more();
2987         return _gozag_choose_shop(); // tail recurse
2988     }
2989 
2990     return shop_index;
2991 }
2992 
2993 /**
2994  * Make a vault spec for the gozag shop offer at the given index.
2995  */
_gozag_shop_spec(int index)2996 static string _gozag_shop_spec(int index)
2997 {
2998     const shop_type type = _gozag_shop_type(index);
2999     const string name =
3000         you.props[make_stringf(GOZAG_SHOPKEEPER_NAME_KEY, index)];
3001 
3002     string suffix = replace_all(
3003                                 you.props[make_stringf(GOZAG_SHOP_SUFFIX_KEY,
3004                                                        index)]
3005                                 .get_string(), " ", "_");
3006     if (!suffix.empty())
3007         suffix = " suffix:" + suffix;
3008 
3009     return make_stringf("%s shop name:%s%s gozag",
3010                         shoptype_to_str(type),
3011                         replace_all(name, " ", "_").c_str(),
3012                         suffix.c_str());
3013 
3014 }
3015 
3016 /**
3017  * Attempt to call the shop specified at the given index at your position.
3018  *
3019  * @param index     The index of the shop (in gozag props)
3020  */
_gozag_place_shop(int index)3021 static void _gozag_place_shop(int index)
3022 {
3023     ASSERT(env.grid(you.pos()) == DNGN_FLOOR);
3024     keyed_mapspec kmspec;
3025     kmspec.set_feat(_gozag_shop_spec(index), false);
3026 
3027     feature_spec feat = kmspec.get_feat();
3028     if (!feat.shop)
3029         die("Invalid shop spec?");
3030     place_spec_shop(you.pos(), *feat.shop, you.experience_level);
3031 
3032     link_items();
3033     env.markers.add(new map_feature_marker(you.pos(), DNGN_ABANDONED_SHOP));
3034     env.markers.clear_need_activate();
3035 
3036     shop_struct *shop = shop_at(you.pos());
3037     ASSERT(shop);
3038 
3039     const gender_type gender = random_choose(GENDER_FEMALE, GENDER_MALE,
3040                                              GENDER_NEUTRAL);
3041 
3042     mprf(MSGCH_GOD, "%s invites you to visit %s %s%s%s.",
3043                     shop->shop_name.c_str(),
3044                     decline_pronoun(gender, PRONOUN_POSSESSIVE),
3045                     shop_type_name(shop->type).c_str(),
3046                     !shop->shop_suffix_name.empty() ? " " : "",
3047                     shop->shop_suffix_name.c_str());
3048 }
3049 
gozag_call_merchant()3050 bool gozag_call_merchant()
3051 {
3052     // Only offer useful shops.
3053     vector<shop_type> valid_shops;
3054     for (int i = 0; i < NUM_SHOPS; i++)
3055     {
3056         shop_type type = static_cast<shop_type>(i);
3057 #if TAG_MAJOR_VERSION == 34
3058         if (type == SHOP_FOOD || type == SHOP_EVOKABLES)
3059             continue;
3060 #endif
3061         if (type == SHOP_DISTILLERY && you.has_mutation(MUT_NO_DRINK))
3062             continue;
3063 
3064         if (you.has_mutation(MUT_NO_ARMOUR) &&
3065             (type == SHOP_ARMOUR
3066              || type == SHOP_ARMOUR_ANTIQUE))
3067         {
3068             continue;
3069         }
3070         if ((type == SHOP_WEAPON || type == SHOP_WEAPON_ANTIQUE)
3071             && you.has_mutation(MUT_NO_GRASPING))
3072         {
3073             continue;
3074         }
3075         valid_shops.push_back(type);
3076     }
3077 
3078     // Set up some dummy shops.
3079     // Generate some shop inventory and store it as a store spec.
3080     // We still set up the shops in advance in case of hups.
3081     for (int i = 0; i < GOZAG_MAX_SHOPS; i++)
3082         if (!you.props.exists(make_stringf(GOZAG_SHOPKEEPER_NAME_KEY, i)))
3083             _setup_gozag_shop(i, valid_shops);
3084 
3085     const int shop_index = _gozag_choose_shop();
3086     if (shop_index == -1) // hup!
3087         return false;
3088 
3089     ASSERT(shop_index >= 0 && shop_index < GOZAG_MAX_SHOPS);
3090 
3091     const int cost = _gozag_shop_price(shop_index);
3092     ASSERT(you.gold >= cost);
3093 
3094     you.del_gold(cost);
3095     you.attribute[ATTR_GOZAG_GOLD_USED] += cost;
3096 
3097     _gozag_place_shop(shop_index);
3098 
3099     you.attribute[ATTR_GOZAG_SHOPS]++;
3100     you.attribute[ATTR_GOZAG_SHOPS_CURRENT]++;
3101 
3102     for (int j = 0; j < GOZAG_MAX_SHOPS; j++)
3103     {
3104         you.props.erase(make_stringf(GOZAG_SHOPKEEPER_NAME_KEY, j));
3105         you.props.erase(make_stringf(GOZAG_SHOP_TYPE_KEY, j));
3106         you.props.erase(make_stringf(GOZAG_SHOP_SUFFIX_KEY, j));
3107         you.props.erase(make_stringf(GOZAG_SHOP_COST_KEY, j));
3108     }
3109 
3110     return true;
3111 }
3112 
gozag_fixup_branch(branch_type branch)3113 branch_type gozag_fixup_branch(branch_type branch)
3114 {
3115     if (is_hell_subbranch(branch))
3116         return BRANCH_VESTIBULE;
3117 
3118     return branch;
3119 }
3120 
3121 static const map<branch_type, int> branch_bribability_factor =
3122 {
3123     { BRANCH_DUNGEON,     2 },
3124     { BRANCH_ORC,         2 },
3125     { BRANCH_ELF,         3 },
3126     { BRANCH_SNAKE,       3 },
3127     { BRANCH_SHOALS,      3 },
3128     { BRANCH_CRYPT,       3 },
3129     { BRANCH_TOMB,        3 },
3130     { BRANCH_DEPTHS,      4 },
3131     { BRANCH_VAULTS,      4 },
3132     { BRANCH_ZOT,         4 },
3133     { BRANCH_VESTIBULE,   4 },
3134     { BRANCH_PANDEMONIUM, 4 },
3135 };
3136 
3137 // An x-in-8 chance of a monster of the given type being bribed.
3138 // Tougher monsters have a stronger chance of being bribed.
gozag_type_bribable(monster_type type)3139 int gozag_type_bribable(monster_type type)
3140 {
3141     if (!you_worship(GOD_GOZAG))
3142         return 0;
3143 
3144     if (mons_class_intel(type) < I_HUMAN)
3145         return 0;
3146 
3147     // Unique rune guardians can't be bribed, sorry!
3148     if (mons_is_unique(type)
3149         && (mons_genus(type) == MONS_HELL_LORD
3150             || mons_genus(type) == MONS_PANDEMONIUM_LORD
3151             || type == MONS_ANTAEUS))
3152     {
3153         return 0;
3154     }
3155 
3156     const int *factor = map_find(branch_bribability_factor,
3157                                  gozag_fixup_branch(you.where_are_you));
3158     if (!factor)
3159         return 0;
3160 
3161     const int chance = max(mons_class_hit_dice(type) / *factor, 1);
3162     dprf("%s, bribe chance: %d", mons_type_name(type, DESC_PLAIN).c_str(),
3163                                  chance);
3164 
3165     return chance;
3166 }
3167 
gozag_branch_bribable(branch_type branch)3168 bool gozag_branch_bribable(branch_type branch)
3169 {
3170     return map_find(branch_bribability_factor, gozag_fixup_branch(branch));
3171 }
3172 
gozag_deduct_bribe(branch_type br,int amount)3173 void gozag_deduct_bribe(branch_type br, int amount)
3174 {
3175     if (branch_bribe[br] <= 0)
3176         return;
3177 
3178     branch_bribe[br] = max(0, branch_bribe[br] - amount);
3179     if (branch_bribe[br] <= 0)
3180     {
3181         mprf(MSGCH_DURATION, "Your bribe of %s has been exhausted.",
3182              branches[br].longname);
3183         add_daction(DACT_BRIBE_TIMEOUT);
3184     }
3185 }
3186 
gozag_check_bribe_branch(bool quiet)3187 bool gozag_check_bribe_branch(bool quiet)
3188 {
3189     const int bribe_amount = GOZAG_BRIBE_AMOUNT;
3190     if (you.gold < bribe_amount)
3191     {
3192         if (!quiet)
3193             mprf("You need at least %d gold to offer a bribe.", bribe_amount);
3194         return false;
3195     }
3196     branch_type branch = you.where_are_you;
3197     branch_type branch2 = NUM_BRANCHES;
3198     if (feat_is_branch_entrance(env.grid(you.pos())))
3199     {
3200         for (branch_iterator it; it; ++it)
3201             if (it->entry_stairs == env.grid(you.pos())
3202                 && gozag_branch_bribable(it->id))
3203             {
3204                 branch2 = it->id;
3205                 break;
3206             }
3207     }
3208     const string who = make_stringf("the denizens of %s",
3209                                    branches[branch].longname);
3210     const string who2 = branch2 != NUM_BRANCHES
3211                         ? make_stringf("the denizens of %s",
3212                                        branches[branch2].longname)
3213                         : "";
3214     if (!gozag_branch_bribable(branch)
3215         && (branch2 == NUM_BRANCHES
3216             || !gozag_branch_bribable(branch2)))
3217     {
3218         if (!quiet)
3219         {
3220             if (branch2 != NUM_BRANCHES)
3221                 mprf("You can't bribe %s or %s.", who.c_str(), who2.c_str());
3222             else
3223                 mprf("You can't bribe %s.", who.c_str());
3224         }
3225         return false;
3226     }
3227     return true;
3228 }
3229 
gozag_bribe_branch()3230 bool gozag_bribe_branch()
3231 {
3232     const int bribe_amount = GOZAG_BRIBE_AMOUNT;
3233     ASSERT(you.gold >= bribe_amount);
3234     bool prompted = false;
3235     branch_type branch = gozag_fixup_branch(you.where_are_you);
3236     if (feat_is_branch_entrance(env.grid(you.pos())))
3237     {
3238         for (branch_iterator it; it; ++it)
3239             if (it->entry_stairs == env.grid(you.pos())
3240                 && gozag_branch_bribable(it->id))
3241             {
3242                 branch_type stair_branch = gozag_fixup_branch(it->id);
3243                 string prompt =
3244                     make_stringf("Do you want to bribe the denizens of %s?",
3245                                  stair_branch == BRANCH_VESTIBULE ? "the Hells"
3246                                  : branches[stair_branch].longname);
3247                 if (yesno(prompt.c_str(), true, 'n'))
3248                 {
3249                     branch = stair_branch;
3250                     prompted = true;
3251                 }
3252                 // If we're in the Vestibule, standing on a portal to a Hell
3253                 // sub-branch, don't prompt twice to bribe the Hells.
3254                 else if (branch == stair_branch)
3255                 {
3256                     canned_msg(MSG_OK);
3257                     return false;
3258                 }
3259                 break;
3260             }
3261     }
3262     string who = make_stringf("the denizens of %s",
3263                               branches[branch].longname);
3264     if (!gozag_branch_bribable(branch))
3265     {
3266         mprf("You can't bribe %s.", who.c_str());
3267         return false;
3268     }
3269 
3270     string prompt =
3271         make_stringf("Do you want to bribe the denizens of %s?",
3272                      branch == BRANCH_VESTIBULE ? "the Hells" :
3273                      branches[branch].longname);
3274 
3275     if (prompted || yesno(prompt.c_str(), true, 'n'))
3276     {
3277         you.del_gold(bribe_amount);
3278         you.attribute[ATTR_GOZAG_GOLD_USED] += bribe_amount;
3279         branch_bribe[branch] += bribe_amount;
3280         string msg = make_stringf(" spreads your bribe to %s!",
3281                                   branch == BRANCH_VESTIBULE ? "the Hells" :
3282                                   branches[branch].longname);
3283         simple_god_message(msg.c_str());
3284         add_daction(DACT_SET_BRIBES);
3285         return true;
3286     }
3287 
3288     canned_msg(MSG_OK);
3289     return false;
3290 }
3291 
_upheaval_radius(int pow)3292 static int _upheaval_radius(int pow)
3293 {
3294     return pow >= 100 ? 2 : 1;
3295 }
3296 
qazlal_upheaval(coord_def target,bool quiet,bool fail,dist * player_target)3297 spret qazlal_upheaval(coord_def target, bool quiet, bool fail, dist *player_target)
3298 {
3299     int pow = you.skill(SK_INVOCATIONS, 6);
3300     const int max_radius = _upheaval_radius(pow);
3301 
3302     bolt beam;
3303     beam.name        = "****";
3304     beam.source_id   = MID_PLAYER;
3305     beam.source_name = "you";
3306     beam.thrower     = KILL_YOU;
3307     beam.range       = LOS_RADIUS;
3308     beam.damage      = calc_dice(3, 27 + div_rand_round(2 * pow, 5));
3309     beam.hit         = AUTOMATIC_HIT;
3310     beam.glyph       = dchar_glyph(DCHAR_EXPLOSION);
3311     beam.loudness    = 10;
3312 #ifdef USE_TILE
3313     beam.tile_beam = -1;
3314 #endif
3315 
3316     if (target.origin())
3317     {
3318         dist target_local;
3319         if (!player_target)
3320             player_target = &target_local;
3321 
3322         targeter_smite tgt(&you, LOS_RADIUS, 0, max_radius);
3323         direction_chooser_args args;
3324         args.restricts = DIR_TARGET;
3325         args.mode = TARG_HOSTILE;
3326         args.needs_path = false;
3327         args.top_prompt = "Aiming: <white>Upheaval</white>";
3328         args.self = confirm_prompt_type::cancel;
3329         args.hitfunc = &tgt;
3330         if (!spell_direction(*player_target, beam, &args))
3331             return spret::abort;
3332 
3333         if (cell_is_solid(beam.target))
3334         {
3335             mprf("There is %s there.",
3336                  article_a(feat_type_name(env.grid(beam.target))).c_str());
3337             return spret::abort;
3338         }
3339 
3340         bolt tempbeam;
3341         tempbeam.source    = beam.target;
3342         tempbeam.target    = beam.target;
3343         tempbeam.flavour   = BEAM_MISSILE;
3344         tempbeam.ex_size   = max_radius;
3345         tempbeam.hit       = AUTOMATIC_HIT;
3346         tempbeam.damage    = dice_def(AUTOMATIC_HIT, 1);
3347         tempbeam.thrower   = KILL_YOU;
3348         tempbeam.is_tracer = true;
3349         tempbeam.explode(false);
3350         if (tempbeam.beam_cancelled)
3351             return spret::abort;
3352     }
3353     else
3354         beam.target = target;
3355 
3356     fail_check();
3357 
3358     string message = "";
3359 
3360     switch (random2(4))
3361     {
3362         case 0:
3363             beam.name     = "blast of magma";
3364             beam.flavour  = BEAM_LAVA;
3365             beam.colour   = RED;
3366             beam.hit_verb = "engulfs";
3367             message       = "Magma suddenly erupts from the ground!";
3368             break;
3369         case 1:
3370             beam.name    = "blast of ice";
3371             beam.flavour = BEAM_ICE;
3372             beam.colour  = WHITE;
3373             message      = "A blizzard blasts the area with ice!";
3374             break;
3375         case 2:
3376             beam.name    = "cutting wind";
3377             beam.flavour = BEAM_AIR;
3378             beam.colour  = LIGHTGRAY;
3379             message      = "A storm cloud blasts the area with cutting wind!";
3380             break;
3381         case 3:
3382             beam.name    = "blast of rubble";
3383             beam.flavour = BEAM_FRAG;
3384             beam.colour  = BROWN;
3385             message      = "The ground shakes violently, spewing rubble!";
3386             break;
3387         default:
3388             break;
3389     }
3390 
3391     vector<coord_def> affected;
3392     affected.push_back(beam.target);
3393     for (radius_iterator ri(beam.target, max_radius, C_SQUARE, LOS_SOLID, true);
3394          ri; ++ri)
3395     {
3396         if (!in_bounds(*ri) || cell_is_solid(*ri))
3397             continue;
3398 
3399         int chance = pow;
3400 
3401         bool adj = adjacent(beam.target, *ri);
3402         if (!adj && max_radius > 1)
3403             chance -= 100;
3404         if (adj && max_radius > 1 || x_chance_in_y(chance, 100))
3405         {
3406             if (beam.flavour == BEAM_FRAG || !cell_is_solid(*ri))
3407                 affected.push_back(*ri);
3408         }
3409     }
3410     if (!quiet)
3411         shuffle_array(affected);
3412 
3413     // for `quiet` calls (i.e. disaster area), don't delay for individual tiles
3414     // at all -- do the delay per upheaval draw. This will also fully suppress
3415     // the redraw per tile.
3416     beam.draw_delay = quiet ? 0 : 25;
3417     for (coord_def pos : affected)
3418         beam.draw(pos, false);
3419 
3420     if (quiet)
3421     {
3422         // When `quiet`, refresh the view after each complete draw pass.
3423         // why this call dance to refresh? I just copied it from bolt::draw
3424         viewwindow(false);
3425         update_screen();
3426         scaled_delay(50); // add some delay per upheaval draw, otherwise it all
3427                           // goes by too fast.
3428     }
3429     else
3430     {
3431         scaled_delay(200); // This is here to make it easy for the player to
3432                            // see the overall impact of the upheaval
3433         mprf(MSGCH_GOD, "%s", message.c_str());
3434     }
3435 
3436     int wall_count = 0;
3437     beam.animate = false; // already drawn
3438 
3439     for (coord_def pos : affected)
3440     {
3441         beam.source = pos;
3442         beam.target = pos;
3443         beam.fire();
3444 
3445         switch (beam.flavour)
3446         {
3447             case BEAM_LAVA:
3448                 if (env.grid(pos) == DNGN_FLOOR && !actor_at(pos) && coinflip())
3449                 {
3450                     temp_change_terrain(
3451                         pos, DNGN_LAVA,
3452                         random2(you.skill(SK_INVOCATIONS, BASELINE_DELAY)),
3453                         TERRAIN_CHANGE_FLOOD);
3454                 }
3455                 break;
3456             case BEAM_AIR:
3457                 if (!cell_is_solid(pos) && !cloud_at(pos) && coinflip())
3458                 {
3459                     place_cloud(CLOUD_STORM, pos,
3460                                 random2(you.skill_rdiv(SK_INVOCATIONS, 1, 4)),
3461                                 &you);
3462                 }
3463                 break;
3464             case BEAM_FRAG:
3465                 if (((env.grid(pos) == DNGN_ROCK_WALL
3466                      || env.grid(pos) == DNGN_CLEAR_ROCK_WALL
3467                      || env.grid(pos) == DNGN_SLIMY_WALL)
3468                      && x_chance_in_y(pow / 4, 100)
3469                     || feat_is_door(env.grid(pos))
3470                     || env.grid(pos) == DNGN_GRATE))
3471                 {
3472                     noisy(30, pos);
3473                     destroy_wall(pos);
3474                     wall_count++;
3475                 }
3476                 break;
3477             default:
3478                 break;
3479         }
3480     }
3481 
3482     if (wall_count && !quiet)
3483         mpr("Ka-crash!");
3484 
3485     return spret::success;
3486 }
3487 
qazlal_elemental_force(bool fail)3488 spret qazlal_elemental_force(bool fail)
3489 {
3490     static const map<cloud_type, monster_type> elemental_clouds = {
3491         { CLOUD_FIRE,           MONS_FIRE_ELEMENTAL },
3492         { CLOUD_FOREST_FIRE,    MONS_FIRE_ELEMENTAL },
3493         { CLOUD_COLD,           MONS_WATER_ELEMENTAL },
3494         { CLOUD_RAIN,           MONS_WATER_ELEMENTAL },
3495         { CLOUD_DUST,           MONS_EARTH_ELEMENTAL },
3496         { CLOUD_PETRIFY,        MONS_EARTH_ELEMENTAL },
3497         { CLOUD_BLACK_SMOKE,    MONS_AIR_ELEMENTAL },
3498         { CLOUD_GREY_SMOKE,     MONS_AIR_ELEMENTAL },
3499         { CLOUD_BLUE_SMOKE,     MONS_AIR_ELEMENTAL },
3500         { CLOUD_PURPLE_SMOKE,   MONS_AIR_ELEMENTAL },
3501         { CLOUD_STORM,          MONS_AIR_ELEMENTAL },
3502     };
3503 
3504     vector<coord_def> targets;
3505     for (radius_iterator ri(you.pos(), LOS_RADIUS, C_SQUARE, true); ri; ++ri)
3506     {
3507         const cloud_struct* cloud = cloud_at(*ri);
3508         if (!cloud || !elemental_clouds.count(cloud->type))
3509             continue;
3510 
3511         const actor *agent = actor_by_mid(cloud->source);
3512         if (agent && agent->is_player())
3513             targets.push_back(*ri);
3514     }
3515 
3516     if (targets.empty())
3517     {
3518         mpr("You can't see any clouds you can empower.");
3519         return spret::abort;
3520     }
3521 
3522     fail_check();
3523 
3524     shuffle_array(targets);
3525     const int count = max(1, min((int)targets.size(),
3526                                  random2avg(you.skill(SK_INVOCATIONS), 2)));
3527     mgen_data mg;
3528     mg.summon_type = MON_SUMM_AID;
3529     mg.abjuration_duration = 1;
3530     mg.flags |= MG_FORCE_PLACE | MG_AUTOFOE;
3531     mg.summoner = &you;
3532     int placed = 0;
3533     for (unsigned int i = 0; placed < count && i < targets.size(); i++)
3534     {
3535         coord_def pos = targets[i];
3536         ASSERT(cloud_at(pos));
3537         const cloud_struct &cl = *cloud_at(pos);
3538         mg.behaviour = BEH_FRIENDLY;
3539         mg.pos       = pos;
3540         auto mons_type = map_find(elemental_clouds, cl.type);
3541         // it is not impossible that earlier placements caused new clouds not
3542         // in the map.
3543         if (!mons_type)
3544             continue;
3545         mg.cls = *mons_type;
3546         if (!create_monster(mg))
3547             continue;
3548         delete_cloud(pos);
3549         placed++;
3550     }
3551 
3552     if (placed)
3553         mprf(MSGCH_GOD, "Clouds arounds you coalesce and take form!");
3554     else
3555         canned_msg(MSG_NOTHING_HAPPENS); // can this ever happen?
3556 
3557     return spret::success;
3558 }
3559 
qazlal_disaster_area()3560 bool qazlal_disaster_area()
3561 {
3562     bool friendlies = false;
3563     vector<coord_def> targets;
3564     vector<int> weights;
3565     const int pow = you.skill(SK_INVOCATIONS, 6);
3566     const int upheaval_radius = _upheaval_radius(pow);
3567     for (radius_iterator ri(you.pos(), LOS_RADIUS, C_SQUARE, LOS_NO_TRANS, true);
3568          ri; ++ri)
3569     {
3570         if (!in_bounds(*ri) || cell_is_solid(*ri))
3571             continue;
3572 
3573         const monster_info* m = env.map_knowledge(*ri).monsterinfo();
3574         if (m && mons_att_wont_attack(m->attitude)
3575             && !mons_is_projectile(m->type))
3576         {
3577             friendlies = true;
3578         }
3579 
3580         const int range = you.pos().distance_from(*ri);
3581         const int dist = grid_distance(you.pos(), *ri);
3582         if (range <= upheaval_radius)
3583             continue;
3584 
3585         targets.push_back(*ri);
3586         // We weight using the square of grid distance, so monsters fewer tiles
3587         // away are more likely to be hit.
3588         int weight = LOS_RADIUS * LOS_RADIUS + 1 - dist * dist;
3589         if (actor_at(*ri))
3590             weight *= 10;
3591         weights.push_back(weight);
3592     }
3593 
3594     if (targets.empty())
3595     {
3596         mpr("There isn't enough space here!");
3597         return false;
3598     }
3599 
3600     if (friendlies
3601         && !yesno("There are friendlies around; are you sure you want to hurt "
3602                   "them?", true, 'n'))
3603     {
3604         canned_msg(MSG_OK);
3605         return false;
3606     }
3607 
3608     mprf(MSGCH_GOD, "Nature churns violently around you!");
3609 
3610     // TODO: should count get a cap proportional to targets.size()?
3611     int count = max(1, min((int)targets.size(),
3612                             max(you.skill_rdiv(SK_INVOCATIONS, 1, 2),
3613                                 random2avg(you.skill(SK_INVOCATIONS, 2), 2))));
3614 
3615     for (int i = 0; i < count; i++)
3616     {
3617         if (targets.size() == 0)
3618             break;
3619         int which = choose_random_weighted(weights.begin(), weights.end());
3620         // Downweight adjacent potential targets (but don't rule them out
3621         // entirely).
3622         for (unsigned int j = 0; j < targets.size(); j++)
3623             if (adjacent(targets[which], targets[j]))
3624                 weights[j] = max(weights[j] / 2, 1);
3625         qazlal_upheaval(targets[which], true);
3626         targets.erase(targets.begin() + which);
3627         weights.erase(weights.begin() + which);
3628     }
3629     scaled_delay(200);
3630 
3631     return true;
3632 }
3633 
3634 static map<ability_type, const sacrifice_def *> sacrifice_data_map;
3635 
init_sac_index()3636 void init_sac_index()
3637 {
3638     for (unsigned int i = ABIL_FIRST_SACRIFICE; i <= ABIL_FINAL_SACRIFICE; ++i)
3639     {
3640         unsigned int sac_index = i - ABIL_FIRST_SACRIFICE;
3641         sacrifice_data_map[static_cast<ability_type>(i)] = &sac_data[sac_index];
3642     }
3643 }
3644 
_get_sacrifice_def(ability_type sac)3645 static const sacrifice_def &_get_sacrifice_def(ability_type sac)
3646 {
3647     ASSERT_RANGE(sac, ABIL_FIRST_SACRIFICE, ABIL_FINAL_SACRIFICE+1);
3648     return *sacrifice_data_map[sac];
3649 }
3650 
3651 /// A map between sacrifice_def.sacrifice_vector strings & possible mut lists.
3652 /// Abilities that map to a single mutation are not here!
3653 static map<const char*, vector<mutation_type>> sacrifice_vector_map =
3654 {
3655     /// Mutations granted by ABIL_RU_SACRIFICE_HEALTH
3656     { HEALTH_SAC_KEY, {
3657         MUT_FRAIL,
3658         MUT_PHYSICAL_VULNERABILITY,
3659         MUT_SLOW_REFLEXES,
3660     }},
3661     /// Mutations granted by ABIL_RU_SACRIFICE_ESSENCE
3662     { ESSENCE_SAC_KEY, {
3663         MUT_ANTI_WIZARDRY,
3664         MUT_WEAK_WILLED,
3665         MUT_WEAK_WILLED,
3666         MUT_LOW_MAGIC,
3667     }},
3668     /// Mutations granted by ABIL_RU_SACRIFICE_PURITY
3669     { PURITY_SAC_KEY, {
3670         MUT_SCREAM,
3671         MUT_INHIBITED_REGENERATION,
3672         MUT_NO_POTION_HEAL,
3673         MUT_DOPEY,
3674         MUT_CLUMSY,
3675         MUT_WEAK,
3676     }},
3677 };
3678 
3679 /// School-disabling mutations that will be painful for most characters.
3680 static const vector<mutation_type> _major_arcane_sacrifices =
3681 {
3682     MUT_NO_CONJURATION_MAGIC,
3683     MUT_NO_NECROMANCY_MAGIC,
3684     MUT_NO_SUMMONING_MAGIC,
3685     MUT_NO_TRANSLOCATION_MAGIC,
3686 };
3687 
3688 /// School-disabling mutations that are unfortunate for most characters.
3689 static const vector<mutation_type> _moderate_arcane_sacrifices =
3690 {
3691     MUT_NO_TRANSMUTATION_MAGIC,
3692     MUT_NO_HEXES_MAGIC,
3693 };
3694 
3695 /// School-disabling mutations that are mostly easy to deal with.
3696 static const vector<mutation_type> _minor_arcane_sacrifices =
3697 {
3698     MUT_NO_AIR_MAGIC,
3699     MUT_NO_FIRE_MAGIC,
3700     MUT_NO_ICE_MAGIC,
3701     MUT_NO_EARTH_MAGIC,
3702     MUT_NO_POISON_MAGIC,
3703 };
3704 
3705 /// The list of all lists of arcana sacrifice mutations.
3706 static const vector<mutation_type> _arcane_sacrifice_lists[] =
3707 {
3708     _minor_arcane_sacrifices,
3709     _moderate_arcane_sacrifices,
3710     _major_arcane_sacrifices,
3711 };
3712 
3713 // this function is for checks that can be done with the mutation_type alone.
_sac_mut_maybe_valid(mutation_type mut)3714 static bool _sac_mut_maybe_valid(mutation_type mut)
3715 {
3716     // can't give the player this if they're already at max
3717     if (you.get_mutation_level(mut) >= mutation_max_levels(mut))
3718         return false;
3719 
3720     // can't give the player this if they have an innate mut that conflicts
3721     if (mut_check_conflict(mut, true))
3722         return false;
3723 
3724     // Don't offer sacrifices of skills that a player already can't use.
3725     if (!can_sacrifice_skill(mut))
3726         return false;
3727 
3728     // Special case a few weird interactions:
3729 
3730     // Don't offer to sacrifice summoning magic when already hated by all.
3731     if (mut == MUT_NO_SUMMONING_MAGIC
3732         && you.get_mutation_level(MUT_NO_LOVE))
3733     {
3734         return false;
3735     }
3736 
3737     // Vampires can't get inhibited regeneration for some reason related
3738     // to their existing regen silliness.
3739     // Neither can deep dwarf, for obvious reasons.
3740     if (mut == MUT_INHIBITED_REGENERATION
3741         && you.has_mutation(MUT_VAMPIRISM))
3742     {
3743         return false;
3744     }
3745 
3746     // demonspawn can't get frail if they have a robust facet
3747     if (you.species == SP_DEMONSPAWN && mut == MUT_FRAIL
3748         && any_of(begin(you.demonic_traits), end(you.demonic_traits),
3749                   [] (player::demon_trait t)
3750                   { return t.mutation == MUT_ROBUST; }))
3751     {
3752         return false;
3753     }
3754 
3755     // No potion heal doesn't affect mummies since they can't quaff potions
3756     if (mut == MUT_NO_POTION_HEAL && you.has_mutation(MUT_NO_DRINK))
3757         return false;
3758 
3759     return true;
3760 }
3761 
3762 /**
3763  * Choose a random mutation from the given list, only including those that are
3764  * valid choices for a Ru sacrifice. (Not already at the max level, not
3765  * conflicting with an innate mut.)
3766  * N.b. this is *only* used for choosing among the sublists for sac health,
3767  * essence, and purity.
3768  *
3769  * @param muts      The list of possible sacrifice mutations.
3770  * @return          A mutation from the list, or MUT_NON_MUTATION if no valid
3771  *                  result was found.
3772  */
_random_valid_sacrifice(const vector<mutation_type> & muts)3773 static mutation_type _random_valid_sacrifice(const vector<mutation_type> &muts)
3774 {
3775     int valid_sacrifices = 0;
3776     mutation_type chosen_sacrifice = MUT_NON_MUTATION;
3777     for (auto mut : muts)
3778     {
3779         if (!_sac_mut_maybe_valid(mut))
3780             continue;
3781         // The Grunt Algorithm
3782         // (choose a random element from a set of unknown size without building
3783         // an explicit list, by giving each one a chance to be chosen equal to
3784         // the size of the known list so far, but not returning until the whole
3785         // set has been seen.)
3786         // TODO: export this to a function?
3787         ++valid_sacrifices;
3788         if (one_chance_in(valid_sacrifices))
3789             chosen_sacrifice = mut;
3790     }
3791 
3792     return chosen_sacrifice;
3793 }
3794 
3795 /**
3796  * Choose a random valid mutation from the given list & insert it into the
3797  * single-element vector player prop.
3798  *
3799  * @param key           The key of the player prop to insert the mut into.
3800  */
_choose_sacrifice_mutation(const char * key)3801 static void _choose_sacrifice_mutation(const char *key)
3802 {
3803     ASSERT(you.props.exists(key));
3804     CrawlVector &current_sacrifice = you.props[key].get_vector();
3805     ASSERT(current_sacrifice.empty());
3806 
3807     const mutation_type mut
3808         = _random_valid_sacrifice(sacrifice_vector_map[key]);
3809     if (mut != MUT_NON_MUTATION)
3810     {
3811         // XXX: why on earth is this a one-element vector?
3812         current_sacrifice.push_back(static_cast<int>(mut));
3813     }
3814 }
3815 
3816 /**
3817  * Choose a set of three spellschools to sacrifice: one major, one moderate,
3818  * and one minor.
3819  */
_choose_arcana_mutations()3820 static void _choose_arcana_mutations()
3821 {
3822     ASSERT(you.props.exists(ARCANA_SAC_KEY));
3823     CrawlVector &current_arcane_sacrifices
3824         = you.props[ARCANA_SAC_KEY].get_vector();
3825     ASSERT(current_arcane_sacrifices.empty());
3826 
3827     for (const vector<mutation_type> &arcane_sacrifice_list :
3828                                     _arcane_sacrifice_lists)
3829     {
3830         const mutation_type sacrifice =
3831             _random_valid_sacrifice(arcane_sacrifice_list);
3832 
3833         if (sacrifice == MUT_NON_MUTATION)
3834             return;  // don't bother filling out the others, we failed
3835         current_arcane_sacrifices.push_back(sacrifice);
3836     }
3837 
3838     ASSERT(current_arcane_sacrifices.size()
3839            == ARRAYSZ(_arcane_sacrifice_lists));
3840 }
3841 
3842 /**
3843  * Has the player sacrificed any arcana?
3844  */
_player_sacrificed_arcana()3845 static bool _player_sacrificed_arcana()
3846 {
3847     for (const vector<mutation_type> &arcane_sacrifice_list :
3848                                     _arcane_sacrifice_lists)
3849     {
3850         for (mutation_type sacrifice : arcane_sacrifice_list)
3851             if (you.get_mutation_level(sacrifice))
3852                 return true;
3853     }
3854     return false;
3855 }
3856 
3857 /**
3858  * Is the given sacrifice a valid one for Ru to offer to the player right now?
3859  *
3860  * @param sacrifice     The sacrifice in question.
3861  * @return              Whether Ru can offer the player that sacrifice, or
3862  *                      whether something is blocking it (e.g. no sacrificing
3863  *                      armour for races that can't wear any...)
3864  */
_sacrifice_is_possible(sacrifice_def & sacrifice)3865 static bool _sacrifice_is_possible(sacrifice_def &sacrifice)
3866 {
3867     // for sacrifices other than health, essence, and arcana there is a
3868     // deterministic mapping between the sacrifice_def and a mutation_type.
3869     if (sacrifice.mutation != MUT_NON_MUTATION
3870         && !_sac_mut_maybe_valid(sacrifice.mutation))
3871     {
3872         return false;
3873     }
3874 
3875     // For health, essence, and arcana, we still need to choose from the
3876     // sublists in sacrifice_vector_map.
3877     if (sacrifice.sacrifice_vector)
3878     {
3879         const char* key = sacrifice.sacrifice_vector;
3880         // XXX: changing state in this function seems sketchy
3881         if (sacrifice.sacrifice == ABIL_RU_SACRIFICE_ARCANA)
3882             _choose_arcana_mutations();
3883         else
3884             _choose_sacrifice_mutation(sacrifice.sacrifice_vector);
3885 
3886         if (you.props[key].get_vector().empty())
3887             return false;
3888     }
3889 
3890     // finally, sacrifices may have custom validity checks.
3891     if (sacrifice.valid != nullptr && !sacrifice.valid())
3892         return false;
3893 
3894     return true;
3895 }
3896 
3897 /**
3898  * Which sacrifices are valid for Ru to potentially present to the player?
3899  *
3900  * @return      A list of potential sacrifices (e.g. ABIL_RU_SACRIFICE_WORDS).
3901  */
_get_possible_sacrifices()3902 static vector<ability_type> _get_possible_sacrifices()
3903 {
3904     vector<ability_type> possible_sacrifices;
3905 
3906     for (auto sacrifice : sac_data)
3907         if (_sacrifice_is_possible(sacrifice))
3908             possible_sacrifices.push_back(sacrifice.sacrifice);
3909 
3910     return possible_sacrifices;
3911 }
3912 
3913 /**
3914  * What's the name of the spell school corresponding to the given Ru mutation?
3915  *
3916  * @param mutation  The variety of MUT_NO_*_MAGIC in question.
3917  * @return          A long school name ("Summoning", "Translocations", etc.)
3918  */
_arcane_mutation_to_school_name(mutation_type mutation)3919 static const char* _arcane_mutation_to_school_name(mutation_type mutation)
3920 {
3921     // XXX: this does a really silly dance back and forth between school &
3922     // spelltype.
3923     const skill_type sk = arcane_mutation_to_skill(mutation);
3924     const spschool school = skill2spell_type(sk);
3925     return spelltype_long_name(school);
3926 }
3927 
3928 /**
3929  * What's the abbreviation of the spell school corresponding to the given Ru
3930  * mutation?
3931  *
3932  * @param mutation  The variety of MUT_NO_*_MAGIC in question.
3933  * @return          A school abbreviation ("Summ", "Tloc", etc.)
3934  */
_arcane_mutation_to_school_abbr(mutation_type mutation)3935 static const char* _arcane_mutation_to_school_abbr(mutation_type mutation)
3936 {
3937     return skill_abbr(arcane_mutation_to_skill(mutation));
3938 }
3939 
_piety_for_skill(skill_type skill)3940 static int _piety_for_skill(skill_type skill)
3941 {
3942     // Gnolls didn't have a choice about training the skill, so don't give
3943     // them more piety for waiting longer before taking the sacrifice.
3944     if (you.has_mutation(MUT_DISTRIBUTED_TRAINING))
3945         return 0;
3946 
3947     // This should be mostly redundant with other checks, but it's a useful
3948     // sanitizer
3949     if (is_useless_skill(skill))
3950         return 0;
3951 
3952     return skill_exp_needed(you.skills[skill], skill, you.species) / 500;
3953 }
3954 
_piety_for_skill_by_sacrifice(ability_type sacrifice)3955 static int _piety_for_skill_by_sacrifice(ability_type sacrifice)
3956 {
3957     int piety_gain = 0;
3958     const sacrifice_def &sac_def = _get_sacrifice_def(sacrifice);
3959 
3960     piety_gain += _piety_for_skill(sac_def.sacrifice_skill);
3961     if (sacrifice == ABIL_RU_SACRIFICE_HAND)
3962     {
3963         // No one-handed staves for small races.
3964         if (species::size(you.species, PSIZE_TORSO) <= SIZE_SMALL)
3965             piety_gain += _piety_for_skill(SK_STAVES);
3966         // No one-handed bows.
3967         if (!you.has_innate_mutation(MUT_QUADRUMANOUS))
3968             piety_gain += _piety_for_skill(SK_BOWS);
3969     }
3970     return piety_gain;
3971 }
3972 
3973 #define AS_MUT(csv) (static_cast<mutation_type>((csv).get_int()))
3974 
3975 /**
3976  * Adjust piety based on stat ranking. You get less piety if you're looking at
3977  * your lower stats.
3978  *
3979  * @param stat_type input_stat The stat we're checking.
3980  * @param int       multiplier How much piety for each rank position off.
3981  * @return          The piety to add.
3982  */
_get_stat_piety(stat_type input_stat,int multiplier)3983 static int _get_stat_piety(stat_type input_stat, int multiplier)
3984 {
3985     int stat_val = 3; // If this is your highest stat.
3986     if (you.base_stats[STAT_INT] > you.base_stats[input_stat])
3987         stat_val -= 1;
3988     if (you.base_stats[STAT_STR] > you.base_stats[input_stat])
3989         stat_val -= 1;
3990     if (you.base_stats[STAT_DEX] > you.base_stats[input_stat])
3991         stat_val -= 1;
3992     return stat_val * multiplier;
3993 }
3994 
get_sacrifice_piety(ability_type sac,bool include_skill)3995 int get_sacrifice_piety(ability_type sac, bool include_skill)
3996 {
3997     if (sac == ABIL_RU_REJECT_SACRIFICES)
3998         return INT_MAX; // used as the null sacrifice
3999 
4000     const sacrifice_def &sac_def = _get_sacrifice_def(sac);
4001     int piety_gain = sac_def.base_piety;
4002     ability_type sacrifice = sac_def.sacrifice;
4003     mutation_type mut = MUT_NON_MUTATION;
4004     int num_sacrifices = 0;
4005 
4006     // Initialize data
4007     if (sac_def.sacrifice_vector)
4008     {
4009         ASSERT(you.props.exists(sac_def.sacrifice_vector));
4010         CrawlVector &sacrifice_muts =
4011             you.props[sac_def.sacrifice_vector].get_vector();
4012         num_sacrifices = sacrifice_muts.size();
4013         // mut can only meaningfully be set here if we have exactly one.
4014         if (num_sacrifices == 1)
4015             mut = AS_MUT(sacrifice_muts[0]);
4016     }
4017     else
4018         mut = sac_def.mutation;
4019 
4020     // Increase piety each skill point removed.
4021     if (sacrifice == ABIL_RU_SACRIFICE_ARCANA)
4022     {
4023         skill_type arcane_skill;
4024         mutation_type arcane_mut;
4025         CrawlVector &sacrifice_muts =
4026             you.props[sac_def.sacrifice_vector].get_vector();
4027         for (int i = 0; i < num_sacrifices; i++)
4028         {
4029             arcane_mut = AS_MUT(sacrifice_muts[i]);
4030             arcane_skill = arcane_mutation_to_skill(arcane_mut);
4031             piety_gain += _piety_for_skill(arcane_skill);
4032         }
4033     }
4034     else if (sac_def.sacrifice_skill != SK_NONE && include_skill)
4035         piety_gain += _piety_for_skill_by_sacrifice(sac_def.sacrifice);
4036 
4037     switch (sacrifice)
4038     {
4039         case ABIL_RU_SACRIFICE_HEALTH:
4040             if (mut == MUT_FRAIL)
4041                 piety_gain += 20; // -health is pretty much always quite bad.
4042             else if (mut == MUT_PHYSICAL_VULNERABILITY)
4043                 piety_gain += 5; // -AC is a bit worse than -EV
4044             break;
4045         case ABIL_RU_SACRIFICE_ESSENCE:
4046             if (mut == MUT_LOW_MAGIC)
4047             {
4048                 piety_gain += 10 + max(you.skill_rdiv(SK_INVOCATIONS, 1, 2),
4049                                        you.skill_rdiv(SK_SPELLCASTING, 1, 2));
4050             }
4051             else if (mut == MUT_WEAK_WILLED)
4052                 piety_gain += 38;
4053             else
4054                 piety_gain += 2 + _get_stat_piety(STAT_INT, 6)
4055                                 + you.skill_rdiv(SK_SPELLCASTING, 1, 2);
4056             break;
4057         case ABIL_RU_SACRIFICE_PURITY:
4058             if (mut == MUT_WEAK || mut == MUT_DOPEY || mut == MUT_CLUMSY)
4059             {
4060                 const stat_type stat = mut == MUT_WEAK   ? STAT_STR
4061                                      : mut == MUT_CLUMSY ? STAT_DEX
4062                                      : mut == MUT_DOPEY  ? STAT_INT
4063                                                          : NUM_STATS;
4064                 piety_gain += 4 + _get_stat_piety(stat, 4);
4065             }
4066             // the other sacrifices get sharply worse if you already
4067             // have levels of them.
4068             else if (you.get_mutation_level(mut) == 2)
4069                 piety_gain += 28;
4070             else if (you.get_mutation_level(mut) == 1)
4071                 piety_gain += 21;
4072             else
4073                 piety_gain += 14;
4074 
4075             if (mut == MUT_SCREAM)
4076                 piety_gain /= 2; // screaming just isn't that bad.
4077 
4078             break;
4079         case ABIL_RU_SACRIFICE_ARTIFICE:
4080             if (you.get_mutation_level(MUT_NO_LOVE))
4081                 piety_gain -= 10; // You've already lost some value here
4082             break;
4083         case ABIL_RU_SACRIFICE_SKILL:
4084             // give a small bonus if sacrifice skill is taken multiple times
4085             piety_gain += 7 * you.get_mutation_level(mut);
4086             break;
4087         case ABIL_RU_SACRIFICE_NIMBLENESS:
4088             if (you.get_mutation_level(MUT_NO_ARMOUR_SKILL))
4089                 piety_gain += 20;
4090             else if (species_apt(SK_ARMOUR) == UNUSABLE_SKILL)
4091                 piety_gain += 28; // this sacrifice is worse for these races
4092             break;
4093         // words and drink cut off a lot of options if taken together
4094         case ABIL_RU_SACRIFICE_DRINK:
4095             if (you.get_mutation_level(MUT_READ_SAFETY))
4096                 piety_gain += 10;
4097             break;
4098         case ABIL_RU_SACRIFICE_WORDS:
4099             if (you.get_mutation_level(MUT_DRINK_SAFETY))
4100                 piety_gain += 10;
4101             else if (you.get_mutation_level(MUT_NO_DRINK))
4102                 piety_gain += 15; // extra bad for mummies
4103             break;
4104         case ABIL_RU_SACRIFICE_DURABILITY:
4105             if (you.get_mutation_level(MUT_NO_DODGING))
4106                 piety_gain += 20;
4107             break;
4108         case ABIL_RU_SACRIFICE_LOVE:
4109             if (you.get_mutation_level(MUT_NO_SUMMONING_MAGIC)
4110                 && you.get_mutation_level(MUT_NO_ARTIFICE))
4111             {
4112                 // this is virtually useless, aside from zot_tub
4113                 piety_gain = 1;
4114             }
4115             else if (you.get_mutation_level(MUT_NO_SUMMONING_MAGIC)
4116                      || you.get_mutation_level(MUT_NO_ARTIFICE))
4117             {
4118                 piety_gain /= 2;
4119             }
4120             break;
4121         case ABIL_RU_SACRIFICE_EXPERIENCE:
4122             if (you.get_mutation_level(MUT_COWARDICE))
4123                 piety_gain += 12;
4124             // Ds are highly likely to miss at least one mutation. This isn't
4125             // absolutely certain, but it's very likely and they should still
4126             // get a bonus for the risk. Could check the exact mutation
4127             // schedule, but this seems too leaky.
4128             // Dj are guaranteed to lose a spell each time, which is pretty sad too.
4129             if (you.species == SP_DEMONSPAWN || you.species == SP_DJINNI)
4130                 piety_gain += 16;
4131             break;
4132         case ABIL_RU_SACRIFICE_COURAGE:
4133             piety_gain += 12 * you.get_mutation_level(MUT_INEXPERIENCED);
4134             break;
4135 
4136         default:
4137             break;
4138     }
4139 
4140     // Award piety for any mutations removed by adding new innate muts
4141     // These can only be removed positive mutations, so we'll always give piety.
4142     if (sacrifice == ABIL_RU_SACRIFICE_PURITY
4143         || sacrifice == ABIL_RU_SACRIFICE_HEALTH
4144         || sacrifice == ABIL_RU_SACRIFICE_ESSENCE)
4145     {
4146         piety_gain *= 1 + mut_check_conflict(mut);
4147     }
4148 
4149     // Randomize piety gain very slightly to prevent counting.
4150     // We fuzz the piety gain by up to +-10%, or 5 piety, whichever is smaller.
4151     int piety_blur_inc = min(5, piety_gain / 10);
4152     int piety_blur = random2((2 * piety_blur_inc) + 1) - piety_blur_inc;
4153 
4154     return piety_gain + piety_blur;
4155 }
4156 
4157 // Remove the offer of sacrifices after they've been offered for sufficient
4158 // time or it's time to offer something new.
_ru_expire_sacrifices()4159 static void _ru_expire_sacrifices()
4160 {
4161     static const char *sacrifice_keys[] =
4162     {
4163         AVAILABLE_SAC_KEY,
4164         ESSENCE_SAC_KEY,
4165         HEALTH_SAC_KEY,
4166         PURITY_SAC_KEY,
4167         ARCANA_SAC_KEY,
4168     };
4169 
4170     for (auto key : sacrifice_keys)
4171     {
4172         ASSERT(you.props.exists(key));
4173         you.props[key].get_vector().clear();
4174     }
4175 
4176     // Clear out stored sacrfiice values.
4177     for (int i = 0; i < NUM_ABILITIES; ++i)
4178         you.sacrifice_piety[i] = 0;
4179 }
4180 
4181 /**
4182  * Choose a random sacrifice from those in the list, filtering to only those
4183  * with piety values <= the given cap.
4184  *
4185  * @param possible_sacrifices   The list of sacrifices to choose from.
4186  * @param min_piety             The maximum sac piety cost to accept.
4187  * @return                      The ability_type of a valid sacrifice, or
4188  *                              ABIL_RU_REJECT_SACRIFICES if none were found
4189  *                              (should never happen!)
4190  */
_random_cheap_sacrifice(const vector<ability_type> & possible_sacrifices,int piety_cap)4191 static ability_type _random_cheap_sacrifice(
4192         const vector<ability_type> &possible_sacrifices,
4193                                             int piety_cap)
4194 {
4195     // XXX: replace this with random_if when that's merged
4196     ability_type chosen_sacrifice = ABIL_RU_REJECT_SACRIFICES;
4197     int valid_sacrifices = 0;
4198     for (auto sacrifice : possible_sacrifices)
4199     {
4200         if (get_sacrifice_piety(sacrifice) + you.piety > piety_cap)
4201             continue;
4202 
4203         ++valid_sacrifices;
4204         if (one_chance_in(valid_sacrifices))
4205             chosen_sacrifice = sacrifice;
4206     }
4207 
4208     dprf("found %d valid sacrifices; chose %d",
4209          valid_sacrifices, chosen_sacrifice);
4210 
4211     return chosen_sacrifice;
4212 }
4213 
4214 /**
4215  * Choose the cheapest remaining sacrifice. This is used when the cheapest
4216  * remaining sacrifice is over the piety cap and we still need to fill out 3
4217  * options.
4218  *
4219  * @param possible_sacrifices   The list of sacrifices to choose from.
4220  * @return                      The ability_type of the cheapest remaining
4221  *                              sacrifice.
4222  */
_get_cheapest_sacrifice(const vector<ability_type> & possible_sacrifices)4223 static ability_type _get_cheapest_sacrifice(
4224         const vector<ability_type> &possible_sacrifices)
4225 {
4226     // XXX: replace this with random_if when that's merged
4227     ability_type chosen_sacrifice = ABIL_RU_REJECT_SACRIFICES;
4228     int last_piety = 999;
4229     int cheapest_sacrifices = 0;
4230     for (auto sacrifice : possible_sacrifices)
4231     {
4232         int sac_piety = get_sacrifice_piety(sacrifice);
4233         if (sac_piety >= last_piety)
4234             continue;
4235 
4236         ++cheapest_sacrifices;
4237         if (one_chance_in(cheapest_sacrifices))
4238         {
4239             chosen_sacrifice = sacrifice;
4240             last_piety = sac_piety;
4241         }
4242     }
4243 
4244     dprf("found %d cheapest sacrifices; chose %d",
4245          cheapest_sacrifices, chosen_sacrifice);
4246 
4247     return chosen_sacrifice;
4248 }
4249 
4250 /**
4251  * Chooses three distinct sacrifices to offer the player, store them in
4252  * available_sacrifices, and print a message to the player letting them
4253  * know that their new sacrifices are ready.
4254  */
ru_offer_new_sacrifices()4255 void ru_offer_new_sacrifices()
4256 {
4257     _ru_expire_sacrifices();
4258 
4259     vector<ability_type> possible_sacrifices = _get_possible_sacrifices();
4260 
4261     // for now we'll just pick three at random
4262     int num_sacrifices = possible_sacrifices.size();
4263 
4264     const int num_expected_offers = 3;
4265 
4266     // This can't happen outside wizmode, but may as well handle gracefully
4267     if (num_sacrifices < num_expected_offers)
4268         return;
4269 
4270     ASSERT(you.props.exists(AVAILABLE_SAC_KEY));
4271     CrawlVector &available_sacrifices
4272         = you.props[AVAILABLE_SAC_KEY].get_vector();
4273 
4274     for (int sac_num = 0; sac_num < num_expected_offers; ++sac_num)
4275     {
4276         // find the cheapest available sacrifice, in case we're close to ru's
4277         // max piety. (minimize 'wasted' piety in those cases.)
4278         const ability_type min_piety_sacrifice
4279             = accumulate(possible_sacrifices.begin(),
4280                          possible_sacrifices.end(),
4281                          ABIL_RU_REJECT_SACRIFICES,
4282                          [](ability_type a, ability_type b) {
4283                              return get_sacrifice_piety(a)
4284                                   < get_sacrifice_piety(b) ? a : b;
4285                          });
4286         const int min_piety = get_sacrifice_piety(min_piety_sacrifice);
4287         const int piety_cap = max(179, you.piety + min_piety);
4288 
4289         dprf("cheapest sac %d (%d piety); cap %d",
4290              min_piety_sacrifice, min_piety, piety_cap);
4291 
4292         // XXX: replace this with random_if when that's merged
4293         ability_type chosen_sacrifice
4294             = _random_cheap_sacrifice(possible_sacrifices, piety_cap);
4295 
4296         if (chosen_sacrifice < ABIL_FIRST_SACRIFICE ||
4297                 chosen_sacrifice > ABIL_FINAL_SACRIFICE)
4298         {
4299            chosen_sacrifice = _get_cheapest_sacrifice(possible_sacrifices);
4300         }
4301 
4302         if (chosen_sacrifice > ABIL_FINAL_SACRIFICE)
4303         {
4304             // We don't have three sacrifices to offer for some reason.
4305             // Either the player is messing around in wizmode or has rejoined
4306             // Ru repeatedly. In either case, we'll just stop offering
4307             // sacrifices rather than crashing.
4308             _ru_expire_sacrifices();
4309             ru_reset_sacrifice_timer(false);
4310             return;
4311         }
4312 
4313         // add it to the list of chosen sacrifices to offer, and remove it from
4314         // the list of possibilities for the later sacrifices
4315         available_sacrifices.push_back(chosen_sacrifice);
4316         you.sacrifice_piety[chosen_sacrifice] =
4317                                 get_sacrifice_piety(chosen_sacrifice, false);
4318         possible_sacrifices.erase(remove(possible_sacrifices.begin(),
4319                                          possible_sacrifices.end(),
4320                                          chosen_sacrifice),
4321                                   possible_sacrifices.end());
4322     }
4323 
4324     simple_god_message(" believes you are ready to make a new sacrifice.");
4325     // included in default force_more_message
4326 }
4327 
4328 /// What key corresponds to the potential/chosen mut(s) for this sacrifice?
ru_sacrifice_vector(ability_type sac)4329 string ru_sacrifice_vector(ability_type sac)
4330 {
4331     const sacrifice_def &sac_def = _get_sacrifice_def(sac);
4332     return sac_def.sacrifice_vector ? sac_def.sacrifice_vector : "";
4333 }
4334 
_describe_sacrifice_piety_gain(int piety_gain)4335 static const char* _describe_sacrifice_piety_gain(int piety_gain)
4336 {
4337     if (piety_gain >= 40)
4338         return "an incredible";
4339     else if (piety_gain >= 29)
4340         return "a major";
4341     else if (piety_gain >= 21)
4342         return "a significant";
4343     else if (piety_gain >= 13)
4344         return "a modest";
4345     else
4346         return "a trivial";
4347 }
4348 
_piety_asterisks(int piety)4349 static const string _piety_asterisks(int piety)
4350 {
4351     const int prank = piety_rank(piety);
4352     return string(prank, '*') + string(NUM_PIETY_STARS - prank, '.');
4353 }
4354 
_apply_ru_sacrifice(mutation_type sacrifice)4355 static void _apply_ru_sacrifice(mutation_type sacrifice)
4356 {
4357     perma_mutate(sacrifice, 1, "Ru sacrifice");
4358     you.sacrifices[sacrifice] += 1;
4359 }
4360 
_execute_sacrifice(ability_type sac,const char * message)4361 static bool _execute_sacrifice(ability_type sac, const char* message)
4362 {
4363     mprf("Ru asks you to %s.", message);
4364     mpr(ru_sacrifice_description(sac));
4365     if (!yesno("Do you really want to make this sacrifice?",
4366                false, 'n'))
4367     {
4368         canned_msg(MSG_OK);
4369         return false;
4370     }
4371     return true;
4372 }
4373 
_ru_kill_skill(skill_type skill)4374 static void _ru_kill_skill(skill_type skill)
4375 {
4376     change_skill_points(skill, -you.skill_points[skill], true);
4377     you.can_currently_train.set(skill, false);
4378     reset_training();
4379     check_selected_skills();
4380 }
4381 
_extra_sacrifice_code(ability_type sac)4382 static void _extra_sacrifice_code(ability_type sac)
4383 {
4384     const sacrifice_def &sac_def = _get_sacrifice_def(sac);
4385     if (sac_def.sacrifice == ABIL_RU_SACRIFICE_HAND)
4386     {
4387         auto ring_slots = species::ring_slots(you.species, true);
4388         equipment_type sac_ring_slot = species::sacrificial_arm(you.species);
4389 
4390         item_def* const shield = you.slot_item(EQ_SHIELD, true);
4391         item_def* const weapon = you.slot_item(EQ_WEAPON, true);
4392         item_def* const ring = you.slot_item(sac_ring_slot, true);
4393         int ring_inv_slot = you.equip[sac_ring_slot];
4394         equipment_type open_ring_slot = EQ_NONE;
4395 
4396         // Drop your shield if there is one
4397         if (shield != nullptr)
4398         {
4399             mprf("You can no longer hold %s!",
4400                 shield->name(DESC_YOUR).c_str());
4401             unequip_item(EQ_SHIELD);
4402         }
4403 
4404         // And your two-handed weapon
4405         if (weapon != nullptr)
4406         {
4407             if (you.hands_reqd(*weapon) == HANDS_TWO)
4408             {
4409                 mprf("You can no longer hold %s!",
4410                     weapon->name(DESC_YOUR).c_str());
4411                 unequip_item(EQ_WEAPON);
4412             }
4413         }
4414 
4415         // And one ring
4416         if (ring != nullptr)
4417         {
4418             // XX does not handle an open slot on the finger amulet
4419             for (const auto &eq : ring_slots)
4420                 if (!you.slot_item(eq, true))
4421                 {
4422                     open_ring_slot = eq;
4423                     break;
4424                 }
4425 
4426             const bool can_keep = open_ring_slot != EQ_NONE;
4427 
4428             mprf("You can no longer wear %s!",
4429                 ring->name(DESC_YOUR).c_str());
4430             unequip_item(sac_ring_slot, true, can_keep);
4431             if (can_keep)
4432             {
4433                 mprf("You put %s back on %s %s!",
4434                      ring->name(DESC_YOUR).c_str(),
4435                      (ring_slots.size() > 1 ? "another" : "your other"),
4436                      you.hand_name(true).c_str());
4437                 equip_item(open_ring_slot, ring_inv_slot, false, true);
4438             }
4439         }
4440     }
4441     else if (sac_def.sacrifice == ABIL_RU_SACRIFICE_EXPERIENCE)
4442         level_change();
4443     else if (sac_def.sacrifice == ABIL_RU_SACRIFICE_SKILL)
4444     {
4445         uint8_t saved_skills[NUM_SKILLS];
4446         for (skill_type sk = SK_FIRST_SKILL; sk < NUM_SKILLS; ++sk)
4447         {
4448             saved_skills[sk] = you.skills[sk];
4449             check_skill_level_change(sk, false);
4450         }
4451 
4452         // Produce messages about skill increases/decreases. We
4453         // restore one skill level at a time so that at most the
4454         // skill being checked is at the wrong level.
4455         for (skill_type sk = SK_FIRST_SKILL; sk < NUM_SKILLS; ++sk)
4456         {
4457             you.skills[sk] = saved_skills[sk];
4458             check_skill_level_change(sk);
4459         }
4460 
4461         redraw_screen();
4462         update_screen();
4463     }
4464 }
4465 
4466 /**
4467  * Describe variable costs for a given Ru sacrifice being offered.
4468  *
4469  * @param sac       The sacrifice in question.
4470  * @return          Extra costs.
4471  *                  For ABIL_RU_SACRIFICE_ARCANA: e.g. " (Tloc/Air/Fire)"
4472  *                  For other variable muts: e.g. " (frail)"
4473  *                  Otherwise, "".
4474  */
ru_sac_text(ability_type sac)4475 string ru_sac_text(ability_type sac)
4476 {
4477     const sacrifice_def &sac_def = _get_sacrifice_def(sac);
4478     if (!sac_def.sacrifice_vector)
4479         return "";
4480 
4481     ASSERT(you.props.exists(sac_def.sacrifice_vector));
4482     const CrawlVector &sacrifice_muts =
4483         you.props[sac_def.sacrifice_vector].get_vector();
4484 
4485     if (sac != ABIL_RU_SACRIFICE_ARCANA)
4486     {
4487         ASSERT(sacrifice_muts.size() == 1);
4488         const mutation_type mut = AS_MUT(sacrifice_muts[0]);
4489         return make_stringf(" (%s)", mutation_name(mut));
4490     }
4491 
4492     // "Tloc/Fire/Ice"
4493     const string school_names
4494         = comma_separated_fn(sacrifice_muts.begin(), sacrifice_muts.end(),
4495                 [](CrawlStoreValue mut) {
4496                     return _arcane_mutation_to_school_abbr(AS_MUT(mut));
4497                 }, "/", "/");
4498 
4499     return make_stringf(" (%s)", school_names.c_str());
4500 }
4501 
_ru_get_sac_piety_gain(ability_type sac)4502 static int _ru_get_sac_piety_gain(ability_type sac)
4503 {
4504     const sacrifice_def &sac_def = _get_sacrifice_def(sac);
4505 
4506     // If we're haven't yet calculated piety gain, get it now. Otherwise,
4507     // use the calculated value and then add in the skill piety, which isn't
4508     // saved because it can change over time.
4509     const int base_piety_gain = you.sacrifice_piety[sac];
4510 
4511     if (base_piety_gain == 0)
4512         return get_sacrifice_piety(sac);
4513 
4514     if (sac_def.sacrifice_skill != SK_NONE)
4515         return base_piety_gain + _piety_for_skill_by_sacrifice(sac);
4516 
4517     return base_piety_gain;
4518 }
4519 
ru_sacrifice_description(ability_type sac)4520 string ru_sacrifice_description(ability_type sac)
4521 {
4522     if (!you_worship(GOD_RU))
4523         return "";
4524 
4525     const int piety_gain = _ru_get_sac_piety_gain(sac);
4526     return make_stringf("This is %s sacrifice. Piety after sacrifice: %s",
4527                         _describe_sacrifice_piety_gain(piety_gain),
4528                         _piety_asterisks(you.piety + piety_gain).c_str());
4529 }
4530 
4531 
ru_do_sacrifice(ability_type sac)4532 bool ru_do_sacrifice(ability_type sac)
4533 {
4534     const sacrifice_def &sac_def = _get_sacrifice_def(sac);
4535     bool variable_sac;
4536     mutation_type mut = MUT_NON_MUTATION;
4537     int num_sacrifices;
4538     string offer_text;
4539     string mile_text;
4540     string sac_text;
4541     const bool is_sac_arcana = sac == ABIL_RU_SACRIFICE_ARCANA;
4542 
4543     // For variable sacrifices, we need to compose the text that will be
4544     // displayed at the time of sacrifice offer and as a milestone if the
4545     // sacrifice is accepted. We also need to figure out piety.
4546     if (sac_def.sacrifice_vector)
4547     {
4548         variable_sac = true;
4549         ASSERT(you.props.exists(sac_def.sacrifice_vector));
4550         CrawlVector &sacrifice_muts =
4551             you.props[sac_def.sacrifice_vector].get_vector();
4552         num_sacrifices = sacrifice_muts.size();
4553 
4554         for (int i = 0; i < num_sacrifices; i++)
4555         {
4556             mut = AS_MUT(sacrifice_muts[i]);
4557 
4558             // format the text that will be displayed
4559             if (is_sac_arcana)
4560             {
4561                 if (i == num_sacrifices - 1)
4562                 {
4563                     sac_text = make_stringf("%sand %s", sac_text.c_str(),
4564                         _arcane_mutation_to_school_name(mut));
4565                 }
4566                 else
4567                 {
4568                     sac_text = make_stringf("%s%s, ", sac_text.c_str(),
4569                         _arcane_mutation_to_school_name(mut));
4570                 }
4571             }
4572             else
4573                 sac_text = mut_upgrade_summary(mut);
4574         }
4575         offer_text = make_stringf("%s: %s", sac_def.sacrifice_text,
4576             sac_text.c_str());
4577         mile_text = make_stringf("%s: %s.", sac_def.milestone_text,
4578             sac_text.c_str());
4579     }
4580     else
4581     {
4582         variable_sac = false;
4583         mut = sac_def.mutation;
4584         num_sacrifices = 1;
4585         string handtxt = "";
4586         if (sac == ABIL_RU_SACRIFICE_HAND)
4587             handtxt = you.hand_name(true);
4588 
4589         offer_text = sac_def.sacrifice_text + handtxt;
4590         mile_text = make_stringf("%s.", sac_def.milestone_text);
4591     }
4592 
4593     // get confirmation that the sacrifice is desired.
4594     if (!_execute_sacrifice(sac, offer_text.c_str()))
4595         return false;
4596     // save piety gain, since sacrificing skills can lower the piety gain
4597     const int piety_gain = _ru_get_sac_piety_gain(sac);
4598     // Apply the sacrifice, starting by mutating the player.
4599     if (variable_sac)
4600     {
4601         CrawlVector &sacrifice_muts =
4602             you.props[sac_def.sacrifice_vector].get_vector();
4603         for (int i = 0; i < num_sacrifices; i++)
4604         {
4605             mut = AS_MUT(sacrifice_muts[i]);
4606             _apply_ru_sacrifice(mut);
4607         }
4608     }
4609     else
4610         _apply_ru_sacrifice(mut);
4611 
4612     // Remove any no-longer-usable skills.
4613     if (is_sac_arcana)
4614     {
4615         skill_type arcane_skill;
4616         mutation_type arcane_mut;
4617         CrawlVector &sacrifice_muts =
4618             you.props[sac_def.sacrifice_vector].get_vector();
4619         for (int i = 0; i < num_sacrifices; i++)
4620         {
4621             arcane_mut = AS_MUT(sacrifice_muts[i]);
4622             arcane_skill = arcane_mutation_to_skill(arcane_mut);
4623             _ru_kill_skill(arcane_skill);
4624         }
4625     }
4626     else if (sac_def.sacrifice_skill != SK_NONE)
4627         _ru_kill_skill(sac_def.sacrifice_skill);
4628 
4629     // Maybe this should go in _extra_sacrifice_code, but it would be
4630     // inconsistent for the milestone to have reduced Shields skill
4631     // but not the others.
4632     if (sac == ABIL_RU_SACRIFICE_HAND)
4633     {
4634         // No one-handed staves for small races.
4635         if (species::size(you.species, PSIZE_TORSO) <= SIZE_SMALL)
4636             _ru_kill_skill(SK_STAVES);
4637         // No one-handed bows.
4638         if (!you.has_innate_mutation(MUT_QUADRUMANOUS))
4639             _ru_kill_skill(SK_BOWS);
4640     }
4641 
4642     mark_milestone("sacrifice", mile_text);
4643 
4644     // Any special handling that's needed.
4645     _extra_sacrifice_code(sac);
4646 
4647     // Update how many Ru sacrifices you have. This is used to avoid giving the
4648     // player extra silver damage.
4649     if (you.props.exists("num_sacrifice_muts"))
4650     {
4651         you.props["num_sacrifice_muts"] = num_sacrifices +
4652             you.props["num_sacrifice_muts"].get_int();
4653     }
4654     else
4655         you.props["num_sacrifice_muts"] = num_sacrifices;
4656 
4657     // Actually give the piety for this sacrifice.
4658     set_piety(min(piety_breakpoint(5), you.piety + piety_gain));
4659 
4660     if (you.piety == piety_breakpoint(5))
4661         simple_god_message(" indicates that your awakening is complete.");
4662 
4663     // Clean up.
4664     _ru_expire_sacrifices();
4665     ru_reset_sacrifice_timer(true);
4666     redraw_screen(); // pretty much everything could have changed
4667     update_screen();
4668     return true;
4669 }
4670 
4671 /**
4672  * If forced_rejection is false, prompt the player if they want to reject the
4673  * currently offered sacrifices. If true, or if the prompt is accepted,
4674  * remove the currently offered sacrifices & increase the time until the next
4675  * sacrifices will be offered.
4676  *
4677  * @param forced_rejection      Whether the rejection is caused by the removal
4678  *                              of an amulet of faith, in which case there's
4679  *                              no prompt & an increased sac time penalty.
4680  * @return                      Whether the sacrifices were actually rejected.
4681  */
ru_reject_sacrifices(bool forced_rejection)4682 bool ru_reject_sacrifices(bool forced_rejection)
4683 {
4684     if (!forced_rejection &&
4685         !yesno("Do you really want to reject the sacrifices Ru is offering?",
4686                false, 'n'))
4687     {
4688         canned_msg(MSG_OK);
4689         return false;
4690     }
4691 
4692     ru_reset_sacrifice_timer(false, true);
4693     _ru_expire_sacrifices();
4694     simple_god_message(" will take longer to evaluate your readiness.");
4695     return true;
4696 }
4697 
4698 /**
4699  * Reset the time until the next set of Ru sacrifices are offered.
4700  *
4701  * @param clear_timer       Whether to reset the timer to the base time-between-
4702  *                          sacrifices, rather than adding to it.
4703  * @param faith_penalty     Whether this is a penalty for removing "faith.
4704  */
ru_reset_sacrifice_timer(bool clear_timer,bool faith_penalty)4705 void ru_reset_sacrifice_timer(bool clear_timer, bool faith_penalty)
4706 {
4707     ASSERT(you.props.exists(RU_SACRIFICE_PROGRESS_KEY));
4708     ASSERT(you.props.exists(RU_SACRIFICE_DELAY_KEY));
4709     ASSERT(you.props.exists(RU_SACRIFICE_PENALTY_KEY));
4710 
4711     // raise the delay if there's an active sacrifice, and more so the more
4712     // often you pass on a sacrifice and the more piety you have.
4713     const int base_delay = 90;
4714     int delay = you.props[RU_SACRIFICE_DELAY_KEY].get_int();
4715     int added_delay;
4716     if (clear_timer)
4717     {
4718         added_delay = 0;
4719         delay = base_delay;
4720         you.props[RU_SACRIFICE_PENALTY_KEY] = 0;
4721     }
4722     else
4723     {
4724         // if you rejected a sacrifice, add between 33 and 53 to the timer,
4725         // based on piety. This extra delay stacks with any added delay for
4726         // previous rejections.
4727         added_delay = you.props[RU_SACRIFICE_PENALTY_KEY].get_int();
4728         const int new_penalty = (max(100, static_cast<int>(you.piety))) / 3;
4729         added_delay += new_penalty;
4730 
4731         // longer delay for each real rejection
4732         if (!you.props[AVAILABLE_SAC_KEY].get_vector().empty())
4733             you.props[RU_SACRIFICE_PENALTY_KEY] = added_delay;
4734 
4735         if (faith_penalty)
4736         {
4737             // the player will end up waiting longer than they would otherwise,
4738             // but multiple removals of the amulet of faith won't stack -
4739             // they'll just put the player back to around the same delay
4740             // each time.
4741             added_delay += new_penalty * 2;
4742             delay = base_delay;
4743         }
4744     }
4745 
4746     delay = div_rand_round((delay + added_delay) * (3 - you.faith()), 3);
4747     if (crawl_state.game_is_sprint())
4748         delay /= SPRINT_MULTIPLIER;
4749 
4750     you.props[RU_SACRIFICE_DELAY_KEY] = delay;
4751     you.props[RU_SACRIFICE_PROGRESS_KEY] = 0;
4752 }
4753 
4754 // Check to see if you're eligible to retaliate.
4755 //Your chance of eligiblity scales with piety.
will_ru_retaliate()4756 bool will_ru_retaliate()
4757 {
4758     // Scales up to a 25% chance of retribution
4759     return have_passive(passive_t::upgraded_aura_of_power)
4760            && crawl_state.which_god_acting() != GOD_RU
4761            && one_chance_in(div_rand_round(640, you.piety));
4762 }
4763 
4764 // Power of retribution increases with damage, decreases with monster HD.
ru_do_retribution(monster * mons,int damage)4765 void ru_do_retribution(monster* mons, int damage)
4766 {
4767     int power = max(0, random2(div_rand_round(you.piety*10, 32))
4768         + damage - (2 * mons->get_hit_dice()));
4769     const actor* act = &you;
4770 
4771     if (power > 50 && (mons->antimagic_susceptible()))
4772     {
4773         mprf(MSGCH_GOD, "You focus your inner power and drain %s's magic in "
4774                 "retribution!", mons->name(DESC_THE).c_str());
4775         mons->add_ench(mon_enchant(ENCH_ANTIMAGIC, 1, act, power+random2(320)));
4776     }
4777     else if (power > 35)
4778     {
4779         mprf(MSGCH_GOD, "You focus your inner power and paralyse %s in retribution!",
4780                 mons->name(DESC_THE).c_str());
4781         mons->add_ench(mon_enchant(ENCH_PARALYSIS, 1, act, power+random2(60)));
4782     }
4783     else if (power > 25)
4784     {
4785         mprf(MSGCH_GOD, "You focus your inner power and slow %s in retribution!",
4786                 mons->name(DESC_THE).c_str());
4787         mons->add_ench(mon_enchant(ENCH_SLOW, 1, act, power+random2(100)));
4788     }
4789     else if (power > 10 && mons_can_be_blinded(mons->type))
4790     {
4791         mprf(MSGCH_GOD, "You focus your inner power and blind %s in retribution!",
4792                 mons->name(DESC_THE).c_str());
4793         mons->add_ench(mon_enchant(ENCH_BLIND, 1, act, power+random2(100)));
4794     }
4795     else if (power > 0)
4796     {
4797         mprf(MSGCH_GOD, "You focus your inner power and illuminate %s in retribution!",
4798                 mons->name(DESC_THE).c_str());
4799         mons->add_ench(mon_enchant(ENCH_CORONA, 1, act, power+random2(150)));
4800     }
4801 }
4802 
ru_draw_out_power()4803 void ru_draw_out_power()
4804 {
4805     mpr("You are restored by drawing out deep reserves of power within.");
4806 
4807     //Escape nets and webs
4808     int net = get_trapping_net(you.pos());
4809     if (net == NON_ITEM)
4810     {
4811         trap_def *trap = trap_at(you.pos());
4812         if (trap && trap->type == TRAP_WEB)
4813         {
4814             destroy_trap(you.pos());
4815             // XXX: destroying them is dubious in general - abuseable by loons?
4816             // (but definitely destroy if ammo == 1, per trap-def.h!)
4817             mpr("You burst free from the webs!");
4818         }
4819     }
4820     else
4821     {
4822         destroy_item(net);
4823         mpr("You burst free from the net!");
4824     }
4825     stop_being_held();
4826 
4827     // Escape constriction
4828     you.stop_being_constricted(false);
4829 
4830     // cancel petrification/confusion/slow
4831     you.duration[DUR_CONF] = 0;
4832     you.duration[DUR_SLOW] = 0;
4833     you.duration[DUR_PETRIFYING] = 0;
4834 
4835     int hp_inc = div_rand_round(you.piety, 16);
4836     hp_inc += roll_dice(div_rand_round(you.piety, 20), 6);
4837     inc_hp(hp_inc);
4838     int mp_inc = div_rand_round(you.piety, 48);
4839     mp_inc += roll_dice(div_rand_round(you.piety, 40), 4);
4840     inc_mp(mp_inc);
4841     drain_player(30, false, true);
4842 }
4843 
ru_power_leap()4844 bool ru_power_leap()
4845 {
4846     ASSERT(!crawl_state.game_is_arena());
4847 
4848     if (crawl_state.is_replaying_keys())
4849     {
4850         crawl_state.cancel_cmd_all("You can't repeat Power Leap.");
4851         return false;
4852     }
4853     if (you.is_nervous())
4854     {
4855         mpr("You are too terrified to leap around!");
4856         return false;
4857     }
4858 
4859     // query for location:
4860     dist beam;
4861 
4862     while (1)
4863     {
4864         direction_chooser_args args;
4865         args.restricts = DIR_LEAP;
4866         args.mode = TARG_ANY;
4867         args.range = 3;
4868         args.needs_path = false;
4869         args.top_prompt = "Aiming: <white>Power Leap</white>";
4870         args.self = confirm_prompt_type::cancel;
4871         const int explosion_size = 1;
4872         targeter_smite tgt(&you, args.range, explosion_size, explosion_size);
4873         tgt.obeys_mesmerise = true;
4874         args.hitfunc = &tgt;
4875         direction(beam, args);
4876         if (crawl_state.seen_hups)
4877         {
4878             clear_messages();
4879             mpr("Cancelling leap due to HUP.");
4880             return false;
4881         }
4882 
4883         if (!beam.isValid || beam.target == you.pos())
4884             return false;         // early return
4885 
4886         monster* beholder = you.get_beholder(beam.target);
4887         if (beholder)
4888         {
4889             clear_messages();
4890             mprf("You cannot leap away from %s!",
4891                  beholder->name(DESC_THE, true).c_str());
4892             continue;
4893         }
4894 
4895         monster* fearmonger = you.get_fearmonger(beam.target);
4896         if (fearmonger)
4897         {
4898             clear_messages();
4899             mprf("You cannot leap closer to %s!",
4900                  fearmonger->name(DESC_THE, true).c_str());
4901             continue;
4902         }
4903 
4904         monster* mons = monster_at(beam.target);
4905         if (mons && you.can_see(*mons))
4906         {
4907             clear_messages();
4908             mpr("You can't leap on top of the monster!");
4909             continue;
4910         }
4911 
4912         if (env.grid(beam.target) == DNGN_OPEN_SEA)
4913         {
4914             clear_messages();
4915             mpr("You can't leap into the sea!");
4916             continue;
4917         }
4918         else if (env.grid(beam.target) == DNGN_LAVA_SEA)
4919         {
4920             clear_messages();
4921             mpr("You can't leap into the sea of lava!");
4922             continue;
4923         }
4924         else if (!check_moveto(beam.target, "leap", false))
4925         {
4926             // try again (messages handled by check_moveto)
4927         }
4928         else if (you.see_cell_no_trans(beam.target))
4929         {
4930             // Grid in los, no problem.
4931             break;
4932         }
4933         else if (you.trans_wall_blocking(beam.target))
4934         {
4935             clear_messages();
4936             canned_msg(MSG_SOMETHING_IN_WAY);
4937         }
4938         else
4939         {
4940             clear_messages();
4941             canned_msg(MSG_CANNOT_SEE);
4942         }
4943     }
4944 
4945     if (!you.attempt_escape(2)) // returns true if not constricted
4946         return true;
4947 
4948     if (cell_is_solid(beam.target) || monster_at(beam.target))
4949     {
4950         // XXX: try to jump somewhere nearby?
4951         mpr("Something unexpectedly blocked you, preventing you from leaping!");
4952         return true;
4953     }
4954 
4955     move_player_to_grid(beam.target, false);
4956     apply_barbs_damage();
4957 
4958     crawl_state.cancel_cmd_again();
4959     crawl_state.cancel_cmd_repeat();
4960 
4961     bolt wave;
4962     wave.thrower = KILL_YOU;
4963     wave.name = "power leap";
4964     wave.source_name = "you";
4965     wave.source_id = MID_PLAYER;
4966     wave.flavour = BEAM_VISUAL;
4967     wave.colour = BROWN;
4968     wave.glyph = dchar_glyph(DCHAR_EXPLOSION);
4969     wave.range = 1;
4970     wave.ex_size = 1;
4971     wave.is_explosion = true;
4972     wave.source = you.pos();
4973     wave.target = you.pos();
4974     wave.hit = AUTOMATIC_HIT;
4975     wave.loudness = 2;
4976     wave.explode();
4977 
4978     // we need to exempt the player from damage.
4979     for (adjacent_iterator ai(you.pos(), false); ai; ++ai)
4980     {
4981         monster* mon = monster_at(*ai);
4982         if (mon == nullptr || mons_is_projectile(mon->type) || mon->friendly())
4983             continue;
4984         ASSERT(mon);
4985 
4986         //damage scales with XL amd piety
4987         mon->hurt((actor*)&you, roll_dice(1 + div_rand_round(you.piety *
4988             (54 + you.experience_level), 777), 3),
4989             BEAM_ENERGY, KILLED_BY_BEAM, "", "", true);
4990     }
4991 
4992     return true;
4993 }
4994 
cell_has_valid_target(coord_def where)4995 int cell_has_valid_target(coord_def where)
4996 {
4997     monster* mon = monster_at(where);
4998     if (mon == nullptr || mons_is_projectile(mon->type) || mon->friendly())
4999         return 0;
5000     return 1;
5001 }
5002 
_apply_apocalypse(coord_def where)5003 static int _apply_apocalypse(coord_def where)
5004 {
5005     if (!cell_has_valid_target(where))
5006         return 0;
5007     monster* mons = monster_at(where);
5008     ASSERT(mons);
5009 
5010     int duration = 0;
5011     string message = "";
5012     enchant_type enchantment = ENCH_NONE;
5013 
5014     int effect = random2(4);
5015     if (mons_is_firewood(*mons))
5016         effect = 99; // > 2 is just damage -- no slowed toadstools
5017 
5018     int num_dice;
5019     switch (effect)
5020     {
5021         case 0:
5022             if (mons->antimagic_susceptible())
5023             {
5024                 message = " doubts " + mons->pronoun(PRONOUN_POSSESSIVE)
5025                           + " magic when faced with ultimate truth!";
5026                 enchantment = ENCH_ANTIMAGIC;
5027                 duration = 500 + random2(200);
5028                 num_dice = 4;
5029                 break;
5030             } // if not antimagicable, fall through to paralysis.
5031         case 1:
5032             message = " is paralysed by terrible understanding!";
5033             enchantment = ENCH_PARALYSIS;
5034             duration = 80 + random2(60);
5035             num_dice = 4;
5036             break;
5037 
5038         case 2:
5039             message = " slows down under the weight of truth!";
5040             enchantment = ENCH_SLOW;
5041             duration = 300 + random2(100);
5042             num_dice = 6;
5043             break;
5044 
5045         default:
5046             num_dice = 8;
5047             break;
5048     }
5049 
5050     //damage scales with XL and piety
5051     const int pow = you.piety;
5052     int die_size = 1 + div_rand_round(pow * (54 + you.experience_level), 584);
5053     int dmg = 10 + roll_dice(num_dice, die_size);
5054 
5055     mons->hurt(&you, dmg, BEAM_ENERGY, KILLED_BY_BEAM, "", "", true);
5056 
5057     if (mons->alive() && enchantment != ENCH_NONE)
5058     {
5059         simple_monster_message(*mons, message.c_str());
5060         mons->add_ench(mon_enchant(enchantment, 1, &you, duration));
5061     }
5062     return 1;
5063 }
5064 
ru_apocalypse()5065 bool ru_apocalypse()
5066 {
5067     int count = apply_area_visible(cell_has_valid_target, you.pos());
5068     if (!count)
5069     {
5070         if (!yesno("There are no visible enemies. Unleash your apocalypse anyway?",
5071             true, 'n'))
5072         {
5073             return false;
5074         }
5075     }
5076     mpr("You reveal the great annihilating truth to your foes!");
5077     noisy(30, you.pos());
5078     apply_area_visible(_apply_apocalypse, you.pos());
5079     drain_player(100, false, true);
5080     return true;
5081 }
5082 
_mons_stompable(const monster & mons)5083 static bool _mons_stompable(const monster &mons)
5084 {
5085     // Don't hurt your own demonic guardians
5086     return !testbits(mons.flags, MF_DEMONIC_GUARDIAN) || !mons.friendly();
5087 }
5088 
_get_stomped(monster & mons)5089 static bool _get_stomped(monster& mons)
5090 {
5091     if (!_mons_stompable(mons))
5092         return false;
5093 
5094     behaviour_event(&mons, ME_ANNOY, &you);
5095 
5096     // Damage starts at 1/6th of monster current HP, then gets some damage
5097     // scaling off Invo power.
5098     int damage = div_rand_round(mons.hit_points, 6);
5099     int die_size = 2 + div_rand_round(you.skill(SK_INVOCATIONS), 2);
5100     damage += roll_dice(2, die_size);
5101 
5102     mons.hurt(&you, damage, BEAM_ENERGY, KILLED_BY_BEAM, "", "", true);
5103 
5104     if (mons.alive() && you.can_see(mons))
5105         print_wounds(mons);
5106 
5107     return true;
5108 }
5109 
uskayaw_stomp()5110 bool uskayaw_stomp()
5111 {
5112     // Demonic guardians are immune but check for other friendlies
5113     const bool friendlies = apply_monsters_around_square([] (monster& mons) {
5114         return _mons_stompable(mons) && mons_att_wont_attack(mons.attitude);
5115     }, you.pos());
5116 
5117     // XXX: this 'friendlies' wording feels a little odd, but we do use it in a
5118     // a few places already; see spl-vortex.cc, disaster area, etc.
5119     if (friendlies
5120         && !yesno("There are friendlies around, "
5121                   "are you sure you want to hurt them?",
5122                   true, 'n'))
5123     {
5124         canned_msg(MSG_OK);
5125         return false;
5126     }
5127 
5128     mpr("You stomp with the beat, sending a shockwave through the revellers "
5129             "around you!");
5130     apply_monsters_around_square(_get_stomped, you.pos());
5131     return true;
5132 }
5133 
uskayaw_line_pass()5134 bool uskayaw_line_pass()
5135 {
5136     ASSERT(!crawl_state.game_is_arena());
5137 
5138     if (crawl_state.is_replaying_keys())
5139     {
5140         crawl_state.cancel_cmd_all("You can't repeat Line Pass.");
5141         return false;
5142     }
5143 
5144     // query for location:
5145     int range = 8;
5146     int invo_skill = you.skill(SK_INVOCATIONS);
5147     int pow = (25 + invo_skill + random2(invo_skill));
5148     dist beam;
5149     bolt line_pass;
5150     line_pass.thrower = KILL_YOU;
5151     line_pass.name = "line pass";
5152     line_pass.source_name = "you";
5153     line_pass.source_id = MID_PLAYER;
5154     line_pass.flavour = BEAM_IRRESISTIBLE_CONFUSION;
5155     line_pass.source = you.pos();
5156     line_pass.hit = AUTOMATIC_HIT;
5157     line_pass.range = range;
5158     line_pass.ench_power = pow;
5159     line_pass.pierce = true;
5160 
5161     while (1)
5162     {
5163         unique_ptr<targeter> hitfunc;
5164         hitfunc = make_unique<targeter_monster_sequence>(&you, pow, range);
5165 
5166         direction_chooser_args args;
5167         args.hitfunc = hitfunc.get();
5168         args.restricts = DIR_LEAP;
5169         args.mode = TARG_ANY;
5170         args.needs_path = false;
5171         args.top_prompt = "Aiming: <white>Line Pass</white>";
5172         args.range = 8;
5173 
5174         if (!spell_direction(beam, line_pass, &args))
5175             return false;
5176 
5177         if (crawl_state.seen_hups)
5178         {
5179             clear_messages();
5180             mpr("Cancelling line pass due to HUP.");
5181             return false;
5182         }
5183 
5184         if (!beam.isValid || beam.target == you.pos())
5185             return false;         // early return
5186 
5187         monster* beholder = you.get_beholder(beam.target);
5188         if (beholder)
5189         {
5190             clear_messages();
5191             mprf("You cannot move away from %s!",
5192                  beholder->name(DESC_THE, true).c_str());
5193             continue;
5194         }
5195 
5196         monster* fearmonger = you.get_fearmonger(beam.target);
5197         if (fearmonger)
5198         {
5199             clear_messages();
5200             mprf("You cannot move closer to %s!",
5201                  fearmonger->name(DESC_THE, true).c_str());
5202             continue;
5203         }
5204 
5205         monster* mons = monster_at(beam.target);
5206         if (mons && you.can_see(*mons))
5207         {
5208             clear_messages();
5209             mpr("You can't stand on top of the monster!");
5210             continue;
5211         }
5212 
5213         if (env.grid(beam.target) == DNGN_OPEN_SEA)
5214         {
5215             clear_messages();
5216             mpr("You can't line pass into the sea!");
5217             continue;
5218         }
5219         else if (env.grid(beam.target) == DNGN_LAVA_SEA)
5220         {
5221             clear_messages();
5222             mpr("You can't line pass into the sea of lava!");
5223             continue;
5224         }
5225         else if (cell_is_solid(beam.target))
5226         {
5227             clear_messages();
5228             mpr("You can't walk through walls!");
5229             continue;
5230         }
5231         else if (!check_moveto(beam.target, "line pass", false))
5232         {
5233             // try again (messages handled by check_moveto)
5234         }
5235         else if (you.see_cell_no_trans(beam.target))
5236         {
5237             // Grid in los, no problem.
5238             break;
5239         }
5240         else if (you.trans_wall_blocking(beam.target))
5241         {
5242             clear_messages();
5243             canned_msg(MSG_SOMETHING_IN_WAY);
5244         }
5245         else
5246         {
5247             clear_messages();
5248             canned_msg(MSG_CANNOT_SEE);
5249         }
5250     }
5251 
5252     if (monster_at(beam.target))
5253         mpr("Something unexpectedly blocked you, preventing you from passing!");
5254     else
5255     {
5256         line_pass.fire();
5257         you.stop_being_constricted(false);
5258         move_player_to_grid(beam.target, false);
5259         apply_barbs_damage();
5260     }
5261 
5262     crawl_state.cancel_cmd_again();
5263     crawl_state.cancel_cmd_repeat();
5264 
5265     return true;
5266 }
5267 
uskayaw_grand_finale(bool fail)5268 spret uskayaw_grand_finale(bool fail)
5269 {
5270     ASSERT(!crawl_state.game_is_arena());
5271 
5272     if (crawl_state.is_replaying_keys())
5273     {
5274         crawl_state.cancel_cmd_all("No encores!");
5275         return spret::abort;
5276     }
5277 
5278     // query for location:
5279     dist beam;
5280 
5281     monster* mons;
5282 
5283     while (1)
5284     {
5285         direction_chooser_args args;
5286         args.mode = TARG_HOSTILE;
5287         args.needs_path = false;
5288         args.top_prompt = "Aiming: <white>Grand Finale</white>";
5289         args.self = confirm_prompt_type::cancel;
5290         targeter_smite tgt(&you);
5291         args.hitfunc = &tgt;
5292         direction(beam, args);
5293         if (crawl_state.seen_hups)
5294         {
5295             clear_messages();
5296             mpr("Cancelling grand finale due to HUP.");
5297             return spret::abort;
5298         }
5299 
5300         if (!beam.isValid || beam.target == you.pos())
5301             return spret::abort;   // early return
5302 
5303         mons = monster_at(beam.target);
5304         if (!mons || !you.can_see(*mons))
5305         {
5306             clear_messages();
5307             mpr("You can't perceive a target there!");
5308             continue;
5309         }
5310 
5311         if (!check_moveto(beam.target, "move", false))
5312         {
5313             // try again (messages handled by check_moveto)
5314         }
5315         else if (you.see_cell_no_trans(beam.target))
5316         {
5317             // Grid in los, no problem.
5318             break;
5319         }
5320         else if (you.trans_wall_blocking(beam.target))
5321         {
5322             clear_messages();
5323             canned_msg(MSG_SOMETHING_IN_WAY);
5324         }
5325         else
5326         {
5327             clear_messages();
5328             canned_msg(MSG_CANNOT_SEE);
5329         }
5330     }
5331 
5332     fail_check();
5333 
5334     ASSERT(mons);
5335 
5336     // kill the target
5337     if (mons->type == MONS_ROYAL_JELLY && !mons->is_summoned())
5338     {
5339         // need to do this here, because react_to_damage is never called
5340         mprf("%s explodes violently into a cloud of jellies!",
5341                                         mons->name(DESC_THE, false).c_str());
5342         trj_spawn_fineff::schedule(&you, mons, mons->pos(), mons->hit_points);
5343     }
5344     else
5345         mprf("%s explodes violently!", mons->name(DESC_THE, false).c_str());
5346     mons->flags |= MF_EXPLODE_KILL;
5347     if (!mons->is_insubstantial())
5348     {
5349         blood_spray(mons->pos(), mons->mons_species(), mons->hit_points / 5);
5350         throw_monster_bits(*mons); // have some fun while we're at it
5351     }
5352 
5353     // throw_monster_bits can cause mons to be killed already, e.g. via pain
5354     // bond or dismissing summons
5355     if (mons->alive())
5356         monster_die(*mons, KILL_YOU, NON_MONSTER, false);
5357 
5358     if (!mons->alive())
5359         move_player_to_grid(beam.target, false);
5360     else
5361         mpr("You spring back to your original position.");
5362 
5363     crawl_state.cancel_cmd_again();
5364     crawl_state.cancel_cmd_repeat();
5365 
5366     set_piety(piety_breakpoint(0)); // Reset piety to 1*.
5367 
5368     return spret::success;
5369 }
5370 
5371 /**
5372  * Permanently choose a class for the player's companion,
5373  * after prompting to make sure the player is certain.
5374  *
5375  * @param ancestor_choice     The ancestor's class; should be an ability enum.
5376  * @return                  Whether the player went through with the choice.
5377  */
hepliaklqana_choose_ancestor_type(int ancestor_choice)5378 bool hepliaklqana_choose_ancestor_type(int ancestor_choice)
5379 {
5380     if (hepliaklqana_ancestor()
5381         && companion_is_elsewhere(hepliaklqana_ancestor()))
5382     {
5383         // ugly hack to avoid dealing with upgrading offlevel ancestors
5384         mpr("You can't make this choice while your ancestor is elsewhere.");
5385         return false;
5386     }
5387 
5388     static const map<int, monster_type> ancestor_types = {
5389         { ABIL_HEPLIAKLQANA_TYPE_KNIGHT, MONS_ANCESTOR_KNIGHT },
5390         { ABIL_HEPLIAKLQANA_TYPE_BATTLEMAGE, MONS_ANCESTOR_BATTLEMAGE },
5391         { ABIL_HEPLIAKLQANA_TYPE_HEXER, MONS_ANCESTOR_HEXER },
5392     };
5393 
5394     auto ancestor_mapped = map_find(ancestor_types, ancestor_choice);
5395     ASSERT(ancestor_mapped);
5396     const auto ancestor_type = *ancestor_mapped;
5397     const string ancestor_type_name = mons_type_name(ancestor_type, DESC_A);
5398 
5399     if (!yesno(make_stringf("Are you sure you want to remember your ancestor "
5400                             "as %s?", ancestor_type_name.c_str()).c_str(),
5401                false, 'n'))
5402     {
5403         canned_msg(MSG_OK);
5404         return false;
5405     }
5406 
5407     you.props[HEPLIAKLQANA_ALLY_TYPE_KEY] = ancestor_type;
5408 
5409     if (monster* ancestor = hepliaklqana_ancestor_mon())
5410     {
5411         ancestor->type = ancestor_type;
5412         give_weapon(ancestor, -1);
5413         ASSERT(ancestor->weapon());
5414         give_shield(ancestor);
5415         set_ancestor_spells(*ancestor);
5416     }
5417 
5418     god_speaks(you.religion, "It is so.");
5419     take_note(Note(NOTE_ANCESTOR_TYPE, 0, 0, ancestor_type_name));
5420     const string mile_text
5421         = make_stringf("remembered their ancestor %s as %s.",
5422                        hepliaklqana_ally_name().c_str(),
5423                        ancestor_type_name.c_str());
5424     mark_milestone("ancestor.class", mile_text);
5425 
5426     return true;
5427 }
5428 
5429 
5430 /**
5431  * Heal the player's ancestor, and apply the Idealised buff for a few turns.
5432  *
5433  * @param fail      Whether the effect should fail after checking validity.
5434  * @return          Whether the healing succeeded, failed, or was aborted.
5435  */
hepliaklqana_idealise(bool fail)5436 spret hepliaklqana_idealise(bool fail)
5437 {
5438     const mid_t ancestor_mid = hepliaklqana_ancestor();
5439     if (ancestor_mid == MID_NOBODY)
5440     {
5441         mpr("You have no ancestor to preserve!");
5442         return spret::abort;
5443     }
5444 
5445     monster *ancestor = monster_by_mid(ancestor_mid);
5446     if (!ancestor || !you.can_see(*ancestor))
5447     {
5448         mprf("%s is not nearby!", hepliaklqana_ally_name().c_str());
5449         return spret::abort;
5450     }
5451 
5452     fail_check();
5453 
5454     simple_god_message(make_stringf(" grants %s healing and protection!",
5455                                     ancestor->name(DESC_YOUR).c_str()).c_str());
5456 
5457     // 1/3 mhp healed at 0 skill, full at 27 invo
5458     const int healing = ancestor->max_hit_points
5459                          * (9 + you.skill(SK_INVOCATIONS)) / 36;
5460 
5461     if (ancestor->heal(healing))
5462     {
5463         if (ancestor->hit_points == ancestor->max_hit_points)
5464             simple_monster_message(*ancestor, " is fully restored!");
5465         else
5466             simple_monster_message(*ancestor, " is healed somewhat.");
5467     }
5468 
5469     const int dur = random_range(50, 80)
5470                     + random2avg(you.skill(SK_INVOCATIONS, 20), 2);
5471     ancestor->add_ench({ ENCH_IDEALISED, 1, &you, dur});
5472     return spret::success;
5473 }
5474 
5475 /**
5476  * Prompt to allow the player to choose a target for the Transference ability.
5477  *
5478  * @return  The chosen target, or the origin if none was chosen.
5479  */
_get_transference_target()5480 static coord_def _get_transference_target()
5481 {
5482     dist spd;
5483 
5484     const int aoe_radius = have_passive(passive_t::transfer_drain) ? 1 : 0;
5485     targeter_transference tgt(&you, aoe_radius);
5486     direction_chooser_args args;
5487     args.hitfunc = &tgt;
5488     args.restricts = DIR_TARGET;
5489     args.mode = TARG_MOBILE_MONSTER;
5490     args.range = LOS_RADIUS;
5491     args.needs_path = false;
5492     args.self = confirm_prompt_type::none;
5493     args.show_floor_desc = true;
5494     args.top_prompt = "Select a target.";
5495 
5496     direction(spd, args);
5497 
5498     if (!spd.isValid)
5499         return coord_def();
5500     return spd.target;
5501 }
5502 
5503 /// Drain any monsters near the destination of Tranference.
_transfer_drain_nearby(coord_def destination)5504 static void _transfer_drain_nearby(coord_def destination)
5505 {
5506     for (adjacent_iterator it(destination); it; ++it)
5507     {
5508         monster* mon = monster_at(*it);
5509         if (!mon || god_protects(mon))
5510             continue;
5511 
5512         const int dur = random_range(60, 150);
5513         // 1-2 at 0 skill, 2-6 at 27 skill.
5514         const int degree
5515             = random_range(1 + you.skill_rdiv(SK_INVOCATIONS, 1, 27),
5516                            2 + you.skill_rdiv(SK_INVOCATIONS, 4, 27));
5517         if (mon->add_ench(mon_enchant(ENCH_DRAINED, degree, &you, dur)))
5518             simple_monster_message(*mon, " is drained by nostalgia.");
5519     }
5520 }
5521 
5522 /**
5523  * Activate Hepliaklqana's Transference ability, swapping the player's
5524  * ancestor with a targeted creature & potentially slowing monsters adjacent
5525  * to the target.
5526  *
5527  * @param fail      Whether the effect should fail after checking validity.
5528  * @return          Whether the ability succeeded, failed, or was aborted.
5529  */
hepliaklqana_transference(bool fail)5530 spret hepliaklqana_transference(bool fail)
5531 {
5532     monster *ancestor = hepliaklqana_ancestor_mon();
5533     if (!ancestor || !you.can_see(*ancestor))
5534     {
5535         mprf("%s is not nearby!", hepliaklqana_ally_name().c_str());
5536         return spret::abort;
5537     }
5538 
5539     coord_def target = _get_transference_target();
5540     if (target.origin())
5541     {
5542         canned_msg(MSG_OK);
5543         return spret::abort;
5544     }
5545 
5546     actor* victim = actor_at(target);
5547     const bool victim_visible = victim && you.can_see(*victim);
5548     if ((!victim || !victim_visible)
5549         && !yesno("You can't see anything there. Try transferring anyway?",
5550                   true, 'n'))
5551     {
5552         canned_msg(MSG_OK);
5553         return spret::abort;
5554     }
5555 
5556     if (victim == ancestor)
5557     {
5558         mprf("You can't transfer your ancestor with %s.",
5559              ancestor->pronoun(PRONOUN_REFLEXIVE).c_str());
5560         return spret::abort;
5561     }
5562 
5563     const bool victim_immovable
5564         = victim && (mons_is_tentacle_or_tentacle_segment(victim->type)
5565                      || victim->is_stationary());
5566     if (victim_visible && victim_immovable)
5567     {
5568         mpr("You can't transfer that.");
5569         return spret::abort;
5570     }
5571 
5572     const coord_def destination = ancestor->pos();
5573     if (victim == &you && !check_moveto(destination, "transfer", false))
5574         return spret::abort;
5575 
5576     const bool uninhabitable = victim && !victim->is_habitable(destination);
5577     if (uninhabitable && victim_visible)
5578     {
5579         mprf("%s can't be transferred there.", victim->name(DESC_THE).c_str());
5580         return spret::abort;
5581     }
5582 
5583     // we assume the ancestor flies & so can survive anywhere anything can.
5584 
5585     fail_check();
5586 
5587     if (!victim || uninhabitable || victim_immovable)
5588     {
5589         canned_msg(MSG_NOTHING_HAPPENS);
5590         return spret::success;
5591     }
5592 
5593     if (victim->is_player())
5594     {
5595         if (cancel_harmful_move(false))
5596             return spret::abort;
5597         ancestor->move_to_pos(target, true, true);
5598         victim->move_to_pos(destination, true, true);
5599     }
5600     else
5601         ancestor->swap_with(victim->as_monster());
5602 
5603     mprf("%s swap%s with %s!",
5604          victim->name(DESC_THE).c_str(),
5605          victim->is_player() ? "" : "s",
5606          ancestor->name(DESC_YOUR).c_str());
5607 
5608     check_place_cloud(CLOUD_MIST, target, random_range(10,20), ancestor);
5609     check_place_cloud(CLOUD_MIST, destination, random_range(10,20), ancestor);
5610 
5611     if (victim->is_monster())
5612         mons_relocated(victim->as_monster());
5613 
5614     ancestor->apply_location_effects(destination);
5615     victim->apply_location_effects(target);
5616     if (victim->is_monster())
5617         behaviour_event(victim->as_monster(), ME_DISTURB, &you, target);
5618 
5619     if (have_passive(passive_t::transfer_drain))
5620         _transfer_drain_nearby(target);
5621 
5622     return spret::success;
5623 }
5624 
5625 /// Prompt to rename your ancestor.
_hepliaklqana_choose_name()5626 static void _hepliaklqana_choose_name()
5627 {
5628     const string old_name = hepliaklqana_ally_name();
5629     string prompt  = make_stringf("Remember %s name as what? ",
5630                                   apostrophise(old_name).c_str());
5631 
5632     char buf[18];
5633     int ret = msgwin_get_line(prompt, buf, sizeof buf, nullptr, old_name);
5634     if (ret)
5635     {
5636         canned_msg(MSG_OK);
5637         return;
5638     }
5639 
5640     // strip whitespace & colour tags
5641     const string new_name
5642         = trimmed_string(formatted_string::parse_string(buf).tostring());
5643     if (old_name == new_name || !new_name.size())
5644     {
5645         canned_msg(MSG_OK);
5646         return;
5647     }
5648 
5649     you.props[HEPLIAKLQANA_ALLY_NAME_KEY] = new_name;
5650     mprf("Yes, %s is definitely a better name.", new_name.c_str());
5651     upgrade_hepliaklqana_ancestor(true);
5652 }
5653 
_hepliaklqana_choose_gender()5654 static void _hepliaklqana_choose_gender()
5655 {
5656     static const map<gender_type, string> gender_map =
5657     {
5658         { GENDER_NEUTRAL, "neither" },
5659         { GENDER_MALE,    "male"    },
5660         { GENDER_FEMALE,  "female"  },
5661     };
5662 
5663     const gender_type current_gender =
5664         (gender_type)you.props[HEPLIAKLQANA_ALLY_GENDER_KEY].get_int();
5665     const string* desc = map_find(gender_map, current_gender);
5666     ASSERT(desc);
5667 
5668     mprf(MSGCH_PROMPT,
5669          "Was %s a) male, b) female, or c) neither? (Currently %s.)",
5670          hepliaklqana_ally_name().c_str(),
5671          desc->c_str());
5672 
5673     int keyin = toalower(get_ch());
5674     if (!isaalpha(keyin))
5675     {
5676         canned_msg(MSG_OK);
5677         return;
5678     }
5679 
5680     static const gender_type gender_options[] = { GENDER_MALE,
5681                                                   GENDER_FEMALE,
5682                                                   GENDER_NEUTRAL };
5683 
5684     const uint32_t choice = keyin - 'a';
5685     if (choice >= ARRAYSZ(gender_options))
5686     {
5687         canned_msg(MSG_OK);
5688         return;
5689     }
5690 
5691     const gender_type new_gender = gender_options[choice];
5692 
5693     if (new_gender == current_gender)
5694     {
5695         canned_msg(MSG_OK);
5696         return;
5697     }
5698 
5699     you.props[HEPLIAKLQANA_ALLY_GENDER_KEY] = new_gender;
5700     mprf("%s was always %s, you're pretty sure.",
5701          hepliaklqana_ally_name().c_str(),
5702          map_find(gender_map, new_gender)->c_str());
5703     upgrade_hepliaklqana_ancestor(true);
5704 }
5705 
5706 /// Rename and/or re-gender your ancestor.
hepliaklqana_choose_identity()5707 void hepliaklqana_choose_identity()
5708 {
5709     _hepliaklqana_choose_name();
5710     _hepliaklqana_choose_gender();
5711 }
5712 
wu_jian_can_wall_jump_in_principle(const coord_def & target)5713 bool wu_jian_can_wall_jump_in_principle(const coord_def& target)
5714 {
5715     if (!have_passive(passive_t::wu_jian_wall_jump)
5716         || !feat_can_wall_jump_against(env.grid(target))
5717         || you.is_stationary()
5718         || you.digging)
5719     {
5720         return false;
5721     }
5722     return true;
5723 }
5724 
wu_jian_can_wall_jump(const coord_def & target,string & error_ret)5725 bool wu_jian_can_wall_jump(const coord_def& target, string &error_ret)
5726 {
5727     if (target.distance_from(you.pos()) != 1)
5728     {
5729         error_ret = "Please select an adjacent position to wall jump against.";
5730         return false;
5731     }
5732 
5733     if (!wu_jian_can_wall_jump_in_principle(target))
5734     {
5735         if (!feat_can_wall_jump_against(env.grid(target)))
5736         {
5737             error_ret = string("You cannot wall jump against ") +
5738                 feature_description_at(target, false, DESC_THE) + ".";
5739         }
5740         else
5741             error_ret = "";
5742         return false;
5743     }
5744 
5745     auto wall_jump_direction = (you.pos() - target).sgn();
5746     auto wall_jump_landing_spot = (you.pos() + wall_jump_direction
5747                                    + wall_jump_direction);
5748 
5749     monster* beholder = you.get_beholder(wall_jump_landing_spot);
5750     if (beholder)
5751     {
5752         error_ret = make_stringf("You cannot wall jump away from %s!",
5753              beholder->name(DESC_THE, true).c_str());
5754         return false;
5755     }
5756 
5757     monster* fearmonger = you.get_fearmonger(wall_jump_landing_spot);
5758     if (fearmonger)
5759     {
5760         error_ret = make_stringf("You cannot wall jump closer to %s!",
5761              fearmonger->name(DESC_THE, true).c_str());
5762         return false;
5763     }
5764 
5765     const actor* landing_actor = actor_at(wall_jump_landing_spot);
5766     if (feat_is_solid(env.grid(you.pos() + wall_jump_direction))
5767         || !in_bounds(wall_jump_landing_spot)
5768         || !you.is_habitable(wall_jump_landing_spot)
5769         || landing_actor)
5770     {
5771         if (landing_actor)
5772         {
5773             error_ret = make_stringf(
5774                 "You have no room to wall jump there; %s is in the way.",
5775                 landing_actor->observable()
5776                             ? landing_actor->name(DESC_THE).c_str()
5777                             : "something you can't see");
5778         }
5779         else
5780             error_ret = "You have no room to wall jump there.";
5781         return false;
5782     }
5783     error_ret = "";
5784     return true;
5785 }
5786 
5787 /**
5788  * Do a walljump.
5789  *
5790  * This doesn't check whether there's space; see `wu_jian_can_wall_jump`.
5791  * It does check whether the landing spot is safe, excluded, etc.
5792  *
5793  * @param targ the movement target (i.e. the wall being moved against).
5794  * @return whether the jump culminated.
5795  */
wu_jian_do_wall_jump(coord_def targ)5796 bool wu_jian_do_wall_jump(coord_def targ)
5797 {
5798     // whether there's space in the first place is checked earlier
5799     // in wu_jian_can_wall_jump.
5800     auto wall_jump_direction = (you.pos() - targ).sgn();
5801     auto wall_jump_landing_spot = (you.pos() + wall_jump_direction
5802                                    + wall_jump_direction);
5803     if (!check_moveto(wall_jump_landing_spot, "wall jump"))
5804     {
5805         you.turn_is_over = false;
5806         return false;
5807     }
5808 
5809     auto initial_position = you.pos();
5810     move_player_to_grid(wall_jump_landing_spot, false);
5811     wu_jian_wall_jump_effects();
5812     remove_water_hold();
5813 
5814     int wall_jump_modifier = (you.attribute[ATTR_SERPENTS_LASH] != 1) ? 2
5815                                                                       : 1;
5816 
5817     you.time_taken = player_speed() * wall_jump_modifier
5818                      * player_movement_speed();
5819     you.time_taken = div_rand_round(you.time_taken, 10);
5820 
5821     // need to set this here in case serpent's lash isn't active
5822     you.turn_is_over = true;
5823     request_autopickup();
5824     wu_jian_post_move_effects(true, initial_position);
5825 
5826     return true;
5827 }
5828 
wu_jian_wall_jump_ability()5829 spret wu_jian_wall_jump_ability()
5830 {
5831     ASSERT(!crawl_state.game_is_arena());
5832 
5833     if (crawl_state.is_replaying_keys())
5834     {
5835         crawl_state.cancel_cmd_all("You can't repeat a wall jump.");
5836         return spret::abort;
5837     }
5838 
5839     if (you.digging)
5840     {
5841         you.digging = false;
5842         mpr("You retract your mandibles.");
5843     }
5844 
5845     string wj_error;
5846     bool has_targets = false;
5847 
5848     for (adjacent_iterator ai(you.pos()); ai; ++ai)
5849         if (wu_jian_can_wall_jump(*ai, wj_error))
5850         {
5851             has_targets = true;
5852             break;
5853         }
5854 
5855     if (!has_targets)
5856     {
5857         mpr("There is nothing to wall jump against here.");
5858         return spret::abort;
5859     }
5860 
5861     if (you.is_nervous())
5862     {
5863         mpr("You are too terrified to wall jump!");
5864         return spret::abort;
5865     }
5866 
5867     if (you.attribute[ATTR_HELD])
5868     {
5869         mprf("You cannot wall jump while caught in a %s.",
5870              get_trapping_net(you.pos()) == NON_ITEM ? "web" : "net");
5871         return spret::abort;
5872     }
5873 
5874     if (!you.attempt_escape())
5875         return spret::fail;
5876 
5877     // query for location:
5878     dist beam;
5879 
5880     while (1)
5881     {
5882         direction_chooser_args args;
5883         args.restricts = DIR_TARGET;
5884         args.mode = TARG_ANY;
5885         args.range = 1;
5886         args.needs_path = false; // TODO: overridden by hitfunc?
5887         args.top_prompt = "Aiming: <white>Wall Jump</white>";
5888         args.self = confirm_prompt_type::cancel;
5889         targeter_walljump tgt;
5890         tgt.obeys_mesmerise = true;
5891         args.hitfunc = &tgt;
5892         {
5893             // TODO: make this unnecessary
5894             direction_chooser dc(beam, args);
5895             dc.needs_path = false;
5896             dc.choose_direction();
5897         }
5898         if (crawl_state.seen_hups)
5899         {
5900             clear_messages();
5901             mpr("Cancelling wall jump due to HUP.");
5902             return spret::abort;
5903         }
5904 
5905         if (!beam.isValid || beam.target == you.pos())
5906             return spret::abort; // early return
5907 
5908         if (wu_jian_can_wall_jump(beam.target, wj_error))
5909             break;
5910     }
5911 
5912     if (!wu_jian_do_wall_jump(beam.target))
5913         return spret::abort;
5914 
5915     crawl_state.cancel_cmd_again();
5916     crawl_state.cancel_cmd_repeat();
5917 
5918     apply_barbs_damage();
5919     return spret::success;
5920 }
5921 
wu_jian_heavenly_storm()5922 void wu_jian_heavenly_storm()
5923 {
5924     mprf(MSGCH_GOD, "The air is filled with shimmering golden clouds!");
5925     wu_jian_sifu_message(" says: The storm will not cease as long as you "
5926                          "keep fighting, disciple!");
5927 
5928     for (radius_iterator ai(you.pos(), 2, C_SQUARE, LOS_SOLID); ai; ++ai)
5929         if (!cell_is_solid(*ai))
5930             place_cloud(CLOUD_GOLD_DUST, *ai, 5 + random2(5), &you);
5931 
5932     you.set_duration(DUR_HEAVENLY_STORM, random_range(2, 3));
5933     you.props[WU_JIAN_HEAVENLY_STORM_KEY] = WU_JIAN_HEAVENLY_STORM_INITIAL;
5934     invalidate_agrid(true);
5935 }
5936 
okawaru_remove_heroism()5937 void okawaru_remove_heroism()
5938 {
5939     mprf(MSGCH_DURATION, "You feel like a meek peon again.");
5940     you.duration[DUR_HEROISM] = 0;
5941     you.redraw_evasion      = true;
5942     you.redraw_armour_class = true;
5943 }
5944 
okawaru_remove_finesse()5945 void okawaru_remove_finesse()
5946 {
5947     mprf(MSGCH_DURATION, "%s", you.hands_act("slow", "down.").c_str());
5948     you.duration[DUR_FINESSE] = 0;
5949 }
5950