1 /**
2  * @file
3  * @brief Summoning spells and other effects creating monsters.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "spl-summoning.h"
9 
10 #include <algorithm>
11 #include <cmath>
12 
13 #include "act-iter.h"
14 #include "areas.h"
15 #include "artefact.h"
16 #include "cloud.h"
17 #include "colour.h"
18 #include "coordit.h"
19 #include "corpse.h"
20 #include "database.h"
21 #include "delay.h"
22 #include "directn.h"
23 #include "dungeon.h"
24 #include "english.h"
25 #include "env.h"
26 #include "fight.h"
27 #include "fprop.h"
28 #include "god-conduct.h"
29 #include "god-item.h"
30 #include "invent.h"
31 #include "item-prop.h"
32 #include "item-status-flag-type.h"
33 #include "items.h"
34 #include "libutil.h"
35 #include "mapmark.h"
36 #include "message.h"
37 #include "mgen-data.h"
38 #include "mon-abil.h"
39 #include "mon-act.h"
40 #include "mon-behv.h"
41 #include "mon-book.h" // MON_SPELL_WIZARD
42 #include "mon-cast.h"
43 #include "mon-death.h"
44 #include "mon-movetarget.h"
45 #include "mon-place.h"
46 #include "mon-speak.h"
47 #include "player-equip.h"
48 #include "player-stats.h"
49 #include "prompt.h"
50 #include "religion.h"
51 #include "shout.h"
52 #include "spl-util.h"
53 #include "spl-wpnench.h"
54 #include "spl-zap.h"
55 #include "state.h"
56 #include "stepdown.h"
57 #include "stringutil.h"
58 #include "target.h"
59 #include "teleport.h"
60 #include "terrain.h"
61 #include "timed-effects.h"
62 #include "unwind.h"
63 #include "viewchar.h"
64 #include "xom.h"
65 
_monster_greeting(monster * mons,const string & key)66 static void _monster_greeting(monster *mons, const string &key)
67 {
68     string msg = getSpeakString(key);
69     if (msg == "__NONE")
70         msg.clear();
71     mons_speaks_msg(mons, msg, MSGCH_TALK, silenced(mons->pos()));
72 }
73 
_summon_data(const actor & caster,monster_type mtyp,int dur,god_type god,spell_type spell)74 static mgen_data _summon_data(const actor &caster, monster_type mtyp,
75                               int dur, god_type god, spell_type spell)
76 {
77     return mgen_data(mtyp, BEH_COPY, caster.pos(),
78                      caster.is_player() ? int{MHITYOU}
79                                         : caster.as_monster()->foe,
80                      MG_AUTOFOE)
81                      .set_summoned(&caster, dur, spell, god);
82 }
83 
_pal_data(monster_type pal,int dur,god_type god,spell_type spell)84 static mgen_data _pal_data(monster_type pal, int dur, god_type god,
85                            spell_type spell)
86 {
87     return _summon_data(you, pal, dur, god, spell);
88 }
89 
cast_summon_small_mammal(int pow,god_type god,bool fail)90 spret cast_summon_small_mammal(int pow, god_type god, bool fail)
91 {
92     if (rude_stop_summoning_prompt())
93         return spret::abort;
94 
95     fail_check();
96 
97     monster_type mon = MONS_PROGRAM_BUG;
98 
99     if (x_chance_in_y(10, pow + 1))
100         mon = random_choose(MONS_BAT, MONS_RAT);
101     else
102         mon = MONS_QUOKKA;
103 
104     if (!create_monster(_pal_data(mon, 3, god, SPELL_SUMMON_SMALL_MAMMAL)))
105         canned_msg(MSG_NOTHING_HAPPENS);
106 
107     return spret::success;
108 }
109 
cast_call_canine_familiar(int pow,god_type god,bool fail)110 spret cast_call_canine_familiar(int pow, god_type god, bool fail)
111 {
112     if (rude_stop_summoning_prompt())
113         return spret::abort;
114 
115     fail_check();
116     monster_type mon = MONS_PROGRAM_BUG;
117 
118     const int chance = pow + random_range(-10, 10);
119 
120     if (chance > 59)
121         mon = MONS_WARG;
122     else if (chance > 39)
123         mon = MONS_WOLF;
124     else
125         mon = MONS_HOUND;
126 
127     const int dur = min(2 + (random2(pow) / 4), 6);
128 
129     if (!create_monster(_pal_data(mon, dur, god, SPELL_CALL_CANINE_FAMILIAR)))
130         canned_msg(MSG_NOTHING_HAPPENS);
131 
132     return spret::success;
133 }
134 
cast_summon_armour_spirit(int pow,god_type god,bool fail)135 spret cast_summon_armour_spirit(int pow, god_type god, bool fail)
136 {
137     if (rude_stop_summoning_prompt())
138         return spret::abort;
139 
140     const item_def *armour = you.slot_item(EQ_BODY_ARMOUR);
141     if (armour == nullptr)
142     {
143         // I don't think we can ever reach this line, but let's be safe.
144         mpr("You aren't wearing any armour!");
145         return spret::abort;
146     }
147 
148     int mitm_slot = get_mitm_slot(10);
149     if (mitm_slot == NON_ITEM)
150     {
151         canned_msg(MSG_NOTHING_HAPPENS);
152         return spret::abort;
153     }
154 
155     fail_check();
156 
157     mgen_data mg = _pal_data(MONS_ANIMATED_ARMOUR, 2, god,
158                              SPELL_ANIMATE_ARMOUR);
159     mg.hd = 15 + div_rand_round(pow, 10);
160     monster* spirit = create_monster(mg);
161     if (!spirit)
162     {
163         canned_msg(MSG_NOTHING_HAPPENS);
164         return spret::success;
165     }
166 
167     item_def &fake_armour = env.item[mitm_slot];
168     fake_armour.clear();
169     fake_armour.base_type = OBJ_ARMOUR;
170     fake_armour.sub_type = armour->sub_type;
171     fake_armour.quantity = 1;
172     fake_armour.rnd = armour->rnd ? armour->rnd : 1; // unrands have no rnd; hackily add one
173     fake_armour.flags |= ISFLAG_SUMMONED | ISFLAG_KNOW_PLUSES;
174     item_set_appearance(fake_armour);
175 
176     spirit->pickup_item(fake_armour, false, true);
177 
178     return spret::success;
179 }
180 
181 
182 
cast_summon_ice_beast(int pow,god_type god,bool fail)183 spret cast_summon_ice_beast(int pow, god_type god, bool fail)
184 {
185     if (rude_stop_summoning_prompt())
186         return spret::abort;
187 
188     fail_check();
189     const int dur = min(2 + (random2(pow) / 4), 4);
190 
191     mgen_data ice_beast = _pal_data(MONS_ICE_BEAST, dur, god,
192                                     SPELL_SUMMON_ICE_BEAST);
193     ice_beast.hd = (3 + div_rand_round(pow, 13));
194 
195     if (create_monster(ice_beast))
196         mpr("A chill wind blows around you.");
197     else
198         canned_msg(MSG_NOTHING_HAPPENS);
199 
200     return spret::success;
201 }
202 
cast_monstrous_menagerie(actor * caster,int pow,god_type god,bool fail)203 spret cast_monstrous_menagerie(actor* caster, int pow, god_type god, bool fail)
204 {
205     if (caster->is_player() && rude_stop_summoning_prompt())
206         return spret::abort;
207 
208     fail_check();
209     monster_type type = MONS_PROGRAM_BUG;
210 
211     if (random2(pow) > 60 && coinflip())
212         type = MONS_SPHINX;
213     else
214         type = coinflip() ? MONS_MANTICORE : MONS_LINDWURM;
215 
216     mgen_data mdata = _summon_data(*caster, type, 4, god,
217                                    SPELL_MONSTROUS_MENAGERIE);
218     mdata.flags |= MG_DONT_CAP;
219     if (caster->is_player())
220         mdata.hd = get_monster_data(type)->HD + div_rand_round(pow - 50, 25);
221 
222     monster* beast = create_monster(mdata);
223     if (!beast)
224     {
225         canned_msg(MSG_NOTHING_HAPPENS);
226         return spret::success;
227     }
228 
229     summoned_monster(beast, &you, SPELL_MONSTROUS_MENAGERIE);
230 
231     if (you.can_see(*beast))
232     {
233         mprf("%s %s %s!", caster->name(DESC_THE).c_str(),
234                           caster->conj_verb("summon").c_str(),
235                           mons_type_name(type, DESC_A).c_str());
236     }
237     else
238         canned_msg(MSG_NOTHING_HAPPENS);
239 
240     return spret::success;
241 }
242 
cast_summon_hydra(actor * caster,int pow,god_type god,bool fail)243 spret cast_summon_hydra(actor *caster, int pow, god_type god, bool fail)
244 {
245     if (caster->is_player() && rude_stop_summoning_prompt())
246         return spret::abort;
247 
248     fail_check();
249     // Power determines number of heads. Minimum 4 heads, maximum 12.
250     // Rare to get more than 8.
251     const int maxheads = one_chance_in(6) ? 12 : 8;
252     const int heads = max(4, min(random2(pow) / 6, maxheads));
253 
254     // Duration is always very short - just 1.
255     mgen_data mg = _summon_data(*caster, MONS_HYDRA, 1, god,
256                                 SPELL_SUMMON_HYDRA);
257     mg.props[MGEN_NUM_HEADS] = heads;
258     if (monster *hydra = create_monster(mg))
259     {
260         if (you.see_cell(hydra->pos()))
261             mprf("%s appears.", hydra->name(DESC_A).c_str());
262     }
263     else if (caster->is_player())
264         canned_msg(MSG_NOTHING_HAPPENS);
265 
266     return spret::success;
267 }
268 
_choose_dragon_type(int pow,god_type,bool player)269 static monster_type _choose_dragon_type(int pow, god_type /*god*/, bool player)
270 {
271     monster_type mon = MONS_PROGRAM_BUG;
272 
273     const int chance = random2(pow);
274 
275     if (chance >= 80 || one_chance_in(6))
276         mon = random_choose(MONS_GOLDEN_DRAGON, MONS_QUICKSILVER_DRAGON);
277     else if (chance >= 40 || one_chance_in(6))
278         mon = random_choose(MONS_IRON_DRAGON, MONS_SHADOW_DRAGON, MONS_STORM_DRAGON);
279     else
280         mon = random_choose(MONS_FIRE_DRAGON, MONS_ICE_DRAGON);
281 
282     // For good gods, switch away from shadow dragons to storm/iron dragons.
283     if (player && god_hates_monster(mon))
284         mon = random_choose(MONS_STORM_DRAGON, MONS_IRON_DRAGON);
285 
286     return mon;
287 }
288 
cast_dragon_call(int pow,bool fail)289 spret cast_dragon_call(int pow, bool fail)
290 {
291     if (rude_stop_summoning_prompt("call dragons"))
292         return spret::abort;
293 
294     fail_check();
295 
296     mpr("You call out to the draconic realm, and the dragon horde roars back!");
297     noisy(spell_effect_noise(SPELL_DRAGON_CALL), you.pos());
298 
299     you.duration[DUR_DRAGON_CALL] = (15 + pow / 5 + random2(15)) * BASELINE_DELAY;
300 
301     return spret::success;
302 }
303 
_place_dragon()304 static void _place_dragon()
305 {
306     const int pow = calc_spell_power(SPELL_DRAGON_CALL, true);
307     monster_type mon = _choose_dragon_type(pow, you.religion, true);
308     int mp_cost = random_range(2, 3);
309 
310     vector<monster*> targets;
311 
312     // Pick a random hostile in sight
313     for (monster_near_iterator mi(&you, LOS_NO_TRANS); mi; ++mi)
314         if (!mons_aligned(&you, *mi) && mons_is_threatening(**mi))
315             targets.push_back(*mi);
316 
317     shuffle_array(targets);
318 
319     // Attempt to place adjacent to the first chosen hostile. If there is no
320     // valid spot, move on to the next one.
321     for (monster *target : targets)
322     {
323         // Chose a random viable adjacent spot to the select target
324         vector<coord_def> spots;
325         for (adjacent_iterator ai(target->pos()); ai; ++ai)
326         {
327             if (monster_habitable_grid(MONS_FIRE_DRAGON, env.grid(*ai))
328                 && !actor_at(*ai))
329             {
330                 spots.push_back(*ai);
331             }
332         }
333 
334         // Now try to create the actual dragon
335         if (spots.size() <= 0)
336             continue;
337 
338         // Abort if we lack sufficient MP, but the dragon call duration
339         // remains, as the player might soon have enough again.
340         if (!enough_mp(mp_cost, true))
341         {
342             mpr("A dragon tries to answer your call, but you don't have enough "
343                 "magical power!");
344             return;
345         }
346 
347         const coord_def pos = spots[random2(spots.size())];
348         monster *dragon = create_monster(
349             mgen_data(mon, BEH_COPY, pos, MHITYOU, MG_FORCE_PLACE | MG_AUTOFOE)
350             .set_summoned(&you, 2, SPELL_DRAGON_CALL));
351         if (!dragon)
352             continue;
353 
354         pay_mp(mp_cost);
355         if (you.see_cell(dragon->pos()))
356             mpr("A dragon arrives to answer your call!");
357         finalize_mp_cost();
358 
359         // The dragon is allowed to act immediately here
360         dragon->flags &= ~MF_JUST_SUMMONED;
361         return;
362     }
363 
364     return;
365 }
366 
do_dragon_call(int time)367 void do_dragon_call(int time)
368 {
369     noisy(spell_effect_noise(SPELL_DRAGON_CALL), you.pos());
370 
371     while (time > you.attribute[ATTR_NEXT_DRAGON_TIME]
372            && you.duration[DUR_DRAGON_CALL])
373     {
374         time -= you.attribute[ATTR_NEXT_DRAGON_TIME];
375         _place_dragon();
376         you.attribute[ATTR_NEXT_DRAGON_TIME] = 3 + random2(5)
377                                                + count_summons(&you, SPELL_DRAGON_CALL) * 5;
378     }
379     you.attribute[ATTR_NEXT_DRAGON_TIME] -= time;
380 }
381 
382 /**
383  * Handle the Doom Howl status effect, possibly summoning hostile nasties
384  * around the player.
385  *
386  * @param time      The number of aut that the howling has been going on for
387  *                  since the last doom_howl call.
388  */
doom_howl(int time)389 void doom_howl(int time)
390 {
391     // TODO: pull hound-count generation into a helper function
392     int howlcalled_count = 0;
393     if (!you.props.exists(NEXT_DOOM_HOUND_KEY))
394         you.props[NEXT_DOOM_HOUND_KEY] = random_range(20, 40);
395     // 1 nasty beast every 2-4 turns
396     while (time > 0)
397     {
398         const int time_to_call = you.props[NEXT_DOOM_HOUND_KEY].get_int();
399         if (time_to_call <= time)
400         {
401             you.props[NEXT_DOOM_HOUND_KEY] = random_range(20, 40);
402             ++howlcalled_count;
403         }
404         else
405             you.props[NEXT_DOOM_HOUND_KEY].get_int() -= time;
406         time -= time_to_call;
407     }
408 
409     if (!howlcalled_count)
410         return;
411 
412     const actor *target = &you;
413 
414     for (int i = 0; i < howlcalled_count; ++i)
415     {
416         const monster_type howlcalled = random_choose(
417                 MONS_BONE_DRAGON, MONS_SHADOW_DRAGON, MONS_SHADOW_DEMON,
418                 MONS_REAPER, MONS_TORMENTOR, MONS_TZITZIMITL
419         );
420         vector<coord_def> spots;
421         for (adjacent_iterator ai(target->pos()); ai; ++ai)
422         {
423             if (monster_habitable_grid(howlcalled, env.grid(*ai))
424                 && !actor_at(*ai))
425             {
426                 spots.push_back(*ai);
427             }
428         }
429         if (spots.size() <= 0)
430             continue;
431 
432         const coord_def pos = spots[random2(spots.size())];
433 
434         monster *mons = create_monster(mgen_data(howlcalled, BEH_HOSTILE,
435                                                  pos, target->mindex(),
436                                                  MG_FORCE_BEH));
437         if (mons)
438         {
439             mons->add_ench(mon_enchant(ENCH_HAUNTING, 1, target,
440                                        INFINITE_DURATION));
441             mons->behaviour = BEH_SEEK;
442             mons_add_blame(mons, "called by a doom hound"); // assumption!
443             check_place_cloud(CLOUD_BLACK_SMOKE, mons->pos(),
444                               random_range(1,2), mons);
445         }
446     }
447 }
448 
cast_summon_dragon(actor * caster,int pow,god_type god,bool fail)449 spret cast_summon_dragon(actor *caster, int pow, god_type god, bool fail)
450 {
451     // Dragons are always friendly. Dragon type depends on power and
452     // random chance, with two low-tier dragons possible at high power.
453     // Duration fixed at 6.
454 
455     fail_check();
456     bool success = false;
457 
458     if (god == GOD_NO_GOD)
459         god = caster->deity();
460 
461     int how_many = 1;
462     monster_type mon = _choose_dragon_type(pow, god, caster->is_player());
463 
464     if (pow >= 100 && (mon == MONS_FIRE_DRAGON || mon == MONS_ICE_DRAGON))
465         how_many = 2;
466 
467     for (int i = 0; i < how_many; ++i)
468     {
469         if (monster *dragon = create_monster(
470                 _summon_data(*caster, mon, 6, god, SPELL_SUMMON_DRAGON)))
471         {
472             if (you.see_cell(dragon->pos()))
473                 mpr("A dragon appears.");
474             success = true;
475         }
476     }
477 
478     if (!success && caster->is_player())
479         canned_msg(MSG_NOTHING_HAPPENS);
480 
481     return spret::success;
482 }
483 
cast_summon_mana_viper(int pow,god_type god,bool fail)484 spret cast_summon_mana_viper(int pow, god_type god, bool fail)
485 {
486     if (rude_stop_summoning_prompt())
487         return spret::abort;
488 
489     fail_check();
490 
491     mgen_data viper = _pal_data(MONS_MANA_VIPER, 2, god,
492                                 SPELL_SUMMON_MANA_VIPER);
493     viper.hd = (5 + div_rand_round(pow, 12));
494 
495     // Don't scale hp at the same time as their antimagic power
496     viper.hp = hit_points(495); // avg 50
497 
498     if (create_monster(viper))
499         mpr("A mana viper appears with a sibilant hiss.");
500     else
501         canned_msg(MSG_NOTHING_HAPPENS);
502 
503     return spret::success;
504 }
505 
506 // This assumes that the specified monster can go berserk.
_make_mons_berserk_summon(monster * mon)507 static void _make_mons_berserk_summon(monster* mon)
508 {
509     mon->go_berserk(false);
510     mon_enchant berserk = mon->get_ench(ENCH_BERSERK);
511     mon_enchant abj = mon->get_ench(ENCH_ABJ);
512 
513     // Let Trog's gifts berserk longer, and set the abjuration timeout
514     // to the berserk timeout.
515     berserk.duration = berserk.duration * 3 / 2;
516     berserk.maxduration = berserk.duration;
517     abj.duration = abj.maxduration = berserk.duration;
518     mon->update_ench(berserk);
519     mon->update_ench(abj);
520 }
521 
522 // This is actually one of Trog's wrath effects.
summon_berserker(int pow,actor * caster,monster_type override_mons)523 bool summon_berserker(int pow, actor *caster, monster_type override_mons)
524 {
525     monster_type mon = MONS_PROGRAM_BUG;
526 
527     const int dur = min(2 + (random2(pow) / 4), 6);
528 
529     if (override_mons != MONS_PROGRAM_BUG)
530         mon = override_mons;
531     else
532     {
533         if (pow <= 100)
534         {
535             // bears
536             mon = random_choose(MONS_BLACK_BEAR, MONS_POLAR_BEAR);
537         }
538         else if (pow <= 140)
539         {
540             // ogres
541             mon = random_choose_weighted(1, MONS_TWO_HEADED_OGRE, 2, MONS_OGRE);
542         }
543         else if (pow <= 180)
544         {
545             // trolls
546             mon = random_choose_weighted(3, MONS_TROLL,
547                                          3, MONS_DEEP_TROLL,
548                                          2, MONS_IRON_TROLL);
549         }
550         else
551         {
552             // giants
553             mon = random_choose(MONS_CYCLOPS, MONS_STONE_GIANT);
554         }
555     }
556 
557     mgen_data mg(mon, caster ? BEH_COPY : BEH_HOSTILE,
558                  caster ? caster->pos() : you.pos(),
559                  (caster && caster->is_monster()) ? ((monster*)caster)->foe
560                                                   : int{MHITYOU},
561                  MG_AUTOFOE);
562     mg.set_summoned(caster, caster ? dur : 0, SPELL_NO_SPELL, GOD_TROG);
563 
564     if (!caster)
565     {
566         mg.non_actor_summoner = "the rage of " + god_name(GOD_TROG, false);
567         mg.extra_flags |= (MF_NO_REWARD | MF_HARD_RESET);
568     }
569 
570     monster *mons = create_monster(mg);
571 
572     if (!mons)
573         return false;
574 
575     _make_mons_berserk_summon(mons);
576     return true;
577 }
578 
579 // Not a spell. Rather, this is TSO's doing.
summon_holy_warrior(int pow,bool punish)580 bool summon_holy_warrior(int pow, bool punish)
581 {
582     mgen_data mg(random_choose(MONS_ANGEL, MONS_DAEVA),
583                  punish ? BEH_HOSTILE : BEH_FRIENDLY,
584                  you.pos(), MHITYOU, MG_FORCE_BEH | MG_AUTOFOE);
585     mg.set_summoned(punish ? 0 : &you,
586                     punish ? 0 : min(2 + (random2(pow) / 4), 6),
587                     SPELL_NO_SPELL, GOD_SHINING_ONE);
588 
589     if (punish)
590     {
591         mg.extra_flags |= (MF_NO_REWARD | MF_HARD_RESET);
592         mg.non_actor_summoner = god_name(GOD_SHINING_ONE, false);
593     }
594 
595     monster *summon = create_monster(mg);
596 
597     if (!summon)
598         return false;
599 
600     summon->flags |= MF_ATT_CHANGE_ATTEMPT;
601 
602     if (!punish)
603         mpr("You are momentarily dazzled by a brilliant light.");
604 
605     return true;
606 }
607 
608 /**
609  * Essentially a macro to allow for a generic fail pattern to avoid leaking
610  * information about invisible enemies. (Not implemented as a macro because I
611  * find they create unreadable code.)
612  *
613  * @return spret::success
614  **/
_fail_tukimas()615 static bool _fail_tukimas()
616 {
617     mprf("You can't see a target there!");
618     return false; // Waste the turn - no anti-invis tech
619 }
620 
621 /**
622  * Gets an item description for use in Tukima's Dance messages.
623  **/
_get_item_desc(const item_def * wpn,bool target_is_player)624 static string _get_item_desc(const item_def* wpn, bool target_is_player)
625 {
626     return wpn->name(target_is_player ? DESC_YOUR : DESC_THE);
627 }
628 
629 /**
630  * Checks if Tukima's Dance can actually affect the target (and anger them)
631  *
632  * @param target  The targeted monster (or player).
633  * @return        Whether the target can be affected by Tukima's Dance.
634  **/
tukima_affects(const actor & target)635 bool tukima_affects(const actor &target)
636 {
637     const item_def* wpn = target.weapon();
638     return wpn
639            && is_weapon(*wpn)
640            && !is_special_unrandom_artefact(*wpn)
641            && !mons_class_is_animated_weapon(target.type)
642            // XX use god_protects here. But, need to know the caster too...
643            && !mons_is_hepliaklqana_ancestor(target.type);
644 }
645 
646 /**
647  * Checks if Tukima's Dance is being cast on a valid target.
648  *
649  * @param target     The spell's target.
650  * @return           Whether the target is valid.
651  **/
_check_tukima_validity(const actor * target)652 static bool _check_tukima_validity(const actor *target)
653 {
654     bool target_is_player = target == &you;
655     const item_def* wpn = target->weapon();
656     bool can_see_target = target_is_player || target->visible_to(&you);
657 
658     // See if the wielded item is appropriate.
659     if (!wpn)
660     {
661         if (!can_see_target)
662             return _fail_tukimas();
663 
664         if (target_is_player)
665             mpr(you.hands_act("twitch", "."));
666         else
667         {
668             // FIXME: maybe move hands_act to class actor?
669             bool plural = true;
670             const string hand = target->hand_name(true, &plural);
671 
672             mprf("%s %s %s.",
673                  apostrophise(target->name(DESC_THE)).c_str(),
674                  hand.c_str(), conjugate_verb("twitch", plural).c_str());
675         }
676         return false;
677     }
678 
679     if (!tukima_affects(*target))
680     {
681         if (!can_see_target)
682             return _fail_tukimas();
683 
684         if (mons_class_is_animated_weapon(target->type))
685         {
686             simple_monster_message(*(monster*)target,
687                                    " is already dancing.");
688         }
689         else
690         {
691             mprf("%s vibrate%s crazily for a second.",
692                  _get_item_desc(wpn, target_is_player).c_str(),
693                  wpn->quantity > 1 ? "" : "s");
694         }
695         return false;
696     }
697 
698     return true;
699 }
700 
701 
702 /**
703  * Actually animates the weapon of the target creature (no checks).
704  *
705  * @param pow               Spellpower.
706  * @param target            The spell's target (monster or player)
707  **/
_animate_weapon(int pow,actor * target)708 static void _animate_weapon(int pow, actor* target)
709 {
710     bool target_is_player = target == &you;
711     item_def * const wpn = target->weapon();
712     ASSERT(wpn);
713     if (target_is_player)
714     {
715         // Clear temp branding so we don't change the brand permanently.
716         if (you.duration[DUR_EXCRUCIATING_WOUNDS])
717             end_weapon_brand(*wpn);
718 
719         // Mark weapon as "thrown", so we'll autopickup it later.
720         wpn->flags |= ISFLAG_THROWN;
721     }
722     // If sac love, the weapon will go after you, not the target.
723     const bool sac_love = you.get_mutation_level(MUT_NO_LOVE);
724     // Self-casting haunts yourself! MUT_NO_LOVE overrides force friendly.
725     const bool friendly = !target_is_player && !sac_love;
726     const int dur = min(2 + (random2(pow) / 5), 6);
727 
728     mgen_data mg(MONS_DANCING_WEAPON,
729                  friendly ? BEH_FRIENDLY : BEH_HOSTILE,
730                  target->pos(),
731                  (target_is_player || sac_love) ? MHITYOU : target->mindex(),
732                  sac_love ? MG_NONE : MG_FORCE_BEH);
733     mg.set_summoned(&you, dur, SPELL_TUKIMAS_DANCE);
734     mg.props[TUKIMA_WEAPON] = *wpn;
735     mg.props[TUKIMA_POWER] = pow;
736 
737     monster * const mons = create_monster(mg);
738 
739     if (!mons)
740     {
741         mprf("%s twitches for a moment.",
742              _get_item_desc(wpn, target_is_player).c_str());
743         return;
744     }
745 
746     // Don't haunt yourself under sac love.
747     if (!sac_love)
748     {
749         mons->add_ench(mon_enchant(ENCH_HAUNTING, 1, target,
750                                    INFINITE_DURATION));
751         mons->foe = target->mindex();
752     }
753 
754     // We are successful. Unwield the weapon, removing any wield effects.
755     mprf("%s dances into the air!",
756          _get_item_desc(wpn, target_is_player).c_str());
757     if (target_is_player)
758         unwield_item();
759     else
760     {
761         monster * const montarget = target->as_monster();
762         const int primary_weap = montarget->inv[MSLOT_WEAPON];
763         const mon_inv_type wp_slot = (primary_weap != NON_ITEM
764                                       && &env.item[primary_weap] == wpn) ?
765                                          MSLOT_WEAPON : MSLOT_ALT_WEAPON;
766         ASSERT(montarget->inv[wp_slot] != NON_ITEM);
767         ASSERT(&env.item[montarget->inv[wp_slot]] == wpn);
768 
769         montarget->unequip(*(montarget->mslot_item(wp_slot)), false, true);
770         montarget->inv[wp_slot] = NON_ITEM;
771 
772         // Also steal ammo for launchers.
773         if (is_range_weapon(*wpn))
774         {
775             const int ammo = montarget->inv[MSLOT_MISSILE];
776             if (ammo != NON_ITEM)
777             {
778                 ASSERT(mons->inv[MSLOT_MISSILE] == NON_ITEM);
779                 mons->inv[MSLOT_MISSILE] = ammo;
780                 montarget->inv[MSLOT_MISSILE] = NON_ITEM;
781                 env.item[ammo].set_holding_monster(*mons);
782             }
783         }
784     }
785 
786     // Find out what our god thinks before killing the item.
787     conduct_type why = god_hates_item_handling(*wpn);
788 
789     wpn->clear();
790 
791     if (why)
792     {
793         simple_god_message(" booms: How dare you animate that foul thing!");
794         did_god_conduct(why, 10, true, mons);
795     }
796 }
797 
798 /**
799  * Casts Tukima's Dance, animating the weapon of the target creature (if valid)
800  *
801  * @param pow               Spellpower.
802  * @param where             The target grid.
803  **/
cast_tukimas_dance(int pow,actor * target)804 void cast_tukimas_dance(int pow, actor* target)
805 {
806     ASSERT(target);
807 
808     if (!_check_tukima_validity(target))
809         return;
810 
811     _animate_weapon(pow, target);
812 }
813 
814 /// When the player conjures ball lightning with the given spellpower, what
815 /// HD will the resulting lightning have?
ball_lightning_hd(int pow,bool random)816 int ball_lightning_hd(int pow, bool random)
817 {
818     if (random)
819         return max(1, div_rand_round(pow, 6) - 6);
820     return max(1, pow / 6 - 6);
821 }
822 
mons_ball_lightning_hd(int pow,bool random)823 int mons_ball_lightning_hd(int pow, bool random)
824 {
825     // We love players, don't we? Let's be nice.
826     return ball_lightning_hd(pow, random) / 2;
827 }
828 
cast_conjure_ball_lightning(int pow,god_type god,bool fail)829 spret cast_conjure_ball_lightning(int pow, god_type god, bool fail)
830 {
831     fail_check();
832     bool success = false;
833 
834     mgen_data cbl =_pal_data(MONS_BALL_LIGHTNING, 0, god,
835                              SPELL_CONJURE_BALL_LIGHTNING);
836     cbl.hd = ball_lightning_hd(pow);
837 
838     for (int i = 0; i < 3; ++i)
839     {
840         if (monster *ball = create_monster(cbl))
841         {
842             success = true;
843             ball->add_ench(ENCH_SHORT_LIVED);
844 
845             // Avoid ball lightnings without targets always moving towards (0,0)
846             if (!(ball->get_foe() && ball->get_foe()->is_monster()))
847                 set_random_target(ball);
848         }
849     }
850 
851     if (success)
852         mpr("You create some ball lightning!");
853     else
854         canned_msg(MSG_NOTHING_HAPPENS);
855 
856     return spret::success;
857 }
858 
cast_summon_lightning_spire(int pow,god_type god,bool fail)859 spret cast_summon_lightning_spire(int pow, god_type god, bool fail)
860 {
861     if (rude_stop_summoning_prompt())
862         return spret::abort;
863 
864     fail_check();
865 
866     mgen_data spire = _pal_data(MONS_LIGHTNING_SPIRE, 2, god,
867                                 SPELL_SUMMON_LIGHTNING_SPIRE);
868     spire.hd = max(1, div_rand_round(pow, 10));
869 
870     monster* mons = create_monster(spire);
871 
872     if (mons && !silenced(mons->pos()))
873         mpr("An electric hum fills the air.");
874     else
875         canned_msg(MSG_NOTHING_HAPPENS);
876 
877     return spret::success;
878 }
879 
cast_summon_guardian_golem(int pow,god_type god,bool fail)880 spret cast_summon_guardian_golem(int pow, god_type god, bool fail)
881 {
882     if (rude_stop_summoning_prompt())
883         return spret::abort;
884 
885     fail_check();
886 
887     mgen_data golem = _pal_data(MONS_GUARDIAN_GOLEM, 3, god,
888                                 SPELL_SUMMON_GUARDIAN_GOLEM);
889     golem.flags &= ~MG_AUTOFOE; // !!!
890     golem.hd = 4 + div_rand_round(pow, 16);
891 
892     monster* mons = (create_monster(golem));
893 
894     if (mons)
895     {
896         // Immediately apply injury bond
897         guardian_golem_bond(*mons);
898 
899         mpr("A guardian golem appears, shielding your allies.");
900     }
901     else
902         canned_msg(MSG_NOTHING_HAPPENS);
903 
904     return spret::success;
905 }
906 
907 /**
908  * Choose a type of imp to summon with Call Imp.
909  *
910  * @return      An appropriate imp type.
911  */
_get_imp_type()912 static monster_type _get_imp_type()
913 {
914     if (x_chance_in_y(5, 18))
915         return MONS_WHITE_IMP;
916 
917     // 3/13 * 13/18 = 1/6 chance of one of these two.
918     if (x_chance_in_y(3, 13))
919         return one_chance_in(3) ? MONS_IRON_IMP : MONS_SHADOW_IMP;
920 
921     // 5/9 chance of getting, regrettably, a crimson imp.
922     return MONS_CRIMSON_IMP;
923 }
924 
925 static map<monster_type, const char*> _imp_summon_messages = {
926     { MONS_WHITE_IMP,
927         "A beastly little devil appears in a puff of frigid air." },
928     { MONS_IRON_IMP, "A metallic apparition takes form in the air." },
929     { MONS_SHADOW_IMP, "A shadowy apparition takes form in the air." },
930     { MONS_CRIMSON_IMP, "A beastly little devil appears in a puff of flame." },
931 };
932 
933 /**
934  * Cast the spell Call Imp, summoning a friendly imp nearby.
935  *
936  * @param pow   The spellpower at which the spell is being cast.
937  * @param god   The god of the caster.
938  * @param fail  Whether the caster (you) failed to cast the spell.
939  * @return      spret::fail if fail is true; spret::success otherwise.
940  */
cast_call_imp(int pow,god_type god,bool fail)941 spret cast_call_imp(int pow, god_type god, bool fail)
942 {
943     if (rude_stop_summoning_prompt())
944         return spret::abort;
945 
946     fail_check();
947 
948     const monster_type imp_type = _get_imp_type();
949 
950     const int dur = min(2 + (random2(pow) / 4), 6);
951 
952     mgen_data imp_data = _pal_data(imp_type, dur, god, SPELL_CALL_IMP);
953     if (monster *imp = create_monster(imp_data))
954     {
955         mpr(_imp_summon_messages[imp_type]);
956         _monster_greeting(imp, "_friendly_imp_greeting");
957     }
958     else
959         canned_msg(MSG_NOTHING_HAPPENS);
960 
961     return spret::success;
962 }
963 
_summon_demon_wrapper(int pow,god_type god,int spell,monster_type mon,int dur,bool friendly,bool charmed)964 static bool _summon_demon_wrapper(int pow, god_type god, int spell,
965                                   monster_type mon, int dur, bool friendly,
966                                   bool charmed)
967 {
968     bool success = false;
969 
970     if (monster *demon = create_monster(
971             mgen_data(mon,
972                       friendly ? BEH_FRIENDLY :
973                        charmed ? BEH_CHARMED
974                                : BEH_HOSTILE,
975                       you.pos(), MHITYOU, MG_FORCE_BEH | MG_AUTOFOE)
976             .set_summoned(&you, dur, spell, god)))
977     {
978         success = true;
979 
980         mpr("A demon appears!");
981 
982         if (!friendly)
983         {
984             mpr(charmed ? "You don't feel so good about this..."
985                         : "It doesn't seem very happy.");
986         }
987         else if (mon == MONS_CRIMSON_IMP || mon == MONS_WHITE_IMP
988                 || mon == MONS_IRON_IMP || mon == MONS_SHADOW_IMP)
989         {
990             _monster_greeting(demon, "_friendly_imp_greeting");
991         }
992 
993         if (charmed && !friendly)
994         {
995             int charm_dur = random_range(15 + pow / 14, 27 + pow / 11)
996                             * BASELINE_DELAY;
997 
998             mon_enchant charm = demon->get_ench(ENCH_CHARM);
999             charm.duration = charm_dur;
1000             demon->update_ench(charm);
1001 
1002             // Ensure that temporarily-charmed demons will outlast their charm
1003             mon_enchant abj = demon->get_ench(ENCH_ABJ);
1004             if (charm.duration + 100 > abj.duration)
1005             {
1006                 abj.duration = charm.duration + 100;
1007                 demon->update_ench(abj);
1008             }
1009 
1010             // Affects messaging, and stuns demon a turn upon charm wearing off
1011             demon->props["charmed_demon"].get_bool() = true;
1012         }
1013     }
1014 
1015     return success;
1016 }
1017 
_summon_common_demon(int pow,god_type god,int spell)1018 static bool _summon_common_demon(int pow, god_type god, int spell)
1019 {
1020     const int chance = 70 - (pow / 3);
1021     monster_type type = MONS_PROGRAM_BUG;
1022 
1023     if (x_chance_in_y(chance, 100))
1024         type = random_demon_by_tier(4);
1025     else
1026         type = random_demon_by_tier(3);
1027 
1028     return _summon_demon_wrapper(pow, god, spell, type,
1029                                  min(2 + (random2(pow) / 4), 6),
1030                                  random2(pow) > 3, false);
1031 }
1032 
summon_demon_type(monster_type mon,int pow,god_type god,int spell,bool friendly)1033 bool summon_demon_type(monster_type mon, int pow, god_type god,
1034                        int spell, bool friendly)
1035 {
1036     return _summon_demon_wrapper(pow, god, spell, mon,
1037                                  min(2 + (random2(pow) / 4), 6),
1038                                  friendly, false);
1039 }
1040 
cast_summon_demon(int pow)1041 spret cast_summon_demon(int pow)
1042 {
1043     // Chaos spawn, orange demons and sixfirhies are not rPois
1044     if (rude_stop_summoning_prompt())
1045         return spret::abort;
1046 
1047     mpr("You open a gate to Pandemonium!");
1048 
1049     if (!_summon_common_demon(pow, GOD_NO_GOD, SPELL_SUMMON_DEMON))
1050         canned_msg(MSG_NOTHING_HAPPENS);
1051 
1052     return spret::success;
1053 }
1054 
cast_shadow_creatures(int st,god_type god,level_id place,bool fail)1055 spret cast_shadow_creatures(int st, god_type god, level_id place,
1056                                  bool fail)
1057 {
1058     if (rude_stop_summoning_prompt("summon"))
1059         return spret::abort;
1060 
1061     fail_check();
1062     const bool scroll = (st == MON_SUMM_SCROLL);
1063     mpr("Wisps of shadow whirl around you...");
1064 
1065     int num = (scroll ? roll_dice(2, 2) : 1);
1066     int num_created = 0;
1067 
1068     for (int i = 0; i < num; ++i)
1069     {
1070         if (monster *mons = create_monster(
1071             mgen_data(RANDOM_COMPATIBLE_MONSTER, BEH_FRIENDLY, you.pos(),
1072                       MHITYOU, MG_FORCE_BEH | MG_AUTOFOE | MG_NO_OOD)
1073                       // This duration is only used for band members.
1074                       .set_summoned(&you, scroll ? 2 : 1, st, god)
1075                       .set_place(place),
1076             false))
1077         {
1078             // In the rare cases that a specific spell set of a monster will
1079             // cause anger, even if others do not, try rerolling
1080             int tries = 0;
1081             while (god_hates_monster(*mons) && ++tries <= 20)
1082             {
1083                 // Save the enchantments, particularly ENCH_SUMMON etc.
1084                 mon_enchant_list ench = mons->enchantments;
1085                 FixedBitVector<NUM_ENCHANTMENTS> cache = mons->ench_cache;
1086                 if (mons_class_is_zombified(mons->type))
1087                     define_zombie(mons, mons->base_monster, mons->type);
1088                 else
1089                     define_monster(*mons);
1090                 mons->enchantments = ench;
1091                 mons->ench_cache = cache;
1092             }
1093 
1094             // If we didn't find a valid spell set yet, just give up
1095             if (tries > 20)
1096                 monster_die(*mons, KILL_RESET, NON_MONSTER);
1097             else
1098             {
1099                 // Choose a new duration based on HD.
1100                 int x = max(mons->get_experience_level() - 3, 1);
1101                 int d = div_rand_round(17,x);
1102                 if (scroll)
1103                     d++;
1104                 if (d < 1)
1105                     d = 1;
1106                 if (d > 4)
1107                     d = 4;
1108                 mon_enchant me = mon_enchant(ENCH_ABJ, d);
1109                 me.set_duration(mons, &me);
1110                 mons->update_ench(me);
1111 
1112                 // Set summon ID, to share summon cap with its band members
1113                 mons->props["summon_id"].get_int() = mons->mid;
1114             }
1115 
1116             // Remove any band members that would turn hostile, and link their
1117             // summon IDs
1118             for (monster_iterator mi; mi; ++mi)
1119             {
1120                 if (testbits(mi->flags, MF_BAND_MEMBER)
1121                     && (mid_t) mi->props["band_leader"].get_int() == mons->mid)
1122                 {
1123                     if (god_hates_monster(**mi))
1124                         monster_die(**mi, KILL_RESET, NON_MONSTER);
1125 
1126                     mi->props["summon_id"].get_int() = mons->mid;
1127                 }
1128             }
1129 
1130             num_created++;
1131         }
1132     }
1133 
1134     if (!num_created)
1135         mpr("The shadows disperse without effect.");
1136 
1137     return spret::success;
1138 }
1139 
can_cast_malign_gateway()1140 bool can_cast_malign_gateway()
1141 {
1142     timeout_malign_gateways(0);
1143 
1144     return count_malign_gateways() < 1;
1145 }
1146 
find_gateway_location(actor * caster)1147 coord_def find_gateway_location(actor* caster)
1148 {
1149     vector<coord_def> points;
1150 
1151     for (coord_def delta : Compass)
1152     {
1153         coord_def test = coord_def(-1, -1);
1154 
1155         for (int t = 0; t < 11; t++)
1156         {
1157             test = caster->pos() + (delta * (2+t));
1158             if (!in_bounds(test) || !feat_is_malign_gateway_suitable(env.grid(test))
1159                 || actor_at(test)
1160                 || count_neighbours_with_func(test, &feat_is_solid) != 0
1161                 || !caster->see_cell_no_trans(test))
1162             {
1163                 continue;
1164             }
1165 
1166             points.push_back(test);
1167         }
1168     }
1169 
1170     if (points.empty())
1171         return coord_def(0, 0);
1172 
1173     return points[random2(points.size())];
1174 }
1175 
create_malign_gateway(coord_def point,beh_type beh,string cause,int pow,god_type god,bool is_player)1176 void create_malign_gateway(coord_def point, beh_type beh, string cause,
1177                            int pow, god_type god, bool is_player)
1178 {
1179     const int malign_gateway_duration = BASELINE_DELAY * (random2(2) + 1);
1180     env.markers.add(new map_malign_gateway_marker(point,
1181                             malign_gateway_duration,
1182                             is_player,
1183                             is_player ? "" : cause,
1184                             beh,
1185                             god,
1186                             pow));
1187     env.markers.clear_need_activate();
1188     env.grid(point) = DNGN_MALIGN_GATEWAY;
1189     set_terrain_changed(point);
1190 
1191     noisy(spell_effect_noise(SPELL_MALIGN_GATEWAY), point);
1192     mprf(MSGCH_WARN, "The dungeon shakes, a horrible noise fills the air, "
1193                      "and a portal to some otherworldly place is opened!");
1194 }
1195 
cast_malign_gateway(actor * caster,int pow,god_type god,bool fail)1196 spret cast_malign_gateway(actor * caster, int pow, god_type god, bool fail)
1197 {
1198     if (caster->is_player() && rude_stop_summoning_prompt())
1199         return spret::abort;
1200 
1201     coord_def point = find_gateway_location(caster);
1202     bool success = point != coord_def(0, 0);
1203 
1204     bool is_player = caster->is_player();
1205 
1206     if (success)
1207     {
1208         fail_check();
1209 
1210         create_malign_gateway(
1211             point,
1212             is_player ? BEH_FRIENDLY
1213                       : attitude_creation_behavior(
1214                           caster->as_monster()->attitude),
1215             is_player ? ""
1216                       : caster->as_monster()->full_name(DESC_A),
1217             pow,
1218             god,
1219             is_player);
1220 
1221         return spret::success;
1222     }
1223 
1224     // We don't care if monsters fail to cast it.
1225     if (is_player)
1226         mpr("A gateway cannot be opened in this cramped space!");
1227 
1228     return spret::abort;
1229 }
1230 
cast_summon_horrible_things(int pow,god_type god,bool fail)1231 spret cast_summon_horrible_things(int pow, god_type god, bool fail)
1232 {
1233     if (rude_stop_summoning_prompt())
1234         return spret::abort;
1235 
1236     fail_check();
1237     if (god == GOD_NO_GOD && one_chance_in(5))
1238     {
1239         // if someone deletes the db, no message is ok
1240         mpr(getMiscString("SHT_int_loss"));
1241         lose_stat(STAT_INT, 1);
1242     }
1243 
1244     int num_abominations = random_range(2, 4) + x_chance_in_y(pow, 200);
1245     int num_tmons = random2(pow) > 120 ? 2 : random2(pow) > 50 ? 1 : 0;
1246 
1247     if (num_tmons == 0)
1248         num_abominations++;
1249 
1250     int count = 0;
1251 
1252     while (num_abominations-- > 0)
1253     {
1254         const mgen_data abom = _pal_data(MONS_ABOMINATION_LARGE, 3, god,
1255                                          SPELL_SUMMON_HORRIBLE_THINGS);
1256         if (create_monster(abom))
1257             ++count;
1258     }
1259 
1260     while (num_tmons-- > 0)
1261     {
1262         const mgen_data tmons = _pal_data(MONS_TENTACLED_MONSTROSITY, 3, god,
1263                                           SPELL_SUMMON_HORRIBLE_THINGS);
1264         if (create_monster(tmons))
1265             ++count;
1266     }
1267 
1268     if (!count)
1269         canned_msg(MSG_NOTHING_HAPPENS);
1270 
1271     return spret::success;
1272 }
1273 
_water_adjacent(coord_def p)1274 static bool _water_adjacent(coord_def p)
1275 {
1276     for (orth_adjacent_iterator ai(p); ai; ++ai)
1277     {
1278         if (feat_is_water(env.grid(*ai)))
1279             return true;
1280     }
1281 
1282     return false;
1283 }
1284 
1285 /**
1286  * Cast summon forest.
1287  *
1288  * @param caster The caster.
1289  * @param pow    The spell power.
1290  * @param god    The god of the summoned dryad (usually the caster's).
1291  * @param fail   Did this spell miscast? If true, abort the cast.
1292  * @return       spret::abort if a summoning area couldn't be found,
1293  *               spret::fail if one could be found but we miscast, and
1294  *               spret::success if the spell was successfully cast.
1295 */
cast_summon_forest(actor * caster,int pow,god_type god,bool fail,bool test)1296 spret cast_summon_forest(actor* caster, int pow, god_type god, bool fail, bool test)
1297 {
1298 
1299     // Is this area open enough to summon a forest?
1300     bool success = false;
1301     for (adjacent_iterator ai(caster->pos(), false); ai; ++ai)
1302     {
1303         if (count_neighbours_with_func(*ai, &feat_is_solid) == 0)
1304         {
1305             if (test)
1306                 return spret::success;
1307             success = true;
1308             break;
1309         }
1310     }
1311 
1312     if (test)
1313         return spret::abort;
1314 
1315     const int duration = random_range(120 + pow, 200 + pow * 3 / 2);
1316 
1317     if (success)
1318     {
1319         if (rude_stop_summoning_prompt("summon a forest"))
1320             return spret::abort;
1321 
1322         fail_check();
1323         // Replace some rock walls with trees, then scatter a smaller number
1324         // of trees on unoccupied floor (such that they do not break connectivity)
1325         for (distance_iterator di(caster->pos(), false, true,
1326                                   LOS_DEFAULT_RANGE); di; ++di)
1327         {
1328             if ((feat_is_wall(env.grid(*di)) && !feat_is_permarock(env.grid(*di))
1329                  && x_chance_in_y(pow, 150))
1330                 || (env.grid(*di) == DNGN_FLOOR && x_chance_in_y(pow, 1250)
1331                     && !actor_at(*di) && !plant_forbidden_at(*di, true)))
1332             {
1333                 temp_change_terrain(*di, DNGN_TREE, duration,
1334                         TERRAIN_CHANGE_FORESTED);
1335             }
1336         }
1337 
1338         // Maybe make a pond
1339         if (coinflip())
1340         {
1341             coord_def pond = find_gateway_location(caster);
1342             int num = random_range(10, 22);
1343             int deep = (!one_chance_in(3) ? div_rand_round(num, 3) : 0);
1344 
1345             for (distance_iterator di(pond, true, false, 4); di && num > 0; ++di)
1346             {
1347                 if (env.grid(*di) == DNGN_FLOOR
1348                     && (di.radius() == 0 || _water_adjacent(*di))
1349                     && x_chance_in_y(4, di.radius() + 3))
1350                 {
1351                     num--;
1352                     deep--;
1353 
1354                     dungeon_feature_type feat = DNGN_SHALLOW_WATER;
1355                     if (deep > 0 && *di != you.pos())
1356                     {
1357                         monster* mon = monster_at(*di);
1358                         if (!mon || mon->is_habitable_feat(DNGN_DEEP_WATER))
1359                             feat = DNGN_DEEP_WATER;
1360                     }
1361 
1362                     temp_change_terrain(*di, feat, duration, TERRAIN_CHANGE_FORESTED);
1363                 }
1364             }
1365         }
1366 
1367         mpr("A forested plane collides here with a resounding crunch!");
1368         noisy(spell_effect_noise(SPELL_SUMMON_FOREST), caster->pos());
1369 
1370         mgen_data dryad_data = _pal_data(MONS_DRYAD, 1, god,
1371                                          SPELL_SUMMON_FOREST);
1372         dryad_data.hd = 5 + div_rand_round(pow, 18);
1373 
1374         if (monster *dryad = create_monster(dryad_data))
1375         {
1376             mon_enchant abj = dryad->get_ench(ENCH_ABJ);
1377             abj.duration = duration - 10;
1378             dryad->update_ench(abj);
1379 
1380             // Pre-awaken the forest just summoned.
1381             bolt dummy;
1382             mons_cast(dryad, dummy, SPELL_AWAKEN_FOREST,
1383                       dryad->spell_slot_flags(SPELL_AWAKEN_FOREST));
1384         }
1385 
1386         you.duration[DUR_FORESTED] = duration;
1387 
1388         return spret::success;
1389     }
1390 
1391     return spret::abort;
1392 }
1393 
_animatable_remains(const item_def & item)1394 static bool _animatable_remains(const item_def& item)
1395 {
1396     return item.base_type == OBJ_CORPSES
1397         && mons_class_can_be_zombified(item.mon_type)
1398         // the above allows spectrals/etc
1399         && (mons_zombifiable(item.mon_type)
1400             || mons_skeleton(item.mon_type));
1401 }
1402 
1403 /**
1404  * Equip the dearly departed with its ex-possessions.
1405  *
1406  * This excludes holy items (not wieldable by the undead, and items which were
1407  * dropped onto the square, so the player can't equip their undead slaves with
1408  * items of their choice.
1409  *
1410  * @param a      where to pull items from
1411  * @param corpse the corpse of the dead monster.
1412  * @param mon    the zombie/skeleton/etc. being reequipped.
1413  */
_equip_undead(const coord_def & a,const item_def & corpse,monster * mon)1414 static void _equip_undead(const coord_def &a, const item_def& corpse, monster *mon)
1415 {
1416     if (!corpse.props.exists(MONSTER_MID))
1417         return;
1418     for (stack_iterator si(a); si; ++si)
1419     {
1420         if (!si->props.exists(DROPPER_MID_KEY)
1421             || si->props[DROPPER_MID_KEY].get_int()
1422                != corpse.props[MONSTER_MID].get_int())
1423         {
1424             continue;
1425         }
1426 
1427         // Don't equip the undead with holy items.
1428         if (is_holy_item(*si))
1429             continue;
1430 
1431         // Stupid undead can't use most items.
1432         if (si->base_type != OBJ_WEAPONS
1433             && si->base_type != OBJ_STAVES
1434             && si->base_type != OBJ_ARMOUR
1435             || is_range_weapon(*si))
1436         {
1437             continue;
1438         }
1439 
1440         unwind_var<int> save_speedinc(mon->speed_increment);
1441         mon->pickup_item(*si, false, true);
1442     }
1443 }
1444 
1445 // Displays message when raising dead with Animate Skeleton or Animate Dead.
_display_undead_motions(int motions)1446 static void _display_undead_motions(int motions)
1447 {
1448     vector<const char *> motions_list;
1449 
1450     // Check bitfield from _raise_remains for types of corpse(s) being animated.
1451     if (motions & DEAD_ARE_WALKING)
1452         motions_list.push_back("walking");
1453     if (motions & DEAD_ARE_HOPPING)
1454         motions_list.push_back("hopping");
1455     if (motions & DEAD_ARE_SWIMMING)
1456         motions_list.push_back("swimming");
1457     if (motions & DEAD_ARE_FLYING)
1458         motions_list.push_back("flying");
1459     if (motions & DEAD_ARE_SLITHERING)
1460         motions_list.push_back("slithering");
1461     if (motions & DEAD_ARE_CRAWLING)
1462         motions_list.push_back("crawling");
1463 
1464     // Prevents the message from getting too long and spammy.
1465     if (motions_list.size() > 3)
1466         mpr("The dead have arisen!");
1467     else
1468     {
1469         mprf("The dead are %s!", comma_separated_line(motions_list.begin(),
1470              motions_list.end()).c_str());
1471     }
1472 }
1473 
_raise_remains(const coord_def & pos,int corps,beh_type beha,int pow,unsigned short hitting,actor * as,string nas,god_type god,bool actual,bool apply_lovelessness,monster ** raised,int * motions_r)1474 static bool _raise_remains(const coord_def &pos, int corps, beh_type beha,
1475                            int pow,
1476                            unsigned short hitting, actor *as, string nas,
1477                            god_type god, bool actual, bool apply_lovelessness,
1478                            monster **raised, int* motions_r)
1479 {
1480     if (raised)
1481         *raised = 0;
1482 
1483     const item_def& item = env.item[corps];
1484 
1485     if (!_animatable_remains(item))
1486         return false;
1487 
1488     if (!actual)
1489         return true;
1490 
1491     monster_type zombie_type = item.mon_type;
1492     // hack: don't re-froggify poor prince ribbit after death
1493     if (zombie_type == MONS_PRINCE_RIBBIT)
1494         zombie_type = MONS_HUMAN;
1495 
1496     int hd = (item.props.exists(MONSTER_HIT_DICE)) ?
1497               item.props[MONSTER_HIT_DICE].get_short() : 0;
1498 
1499     // Save the corpse name before because it can get destroyed if it is
1500     // being drained and the raising interrupts it.
1501     monster_flags_t name_type;
1502     string name;
1503     if (is_named_corpse(item))
1504         name = get_corpse_name(item, &name_type);
1505 
1506     monster_type mon = item.sub_type == CORPSE_BODY ? MONS_ZOMBIE
1507                                                     : MONS_SKELETON;
1508     monster_type monnum = static_cast<monster_type>(item.orig_monnum);
1509     // hack: don't re-froggify poor prince ribbit after death
1510     if (monnum == MONS_PRINCE_RIBBIT)
1511         monnum = MONS_HUMAN;
1512 
1513     if (mon == MONS_ZOMBIE && !mons_zombifiable(zombie_type))
1514     {
1515         ASSERT(mons_skeleton(zombie_type));
1516         mon = MONS_SKELETON;
1517     }
1518 
1519     // Use the original monster type as the zombified type here, to get
1520     // the proper stats from it.
1521     mgen_data mg(mon, beha, pos, hitting,
1522                  MG_FORCE_BEH | MG_FORCE_PLACE | MG_AUTOFOE);
1523     mg.set_summoned(as, 0, 0, god);
1524     mg.set_base(monnum);
1525 
1526     if (item.props.exists(CORPSE_HEADS))
1527     {
1528         // Headless hydras cannot be raised, sorry.
1529         if (item.props[CORPSE_HEADS].get_short() == 0)
1530         {
1531             if (you.see_cell(pos))
1532             {
1533                 mprf("The headless hydra %s sways and immediately collapses!",
1534                      item.sub_type == CORPSE_BODY ? "corpse" : "skeleton");
1535             }
1536             return false;
1537         }
1538         mg.props[MGEN_NUM_HEADS] = item.props[CORPSE_HEADS].get_short();
1539     }
1540 
1541     // No experience for monsters animated by god wrath or the Sword of
1542     // Zonguldrok.
1543     if (!nas.empty())
1544         mg.extra_flags |= MF_NO_REWARD;
1545 
1546     mg.non_actor_summoner = nas;
1547 
1548     monster *mons = create_monster(mg);
1549 
1550     if (raised)
1551         *raised = mons;
1552 
1553     if (!mons)
1554         return false;
1555 
1556     if (god == GOD_NO_GOD) // only Yred dead-raising lasts forever.
1557         mons->add_ench(mon_enchant(ENCH_FAKE_ABJURATION, 4));
1558 
1559     // If the original monster has been levelled up, its HD might be different
1560     // from its class HD. For player spells the hd is scaled with spellpower.
1561     if (nas.empty())
1562         hd = div_rand_round(hd * (100 + pow), 300);
1563     if (mons->get_experience_level() != hd)
1564     {
1565         mons->set_hit_dice(max(hd, 1));
1566         roll_zombie_hp(mons);
1567     }
1568 
1569     if (!name.empty()
1570         && (!name_type || (name_type & MF_NAME_MASK) == MF_NAME_REPLACE))
1571     {
1572         name_zombie(*mons, monnum, name);
1573     }
1574 
1575     // Re-equip the zombie.
1576     if (mons_class_itemuse(monnum) >= MONUSE_STARTING_EQUIPMENT)
1577         _equip_undead(pos, item, mons);
1578 
1579     // Destroy the monster's corpse, as it's no longer needed.
1580     item_was_destroyed(item);
1581     destroy_item(corps);
1582 
1583     if (apply_lovelessness)
1584         check_lovelessness(*mons);
1585 
1586     // Bitfield for motions - determines text displayed when animating dead.
1587     // XXX: could this use monster shape in some way?
1588     if (mons_class_primary_habitat(zombie_type)    == HT_WATER
1589         || mons_class_primary_habitat(zombie_type) == HT_LAVA)
1590     {
1591         *motions_r |= DEAD_ARE_SWIMMING;
1592     }
1593     else if (mons_class_flag(zombie_type, M_FLIES))
1594         *motions_r |= DEAD_ARE_FLYING;
1595     else if (mons_genus(zombie_type)    == MONS_SNAKE
1596              || mons_genus(zombie_type) == MONS_NAGA
1597              || mons_genus(zombie_type) == MONS_SALAMANDER
1598              || mons_genus(zombie_type) == MONS_GUARDIAN_SERPENT
1599              || mons_genus(zombie_type) == MONS_ELEPHANT_SLUG
1600              || mons_genus(zombie_type) == MONS_TYRANT_LEECH
1601              || mons_genus(zombie_type) == MONS_WORM)
1602     {
1603         *motions_r |= DEAD_ARE_SLITHERING;
1604     }
1605     else if (mons_genus(zombie_type)    == MONS_FROG
1606              || mons_genus(zombie_type) == MONS_QUOKKA)
1607     {
1608         *motions_r |= DEAD_ARE_HOPPING;
1609     }
1610     else if (mons_base_char(zombie_type) == 's') // many genera
1611         *motions_r |= DEAD_ARE_CRAWLING;
1612     else
1613         *motions_r |= DEAD_ARE_WALKING;
1614 
1615     return true;
1616 }
1617 
1618 // Note that quiet will *not* suppress the message about a corpse
1619 // you are butchering being animated.
1620 // This is called for Animate Skeleton and from animate_dead.
animate_remains(const coord_def & a,corpse_type class_allowed,beh_type beha,int pow,unsigned short hitting,actor * as,string nas,god_type god,bool actual,bool quiet,bool apply_lovelessness,monster ** mon,int * motions_r)1621 int animate_remains(const coord_def &a, corpse_type class_allowed,
1622                     beh_type beha, int pow, unsigned short hitting,
1623                     actor *as, string nas,
1624                     god_type god, bool actual,
1625                     bool quiet, bool apply_lovelessness,
1626                     monster** mon, int* motions_r)
1627 {
1628     if (is_sanctuary(a))
1629         return 0;
1630 
1631     if (env.grid(a) == DNGN_DEEP_WATER)
1632         return 0; // trapped in davy jones' locker...
1633 
1634     int number_found = 0;
1635     bool any_success = false;
1636     int motions = 0;
1637 
1638     // Search all the items on the ground for a corpse.
1639     for (stack_iterator si(a, true); si; ++si)
1640     {
1641         if (si->base_type != OBJ_CORPSES)
1642             continue;
1643 
1644         if (class_allowed != CORPSE_BODY && si->sub_type != CORPSE_SKELETON)
1645             continue;
1646 
1647         number_found++;
1648 
1649         if (!_animatable_remains(*si))
1650             continue;
1651 
1652         const bool success = _raise_remains(a, si.index(), beha, pow,
1653                                             hitting, as, nas, god, actual,
1654                                             apply_lovelessness, mon, &motions);
1655 
1656         if (actual && success)
1657         {
1658             if (!quiet && you.see_cell(a))
1659                 _display_undead_motions(motions);
1660         }
1661 
1662         any_success |= success;
1663 
1664         break;
1665     }
1666 
1667     if (motions_r && you.see_cell(a))
1668         *motions_r |= motions;
1669 
1670     if (number_found == 0)
1671         return -1;
1672 
1673     if (!any_success)
1674         return 0;
1675 
1676     return 1;
1677 }
1678 
animate_dead(actor * caster,int pow,beh_type beha,unsigned short hitting,actor * as,string nas,god_type god,bool actual)1679 int animate_dead(actor *caster, int pow, beh_type beha,
1680                  unsigned short hitting, actor *as, string nas, god_type god,
1681                  bool actual)
1682 {
1683     int number_raised = 0;
1684     int number_seen   = 0;
1685     int motions       = 0;
1686 
1687     for (radius_iterator ri(caster->pos(), LOS_NO_TRANS); ri; ++ri)
1688     {
1689         // There may be many corpses on the same spot.
1690         while (animate_remains(*ri, CORPSE_BODY, beha, pow, hitting,
1691                                as, nas, god,
1692                                actual, true, true, 0, &motions) > 0)
1693         {
1694             number_raised++;
1695             if (you.see_cell(*ri))
1696                 number_seen++;
1697 
1698             // For the tracer, we don't care about exact count (and the
1699             // corpse is not gone).
1700             if (!actual)
1701                 break;
1702         }
1703     }
1704 
1705     if (actual && number_seen > 0)
1706         _display_undead_motions(motions);
1707 
1708     return number_raised;
1709 }
1710 
find_animatable_skeleton(coord_def c)1711 coord_def find_animatable_skeleton(coord_def c)
1712 {
1713     for (radius_iterator ri(c, LOS_NO_TRANS); ri; ++ri)
1714     {
1715         for (stack_iterator si(*ri, true); si; ++si)
1716         {
1717             if (si->base_type == OBJ_CORPSES
1718                 && mons_class_can_be_zombified(si->mon_type)
1719                 && mons_skeleton(si->mon_type))
1720             {
1721                 return *ri;
1722             }
1723         }
1724     }
1725     return coord_def(-1,-1);
1726 }
1727 
cast_animate_skeleton(int pow,god_type god,bool fail)1728 spret cast_animate_skeleton(int pow, god_type god, bool fail)
1729 {
1730     if (rude_stop_summoning_prompt())
1731         return spret::abort;
1732 
1733     coord_def skel_loc = find_animatable_skeleton(you.pos());
1734     if (!in_bounds(skel_loc))
1735         return spret::abort;
1736 
1737     fail_check();
1738 
1739     canned_msg(MSG_ANIMATE_REMAINS);
1740 
1741     for (stack_iterator si(skel_loc, true); si; ++si)
1742     {
1743         if (si->base_type == OBJ_CORPSES
1744             && mons_class_can_be_zombified(si->mon_type)
1745             && mons_skeleton(si->mon_type))
1746         {
1747             if (si->is_type(OBJ_CORPSES, CORPSE_BODY))
1748             {
1749                 butcher_corpse(*si);
1750                 mpr("Before your eyes, flesh is ripped from the corpse!");
1751                 // Only convert the top one.
1752             }
1753 
1754             const int animate_skel_result =
1755                 animate_remains(skel_loc, CORPSE_SKELETON, BEH_FRIENDLY,
1756                                 pow, MHITYOU, &you, "", god);
1757 
1758             if (animate_skel_result != -1)
1759             {
1760                 if (animate_skel_result == 0)
1761                     mpr("...but the skeleton had no space to rise!");
1762                 return spret::success;
1763             }
1764         }
1765     }
1766 
1767     canned_msg(MSG_NOTHING_HAPPENS);
1768 
1769     return spret::success;
1770 }
1771 
cast_animate_dead(int pow,god_type god,bool fail)1772 spret cast_animate_dead(int pow, god_type god, bool fail)
1773 {
1774     if (rude_stop_summoning_prompt())
1775         return spret::abort;
1776 
1777     fail_check();
1778     canned_msg(MSG_CALL_DEAD);
1779 
1780     if (!animate_dead(&you, pow, BEH_FRIENDLY, MHITYOU, &you, "", god))
1781         canned_msg(MSG_NOTHING_HAPPENS);
1782 
1783     return spret::success;
1784 }
1785 
1786 // returns an item index, or -1 on failure
find_simulacrable_corpse(coord_def c)1787 int find_simulacrable_corpse(coord_def c)
1788 {
1789     int co = -1;
1790     for (stack_iterator si(c, true); si; ++si)
1791     {
1792         if (si->is_type(OBJ_CORPSES, CORPSE_BODY)
1793             && mons_class_can_be_zombified(si->mon_type))
1794         {
1795             co = si->index();
1796         }
1797     }
1798     return co;
1799 }
1800 
1801 /**
1802  * Have the player cast simulacrum.
1803  *
1804  * @param pow The spell power.
1805  * @param god The god casting the spell.
1806  * @param fail If true, return spret::fail unless the spell is aborted.
1807  * @return spret::abort if no viable corpse was at the player's location,
1808  *         otherwise spret::success or spret::fail based on fail.
1809  */
cast_simulacrum(int pow,god_type god,bool fail)1810 spret cast_simulacrum(int pow, god_type god, bool fail)
1811 {
1812     if (rude_stop_summoning_prompt())
1813         return spret::abort;
1814 
1815     int co = find_simulacrable_corpse(you.pos());
1816 
1817     if (co < 0)
1818         return spret::abort;
1819 
1820     fail_check();
1821     canned_msg(MSG_ANIMATE_REMAINS);
1822 
1823     item_def& corpse = env.item[co];
1824     // How many simulacra can this particular monster give at maximum.
1825     int num_sim  = 1 + random2(max_corpse_chunks(corpse.mon_type));
1826     num_sim  = stepdown_value(num_sim, 4, 4, 12, 12);
1827 
1828     mgen_data mg = _pal_data(MONS_SIMULACRUM, 0, god, SPELL_SIMULACRUM);
1829     mg.set_base(corpse.mon_type);
1830 
1831     // Can't create more than the max for the monster.
1832     int how_many = min(8, 4 + random2(pow) / 20);
1833     how_many = min<int>(how_many, num_sim);
1834 
1835     if (corpse.props.exists(CORPSE_HEADS))
1836     {
1837         // Avoid headless hydras. Unlike Animate Dead, still consume the flesh.
1838         if (corpse.props[CORPSE_HEADS].get_short() == 0)
1839         {
1840             // No monster to conj_verb with :(
1841             mprf("The headless hydra simulacr%s immediately collapse%s into snow!",
1842                  how_many == 1 ? "um" : "a", how_many == 1 ? "s" : "");
1843             if (!turn_corpse_into_skeleton(corpse))
1844                 butcher_corpse(corpse, false);
1845             return spret::success;
1846         }
1847         mg.props[MGEN_NUM_HEADS] = corpse.props[CORPSE_HEADS].get_short();
1848     }
1849 
1850     int count = 0;
1851     for (int i = 0; i < how_many; ++i)
1852     {
1853         if (monster *sim = create_monster(mg))
1854         {
1855             count++;
1856             sim->add_ench(mon_enchant(ENCH_FAKE_ABJURATION, 4));
1857         }
1858     }
1859 
1860     if (!count)
1861         canned_msg(MSG_NOTHING_HAPPENS);
1862     else if (!turn_corpse_into_skeleton(corpse))
1863         butcher_corpse(corpse, false);
1864 
1865     return spret::success;
1866 }
1867 
pick_random_wraith()1868 monster_type pick_random_wraith()
1869 {
1870     return random_choose_weighted(1, MONS_SHADOW_WRAITH,
1871                                   5, MONS_WRAITH,
1872                                   2, MONS_FREEZING_WRAITH,
1873                                   2, MONS_PHANTASMAL_WARRIOR);
1874 }
1875 
cast_haunt(int pow,const coord_def & where,god_type god,bool fail)1876 spret cast_haunt(int pow, const coord_def& where, god_type god, bool fail)
1877 {
1878     if (rude_stop_summoning_prompt())
1879         return spret::abort;
1880 
1881     monster* m = monster_at(where);
1882 
1883     if (m == nullptr)
1884     {
1885         fail_check();
1886         mpr("An evil force gathers, but it quickly dissipates.");
1887         return spret::success; // still losing a turn
1888     }
1889     else if (m->wont_attack())
1890     {
1891         mpr("You cannot haunt those who bear you no hostility.");
1892         return spret::abort;
1893     }
1894 
1895     int mi = m->mindex();
1896     ASSERT(!invalid_monster_index(mi));
1897 
1898     if (stop_attack_prompt(m, false, you.pos()))
1899         return spret::abort;
1900 
1901     fail_check();
1902 
1903     bool friendly = true;
1904     int success = 0;
1905     int to_summon = stepdown_value(2 + (random2(pow) / 10) + (random2(pow) / 10),
1906                                    2, 2, 6, -1);
1907 
1908     while (to_summon--)
1909     {
1910         const monster_type mon = pick_random_wraith();
1911 
1912         if (monster *mons = create_monster(
1913                 mgen_data(mon, BEH_FRIENDLY, where, mi, MG_FORCE_BEH)
1914                 .set_summoned(&you, 3, SPELL_HAUNT, god)))
1915         {
1916             success++;
1917             mons->add_ench(mon_enchant(ENCH_HAUNTING, 1, m, INFINITE_DURATION));
1918             mons->foe = mi;
1919         }
1920     }
1921 
1922     if (success > 1)
1923     {
1924         mpr(friendly ? "Insubstantial figures form in the air."
1925                      : "You sense hostile presences.");
1926     }
1927     else if (success)
1928     {
1929         mpr(friendly ? "An insubstantial figure forms in the air."
1930                      : "You sense a hostile presence.");
1931     }
1932     else
1933     {
1934         canned_msg(MSG_NOTHING_HAPPENS);
1935         return spret::success;
1936     }
1937 
1938     return spret::success;
1939 }
1940 
1941 
1942 
1943 static spell_type servitor_spells[] =
1944 {
1945     // primary spells
1946     SPELL_LEHUDIBS_CRYSTAL_SPEAR,
1947     SPELL_IOOD,
1948     SPELL_IRON_SHOT,
1949     SPELL_BOLT_OF_FIRE,
1950     SPELL_BOLT_OF_COLD,
1951     SPELL_POISON_ARROW,
1952     SPELL_CONJURE_BALL_LIGHTNING,
1953     SPELL_LIGHTNING_BOLT,
1954     SPELL_BOLT_OF_MAGMA,
1955     SPELL_BOLT_OF_DRAINING,
1956     SPELL_VENOM_BOLT,
1957     SPELL_FIREBALL,
1958     SPELL_THROW_ICICLE,
1959     SPELL_STONE_ARROW,
1960     SPELL_LRD,
1961     SPELL_AIRSTRIKE,
1962     SPELL_FORCE_LANCE,
1963     // secondary spells
1964     SPELL_FREEZING_CLOUD,
1965     SPELL_POISONOUS_CLOUD,
1966     SPELL_MEPHITIC_CLOUD,
1967     // fallback spells
1968     SPELL_STICKY_FLAME,
1969     SPELL_THROW_FLAME,
1970     SPELL_THROW_FROST,
1971     SPELL_FREEZE,
1972     SPELL_FLAME_TONGUE,
1973     SPELL_STING,
1974     SPELL_SANDBLAST,
1975     SPELL_SHOCK,
1976     SPELL_MAGIC_DART,
1977 };
1978 
1979 /**
1980  * Return the spell a player spellforged servitor would use, for the spell
1981  * description.
1982  *
1983  * @return spell_type  The spell a player servitor would use if cast now
1984  */
player_servitor_spell()1985 spell_type player_servitor_spell()
1986 {
1987     for (const spell_type spell : servitor_spells)
1988         if (you.has_spell(spell) && raw_spell_fail(spell) < 50)
1989             return spell;
1990     return SPELL_NO_SPELL;
1991 }
1992 
spell_servitorable(spell_type to_serve)1993 bool spell_servitorable(spell_type to_serve)
1994 {
1995     for (const spell_type spell : servitor_spells)
1996         if (spell == to_serve)
1997             return true;
1998     return false;
1999 }
2000 
2001 /**
2002  * Initialize the given spellforged servitor's HD and spellset, based on the
2003  * caster's spellpower and castable attack spells.
2004  *
2005  * @param mon       The spellforged servitor to be initialized.
2006  * @param caster    The entity summoning the servitor; may be the player.
2007  */
_init_servitor_monster(monster & mon,const actor & caster)2008 static void _init_servitor_monster(monster &mon, const actor& caster)
2009 {
2010     const monster* caster_mon = caster.as_monster();
2011     const int pow = caster_mon ?
2012                         6 * caster_mon->spell_hd(SPELL_SPELLFORGED_SERVITOR) :
2013                         calc_spell_power(SPELL_SPELLFORGED_SERVITOR, true);
2014 
2015     mon.set_hit_dice(9 + div_rand_round(pow, 14));
2016     mon.max_hit_points = mon.hit_points = 60 + roll_dice(7, 5); // 67-95
2017                                             // mhp doesn't vary with HD
2018     int spell_levels = 0;
2019 
2020     for (const spell_type spell : servitor_spells)
2021     {
2022         if (caster.has_spell(spell)
2023             && (caster_mon || raw_spell_fail(spell) < 50))
2024         {
2025             mon.spells.emplace_back(spell, 0, MON_SPELL_WIZARD);
2026             spell_levels += spell_difficulty(spell);
2027 
2028             // Player servitors take a single spell
2029             if (!caster_mon)
2030                 break;
2031         }
2032     }
2033 
2034     // Fix up frequencies now that we know the total number of spell levels.
2035     const int base_freq = caster_mon ? 67 : 150;
2036     for (auto& slot : mon.spells)
2037     {
2038         slot.freq = max(1, div_rand_round(spell_difficulty(slot.spell)
2039                                           * base_freq,
2040                                           spell_levels));
2041     }
2042     mon.props[CUSTOM_SPELLS_KEY].get_bool() = true;
2043 }
2044 
init_servitor(monster * servitor,actor * caster)2045 void init_servitor(monster* servitor, actor* caster)
2046 {
2047     ASSERT(servitor); // XXX: change to monster &servitor
2048     ASSERT(caster); // XXX: change to actor &caster
2049     _init_servitor_monster(*servitor, *caster);
2050 
2051     if (you.can_see(*caster))
2052     {
2053         mprf("%s %s a servant imbued with %s destructive magic!",
2054              caster->name(DESC_THE).c_str(),
2055              caster->conj_verb("summon").c_str(),
2056              caster->pronoun(PRONOUN_POSSESSIVE).c_str());
2057     }
2058     else
2059         simple_monster_message(*servitor, " appears!");
2060 
2061     int shortest_range = LOS_RADIUS + 1;
2062     for (const mon_spell_slot &slot : servitor->spells)
2063     {
2064         if (slot.spell == SPELL_NO_SPELL)
2065             continue;
2066 
2067         int range = spell_range(slot.spell, 100, false);
2068         if (range < shortest_range)
2069             shortest_range = range;
2070     }
2071     servitor->props["ideal_range"].get_int() = shortest_range;
2072 }
2073 
cast_spellforged_servitor(int,god_type god,bool fail)2074 spret cast_spellforged_servitor(int /*pow*/, god_type god, bool fail)
2075 {
2076     if (rude_stop_summoning_prompt())
2077         return spret::abort;
2078 
2079     fail_check();
2080 
2081     mgen_data mdata = _pal_data(MONS_SPELLFORGED_SERVITOR, 4, god,
2082                                 SPELL_SPELLFORGED_SERVITOR);
2083 
2084     if (monster* mon = create_monster(mdata))
2085         init_servitor(mon, &you);
2086     else
2087         canned_msg(MSG_NOTHING_HAPPENS);
2088 
2089     return spret::success;
2090 }
2091 
find_battlesphere(const actor * agent)2092 monster* find_battlesphere(const actor* agent)
2093 {
2094     if (agent->props.exists("battlesphere"))
2095         return monster_by_mid(agent->props["battlesphere"].get_int());
2096     else
2097         return nullptr;
2098 }
2099 
_battlesphere_hd(int pow,bool random=true)2100 static int _battlesphere_hd(int pow, bool random = true)
2101 {
2102     if (random)
2103         return 1 + div_rand_round(pow, 11);
2104     return 1 + pow / 11;
2105 }
2106 
_battlesphere_damage(int hd)2107 static dice_def _battlesphere_damage(int hd)
2108 {
2109     return dice_def(2, 5 + hd);
2110 }
2111 
battlesphere_damage(int pow)2112 dice_def battlesphere_damage(int pow)
2113 {
2114     return _battlesphere_damage(_battlesphere_hd(pow, false));
2115 }
2116 
cast_battlesphere(actor * agent,int pow,god_type god,bool fail)2117 spret cast_battlesphere(actor* agent, int pow, god_type god, bool fail)
2118 {
2119     if (agent->is_player() && rude_stop_summoning_prompt())
2120         return spret::abort;
2121 
2122     fail_check();
2123 
2124     monster* battlesphere;
2125     if (agent->is_player() && (battlesphere = find_battlesphere(&you)))
2126     {
2127         bool recalled = false;
2128         if (!you.can_see(*battlesphere))
2129         {
2130             coord_def empty;
2131             if (find_habitable_spot_near(agent->pos(), MONS_BATTLESPHERE, 3, false, empty)
2132                 && battlesphere->move_to_pos(empty))
2133             {
2134                 recalled = true;
2135             }
2136         }
2137 
2138         if (recalled)
2139         {
2140             mpr("You recall your battlesphere and imbue it with additional"
2141                 " charge.");
2142         }
2143         else
2144             mpr("You imbue your battlesphere with additional charge.");
2145 
2146         battlesphere->battlecharge = min(20, (int) battlesphere->battlecharge
2147                                               + 4 + random2(pow + 10) / 10);
2148 
2149         // Increase duration
2150         mon_enchant abj = battlesphere->get_ench(ENCH_FAKE_ABJURATION);
2151         abj.duration = min(abj.duration + (7 + roll_dice(2, pow)) * 10, 500);
2152         battlesphere->update_ench(abj);
2153     }
2154     else
2155     {
2156         ASSERT(!find_battlesphere(agent));
2157         mgen_data mg (MONS_BATTLESPHERE,
2158                       agent->is_player() ? BEH_FRIENDLY
2159                                          : SAME_ATTITUDE(agent->as_monster()),
2160                       agent->pos(), agent->mindex());
2161         mg.set_summoned(agent, 0, SPELL_BATTLESPHERE, god);
2162         mg.hd = _battlesphere_hd(pow);
2163         battlesphere = create_monster(mg);
2164 
2165         if (battlesphere)
2166         {
2167             int dur = min((7 + roll_dice(2, pow)) * 10, 500);
2168             battlesphere->add_ench(mon_enchant(ENCH_FAKE_ABJURATION, 1, 0, dur));
2169             battlesphere->summoner = agent->mid;
2170             agent->props["battlesphere"].get_int() = battlesphere->mid;
2171 
2172             if (agent->is_player())
2173                 mpr("You conjure a globe of magical energy.");
2174             else
2175             {
2176                 if (you.can_see(*agent) && you.can_see(*battlesphere))
2177                 {
2178                     simple_monster_message(*agent->as_monster(),
2179                                            " conjures a globe of magical energy!");
2180                 }
2181                 else if (you.can_see(*battlesphere))
2182                     simple_monster_message(*battlesphere, " appears!");
2183                 battlesphere->props["band_leader"].get_int() = agent->mid;
2184             }
2185             battlesphere->battlecharge = 4 + random2(pow + 10) / 10;
2186             battlesphere->foe = agent->mindex();
2187             battlesphere->target = agent->pos();
2188         }
2189         else if (agent->is_player() || you.can_see(*agent))
2190             canned_msg(MSG_NOTHING_HAPPENS);
2191     }
2192 
2193     return spret::success;
2194 }
2195 
end_battlesphere(monster * mons,bool killed)2196 void end_battlesphere(monster* mons, bool killed)
2197 {
2198     // Should only happen if you dismiss it in wizard mode, I think
2199     if (!mons)
2200         return;
2201 
2202     actor* agent = actor_by_mid(mons->summoner);
2203     if (agent)
2204         agent->props.erase("battlesphere");
2205 
2206     if (!killed)
2207     {
2208         if (agent && agent->is_player())
2209         {
2210             if (you.can_see(*mons))
2211             {
2212                 if (mons->battlecharge == 0)
2213                 {
2214                     mpr("Your battlesphere expends the last of its energy"
2215                         " and dissipates.");
2216                 }
2217                 else
2218                     mpr("Your battlesphere wavers and loses cohesion.");
2219             }
2220             else
2221                 mpr("You feel your bond with your battlesphere wane.");
2222         }
2223         else if (you.can_see(*mons))
2224             simple_monster_message(*mons, " dissipates.");
2225 
2226         if (!cell_is_solid(mons->pos()))
2227             place_cloud(CLOUD_MAGIC_TRAIL, mons->pos(), 3 + random2(3), mons);
2228 
2229         monster_die(*mons, KILL_RESET, NON_MONSTER);
2230     }
2231 }
2232 
battlesphere_can_mirror(spell_type spell)2233 bool battlesphere_can_mirror(spell_type spell)
2234 {
2235     return spell_typematch(spell, spschool::conjuration)
2236            && spell != SPELL_BATTLESPHERE
2237            && spell != SPELL_SPELLFORGED_SERVITOR;
2238 }
2239 
aim_battlesphere(actor * agent,spell_type spell)2240 bool aim_battlesphere(actor* agent, spell_type spell)
2241 {
2242     //Is this spell something that will trigger the battlesphere?
2243     if (battlesphere_can_mirror(spell))
2244     {
2245         monster* battlesphere = find_battlesphere(agent);
2246 
2247         // If we've somehow gotten separated from the battlesphere (ie:
2248         // abyss level teleport), bail out and cancel the battlesphere bond
2249         if (!battlesphere)
2250         {
2251             agent->props.erase("battlesphere");
2252             return false;
2253         }
2254 
2255         // In case the battlesphere was in the middle of a (failed)
2256         // target-seeking action, cancel it so that it can focus on a new
2257         // target
2258         reset_battlesphere(battlesphere);
2259         battlesphere->props.erase("foe");
2260 
2261         // Pick a random baddie in LOS
2262         vector<actor *> targets;
2263         for (actor_near_iterator ai(agent, LOS_NO_TRANS); ai; ++ai)
2264         {
2265             if (battlesphere->can_see(**ai)
2266                 && !mons_aligned(agent, *ai)
2267                 && (ai->is_player() || !mons_is_firewood(*ai->as_monster())))
2268             {
2269                 targets.push_back(*ai);
2270             }
2271         }
2272 
2273         if (targets.empty())
2274             return false;
2275 
2276         const actor * target = *random_iterator(targets);
2277         battlesphere->foe = target->mindex();
2278         battlesphere->props["foe"] = battlesphere->foe;
2279         battlesphere->props["ready"] = true;
2280 
2281         return true;
2282     }
2283 
2284     return false;
2285 }
2286 
trigger_battlesphere(actor * agent)2287 bool trigger_battlesphere(actor* agent)
2288 {
2289     monster* battlesphere = find_battlesphere(agent);
2290     if (!battlesphere)
2291         return false;
2292 
2293     if (battlesphere->props.exists("ready"))
2294     {
2295         battlesphere->props.erase("ready");
2296         battlesphere->props["firing"] = true;
2297 
2298         // Since monsters may be acting out of sequence, give the battlesphere
2299         // enough energy to attempt to fire this round, and requeue if it's
2300         // already passed its turn
2301         if (agent->is_monster())
2302         {
2303             battlesphere->speed_increment = 100;
2304             queue_monster_for_action(battlesphere);
2305         }
2306 
2307         return true;
2308     }
2309 
2310     return false;
2311 }
2312 
2313 // Called at the start of each round. Cancels firing orders given in the
2314 // previous round, if the battlesphere was not able to execute them fully
2315 // before the next player action
reset_battlesphere(monster * mons)2316 void reset_battlesphere(monster* mons)
2317 {
2318     if (!mons || mons->type != MONS_BATTLESPHERE)
2319         return;
2320 
2321     mons->props.erase("ready");
2322 
2323     if (mons->props.exists("tracking"))
2324     {
2325         mons->props.erase("tracking");
2326         mons->props.erase("firing");
2327         if (mons->props.exists("foe"))
2328             mons->foe = mons->props["foe"].get_int();
2329         mons->behaviour = BEH_SEEK;
2330     }
2331 }
2332 
fire_battlesphere(monster * mons)2333 bool fire_battlesphere(monster* mons)
2334 {
2335     if (!mons || mons->type != MONS_BATTLESPHERE)
2336         return false;
2337 
2338     actor* agent = actor_by_mid(mons->summoner);
2339 
2340     if (!agent || !agent->alive())
2341     {
2342         end_battlesphere(mons, false);
2343         return false;
2344     }
2345 
2346     bool used = false;
2347 
2348     if (mons->props.exists("firing") && mons->battlecharge > 0)
2349     {
2350         if (mons->props.exists("tracking"))
2351         {
2352             if (mons->pos() == mons->props["tracking_target"].get_coord())
2353             {
2354                 mons->props.erase("tracking");
2355                 if (mons->props.exists("foe"))
2356                     mons->foe = mons->props["foe"].get_int();
2357                 mons->behaviour = BEH_SEEK;
2358             }
2359             else // Currently tracking, but have not reached target pos
2360             {
2361                 mons->target = mons->props["tracking_target"].get_coord();
2362                 return false;
2363             }
2364         }
2365         else
2366         {
2367             // If the battlesphere forgot its foe (due to being out of los),
2368             // remind it
2369             if (mons->props.exists("foe"))
2370                 mons->foe = mons->props["foe"].get_int();
2371         }
2372 
2373         // Set up the beam.
2374         bolt beam;
2375         beam.source_name = "battlesphere";
2376 
2377         // If we are locked onto a foe, use its current position
2378         if (!invalid_monster_index(mons->foe) && env.mons[mons->foe].alive())
2379             beam.target = env.mons[mons->foe].pos();
2380 
2381         // Sanity check: if we have somehow ended up targeting ourselves, bail
2382         if (beam.target == mons->pos())
2383         {
2384             mprf(MSGCH_ERROR, "Battlesphere targeting itself? Fixing.");
2385             mons->props.erase("firing");
2386             mons->props.erase("foe");
2387             return false;
2388         }
2389 
2390         beam.name       = "barrage of energy";
2391         beam.range      = LOS_RADIUS;
2392         beam.hit        = AUTOMATIC_HIT;
2393         beam.damage     = _battlesphere_damage(mons->get_hit_dice());
2394         beam.glyph      = dchar_glyph(DCHAR_FIRED_ZAP);
2395         beam.colour     = MAGENTA;
2396         beam.flavour    = BEAM_MMISSILE;
2397         beam.pierce     = false;
2398 
2399         // Fire tracer.
2400         fire_tracer(mons, beam);
2401 
2402         // Never fire if we would hurt the caster, and ensure that the beam
2403         // would hit at least SOMETHING, unless it was targeted at empty space
2404         // in the first place
2405         if (beam.friend_info.count == 0 && beam.foe_info.count > 0)
2406         {
2407             beam.thrower = (agent->is_player()) ? KILL_YOU : KILL_MON;
2408             simple_monster_message(*mons, " fires!");
2409             beam.fire();
2410 
2411             used = true;
2412             // Decrement # of volleys left and possibly expire the battlesphere.
2413             if (--mons->battlecharge == 0)
2414                 end_battlesphere(mons, false);
2415 
2416             mons->props.erase("firing");
2417         }
2418         // If we are firing at something, try to find a nearby position
2419         // from which we could safely fire at it
2420         else
2421         {
2422             const bool empty_beam = (beam.foe_info.count == 0);
2423             for (distance_iterator di(mons->pos(), true, true, 2); di; ++di)
2424             {
2425                 if (*di == beam.target || actor_at(*di)
2426                     || cell_is_solid(*di)
2427                     || !agent->see_cell(*di))
2428                 {
2429                     continue;
2430                 }
2431 
2432                 beam.source = *di;
2433                 beam.is_tracer = true;
2434                 beam.friend_info.reset();
2435                 beam.foe_info.reset();
2436                 beam.fire();
2437                 if (beam.friend_info.count == 0
2438                     && (beam.foe_info.count > 0 || empty_beam))
2439                 {
2440                     if (empty_beam
2441                         && find(beam.path_taken.begin(), beam.path_taken.end(),
2442                                 beam.target) == beam.path_taken.end())
2443                     {
2444                         continue;
2445                     }
2446 
2447                     mons->firing_pos = coord_def(0, 0);
2448                     mons->target = *di;
2449                     mons->behaviour = BEH_WANDER;
2450                     mons->props["foe"] = mons->foe;
2451                     mons->props["tracking"] = true;
2452                     mons->foe = MHITNOT;
2453                     mons->props["tracking_target"] = *di;
2454                     break;
2455                 }
2456             }
2457 
2458             // If we didn't find a better firing position nearby, cancel firing
2459             if (!mons->props.exists("tracking"))
2460                 mons->props.erase("firing");
2461         }
2462     }
2463 
2464     // If our last target is dead, or the player wandered off, resume
2465     // following the player
2466     if ((mons->foe == MHITNOT || !mons->can_see(*agent)
2467          || (!invalid_monster_index(mons->foe)
2468              && !agent->can_see(env.mons[mons->foe])))
2469         && !mons->props.exists("tracking"))
2470     {
2471         mons->foe = agent->mindex();
2472     }
2473 
2474     return used;
2475 }
2476 
prism_hd(int pow,bool random)2477 int prism_hd(int pow, bool random)
2478 {
2479     if (random)
2480         return div_rand_round(pow, 10);
2481     return pow / 10;
2482 }
2483 
cast_fulminating_prism(actor * caster,int pow,const coord_def & where,bool fail)2484 spret cast_fulminating_prism(actor* caster, int pow,
2485                                   const coord_def& where, bool fail)
2486 {
2487     if (grid_distance(where, caster->pos())
2488         > spell_range(SPELL_FULMINANT_PRISM, pow))
2489     {
2490         if (caster->is_player())
2491             mpr("That's too far away.");
2492         return spret::abort;
2493     }
2494 
2495     if (cell_is_solid(where))
2496     {
2497         if (caster->is_player())
2498             mpr("You can't conjure that within a solid object!");
2499         return spret::abort;
2500     }
2501 
2502     actor* victim = monster_at(where);
2503     if (victim)
2504     {
2505         if (caster->can_see(*victim))
2506         {
2507             if (caster->is_player())
2508                 mpr("You can't place the prism on a creature.");
2509             return spret::abort;
2510         }
2511 
2512         fail_check();
2513 
2514         // FIXME: maybe should do _paranoid_option_disable() here?
2515         if (caster->is_player()
2516             || (you.can_see(*caster) && you.see_cell(where)))
2517         {
2518             if (you.can_see(*victim))
2519             {
2520                 mprf("%s %s.", victim->name(DESC_THE).c_str(),
2521                                victim->conj_verb("twitch").c_str());
2522             }
2523             else
2524                 canned_msg(MSG_GHOSTLY_OUTLINE);
2525         }
2526         return spret::success;      // Don't give free detection!
2527     }
2528 
2529     fail_check();
2530 
2531     const int hd = prism_hd(pow);
2532 
2533     mgen_data prism_data = mgen_data(MONS_FULMINANT_PRISM,
2534                                      caster->is_player()
2535                                      ? BEH_FRIENDLY
2536                                      : SAME_ATTITUDE(caster->as_monster()),
2537                                      where, MHITNOT, MG_FORCE_PLACE);
2538     prism_data.set_summoned(caster, 0, SPELL_FULMINANT_PRISM);
2539     prism_data.hd = hd;
2540     monster *prism = create_monster(prism_data);
2541 
2542     if (prism)
2543     {
2544         if (caster->observable())
2545         {
2546             mprf("%s %s a prism of explosive energy!",
2547                  caster->name(DESC_THE).c_str(),
2548                  caster->conj_verb("conjure").c_str());
2549         }
2550         else if (you.can_see(*prism))
2551             mprf("A prism of explosive energy appears from nowhere!");
2552     }
2553     else if (you.can_see(*caster))
2554         canned_msg(MSG_NOTHING_HAPPENS);
2555 
2556     return spret::success;
2557 }
2558 
find_spectral_weapon(const actor * agent)2559 monster* find_spectral_weapon(const actor* agent)
2560 {
2561     if (agent->props.exists("spectral_weapon"))
2562         return monster_by_mid(agent->props["spectral_weapon"].get_int());
2563     else
2564         return nullptr;
2565 }
2566 
end_spectral_weapon(monster * mons,bool killed,bool quiet)2567 void end_spectral_weapon(monster* mons, bool killed, bool quiet)
2568 {
2569     // Should only happen if you dismiss it in wizard mode, I think
2570     if (!mons)
2571         return;
2572 
2573     actor *owner = actor_by_mid(mons->summoner);
2574 
2575     if (owner)
2576         owner->props.erase("spectral_weapon");
2577 
2578     if (!quiet)
2579     {
2580         if (you.can_see(*mons))
2581         {
2582             simple_monster_message(*mons, " fades away.",
2583                                    MSGCH_MONSTER_DAMAGE, MDAM_DEAD);
2584         }
2585         else if (owner && owner->is_player())
2586             mpr("You feel your bond with your spectral weapon wane.");
2587     }
2588 
2589     if (!killed)
2590         monster_die(*mons, KILL_RESET, NON_MONSTER);
2591 }
2592 
_setup_infestation(bolt & beam,int pow)2593 static void _setup_infestation(bolt &beam, int pow)
2594 {
2595     beam.name         = "infestation";
2596     beam.aux_source   = "infestation";
2597     beam.flavour      = BEAM_INFESTATION;
2598     beam.glyph        = dchar_glyph(DCHAR_FIRED_BURST);
2599     beam.colour       = GREEN;
2600     beam.source_id    = MID_PLAYER;
2601     beam.thrower      = KILL_YOU;
2602     beam.is_explosion = true;
2603     beam.ex_size      = 2;
2604     beam.ench_power   = pow;
2605     beam.origin_spell = SPELL_INFESTATION;
2606 }
2607 
cast_infestation(int pow,bolt & beam,bool fail)2608 spret cast_infestation(int pow, bolt &beam, bool fail)
2609 {
2610     if (cell_is_solid(beam.target))
2611     {
2612         canned_msg(MSG_SOMETHING_IN_WAY);
2613         return spret::abort;
2614     }
2615 
2616     fail_check();
2617 
2618     _setup_infestation(beam, pow);
2619     mpr("You call forth a plague of scarabs!");
2620     beam.explode();
2621 
2622     return spret::success;
2623 }
2624 
2625 struct summon_cap
2626 {
2627     int type_cap;
2628     int timeout;
2629 };
2630 
2631 // spell type, cap, timeout
2632 static const map<spell_type, summon_cap> summonsdata =
2633 {
2634     // Beasts
2635     { SPELL_SUMMON_SMALL_MAMMAL,        { 4, 2 } },
2636     { SPELL_CALL_CANINE_FAMILIAR,       { 1, 2 } },
2637     { SPELL_SUMMON_ICE_BEAST,           { 3, 3 } },
2638     { SPELL_SUMMON_HYDRA,               { 3, 2 } },
2639     { SPELL_SUMMON_MANA_VIPER,          { 2, 2 } },
2640     // General monsters
2641     { SPELL_CALL_IMP,                   { 3, 3 } },
2642     { SPELL_MONSTROUS_MENAGERIE,        { 3, 2 } },
2643     { SPELL_SUMMON_HORRIBLE_THINGS,     { 8, 8 } },
2644     { SPELL_SHADOW_CREATURES,           { 4, 2 } },
2645     { SPELL_SUMMON_LIGHTNING_SPIRE,     { 1, 2 } },
2646     { SPELL_SUMMON_GUARDIAN_GOLEM,      { 1, 2 } },
2647     { SPELL_SPELLFORGED_SERVITOR,       { 1, 2 } },
2648     { SPELL_ANIMATE_ARMOUR,       { 1, 2 } },
2649     // Monster spells
2650     { SPELL_SUMMON_UFETUBUS,            { 8, 2 } },
2651     { SPELL_SUMMON_HELL_BEAST,          { 8, 2 } },
2652     { SPELL_SUMMON_UNDEAD,              { 8, 2 } },
2653     { SPELL_SUMMON_DRAKES,              { 4, 2 } },
2654     { SPELL_SUMMON_MUSHROOMS,           { 8, 2 } },
2655     { SPELL_SUMMON_EYEBALLS,            { 4, 2 } },
2656     { SPELL_WATER_ELEMENTALS,           { 3, 2 } },
2657     { SPELL_FIRE_ELEMENTALS,            { 3, 2 } },
2658     { SPELL_EARTH_ELEMENTALS,           { 3, 2 } },
2659     { SPELL_AIR_ELEMENTALS,             { 3, 2 } },
2660     { SPELL_SUMMON_SPECTRAL_ORCS,       { 3, 2 } },
2661     { SPELL_FIRE_SUMMON,                { 4, 2 } },
2662     { SPELL_SUMMON_MINOR_DEMON,         { 3, 3 } },
2663     { SPELL_CALL_LOST_SOUL,             { 3, 2 } },
2664     { SPELL_SUMMON_VERMIN,              { 5, 2 } },
2665     { SPELL_FORCEFUL_INVITATION,        { 3, 1 } },
2666     { SPELL_PLANEREND,                  { 6, 1 } },
2667     { SPELL_SUMMON_DRAGON,              { 4, 8 } },
2668     { SPELL_PHANTOM_MIRROR,             { 4, 1 } },
2669     { SPELL_FAKE_MARA_SUMMON,           { 2, 1 } },
2670     { SPELL_SUMMON_EMPEROR_SCORPIONS,   { 6, 2 } },
2671     { SPELL_SUMMON_SCARABS,             { 8, 1 } },
2672     { SPELL_SUMMON_HOLIES,              { 4, 2 } },
2673     { SPELL_SUMMON_EXECUTIONERS,        { 3, 1 } },
2674     { SPELL_AWAKEN_EARTH,               { 9, 2 } },
2675     { SPELL_GREATER_SERVANT_MAKHLEB,    { 1, 2 } },
2676     { SPELL_SUMMON_GREATER_DEMON,       { 3, 2 } },
2677     { SPELL_SUMMON_DEMON,               { 3, 2 } },
2678     { SPELL_SUMMON_TZITZIMITL,          { 3, 1 } },
2679     { SPELL_SUMMON_HELL_SENTINEL,          { 3, 1 } },
2680 };
2681 
summons_are_capped(spell_type spell)2682 bool summons_are_capped(spell_type spell)
2683 {
2684     ASSERT_RANGE(spell, 0, NUM_SPELLS);
2685     return summonsdata.count(spell);
2686 }
2687 
summons_limit(spell_type spell)2688 int summons_limit(spell_type spell)
2689 {
2690     const summon_cap *cap = map_find(summonsdata, spell);
2691     return cap ? cap->type_cap : 0;
2692 }
2693 
_spell_has_variable_cap(spell_type spell)2694 static bool _spell_has_variable_cap(spell_type spell)
2695 {
2696     return spell == SPELL_SHADOW_CREATURES
2697            || spell == SPELL_MONSTROUS_MENAGERIE;
2698 }
2699 
_expire_capped_summon(monster * mon,int delay,bool recurse)2700 static void _expire_capped_summon(monster* mon, int delay, bool recurse)
2701 {
2702     // Timeout the summon
2703     mon_enchant abj = mon->get_ench(ENCH_ABJ);
2704     abj.duration = delay;
2705     mon->update_ench(abj);
2706     // Mark our cap abjuration so we don't keep abjuring the same
2707     // one if creating multiple summons (also, should show a status light).
2708     mon->add_ench(ENCH_SUMMON_CAPPED);
2709 
2710     if (recurse && mon->props.exists("summon_id"))
2711     {
2712         const int summon_id = mon->props["summon_id"].get_int();
2713         for (monster_iterator mi; mi; ++mi)
2714         {
2715             // Summoner check should be technically unnecessary, but saves
2716             // scanning props for all monsters on the level.
2717             if (mi->summoner == mon->summoner
2718                 && mi->props.exists("summon_id")
2719                 && mi->props["summon_id"].get_int() == summon_id
2720                 && !mi->has_ench(ENCH_SUMMON_CAPPED))
2721             {
2722                 _expire_capped_summon(*mi, delay, false);
2723             }
2724         }
2725     }
2726 }
2727 
2728 // Call when a monster has been summoned, to manage this summoner's caps.
summoned_monster(const monster * mons,const actor * caster,spell_type spell)2729 void summoned_monster(const monster *mons, const actor *caster,
2730                       spell_type spell)
2731 {
2732     const summon_cap *cap = map_find(summonsdata, spell);
2733     if (!cap) // summons aren't capped
2734         return;
2735 
2736     int max_this_time = cap->type_cap;
2737 
2738     // Cap large abominations and tentacled monstrosities separately
2739     if (spell == SPELL_SUMMON_HORRIBLE_THINGS)
2740     {
2741         max_this_time = (mons->type == MONS_ABOMINATION_LARGE ? max_this_time * 3 / 4
2742                                                               : max_this_time * 1 / 4);
2743     }
2744 
2745     monster* oldest_summon = 0;
2746     int oldest_duration = 0;
2747 
2748     // Linked summons that have already been counted once
2749     set<int> seen_ids;
2750 
2751     int count = 1;
2752     for (monster_iterator mi; mi; ++mi)
2753     {
2754         if (mons == *mi)
2755             continue;
2756 
2757         int duration = 0;
2758         int stype    = 0;
2759         const bool summoned = mi->is_summoned(&duration, &stype);
2760         if (summoned && stype == spell && caster->mid == mi->summoner
2761             && mons_aligned(caster, *mi))
2762         {
2763             // Count large abominations and tentacled monstrosities separately
2764             if (spell == SPELL_SUMMON_HORRIBLE_THINGS && mi->type != mons->type)
2765                 continue;
2766 
2767             if (_spell_has_variable_cap(spell) && mi->props.exists("summon_id"))
2768             {
2769                 const int id = mi->props["summon_id"].get_int();
2770 
2771                 // Skip any linked summon whose set we have seen already,
2772                 // otherwise add it to the list of seen summon IDs
2773                 if (seen_ids.find(id) == seen_ids.end())
2774                     seen_ids.insert(id);
2775                 else
2776                     continue;
2777             }
2778 
2779             count++;
2780 
2781             // If this summon is the oldest (well, the closest to expiry)
2782             // remember it (unless already expiring due to a cap)
2783             if (!mi->has_ench(ENCH_SUMMON_CAPPED)
2784                 && (!oldest_summon || duration < oldest_duration))
2785             {
2786                 oldest_summon = *mi;
2787                 oldest_duration = duration;
2788             }
2789         }
2790     }
2791 
2792     if (oldest_summon && count > max_this_time)
2793         _expire_capped_summon(oldest_summon, cap->timeout * 5, true);
2794 }
2795 
count_summons(const actor * summoner,spell_type spell)2796 int count_summons(const actor *summoner, spell_type spell)
2797 {
2798     int count = 0;
2799     for (monster_iterator mi; mi; ++mi)
2800     {
2801         if (summoner == *mi)
2802             continue;
2803 
2804         int stype    = 0;
2805         const bool summoned = mi->is_summoned(nullptr, &stype);
2806         if (summoned && stype == spell && summoner->mid == mi->summoner
2807             && mons_aligned(summoner, *mi))
2808         {
2809             count++;
2810         }
2811     }
2812 
2813     return count;
2814 }
2815 
_create_briar_patch(coord_def & target)2816 static bool _create_briar_patch(coord_def& target)
2817 {
2818     mgen_data mgen = mgen_data(MONS_BRIAR_PATCH, BEH_FRIENDLY, target,
2819             MHITNOT, MG_FORCE_PLACE, GOD_FEDHAS);
2820     mgen.hd = mons_class_hit_dice(MONS_BRIAR_PATCH) +
2821         you.skill_rdiv(SK_INVOCATIONS);
2822     mgen.set_summoned(&you, min(2 + you.skill_rdiv(SK_INVOCATIONS, 1, 5), 6),
2823             SPELL_NO_SPELL);
2824 
2825     if (create_monster(mgen))
2826     {
2827         mpr("A briar patch grows up from the ground.");
2828         return true;
2829     }
2830 
2831     return false;
2832 }
2833 
fedhas_wall_of_briars()2834 bool fedhas_wall_of_briars()
2835 {
2836     // How many adjacent open spaces are there?
2837     vector<coord_def> adjacent;
2838     for (adjacent_iterator adj_it(you.pos()); adj_it; ++adj_it)
2839     {
2840         if (monster_habitable_grid(MONS_BRIAR_PATCH, env.grid(*adj_it))
2841             && !actor_at(*adj_it))
2842         {
2843             adjacent.push_back(*adj_it);
2844         }
2845     }
2846 
2847     // Don't prompt if we can't do anything.
2848     if (adjacent.empty())
2849     {
2850         mpr("No empty adjacent squares.");
2851         return false;
2852     }
2853 
2854     int created_count = 0;
2855     for (auto p : adjacent)
2856     {
2857         if (_create_briar_patch(p))
2858             created_count++;
2859     }
2860 
2861     if (!created_count)
2862         canned_msg(MSG_NOTHING_HAPPENS);
2863 
2864     return created_count;
2865 }
2866 
_overgrow_wall(const coord_def & pos)2867 static void _overgrow_wall(const coord_def &pos)
2868 {
2869     const dungeon_feature_type feat = env.grid(pos);
2870     const string what = feature_description(feat, NUM_TRAPS, "", DESC_THE);
2871 
2872     if (monster_at(pos))
2873     {
2874         mprf("Something unseen blocks growth in %s.", what.c_str());
2875         return;
2876     }
2877 
2878     destroy_wall(pos);
2879 
2880     const monster_type mon = random_choose_weighted(4, MONS_OKLOB_SAPLING,
2881                                                     4, MONS_BURNING_BUSH,
2882                                                     4, MONS_WANDERING_MUSHROOM,
2883                                                     1, MONS_BALLISTOMYCETE,
2884                                                     1, MONS_OKLOB_PLANT);
2885     mgen_data mgen(mon, BEH_FRIENDLY, pos, MHITYOU, MG_FORCE_PLACE);
2886     mgen.hd = mons_class_hit_dice(mon) + you.skill_rdiv(SK_INVOCATIONS);
2887     mgen.set_summoned(&you, min(3 + you.skill_rdiv(SK_INVOCATIONS, 1, 5), 6),
2888             SPELL_NO_SPELL);
2889     if (const monster* const plant = create_monster(mgen))
2890     {
2891         mprf("%s is torn apart as %s grows in its place.", what.c_str(),
2892                 plant->name(DESC_A).c_str());
2893     }
2894     // XXX: Maybe try to make this revert the terrain if a monster isn't placed.
2895     else
2896         mprf("%s falls apart, but nothing grows.", what.c_str());
2897 }
2898 
fedhas_overgrow(bool fail)2899 spret fedhas_overgrow(bool fail)
2900 {
2901     targeter_overgrow tgt;
2902     direction_chooser_args args;
2903     args.hitfunc = &tgt;
2904     args.restricts = DIR_TARGET;
2905     args.mode = TARG_ANY;
2906     args.range = LOS_RADIUS;
2907     args.just_looking = false;
2908     args.needs_path = false;
2909     args.top_prompt = "Aiming: <white>Overgrow</white>";
2910     dist sdirect;
2911     direction(sdirect, args);
2912     if (!sdirect.isValid)
2913         return spret::abort;
2914 
2915     fail_check();
2916 
2917     for (auto site : tgt.affected_positions)
2918         _overgrow_wall(site);
2919 
2920     return spret::success;
2921 }
2922 
fedhas_grow_ballistomycete(bool fail)2923 spret fedhas_grow_ballistomycete(bool fail)
2924 {
2925     dist spd;
2926     bolt beam;
2927     beam.range = 2;
2928     direction_chooser_args args;
2929     args.restricts = DIR_TARGET;
2930     args.mode = TARG_HOSTILE;
2931     args.needs_path = false;
2932     if (!spell_direction(spd, beam, &args))
2933         return spret::abort;
2934 
2935     if (grid_distance(beam.target, you.pos()) > 2 || !in_bounds(beam.target))
2936     {
2937         mpr("That's too far away.");
2938         return spret::abort;
2939     }
2940 
2941     if (!monster_habitable_grid(MONS_BALLISTOMYCETE, env.grid(beam.target)))
2942     {
2943         mpr("You can't grow a ballistomycete there.");
2944         return spret::abort;
2945     }
2946 
2947     monster* mons = monster_at(beam.target);
2948     if (mons)
2949     {
2950         if (you.can_see(*mons))
2951         {
2952             mpr("That space is already occupied.");
2953             return spret::abort;
2954         }
2955 
2956         fail_check();
2957 
2958         // invisible monster
2959         mpr("Something you can't see occupies that space!");
2960         return spret::success;
2961     }
2962 
2963     fail_check();
2964 
2965     mgen_data mgen(MONS_BALLISTOMYCETE, BEH_FRIENDLY, beam.target, MHITYOU,
2966             MG_FORCE_BEH | MG_FORCE_PLACE | MG_AUTOFOE);
2967     mgen.hd = mons_class_hit_dice(MONS_BALLISTOMYCETE) +
2968         you.skill_rdiv(SK_INVOCATIONS);
2969     mgen.set_summoned(&you, min(3 + you.skill_rdiv(SK_INVOCATIONS, 1, 5), 6),
2970             SPELL_NO_SPELL);
2971 
2972     if (create_monster(mgen))
2973         mpr("A ballistomycete grows from the ground.");
2974     else
2975         canned_msg(MSG_NOTHING_HAPPENS);
2976 
2977     return spret::success;
2978 }
2979 
fedhas_grow_oklob(bool fail)2980 spret fedhas_grow_oklob(bool fail)
2981 {
2982     dist spd;
2983     bolt beam;
2984     beam.range = 2;
2985     direction_chooser_args args;
2986     args.restricts = DIR_TARGET;
2987     args.mode = TARG_HOSTILE;
2988     args.needs_path = false;
2989     if (!spell_direction(spd, beam, &args))
2990         return spret::abort;
2991 
2992     if (grid_distance(beam.target, you.pos()) > 2 || !in_bounds(beam.target))
2993     {
2994         mpr("That's too far away.");
2995         return spret::abort;
2996     }
2997 
2998     if (!monster_habitable_grid(MONS_OKLOB_PLANT, env.grid(beam.target)))
2999     {
3000         mpr("You can't grow an oklob plant there.");
3001         return spret::abort;
3002     }
3003 
3004     monster* mons = monster_at(beam.target);
3005     if (mons)
3006     {
3007         if (you.can_see(*mons))
3008         {
3009             mpr("That space is already occupied.");
3010             return spret::abort;
3011         }
3012 
3013         fail_check();
3014 
3015         // invisible monster
3016         mpr("Something you can't see is occupying that space!");
3017         return spret::success;
3018     }
3019 
3020     fail_check();
3021 
3022     mgen_data mgen(MONS_OKLOB_PLANT, BEH_FRIENDLY, beam.target, MHITYOU,
3023             MG_FORCE_BEH | MG_FORCE_PLACE | MG_AUTOFOE);
3024     mgen.hd = mons_class_hit_dice(MONS_OKLOB_PLANT) +
3025         you.skill_rdiv(SK_INVOCATIONS);
3026     mgen.set_summoned(&you, min(3 + you.skill_rdiv(SK_INVOCATIONS, 1, 5), 6),
3027             SPELL_NO_SPELL);
3028 
3029     if (create_monster(mgen))
3030         mpr("An oklob plant grows from the ground.");
3031     else
3032         canned_msg(MSG_NOTHING_HAPPENS);
3033 
3034     return spret::success;
3035 
3036 }
3037 
cast_foxfire(actor & agent,int pow,god_type god,bool fail)3038 spret cast_foxfire(actor &agent, int pow, god_type god, bool fail)
3039 {
3040     bool see_space = false;
3041     for (adjacent_iterator ai(agent.pos()); ai; ++ai)
3042     {
3043         if (cell_is_solid(*ai))
3044             continue;
3045         if (actor_at(*ai) && agent.can_see(*actor_at(*ai)))
3046             continue;
3047         see_space = true;
3048         break;
3049     }
3050 
3051     if (agent.is_player() && !see_space)
3052     {
3053         mpr("There is not enough space to conjure foxfire!");
3054         return spret::abort;
3055     }
3056 
3057     fail_check();
3058 
3059     int created = 0;
3060 
3061     for (fair_adjacent_iterator ai(agent.pos()); ai; ++ai)
3062     {
3063         const auto att = agent.is_player() ? BEH_FRIENDLY
3064                                            : SAME_ATTITUDE(agent.as_monster());
3065         mgen_data fox(MONS_FOXFIRE, att,
3066                       *ai, MHITNOT, MG_FORCE_PLACE | MG_AUTOFOE);
3067         fox.set_summoned(&agent, 0, SPELL_FOXFIRE, god);
3068         fox.hd = pow;
3069         monster *foxfire;
3070 
3071         if (!cell_is_solid(*ai) && !monster_at(*ai)
3072             && (foxfire = create_monster(fox)))
3073         {
3074             ++created;
3075             foxfire->add_ench(ENCH_SHORT_LIVED);
3076             foxfire->steps_remaining = you.current_vision + 2;
3077 
3078             // Avoid foxfire without targets always moving towards (0,0)
3079             if (!foxfire->get_foe()
3080                 || !foxfire->get_foe()->is_monster() && !agent.is_monster())
3081             {
3082                 set_random_target(foxfire);
3083             }
3084         }
3085 
3086         if (created == 2)
3087             break;
3088     }
3089 
3090     if (created && you.see_cell(agent.pos()))
3091     {
3092         mprf("%s conjure%s some foxfire!",
3093              agent.name(DESC_THE).c_str(),
3094              agent.is_monster() ? "s" : "");
3095     }
3096     else if (agent.is_player())
3097         canned_msg(MSG_NOTHING_HAPPENS);
3098 
3099     return spret::success;
3100 }
3101