Use cell_is_solid where appropriate.
[crawl:crawl.git] / crawl-ref / source / evoke.cc
1 /**
2  * @file
3  * @brief Functions for using some of the wackier inventory items.
4 **/
5
6 #include "AppHdr.h"
7 #include <math.h>
8
9 #include "evoke.h"
10
11 #include <algorithm>
12 #include <cstdlib>
13 #include <string.h>
14
15 #include "externs.h"
16
17 #include "act-iter.h"
18 #include "areas.h"
19 #include "artefact.h"
20 #include "cloud.h"
21 #include "coordit.h"
22 #include "decks.h"
23 #include "dungeon.h"
24 #include "effects.h"
25 #include "env.h"
26 #include "exercise.h"
27 #include "fight.h"
28 #include "food.h"
29 #include "ghost.h"
30 #include "invent.h"
31 #include "items.h"
32 #include "item_use.h"
33 #include "itemprop.h"
34 #include "libutil.h"
35 #include "losglobal.h"
36 #include "mapmark.h"
37 #include "melee_attack.h"
38 #include "message.h"
39 #include "mon-chimera.h"
40 #include "mon-pick.h"
41 #include "mon-place.h"
42 #include "mgen_data.h"
43 #include "misc.h"
44 #include "player-stats.h"
45 #include "godconduct.h"
46 #include "shout.h"
47 #include "skills.h"
48 #include "skills2.h"
49 #include "spl-book.h"
50 #include "spl-cast.h"
51 #include "spl-clouds.h"
52 #include "spl-summoning.h"
53 #include "spl-util.h"
54 #include "state.h"
55 #include "stuff.h"
56 #include "target.h"
57 #include "terrain.h"
58 #include "traps.h"
59 #include "view.h"
60 #include "viewchar.h"
61 #include "xom.h"
62
63 #ifdef USE_TILE
64 #include "tilepick.h"
65 #endif
66
67 void shadow_lantern_effect()
68 {
69     int n = div_rand_round(you.time_taken, 10);
70     for (int i = 0; i < n; ++i)
71     {
72         if (x_chance_in_y(you.skill_rdiv(SK_EVOCATIONS, 1, 5) + 1, 14))
73         {
74             create_monster(mgen_data(MONS_SHADOW, BEH_FRIENDLY, &you, 2, 0,
75                                     you.pos(), MHITNOT));
76
77             did_god_conduct(DID_NECROMANCY, 1);
78         }
79     }
80 }
81
82 extern bool apply_berserk_penalty;
83
84 static bool _reaching_weapon_attack(const item_def& wpn)
85 {
86     if (you.caught())
87     {
88         mprf("You cannot attack while %s.", held_status());
89         return false;
90     }
91
92     bool targ_mid = false;
93     dist beam;
94
95     direction_chooser_args args;
96     args.restricts = DIR_TARGET;
97     args.mode = TARG_HOSTILE;
98     args.range = 2;
99     args.top_prompt = "Attack whom?";
100     args.cancel_at_self = true;
101     targetter_reach hitfunc(&you, REACH_TWO);
102     args.hitfunc = &hitfunc;
103
104     direction(beam, args);
105
106     if (!beam.isValid)
107     {
108         if (beam.isCancel)
109             canned_msg(MSG_OK);
110         return false;
111     }
112
113     if (beam.isMe())
114     {
115         canned_msg(MSG_UNTHINKING_ACT);
116         return false;
117     }
118
119     if (you.confused())
120         beam.confusion_fuzz(2);
121
122     const coord_def delta = beam.target - you.pos();
123     const int x_distance  = abs(delta.x);
124     const int y_distance  = abs(delta.y);
125     monster* mons = monster_at(beam.target);
126     // don't allow targetting of submerged monsters (includes trapdoor spiders)
127     if (mons && mons->submerged())
128         mons = NULL;
129
130     const int x_first_middle = you.pos().x + (delta.x)/2;
131     const int y_first_middle = you.pos().y + (delta.y)/2;
132     const int x_second_middle = beam.target.x - (delta.x)/2;
133     const int y_second_middle = beam.target.y - (delta.y)/2;
134     const coord_def first_middle(x_first_middle, y_first_middle);
135     const coord_def second_middle(x_second_middle, y_second_middle);
136
137     if (x_distance > 2 || y_distance > 2)
138     {
139         mpr("Your weapon cannot reach that far!");
140         return false; // Shouldn't happen with confused swings
141     }
142
143     // Calculate attack delay now in case we have to apply it.
144     melee_attack attk(&you, NULL);
145     const int attack_delay = attk.calc_attack_delay();
146
147     if (!feat_is_reachable_past(grd(first_middle))
148         && !feat_is_reachable_past(grd(second_middle)))
149     {
150         // Might also be a granite statue/orcish idol which you
151         // can reach _past_.
152         if (you.confused())
153         {
154             mpr("You swing wildly and hit a wall.");
155             you.time_taken = attack_delay;
156             make_hungry(3, true);
157             return true;
158         }
159         else
160         {
161             mpr("There's a wall in the way.");
162             return false;
163         }
164     }
165
166     // Failing to hit someone due to a friend blocking is infuriating,
167     // shadow-boxing empty space is not (and would be abusable to wait
168     // with no penalty).
169     if (mons)
170         apply_berserk_penalty = false;
171
172     // Choose one of the two middle squares (which might be the same).
173     const coord_def middle =
174         (!feat_is_reachable_past(grd(first_middle)) ? second_middle :
175          (!feat_is_reachable_past(grd(second_middle)) ? first_middle :
176           (coinflip() ? first_middle : second_middle)));
177
178     // Check for a monster in the way. If there is one, it blocks the reaching
179     // attack 50% of the time, and the attack tries to hit it if it is hostile.
180
181     // If we're attacking more than a space away...
182     if (x_distance > 1 || y_distance > 1)
183     {
184         bool success = true;
185         monster *midmons;
186         if ((midmons = monster_at(middle))
187             && !midmons->submerged())
188         {
189             // This chance should possibly depend on your skill with
190             // the weapon.
191             if (coinflip())
192             {
193                 success = false;
194                 beam.target = middle;
195                 mons = midmons;
196                 targ_mid = true;
197                 if (mons->wont_attack())
198                 {
199                     // Let's assume friendlies cooperate.
200                     mpr("You could not reach far enough!");
201                     you.time_taken = attack_delay;
202                     make_hungry(3, true);
203                     return true;
204                 }
205             }
206         }
207         if (success)
208             mpr("You reach to attack!");
209         else
210         {
211             mprf("%s is in the way.",
212                  mons->observable() ? mons->name(DESC_THE).c_str()
213                                     : "Something you can't see");
214         }
215     }
216
217     if (mons == NULL)
218     {
219         // Must return true, otherwise you get a free discovery
220         // of invisible monsters.
221
222         if (you.confused())
223         {
224             mprf("You swing wildly%s", beam.isMe() ?
225                                        " and almost hit yourself!" : ".");
226         }
227         else
228             mpr("You attack empty space.");
229         you.time_taken = attack_delay;
230         make_hungry(3, true);
231         return true;
232     }
233     else if (!fight_melee(&you, mons))
234     {
235         if (targ_mid)
236         {
237             // turn_is_over may have been reset to false by fight_melee, but
238             // a failed attempt to reach further should not be free; instead,
239             // charge the same as a successful attempt.
240             you.time_taken = attack_delay;
241             make_hungry(3, true);
242             you.turn_is_over = true;
243         }
244         else
245         {
246             canned_msg(MSG_OK);
247             return false;
248         }
249     }
250
251     return true;
252 }
253
254 static bool _evoke_horn_of_geryon(item_def &item)
255 {
256     // Note: This assumes that the Vestibule has not been changed.
257     bool rc = false;
258
259     if (silenced(you.pos()))
260     {
261         mpr("You can't produce a sound!");
262         return false;
263     }
264     else if (player_in_branch(BRANCH_VESTIBULE_OF_HELL))
265     {
266         mpr("You produce a weird and mournful sound.");
267
268         if (you.char_direction == GDT_ASCENDING)
269         {
270             mpr("But nothing happens...");
271             return false;
272         }
273
274         for (int count_x = 0; count_x < GXM; count_x++)
275             for (int count_y = 0; count_y < GYM; count_y++)
276             {
277                 if (grd[count_x][count_y] == DNGN_STONE_ARCH)
278                 {
279                     rc = true;
280
281                     map_marker *marker =
282                         env.markers.find(coord_def(count_x, count_y),
283                                          MAT_FEATURE);
284
285                     if (marker)
286                     {
287                         map_feature_marker *featm =
288                             dynamic_cast<map_feature_marker*>(marker);
289                         // [ds] Ensure we're activating the correct feature
290                         // markers. Feature markers are also used for other
291                         // things, notably to indicate the return point from
292                         // a labyrinth or portal vault.
293                         switch (featm->feat)
294                         {
295                         case DNGN_ENTER_COCYTUS:
296                         case DNGN_ENTER_DIS:
297                         case DNGN_ENTER_GEHENNA:
298                         case DNGN_ENTER_TARTARUS:
299                             grd[count_x][count_y] = featm->feat;
300                             env.markers.remove(marker);
301                             item.plus2++;
302                             break;
303                         default:
304                             break;
305                         }
306                     }
307                 }
308             }
309
310         if (rc)
311             mpr("Your way has been unbarred.");
312     }
313     else
314     {
315         mpr("You produce a hideous howling noise!", MSGCH_SOUND);
316         create_monster(
317             mgen_data::hostile_at(MONS_HELL_BEAST, "the horn of Geryon",
318                 true, 4, 0, you.pos()));
319     }
320     return rc;
321 }
322
323 static bool _efreet_flask(int slot)
324 {
325     bool friendly = x_chance_in_y(300 + you.skill(SK_EVOCATIONS, 10), 600);
326
327     mpr("You open the flask...");
328
329     monster *mons =
330         create_monster(
331             mgen_data(MONS_EFREET,
332                       friendly ? BEH_FRIENDLY : BEH_HOSTILE,
333                       &you, 0, 0, you.pos(),
334                       MHITYOU, MG_FORCE_BEH));
335
336     if (mons)
337     {
338         mpr("...and a huge efreet comes out.");
339
340         if (player_angers_monster(mons))
341             friendly = false;
342
343         if (silenced(you.pos()))
344         {
345             mpr(friendly ? "It nods graciously at you."
346                          : "It snaps in your direction!", MSGCH_TALK_VISUAL);
347         }
348         else
349         {
350             mpr(friendly ? "\"Thank you for releasing me!\""
351                          : "It howls insanely!", MSGCH_TALK);
352         }
353     }
354     else
355         canned_msg(MSG_NOTHING_HAPPENS);
356
357     dec_inv_item_quantity(slot, 1);
358
359     return true;
360 }
361
362 static bool _check_crystal_ball()
363 {
364     if (you.species == SP_DJINNI)
365     {
366         mpr("These balls have not yet been approved for use by djinn. "
367             "(OOC: they're supposed to work, but need a redesign.)");
368         return false;
369     }
370
371     if (you.intel() <= 1)
372     {
373         mpr("You lack the intelligence to focus on the shapes in the ball.");
374         return false;
375     }
376
377     if (you.confused())
378     {
379         mpr("You are unable to concentrate on the shapes in the ball.");
380         return false;
381     }
382
383     if (you.magic_points == you.max_magic_points)
384     {
385         mpr("With no energy to recover, the crystal ball of energy is "
386             "presently useless to you.");
387         return false;
388     }
389
390     if (you.skill(SK_EVOCATIONS) < 2)
391     {
392         mpr("You lack the skill to use this item.");
393         return false;
394     }
395
396     return true;
397 }
398
399 bool disc_of_storms(bool drac_breath)
400 {
401     const int fail_rate = 30 - you.skill(SK_EVOCATIONS);
402     bool rc = false;
403
404     if (x_chance_in_y(fail_rate, 100) && !drac_breath)
405         canned_msg(MSG_NOTHING_HAPPENS);
406     else if (x_chance_in_y(fail_rate, 100) && !drac_breath)
407         mpr("The disc glows for a moment, then fades.");
408     else if (x_chance_in_y(fail_rate, 100) && !drac_breath)
409         mpr("Little bolts of electricity crackle over the disc.");
410     else
411     {
412         if (!drac_breath)
413             mpr("The disc erupts in an explosion of electricity!");
414         rc = true;
415
416         const int disc_count = (drac_breath) ? roll_dice(2, 1 + you.experience_level / 7) :
417             roll_dice(2, 1 + you.skill_rdiv(SK_EVOCATIONS, 1, 7));
418
419         for (int i = 0; i < disc_count; ++i)
420         {
421             bolt beam;
422             const zap_type types[] = { ZAP_LIGHTNING_BOLT, ZAP_SHOCK,
423                                        ZAP_ORB_OF_ELECTRICITY };
424
425             const zap_type which_zap = RANDOM_ELEMENT(types);
426
427             // range has no tracer, so randomness is ok
428             beam.range = (drac_breath) ? you.experience_level / 3 + 5 :
429                 you.skill_rdiv(SK_EVOCATIONS, 1, 3) + 5; // 5--14
430             beam.source = you.pos();
431             beam.target = you.pos() + coord_def(random2(13)-6, random2(13)-6);
432             int power = (drac_breath) ? 25 + you.experience_level : 30
433                                            + you.skill(SK_EVOCATIONS, 2);
434             // Non-controlleable, so no player tracer.
435             zapping(which_zap, power, beam);
436
437         }
438
439         if (!drac_breath)
440         {
441             for (radius_iterator ri(you.pos(), LOS_RADIUS, false); ri; ++ri)
442             {
443                 if (!in_bounds(*ri) || cell_is_solid(*ri))
444                     continue;
445
446                 if (one_chance_in(60 - you.skill(SK_EVOCATIONS)))
447                 {
448                     place_cloud(CLOUD_RAIN, *ri,
449                                 random2(you.skill(SK_EVOCATIONS)), &you);
450                 }
451             }
452         }
453     }
454     return rc;
455 }
456
457 void tome_of_power(int slot)
458 {
459     if (you.form == TRAN_WISP)
460     {
461         crawl_state.zero_turns_taken();
462         return mpr("You can't handle books in this form.");
463     }
464
465     const int powc = 5 + you.skill(SK_EVOCATIONS)
466                        + roll_dice(5, you.skill(SK_EVOCATIONS));
467
468     msg::stream << "The book opens to a page covered in "
469                 << weird_writing() << '.' << endl;
470
471     you.turn_is_over = true;
472
473     if (player_mutation_level(MUT_BLURRY_VISION) > 0
474         && x_chance_in_y(player_mutation_level(MUT_BLURRY_VISION), 4))
475     {
476         mpr("The page is too blurry for you to read.");
477         return;
478     }
479
480     mpr("You find yourself reciting the magical words!");
481     practise(EX_WILL_READ_TOME);
482     count_action(CACT_EVOKE, EVOC_MISC);
483
484     if (x_chance_in_y(7, 50))
485     {
486         mpr("A cloud of weird smoke pours from the book's pages!");
487         big_cloud(random_smoke_type(), &you, you.pos(), 20, 10 + random2(8));
488         xom_is_stimulated(12);
489     }
490     else if (x_chance_in_y(2, 43))
491     {
492         mpr("A cloud of choking fumes pours from the book's pages!");
493         big_cloud(CLOUD_POISON, &you, you.pos(), 20, 7 + random2(5));
494         xom_is_stimulated(50);
495     }
496     else if (x_chance_in_y(2, 41))
497     {
498         mpr("A cloud of freezing gas pours from the book's pages!");
499         big_cloud(CLOUD_COLD, &you, you.pos(), 20, 8 + random2(5));
500         xom_is_stimulated(50);
501     }
502     else if (x_chance_in_y(3, 39))
503     {
504         if (one_chance_in(5))
505         {
506             mpr("The book disappears in a mighty explosion!");
507             dec_inv_item_quantity(slot, 1);
508         }
509
510         immolation(15, IMMOLATION_TOME, false);
511
512         xom_is_stimulated(200);
513     }
514     else if (one_chance_in(36))
515     {
516         if (create_monster(
517                 mgen_data::hostile_at(MONS_ABOMINATION_SMALL,
518                     "a tome of Destruction",
519                     true, 6, 0, you.pos())))
520         {
521             mpr("A horrible Thing appears!");
522             mpr("It doesn't look too friendly.");
523         }
524         xom_is_stimulated(200);
525     }
526     else
527     {
528         viewwindow();
529
530         const int temp_rand =
531             min(25, random2(23)
532                     + random2(you.skill_rdiv(SK_EVOCATIONS, 1, 3)));
533
534         const spell_type spell_casted =
535             ((temp_rand > 24) ? SPELL_LEHUDIBS_CRYSTAL_SPEAR :
536              (temp_rand > 21) ? SPELL_BOLT_OF_FIRE :
537              (temp_rand > 18) ? SPELL_BOLT_OF_COLD :
538              (temp_rand > 16) ? SPELL_LIGHTNING_BOLT :
539              (temp_rand > 10) ? SPELL_FIREBALL :
540              (temp_rand >  9) ? SPELL_VENOM_BOLT :
541              (temp_rand >  8) ? SPELL_BOLT_OF_DRAINING :
542              (temp_rand >  7) ? SPELL_BOLT_OF_INACCURACY :
543              (temp_rand >  6) ? SPELL_STICKY_FLAME_RANGE :
544              (temp_rand >  5) ? SPELL_TELEPORT_SELF :
545              (temp_rand >  4) ? SPELL_DAZZLING_SPRAY :
546              (temp_rand >  3) ? SPELL_POLYMORPH :
547              (temp_rand >  2) ? SPELL_MEPHITIC_CLOUD :
548              (temp_rand >  1) ? SPELL_THROW_FLAME :
549              (temp_rand >  0) ? SPELL_THROW_FROST
550                               : SPELL_MAGIC_DART);
551
552         your_spells(spell_casted, powc, false);
553     }
554 }
555
556 // return a slot that has manual for given skill, or -1 if none exists
557 // in case of multiple manuals the one with the fewest charges is returned
558 int manual_slot_for_skill(skill_type skill)
559 {
560     int slot = -1;
561     int charges = -1;
562
563     FixedVector<item_def,ENDOFPACK>::const_pointer iter = you.inv.begin();
564     for (;iter!=you.inv.end(); ++iter)
565     {
566         if (iter->base_type != OBJ_BOOKS || iter->sub_type != BOOK_MANUAL)
567             continue;
568
569         if (static_cast<skill_type>(iter->plus) != skill || iter->plus2 == 0)
570             continue;
571
572         if (slot != -1 && iter->plus2 > charges)
573             continue;
574
575         slot = iter - you.inv.begin();
576         charges = iter->plus2;
577     }
578
579     return slot;
580 }
581
582 bool skill_has_manual(skill_type skill)
583 {
584     return manual_slot_for_skill(skill) != -1;
585 };
586
587 void finish_manual(int slot)
588 {
589     item_def& manual(you.inv[slot]);
590     const skill_type skill = static_cast<skill_type>(manual.plus);
591
592     mprf("You have finished your manual of %s and toss it away.",
593          skill_name(skill));
594     dec_inv_item_quantity(slot, 1);
595 }
596
597 void get_all_manual_charges(vector<int> &charges)
598 {
599     charges.clear();
600
601     FixedVector<item_def,ENDOFPACK>::const_pointer iter = you.inv.begin();
602     for (;iter!=you.inv.end(); ++iter)
603     {
604         if (iter->base_type != OBJ_BOOKS || iter->sub_type != BOOK_MANUAL)
605             continue;
606
607         charges.push_back(iter->plus2);
608     }
609 }
610
611 void set_all_manual_charges(const vector<int> &charges)
612 {
613     vector<int>::const_iterator charge_iter = charges.begin();
614     FixedVector<item_def,ENDOFPACK>::pointer iter = you.inv.begin();
615     for (;iter!=you.inv.end(); ++iter)
616     {
617         if (iter->base_type != OBJ_BOOKS || iter->sub_type != BOOK_MANUAL)
618             continue;
619
620         ASSERT(charge_iter != charges.end());
621         iter->plus2 = *charge_iter;
622         charge_iter++;
623     }
624     ASSERT(charge_iter == charges.end());
625 }
626
627 string manual_skill_names(bool short_text)
628 {
629     skill_set skills;
630
631     FixedVector<item_def,ENDOFPACK>::const_pointer iter = you.inv.begin();
632     for (;iter!=you.inv.end(); ++iter)
633     {
634         if (iter->base_type != OBJ_BOOKS || iter->sub_type != BOOK_MANUAL)
635             continue;
636
637         skills.insert(static_cast<skill_type>(iter->plus));
638     }
639
640     if (short_text && skills.size() > 1)
641     {
642         char buf[40];
643         sprintf(buf, "%lu skills", (unsigned long) skills.size());
644         return string(buf);
645     }
646     else
647         return skill_names(skills);
648 }
649
650 static const pop_entry pop_beasts[] =
651 { // Box of Beasts
652   {  1,  3,  10,  DOWN, MONS_BUTTERFLY },
653   {  1,  5,  100, DOWN, MONS_RAT   },
654   {  1,  5,  100, DOWN, MONS_BAT   },
655   {  2,  8,  100, PEAK, MONS_JACKAL },
656   {  2, 10,  100, PEAK, MONS_ADDER },
657   {  4, 13,  100, PEAK, MONS_HOUND },
658   {  5, 15,  100, PEAK, MONS_WATER_MOCCASIN },
659   {  5, 15,  100, PEAK, MONS_SKY_BEAST },
660   {  8, 18,  100, PEAK, MONS_CROCODILE },
661   {  8, 18,  100, PEAK, MONS_HOG },
662   { 10, 20,  100, PEAK, MONS_ICE_BEAST },
663   { 10, 20,  100, PEAK, MONS_YAK },
664   { 10, 20,  100, PEAK, MONS_POLAR_BEAR },
665   { 10, 20,  100, PEAK, MONS_WYVERN },
666   { 10, 20,  100, PEAK, MONS_WOLF },
667   { 11, 22,  100, PEAK, MONS_ALLIGATOR },
668   { 11, 22,  100, PEAK, MONS_GRIZZLY_BEAR },
669   { 11, 22,  100, PEAK, MONS_WARG },
670   { 13, 25,  100, PEAK, MONS_ELEPHANT },
671   { 13, 25,  100, PEAK, MONS_GRIFFON },
672   { 13, 25,  100, PEAK, MONS_BLACK_BEAR },
673   { 15, 27,   50, PEAK, MONS_CATOBLEPAS },
674   { 15, 27,  100, PEAK, MONS_DEATH_YAK },
675   { 16, 27,  100, PEAK, MONS_ANACONDA },
676   { 16, 27,   50, PEAK, MONS_RAVEN },
677   { 18, 27,   50, UP,   MONS_DIRE_ELEPHANT },
678   { 20, 27,   25, UP,   MONS_DRAGON },
679   { 20, 27,   25, UP,   MONS_ANCIENT_BEAR },
680   { 23, 27,   10, UP,   MONS_APIS },
681   { 23, 27,   10, UP,   MONS_HELLEPHANT },
682   { 23, 27,   10, UP,   MONS_GOLDEN_DRAGON },
683   { 0,0,0,FLAT,MONS_0 }
684 };
685
686 static const pop_entry pop_spiders[] =
687 { // Sack of Spiders
688   {  0,  10,   10, DOWN, MONS_GIANT_MITE },
689   {  0,  15,   50, DOWN, MONS_SPIDER },
690   {  5,  20,  100, PEAK, MONS_TRAPDOOR_SPIDER },
691   {  8,  27,  100, PEAK, MONS_REDBACK },
692   { 12,  27,  100, PEAK, MONS_JUMPING_SPIDER },
693   { 15,  27,  100, PEAK, MONS_ORB_SPIDER },
694   { 18,  27,  100, PEAK, MONS_TARANTELLA },
695   { 20,  27,  100, PEAK, MONS_WOLF_SPIDER },
696   { 25,  27,    5,   UP, MONS_GHOST_MOTH },
697   { 0,0,0,FLAT,MONS_0 }
698 };
699
700 static bool _box_of_beasts_veto_mon(monster_type mon)
701 {
702     // Don't summon any beast that would anger your god.
703     return player_will_anger_monster(mon);
704 }
705
706 static bool _box_of_beasts(item_def &box)
707 {
708     mpr("You open the lid...");
709
710     if (!box.plus)
711     {
712         mpr("...but the box appears empty, and falls apart.");
713         ASSERT(in_inventory(box));
714         dec_inv_item_quantity(box.link, 1);
715         return false;
716     }
717
718     bool success = false;
719     monster* mons = NULL;
720
721     if (!one_chance_in(3))
722     {
723         // Invoke mon-pick with the custom list
724         int pick_level = max(1, you.skill(SK_EVOCATIONS));
725         monster_type mon = pick_monster_from(pop_beasts, pick_level,
726                                              _box_of_beasts_veto_mon);
727
728         // Second monster might be only half as good
729         int pick_level_2 = random_range(max(1,div_rand_round(pick_level,2)), pick_level);
730         monster_type mon2 = pick_monster_from(pop_beasts, pick_level_2,
731                                               _box_of_beasts_veto_mon);
732
733         // Third monster picked from anywhere up to max level
734         int pick_level_3 = random_range(1, pick_level);
735         monster_type mon3 = pick_monster_from(pop_beasts, pick_level_3,
736                                               _box_of_beasts_veto_mon);
737
738         mgen_data mg = mgen_data(MONS_CHIMERA,
739                                  BEH_FRIENDLY, &you,
740                                  3 + random2(3), 0,
741                                  you.pos(),
742                                  MHITYOU);
743         mg.define_chimera(mon, mon2, mon3);
744         mons = create_monster(mg);
745         if (mons)
746             success = true;
747     }
748
749     if (success)
750     {
751         mpr("...and something leaps out!");
752         xom_is_stimulated(10);
753         did_god_conduct(DID_CHAOS, random_range(5,10));
754         // Decrease charges
755         box.plus--;
756         // Let each part announce itself
757         for (int n = 0; n < NUM_CHIMERA_HEADS; ++n)
758         {
759             mons->ghost->acting_part = get_chimera_part(mons, n + 1);
760             handle_monster_shouts(mons, true);
761         }
762         mons->ghost->acting_part = MONS_0;
763     }
764     else
765         // Failed to create monster for some reason
766         mpr("...but nothing happens.");
767
768     return success;
769 }
770
771 static bool _sack_of_spiders(item_def &sack)
772 {
773     mpr("You reach into the bag...");
774
775     if (!sack.plus)
776     {
777         mpr("...but the bag is empty, and unravels at your touch.");
778         ASSERT(in_inventory(sack));
779         dec_inv_item_quantity(sack.link, 1);
780         return false;
781     }
782
783     bool success = false;
784
785     if (!one_chance_in(5))
786     {
787         int count = 1 + random2(3)
788                     + random2(div_rand_round(you.skill(SK_EVOCATIONS,10),40));
789         for (int n = 0; n < count; n++)
790         {
791             // Invoke mon-pick with our custom list
792             monster_type mon = pick_monster_from(pop_spiders,
793                                             max(1, you.skill(SK_EVOCATIONS)),
794                                             _box_of_beasts_veto_mon);
795             mgen_data mg = mgen_data(mon,
796                                      BEH_FRIENDLY, &you,
797                                      3 + random2(4), 0,
798                                      you.pos(),
799                                      MHITYOU);
800             if (create_monster(mg))
801                 success = true;
802         }
803     }
804
805     if (success)
806     {
807         // Also generate webs
808         int rad = LOS_RADIUS / 2 + 2;
809         for (radius_iterator ri(you.pos(), rad, false, true, true); ri; ++ri)
810         {
811             if (grd(*ri) == DNGN_FLOOR)
812             {
813                 int chance = 100 - (100 * (you.pos().range(*ri) - 1) / rad)
814                              - 2 * (27 - you.skill(SK_EVOCATIONS));
815                 if (x_chance_in_y(chance,100) && place_specific_trap(*ri, TRAP_WEB))
816                     // Reveal the trap
817                     grd(*ri) = DNGN_TRAP_WEB;
818             }
819         }
820         mpr("...and things crawl out!");
821         xom_is_stimulated(10);
822         // Decrease charges
823         sack.plus--;
824     }
825     else
826         // Failed to create monster for some reason
827         mpr("...but nothing happens.");
828
829     return success;
830 }
831
832 static bool _ball_of_energy(void)
833 {
834     bool ret = false;
835
836     mpr("You gaze into the crystal ball.");
837
838     int use = random2(you.skill(SK_EVOCATIONS, 6));
839
840     if (use < 2)
841         lose_stat(STAT_INT, 1 + random2avg(7, 2), false, "using a ball of energy");
842     else if (use < 5 && enough_mp(1, true))
843     {
844         mpr("You feel your power drain away!");
845         dec_mp(you.magic_points);
846     }
847     else if (use < 10)
848         confuse_player(10 + random2(10));
849     else
850     {
851         int proportional = (you.magic_points * 100) / you.max_magic_points;
852
853         if (random2avg(77 - you.skill(SK_EVOCATIONS, 2), 4) > proportional
854             || one_chance_in(25))
855         {
856             mpr("You feel your power drain away!");
857             dec_mp(you.magic_points);
858         }
859         else
860         {
861             mpr("You are suffused with power!");
862             inc_mp(5 + random2avg(you.skill(SK_EVOCATIONS), 2));
863
864             ret = true;
865         }
866     }
867
868     return ret;
869 }
870
871 static int _num_evoker_elementals()
872 {
873     int n = 1;
874     if (you.skill(SK_EVOCATIONS, 10) + random2(70) > 110)
875         ++n;
876     if (you.skill(SK_EVOCATIONS, 10) + random2(70) > 170)
877         ++n;
878     return n;
879 }
880
881 static vector<coord_def> _get_jitter_path(coord_def source, coord_def target,
882                                           bool jitter_start,
883                                           bolt &beam1, bolt &beam2)
884 {
885     const int NUM_TRIES = 10;
886     const int RANGE = 8;
887
888     bolt trace_beam;
889     trace_beam.source = source;
890     trace_beam.target = target;
891     trace_beam.aimed_at_spot = false;
892     trace_beam.is_tracer = true;
893     trace_beam.range = RANGE;
894     trace_beam.fire();
895
896     coord_def aim_dir = (source - target).sgn();
897
898     if (trace_beam.path_taken.back() != source)
899         target = trace_beam.path_taken.back();
900
901     if (jitter_start)
902     {
903         for (int n = 0; n < NUM_TRIES; ++n)
904         {
905             coord_def jitter = clamp_in_bounds(target + coord_def(random_range(-2, 2),
906                                                                   random_range(-2, 2)));
907             if (jitter == target || jitter == source || cell_is_solid(jitter))
908                 continue;
909
910             trace_beam.target = jitter;
911             trace_beam.fire();
912
913             coord_def delta = source - trace_beam.path_taken.back();
914             //Don't try to aim at targets in the opposite direction of main aim
915             if ((abs(aim_dir.x - delta.sgn().x) + abs(aim_dir.y - delta.sgn().y) >= 2)
916                  && !delta.origin())
917                 continue;
918
919             target = trace_beam.path_taken.back();
920             break;
921         }
922     }
923
924     vector<coord_def> path = trace_beam.path_taken;
925     unsigned int mid_i = (path.size() / 2);
926     coord_def mid = path[mid_i];
927
928     for (int n = 0; n < NUM_TRIES; ++n)
929     {
930         coord_def jitter = clamp_in_bounds(mid + coord_def(random_range(-3, 3),
931                                                            random_range(-3, 3)));
932         if (jitter == mid || jitter.distance_from(mid) < 2 || jitter == source
933             || cell_is_solid(jitter)
934             || !cell_see_cell(source, jitter, LOS_NO_TRANS)
935             || !cell_see_cell(target, jitter, LOS_NO_TRANS))
936         {
937             continue;
938         }
939
940         trace_beam.aimed_at_feet = false;
941         trace_beam.source = jitter;
942         trace_beam.target = target;
943         trace_beam.fire();
944
945         coord_def delta1 = source - jitter;
946         coord_def delta2 = source - trace_beam.path_taken.back();
947
948         //Don't try to aim at targets in the opposite direction of main aim
949         if (abs(aim_dir.x - delta1.sgn().x) + abs(aim_dir.y - delta1.sgn().y) >= 2
950             || abs(aim_dir.x - delta2.sgn().x) + abs(aim_dir.y - delta2.sgn().y) >= 2)
951         {
952             continue;
953         }
954
955         // Don't make l-turns
956         coord_def delta = jitter-target;
957         if (!delta.x || !delta.y)
958             continue;
959
960         bool match = false;
961         for (unsigned int i = 0; i < path.size(); ++i)
962         {
963             if (path[i] == jitter)
964             {
965                 match = true;
966                 break;
967             }
968         }
969         if (match)
970             continue;
971
972         mid = jitter;
973         break;
974     }
975
976     beam1.source = source;
977     beam1.target = mid;
978     beam1.range = RANGE;
979     beam1.aimed_at_spot = true;
980     beam1.is_tracer = true;
981     beam1.fire();
982     beam1.is_tracer = false;
983
984     beam2.source = mid;
985     beam2.target = target;
986     beam2.range = max(int(RANGE - beam1.path_taken.size()), mid.distance_from(target));
987     beam2.is_tracer = true;
988     beam2.fire();
989     beam2.is_tracer = false;
990
991     vector<coord_def> newpath;
992     newpath.insert(newpath.end(), beam1.path_taken.begin(), beam1.path_taken.end());
993     newpath.insert(newpath.end(), beam2.path_taken.begin(), beam2.path_taken.end());
994
995     return newpath;
996 }
997
998 static bool _check_path_overlap(const vector<coord_def> &path1,
999                                 const vector<coord_def> &path2, int match_len)
1000 {
1001     int max_len = min(path1.size(), path2.size());
1002     match_len = min(match_len, max_len-1);
1003
1004     // Check for overlap with previous path
1005     int matchs = 0;
1006     for (int i = 0; i < max_len; ++i)
1007     {
1008         if (path1[i] == path2[i])
1009             ++matchs;
1010         else
1011             matchs = 0;
1012
1013         if (matchs >= match_len)
1014             return true;
1015     }
1016
1017     return false;
1018 }
1019
1020 static bool _fill_flame_trails(coord_def source, coord_def target,
1021                                vector<bolt> &beams, vector<coord_def> &elementals,
1022                                int num)
1023 {
1024     const int NUM_TRIES = 10;
1025     vector<vector<coord_def> > paths;
1026     for (int n = 0; n < num; ++n)
1027     {
1028         int tries = 0;
1029         vector<coord_def> path;
1030         bolt beam1, beam2;
1031         while (++tries <= NUM_TRIES && path.empty())
1032         {
1033             path = _get_jitter_path(source, target, !paths.empty(), beam1, beam2);
1034             for (unsigned int i = 0; i < paths.size(); ++i)
1035             {
1036                 if (_check_path_overlap(path, paths[i], 3))
1037                 {
1038                     path.clear();
1039                     beam1 = bolt();
1040                     beam2 = bolt();
1041                     break;
1042                 }
1043             }
1044         }
1045
1046         if (!path.empty())
1047         {
1048             paths.push_back(path);
1049             beams.push_back(beam1);
1050             beams.push_back(beam2);
1051             if (path.size() > 3)
1052                 elementals.push_back(path.back());
1053         }
1054     }
1055
1056     return (!paths.empty());
1057 }
1058
1059 static bool _lamp_of_fire()
1060 {
1061     bolt base_beam;
1062     dist target;
1063
1064     const int pow = 8 + you.skill_rdiv(SK_EVOCATIONS, 9, 4);
1065     if (spell_direction(target, base_beam, DIR_TARGET, TARG_ANY, 8,
1066                         true, true, false, NULL,
1067                         "Aim the lamp in which direction?", true, NULL))
1068     {
1069         mpr("The flames dance!");
1070
1071         vector<bolt> beams;
1072         vector<coord_def> elementals;
1073         int num_trails = _num_evoker_elementals();
1074
1075         _fill_flame_trails(you.pos(), target.target, beams, elementals, num_trails);
1076
1077         for (unsigned int n = 0; n < beams.size(); ++n)
1078         {
1079             if (beams[n].source == beams[n].target)
1080                 continue;
1081
1082             beams[n].flavour     = BEAM_FIRE;
1083             beams[n].colour      = RED;
1084             beams[n].beam_source = MHITYOU;
1085             beams[n].thrower     = KILL_YOU;
1086             beams[n].is_beam     = true;
1087             beams[n].name        = "trail of fire";
1088             beams[n].hit         = 10 + (pow/8);
1089             beams[n].damage      = dice_def(2, 5 + pow/4);
1090             beams[n].ench_power  = 1 + (pow/10);
1091             beams[n].loudness    = 5;
1092             beams[n].fire();
1093         }
1094
1095         for (unsigned int n = 0; n < elementals.size(); ++n)
1096         {
1097             mgen_data mg(MONS_FIRE_ELEMENTAL, BEH_FRIENDLY, &you, 3,
1098                          SPELL_NO_SPELL, elementals[n], 0,
1099                          MG_FORCE_BEH | MG_FORCE_PLACE, GOD_NO_GOD,
1100                          MONS_FIRE_ELEMENTAL, 0, BLACK, PROX_CLOSE_TO_PLAYER);
1101             mg.hd = 6 + (pow/20);
1102             create_monster(mg);
1103         }
1104
1105         return true;
1106     }
1107
1108     return false;
1109 }
1110
1111 struct dist_sorter
1112 {
1113     coord_def pos;
1114     bool operator()(const actor* a, const actor* b)
1115     {
1116         return a->pos().distance_from(pos) > b->pos().distance_from(pos);
1117     }
1118 };
1119
1120 static int _gale_push_dist(const actor* agent, const actor* victim)
1121 {
1122     int dist = 1 + you.skill_rdiv(SK_EVOCATIONS, 1, 10);
1123
1124     if (victim->airborne())
1125         dist++;
1126
1127     if (victim->body_size(PSIZE_BODY) < SIZE_MEDIUM)
1128         dist++;
1129     else if (victim->body_size(PSIZE_BODY) > SIZE_BIG)
1130         dist /= 2;
1131     else if (victim->body_size(PSIZE_BODY) > SIZE_MEDIUM)
1132         dist -= 1;
1133
1134     int range = victim->pos().distance_from(agent->pos());
1135     if (range > 5)
1136         dist -= 2;
1137     else if (range > 2)
1138         dist--;
1139
1140     if (dist < 0)
1141         return 0;
1142     else
1143         return dist;
1144 }
1145
1146 static double _angle_between(coord_def origin, coord_def p1, coord_def p2)
1147 {
1148     double ang0 = atan2(p1.x - origin.x, p1.y - origin.y);
1149     double ang  = atan2(p2.x - origin.x, p2.y - origin.y);
1150     return min(fabs(ang - ang0), fabs(ang - ang0 + 2 * PI));
1151 }
1152
1153 void wind_blast(actor* agent, int pow, coord_def target)
1154 {
1155     vector<actor *> act_list;
1156
1157     int radius = min(7, 5 + div_rand_round(pow, 60));
1158
1159     for (actor_near_iterator ai(agent->pos(), LOS_SOLID); ai; ++ai)
1160     {
1161         if (ai->is_stationary()
1162             || ai->pos().distance_from(agent->pos()) > radius
1163             || ai->pos() == agent->pos() // so it's never aimed_at_feet
1164             || !target.origin()
1165                && _angle_between(agent->pos(), target, ai->pos()) > PI/4.0)
1166         {
1167             continue;
1168         }
1169
1170         act_list.push_back(*ai);
1171     }
1172
1173     dist_sorter sorter = {agent->pos()};
1174     sort(act_list.begin(), act_list.end(), sorter);
1175
1176     bolt wind_beam;
1177     wind_beam.hit = AUTOMATIC_HIT;
1178     wind_beam.is_beam = true;
1179     wind_beam.affects_nothing = true;
1180     wind_beam.source = agent->pos();
1181     wind_beam.range = LOS_RADIUS;
1182     wind_beam.is_tracer = true;
1183
1184     bool player_affected = false;
1185     counted_monster_list affected_monsters;
1186
1187     for (unsigned int i = 0; i < act_list.size(); ++i)
1188     {
1189         wind_beam.target = act_list[i]->pos();
1190         wind_beam.fire();
1191
1192         int push = _gale_push_dist(agent, act_list[i]);
1193         bool pushed = false;
1194
1195         for (unsigned int j = 0; j < wind_beam.path_taken.size() - 1 && push;
1196              ++j)
1197         {
1198             if (wind_beam.path_taken[j] == act_list[i]->pos())
1199             {
1200                 coord_def newpos = wind_beam.path_taken[j+1];
1201                 if (!actor_at(newpos) && !cell_is_solid(newpos)
1202                     && act_list[i]->can_pass_through(newpos)
1203                     && act_list[i]->is_habitable(newpos))
1204                 {
1205                     act_list[i]->move_to_pos(newpos);
1206                     --push;
1207                     pushed = true;
1208                 }
1209                 else //Try to find an alternate route to push
1210                 {
1211                     for (adjacent_iterator di(newpos); di; ++di)
1212                     {
1213                         if (adjacent(*di, act_list[i]->pos())
1214                             && di->distance_from(agent->pos())
1215                                 == newpos.distance_from(agent->pos())
1216                             && !actor_at(*di) && !cell_is_solid(*di)
1217                             && act_list[i]->can_pass_through(*di)
1218                             && act_list[i]->is_habitable(*di))
1219                         {
1220                             act_list[i]->move_to_pos(*di);
1221                             --push;
1222                             pushed = true;
1223
1224                             // Adjust wind path for moved monster
1225                             wind_beam.target = *di;
1226                             wind_beam.fire();
1227                             break;
1228                         }
1229                     }
1230                 }
1231             }
1232         }
1233
1234         if (pushed)
1235         {
1236             if (act_list[i]->is_monster())
1237             {
1238                 act_list[i]->as_monster()->speed_increment -= random2(6) + 4;
1239                 if (you.can_see(act_list[i]))
1240                     affected_monsters.add(act_list[i]->as_monster());
1241             }
1242             else
1243                 player_affected = true;
1244         }
1245     }
1246
1247     // Now move clouds
1248     vector<int> cloud_list;
1249     for (distance_iterator di(agent->pos(), true, true, radius + 2); di; ++di)
1250     {
1251         if (env.cgrid(*di) != EMPTY_CLOUD
1252             && cell_see_cell(agent->pos(), *di, LOS_SOLID)
1253             && (target.origin()
1254                 || _angle_between(agent->pos(), target, *di) <= PI/4.0))
1255         {
1256             cloud_list.push_back(env.cgrid(*di));
1257         }
1258     }
1259
1260     for (int i = cloud_list.size() - 1; i >= 0; --i)
1261     {
1262         wind_beam.target = env.cloud[cloud_list[i]].pos;
1263         wind_beam.fire();
1264
1265         int dist = env.cloud[cloud_list[i]].pos.distance_from(agent->pos());
1266         int push = (dist > 5 ? 2 : dist > 2 ? 3 : 4);
1267
1268         for (unsigned int j = 0;
1269              j < wind_beam.path_taken.size() - 1 && push;
1270              ++j)
1271         {
1272             if (env.cgrid(wind_beam.path_taken[j]) == cloud_list[i])
1273             {
1274                 coord_def newpos = wind_beam.path_taken[j+1];
1275                 if (!cell_is_solid(newpos)
1276                     && env.cgrid(newpos) == EMPTY_CLOUD)
1277                 {
1278                     swap_clouds(newpos, wind_beam.path_taken[j]);
1279                     --push;
1280                 }
1281                 else //Try to find an alternate route to push
1282                 {
1283                     for (distance_iterator di(wind_beam.path_taken[j],
1284                          false, true, 1); di; ++di)
1285                     {
1286                         if (di->distance_from(agent->pos())
1287                                 == newpos.distance_from(agent->pos())
1288                             && *di != agent->pos() // never aimed_at_feet
1289                             && !cell_is_solid(*di)
1290                             && env.cgrid(*di) == EMPTY_CLOUD)
1291                         {
1292                             swap_clouds(*di, wind_beam.path_taken[j]);
1293                             --push;
1294                             wind_beam.target = *di;
1295                             wind_beam.fire();
1296                             j--;
1297                             break;
1298                         }
1299                     }
1300                 }
1301             }
1302         }
1303     }
1304
1305     if (agent->is_player())
1306     {
1307         if (pow > 120)
1308             mpr("A mighty gale blasts forth from the fan!");
1309         else
1310             mpr("A fierce wind blows from the fan.");
1311     }
1312
1313     noisy(8, agent->pos());
1314
1315     if (player_affected)
1316         mpr("You are blown backwards!");
1317
1318     if (!affected_monsters.empty())
1319     {
1320         const string message =
1321             make_stringf("%s %s blown away by the wind.",
1322                          affected_monsters.describe().c_str(),
1323                          affected_monsters.count() == 1? "is" : "are");
1324         if (strwidth(message) < get_number_of_cols() - 2)
1325             mpr(message.c_str());
1326         else
1327             mpr("The monsters around you are blown away!");
1328     }
1329 }
1330
1331 static void _fan_of_gales_elementals()
1332 {
1333     int radius = min(7, 5 + you.skill_rdiv(SK_EVOCATIONS, 1, 6));
1334
1335     vector<coord_def> elementals;
1336     for (radius_iterator ri(you.pos(), radius, C_ROUND, NULL, true); ri; ++ri)
1337     {
1338         if (ri->distance_from(you.pos()) >= 3 && !monster_at(*ri)
1339             && !cell_is_solid(*ri)
1340             && cell_see_cell(you.pos(), *ri, LOS_NO_TRANS))
1341         {
1342             elementals.push_back(*ri);
1343         }
1344     }
1345     shuffle_array(elementals);
1346
1347     int num_elementals = _num_evoker_elementals();
1348
1349     bool created = false;
1350     for (int n = 0; n < min(num_elementals, (int)elementals.size()); ++n)
1351     {
1352         mgen_data mg (MONS_AIR_ELEMENTAL, BEH_FRIENDLY, &you, 3, SPELL_NO_SPELL,
1353                       elementals[n], 0, MG_FORCE_BEH | MG_FORCE_PLACE,
1354                       GOD_NO_GOD, MONS_AIR_ELEMENTAL, 0, BLACK,
1355                       PROX_CLOSE_TO_PLAYER);
1356         mg.hd = 6 + you.skill_rdiv(SK_EVOCATIONS, 2, 13);
1357         if (create_monster(mg))
1358             created = true;
1359     }
1360     if (created)
1361         mpr("The winds coalesce and take form.");
1362 }
1363
1364 static bool _is_rock(dungeon_feature_type feat)
1365 {
1366     return (feat == DNGN_ROCK_WALL || feat == DNGN_CLEAR_ROCK_WALL
1367             || feat == DNGN_SLIMY_WALL);
1368 }
1369
1370 static bool _is_rubble_source(dungeon_feature_type feat)
1371 {
1372     switch (feat)
1373     {
1374         case DNGN_ROCK_WALL:
1375         case DNGN_CLEAR_ROCK_WALL:
1376         case DNGN_SLIMY_WALL:
1377         case DNGN_STONE_WALL:
1378         case DNGN_CLEAR_STONE_WALL:
1379         case DNGN_PERMAROCK_WALL:
1380             return true;
1381
1382         default:
1383             return false;
1384     }
1385 }
1386
1387 static bool _adjacent_to_rubble_source(coord_def pos)
1388 {
1389     for (adjacent_iterator ai(pos); ai; ++ai)
1390     {
1391         if (_is_rubble_source(grd(*ai)) && you.see_cell_no_trans(*ai))
1392             return true;
1393     }
1394
1395     return false;
1396 }
1397
1398 static bool _stone_of_tremors()
1399 {
1400     vector<coord_def> wall_pos;
1401     vector<coord_def> rubble_pos;
1402     vector<coord_def> door_pos;
1403
1404     for (distance_iterator di(you.pos(), false, true, LOS_RADIUS); di; ++di)
1405     {
1406         if (_is_rubble_source(grd(*di)))
1407             wall_pos.push_back(*di);
1408         else if (feat_is_door(grd(*di)))
1409             door_pos.push_back(*di);
1410         else if (_adjacent_to_rubble_source(*di))
1411             rubble_pos.push_back(*di);
1412     }
1413
1414     mpr("The dungeon trembles and rubble falls from the walls!");
1415     noisy(15, you.pos());
1416
1417     bolt rubble;
1418     rubble.name        = "falling rubble";
1419     rubble.range       = 1;
1420     rubble.hit         = 10 + you.skill_rdiv(SK_EVOCATIONS, 1, 2);
1421     rubble.damage      = dice_def(3, 5 + you.skill(SK_EVOCATIONS));
1422     rubble.beam_source = MHITYOU;
1423     rubble.glyph       = dchar_glyph(DCHAR_FIRED_MISSILE);
1424     rubble.colour      = LIGHTGREY;
1425     rubble.flavour     = BEAM_MMISSILE;
1426     rubble.thrower     = KILL_YOU;
1427     rubble.is_beam     = false;
1428     rubble.loudness    = 10;
1429     rubble.draw_delay  = 0;
1430
1431     // Hit the affected area with falling rubble.
1432     for (unsigned int i = 0; i < rubble_pos.size(); ++i)
1433     {
1434         rubble.source = rubble_pos[i];
1435         rubble.target = rubble_pos[i];
1436         rubble.fire();
1437     }
1438     update_screen();
1439     delay(200);
1440
1441     // Possibly shaft some monsters.
1442     for (monster_near_iterator mi(you.pos(), LOS_NO_TRANS); mi; ++mi)
1443     {
1444         if (grd(mi->pos()) == DNGN_FLOOR
1445             && !mi->airborne() && is_valid_shaft_level()
1446             && x_chance_in_y(75 + you.skill(SK_EVOCATIONS, 2), 800))
1447         {
1448             mi->do_shaft();
1449         }
1450     }
1451
1452     // Destroy doors.
1453     for (unsigned int i = 0; i < door_pos.size(); ++i)
1454     {
1455         nuke_wall(door_pos[i]);
1456         mpr("The door collapses!");
1457     }
1458
1459     // Collapse some walls and mark collapsed walls as valid elemental positions.
1460     int num_elementals = _num_evoker_elementals();
1461     for (unsigned int i = 0; i < wall_pos.size(); ++i)
1462     {
1463         if (_is_rock(grd(wall_pos[i])) && one_chance_in(3))
1464         {
1465             nuke_wall(wall_pos[i]);
1466             rubble_pos.push_back(wall_pos[i]);
1467         }
1468     }
1469     shuffle_array(rubble_pos);
1470
1471     // Create elementals.
1472     bool created = false;
1473     for (int n = 0; n < min(num_elementals, (int)rubble_pos.size()); ++n)
1474     {
1475         // Skip occupied positions
1476         if (actor_at(rubble_pos[n]))
1477             continue;
1478
1479         mgen_data mg(MONS_EARTH_ELEMENTAL, BEH_FRIENDLY, &you, 3, SPELL_NO_SPELL,
1480                      rubble_pos[n], 0, MG_FORCE_BEH | MG_FORCE_PLACE, GOD_NO_GOD,
1481                      MONS_EARTH_ELEMENTAL, 0, BLACK, PROX_CLOSE_TO_PLAYER);
1482         mg.hd = 6 + you.skill_rdiv(SK_EVOCATIONS, 2, 13);
1483         if (create_monster(mg))
1484             created = true;
1485     }
1486     if (created)
1487         mpr("The rubble rises up and takes form.");
1488
1489     return true;
1490 }
1491
1492 static bool _phial_of_floods()
1493 {
1494     dist target;
1495     bolt beam;
1496
1497     zappy(ZAP_PRIMAL_WAVE, 25 + you.skill(SK_EVOCATIONS, 6), beam);
1498     beam.range = LOS_RADIUS;
1499     beam.thrower = KILL_YOU;
1500     beam.name = "flood of elemental water";
1501     beam.aimed_at_spot = true;
1502
1503     if (spell_direction(target, beam, DIR_NONE, TARG_HOSTILE,
1504                         LOS_RADIUS, true, true, false, NULL,
1505                         "Aim the phial where?"))
1506     {
1507         beam.fire();
1508
1509         vector<coord_def> elementals;
1510         // Flood the endpoint
1511         coord_def center = beam.path_taken.back();
1512         int num = 5 + you.skill_rdiv(SK_EVOCATIONS, 3, 5) + random2(7);
1513         int dur = 40 + you.skill_rdiv(SK_EVOCATIONS, 8, 3);
1514         for (distance_iterator di(center, true, false, 2); di && (num > 0); ++di)
1515         {
1516             if ((grd(*di) == DNGN_FLOOR || grd(*di) == DNGN_SHALLOW_WATER)
1517                 && cell_see_cell(center, *di, LOS_NO_TRANS))
1518             {
1519                 num--;
1520                 temp_change_terrain(*di, DNGN_SHALLOW_WATER,
1521                                     random_range(dur*2, dur*3) - (di.radius()*20),
1522                                     TERRAIN_CHANGE_FLOOD);
1523                 elementals.push_back(*di);
1524             }
1525         }
1526
1527         int num_elementals = _num_evoker_elementals();
1528
1529         bool created = false;
1530         num = min(num_elementals,
1531                   min((int)elementals.size(), (int)elementals.size() / 5 + 1));
1532         for (int n = 0; n < num; ++n)
1533         {
1534             mgen_data mg (MONS_WATER_ELEMENTAL, BEH_FRIENDLY, &you, 3,
1535                           SPELL_NO_SPELL, elementals[n], 0,
1536                           MG_FORCE_BEH | MG_FORCE_PLACE, GOD_NO_GOD,
1537                           MONS_WATER_ELEMENTAL, 0, BLACK, PROX_CLOSE_TO_PLAYER);
1538             mg.hd = 6 + you.skill_rdiv(SK_EVOCATIONS, 2, 15);
1539             if (create_monster(mg))
1540                 created = true;
1541         }
1542         if (created)
1543             mpr("The water rises up and takes form.");
1544
1545         return true;
1546     }
1547
1548     return false;
1549 }
1550
1551 static void _expend_elemental_evoker(item_def &item)
1552 {
1553     item.plus2 = 10;
1554 }
1555
1556 bool evoke_item(int slot)
1557 {
1558     if (you.form == TRAN_WISP)
1559         return mpr("You cannot handle anything in this form."), false;
1560
1561     if (you.berserk() && (slot == -1
1562                        || slot != you.equip[EQ_WEAPON]
1563                        || weapon_reach(*you.weapon()) <= 2))
1564     {
1565         canned_msg(MSG_TOO_BERSERK);
1566         return false;
1567     }
1568
1569     if (slot == -1)
1570     {
1571         slot = prompt_invent_item("Evoke which item? (* to show all)",
1572                                    MT_INVLIST,
1573                                    OSEL_EVOKABLE, true, true, true, 0, -1,
1574                                    NULL, OPER_EVOKE);
1575
1576         if (prompt_failed(slot))
1577             return false;
1578     }
1579     else if (!check_warning_inscriptions(you.inv[slot], OPER_EVOKE))
1580         return false;
1581
1582     ASSERT(slot >= 0);
1583
1584 #ifdef ASSERTS // Used only by an assert
1585     const bool wielded = (you.equip[EQ_WEAPON] == slot);
1586 #endif /* DEBUG */
1587
1588     item_def& item = you.inv[slot];
1589     // Also handles messages.
1590     if (!item_is_evokable(item, true, false, false, true))
1591         return false;
1592
1593     if (you.suppressed() && weapon_reach(item) <= 2)
1594     {
1595         canned_msg(MSG_EVOCATION_SUPPRESSED);
1596         return false;
1597     }
1598
1599     int pract = 0; // By how much Evocations is practised.
1600     bool did_work   = false;  // Used for default "nothing happens" message.
1601     bool unevokable = false;
1602
1603     const unrandart_entry *entry = is_unrandom_artefact(item)
1604         ? get_unrand_entry(item.special) : NULL;
1605
1606     if (entry && entry->evoke_func)
1607     {
1608         ASSERT(item_is_equipped(item));
1609
1610         if (entry->evoke_func(&item, &pract, &did_work, &unevokable))
1611         {
1612             if (!unevokable)
1613                 count_action(CACT_EVOKE, EVOC_MISC);
1614             return did_work;
1615         }
1616     }
1617     else switch (item.base_type)
1618     {
1619     case OBJ_WANDS:
1620         zap_wand(slot);
1621         return true;
1622
1623     case OBJ_WEAPONS:
1624         ASSERT(wielded);
1625
1626         if (weapon_reach(item) > 2)
1627         {
1628             if (_reaching_weapon_attack(item))
1629             {
1630                 pract    = 0;
1631                 did_work = true;
1632             }
1633             else
1634                 return false;
1635         }
1636         else
1637             unevokable = true;
1638         break;
1639
1640     case OBJ_RODS:
1641         ASSERT(wielded);
1642
1643         if (you.confused())
1644         {
1645             canned_msg(MSG_TOO_CONFUSED);
1646             return false;
1647         }
1648
1649         pract = rod_spell(slot);
1650         // [ds] Early exit, no turns are lost.
1651         if (pract == -1)
1652             return false;
1653
1654         did_work = true;  // rod_spell() will handle messages
1655         count_action(CACT_EVOKE, EVOC_ROD);
1656         break;
1657
1658     case OBJ_STAVES:
1659         ASSERT(wielded);
1660         if (item.sub_type != STAFF_ENERGY)
1661         {
1662             unevokable = true;
1663             break;
1664         }
1665
1666         if (you.confused())
1667         {
1668             canned_msg(MSG_TOO_CONFUSED);
1669             return false;
1670         }
1671
1672         if (!you.is_undead && !you_foodless()
1673             && you.hunger_state == HS_STARVING)
1674         {
1675             canned_msg(MSG_TOO_HUNGRY);
1676             return false;
1677         }
1678         else if (you.magic_points >= you.max_magic_points
1679                  && (you.species != SP_DJINNI || you.hp == you.hp_max))
1680         {
1681             mpr("Your reserves of magic are already full.");
1682             return false;
1683         }
1684         else if (x_chance_in_y(you.skill(SK_EVOCATIONS, 100) + 1100, 4000))
1685         {
1686             mpr("You channel some magical energy.");
1687             inc_mp(1 + random2(3));
1688             make_hungry(50, false, true);
1689             pract = 1;
1690             did_work = true;
1691             count_action(CACT_EVOKE, EVOC_MISC);
1692         }
1693         break;
1694
1695     case OBJ_MISCELLANY:
1696         did_work = true; // easier to do it this way for misc items
1697
1698         if (is_deck(item))
1699         {
1700             ASSERT(wielded);
1701
1702             evoke_deck(item);
1703             pract = 1;
1704             count_action(CACT_EVOKE, EVOC_DECK);
1705             break;
1706         }
1707
1708         switch (item.sub_type)
1709         {
1710         case MISC_BOTTLED_EFREET:
1711             if (_efreet_flask(slot))
1712                 pract = 2;
1713             break;
1714
1715         case MISC_FAN_OF_GALES:
1716             if (!evoker_is_charged(item))
1717             {
1718                 mpr("That is presently inert.");
1719                 return false;
1720             }
1721             wind_blast(&you, you.skill(SK_EVOCATIONS, 10), coord_def());
1722             _fan_of_gales_elementals();
1723             _expend_elemental_evoker(item);
1724             break;
1725
1726         case MISC_LAMP_OF_FIRE:
1727             if (!evoker_is_charged(item))
1728             {
1729                 mpr("That is presently inert.");
1730                 return false;
1731             }
1732             if (_lamp_of_fire())
1733                 _expend_elemental_evoker(item);
1734             else
1735                 return false;
1736
1737             break;
1738
1739         case MISC_STONE_OF_TREMORS:
1740             if (!evoker_is_charged(item))
1741             {
1742                 mpr("That is presently inert.");
1743                 return false;
1744             }
1745             if (_stone_of_tremors())
1746                 _expend_elemental_evoker(item);
1747             else
1748                 return false;
1749             break;
1750
1751         case MISC_PHIAL_OF_FLOODS:
1752             if (!evoker_is_charged(item))
1753             {
1754                 mpr("That is presently inert.");
1755                 return false;
1756             }
1757             if (_phial_of_floods())
1758                 _expend_elemental_evoker(item);
1759             else
1760                 return false;
1761             break;
1762
1763         case MISC_HORN_OF_GERYON:
1764             if (_evoke_horn_of_geryon(item))
1765                 pract = 1;
1766             break;
1767
1768         case MISC_BOX_OF_BEASTS:
1769             if (_box_of_beasts(item))
1770                 pract = 1;
1771             break;
1772
1773         case MISC_SACK_OF_SPIDERS:
1774             if (_sack_of_spiders(item))
1775                 pract = 1;
1776             break;
1777
1778         case MISC_CRYSTAL_BALL_OF_ENERGY:
1779             if (!_check_crystal_ball())
1780                 unevokable = true;
1781             else if (_ball_of_energy())
1782                 pract = 1;
1783             break;
1784
1785         case MISC_DISC_OF_STORMS:
1786             if (disc_of_storms())
1787                 pract = (coinflip() ? 2 : 1);
1788             break;
1789
1790         case MISC_QUAD_DAMAGE:
1791             mpr("QUAD DAMAGE!");
1792             you.duration[DUR_QUAD_DAMAGE] = 30 * BASELINE_DELAY;
1793             ASSERT(in_inventory(item));
1794             dec_inv_item_quantity(item.link, 1);
1795             invalidate_agrid(true);
1796             break;
1797
1798         default:
1799             did_work = false;
1800             unevokable = true;
1801             break;
1802         }
1803         if (did_work && !unevokable)
1804             count_action(CACT_EVOKE, EVOC_MISC);
1805         break;
1806
1807     default:
1808         unevokable = true;
1809         break;
1810     }
1811
1812     if (!did_work)
1813         canned_msg(MSG_NOTHING_HAPPENS);
1814     else if (pract > 0)
1815         practise(EX_DID_EVOKE_ITEM, pract);
1816
1817     if (!unevokable)
1818         you.turn_is_over = true;
1819     else
1820         crawl_state.zero_turns_taken();
1821
1822     return did_work;
1823 }