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