1 /**
2  * @file
3  * @brief Functions related to clouds.
4  *
5  * Creating a cloud module so all the cloud stuff can be isolated.
6 **/
7 
8 #include "AppHdr.h"
9 
10 #include "cloud.h"
11 
12 #include <algorithm>
13 
14 #include "areas.h"
15 #include "art-enum.h"
16 #include "colour.h"
17 #include "coordit.h"
18 #include "dungeon.h"
19 #include "english.h"
20 #include "god-conduct.h"
21 #include "god-passive.h"
22 #include "level-state-type.h"
23 #include "libutil.h" // testbits
24 #include "los.h"
25 #include "mapmark.h"
26 #include "map-knowledge.h"
27 #include "melee-attack.h"
28 #include "message.h"
29 #include "mon-behv.h"
30 #include "mon-death.h"
31 #include "mon-place.h"
32 #include "nearby-danger.h" // Compass (for random_walk, CloudGenerator)
33 #include "religion.h"
34 #include "shout.h"
35 #include "spl-util.h"
36 #include "state.h"
37 #include "stringutil.h"
38 #include "tag-version.h"
39 #include "terrain.h"
40 #include "rltiles/tiledef-main.h"
41 #include "unwind.h"
42 
cloud_at(coord_def pos)43 cloud_struct* cloud_at(coord_def pos)
44 {
45     return map_find(env.cloud, pos);
46 }
47 
48 /// damage = base + random2avg(random, random/15 + 1)
49 struct cloud_damage
50 {
51     int base; ///< Flat damage on every hit, pre-defenses.
52     int random; ///< Damage rolled on hit.
53     bool extra_player_dam; //< HACK: does 4+random2(8) extra damage to players.
54     // Yes, we really hate players, damn their guts.
55 };
56 
57 /// Damage for most damaging clouds.
58 static const cloud_damage NORMAL_CLOUD_DAM = { 6, 16, true };
59 // 6+r2a(16,2) for monsters, 10+r2a(23,2) for players
60 
61 /// A portrait of a cloud_type.
62 struct cloud_data
63 {
64     /// A (relatively) short name for the cloud. May be referenced from lua.
65     const char* terse_name;
66     /// Another name for the cloud. If nullptr, defaults to terse name.
67     const char* verbose_name;
68     /// The colour of the cloud in console.
69     colour_t colour;
70     /// Info for calculating cloud tiles.
71     cloud_tile_info tile_info;
72     /// The associated "beam" (effect) for this cloud type.
73     beam_type beam_effect;
74     /// How much damage the cloud does before defenses & resists.
75     cloud_damage damage;
76     /// Do multiple squares of this cloud block LOS?
77     bool opaque;
78 };
79 
80 /// A map from cloud_type to cloud_data.
81 static const cloud_data clouds[] = {
82     // CLOUD_NONE,
83     { "?", "?",                                 // terse, verbose name
84     },
85     // CLOUD_FIRE,
86     { "flame", "blazing flames",                // terse, verbose name
87        COLOUR_UNDEF,                            // colour
88        { TILE_CLOUD_FIRE, CTVARY_DUR },         // tile
89        BEAM_FIRE,                               // beam_effect
90        NORMAL_CLOUD_DAM,                        // base, random damage
91     },
92     // CLOUD_MEPHITIC,
93     { "noxious fumes", nullptr,                 // terse, verbose name
94       GREEN,                                    // colour
95       { TILE_CLOUD_MEPHITIC, CTVARY_DUR },      // tile
96       BEAM_MEPHITIC,                            // beam_effect
97       {0, 3},                                   // base, random damage
98     },
99     // CLOUD_COLD,
100     { "freezing vapour", "freezing vapours",    // terse, verbose name
101       COLOUR_UNDEF,                             // colour
102       { TILE_CLOUD_COLD, CTVARY_DUR },          // tile
103       BEAM_COLD,                                // beam_effect
104       NORMAL_CLOUD_DAM,                         // base, random damage
105     },
106     // CLOUD_POISON,
107     { "poison gas", nullptr,                    // terse, verbose name
108       LIGHTGREEN,                               // colour
109       { TILE_CLOUD_POISON, CTVARY_DUR },        // tile
110       BEAM_POISON,                              // beam_effect
111       {0, 10},                                  // base, random damage
112     },
113     // CLOUD_BLACK_SMOKE,
114     { "black smoke",  nullptr,                  // terse, verbose name
115       DARKGREY,                                 // colour
116       { TILE_CLOUD_BLACK_SMOKE, CTVARY_NONE },  // tile
117       BEAM_NONE, {},                            // beam & damage
118       true,                                     // opacity
119     },
120     // CLOUD_GREY_SMOKE,
121     { "grey smoke",  nullptr,                   // terse, verbose name
122       LIGHTGREY,                                // colour
123       { TILE_CLOUD_GREY_SMOKE, CTVARY_NONE },   // tile
124       BEAM_NONE, {},                            // beam & damage
125       true,                                     // opacity
126     },
127     // CLOUD_BLUE_SMOKE,
128     { "blue smoke",  nullptr,                   // terse, verbose name
129       LIGHTBLUE,                                // colour
130       { TILE_CLOUD_BLUE_SMOKE, CTVARY_NONE },   // tile
131       BEAM_NONE, {},                            // beam & damage
132       true,                                     // opacity
133     },
134     // CLOUD_PURPLE_SMOKE,
135     { "purple smoke",  nullptr,                 // terse, verbose name
136       MAGENTA,                                  // colour
137       { TILE_CLOUD_TLOC_ENERGY, CTVARY_NONE },  // tile
138       BEAM_NONE, {},                            // beam & damage
139       true,                                     // opacity
140     },
141     // CLOUD_TLOC_ENERGY,
142     { "translocational energy",  nullptr,       // terse, verbose name
143       MAGENTA,                                  // colour
144       { TILE_CLOUD_TLOC_ENERGY, CTVARY_NONE },  // tile
145       BEAM_NONE, {},                            // beam & damage
146       true,                                     // opacity
147     },
148     // CLOUD_FOREST_FIRE,
149     { "spreading flames", "a forest fire",      // terse, verbose name
150       COLOUR_UNDEF,                             // colour
151       { TILE_CLOUD_FOREST_FIRE },               // tile
152       BEAM_FIRE,                                // beam_effect
153       NORMAL_CLOUD_DAM,                         // base, random damage
154     },
155     // CLOUD_STEAM,
156     { "steam", "a cloud of scalding steam",     // terse, verbose name
157       LIGHTGREY,                                // colour
158       { TILE_CLOUD_GREY_SMOKE, CTVARY_NONE },   // tile
159       BEAM_STEAM,                               // beam_effect
160       {0, 16},                                  // base, random damage
161       true,                                     // opacity
162     },
163 #if TAG_MAJOR_VERSION == 34
164     // CLOUD_GLOOM,
165     { "gloom", "thick gloom",                   // terse, verbose name
166       MAGENTA,                                  // colour
167       { TILE_CLOUD_GLOOM },                     // tile
168     },
169 #endif
170     // CLOUD_INK,
171     { "ink",  nullptr,                          // terse, verbose name
172       DARKGREY,                                 // colour
173       { TILE_CLOUD_INK },                       // tile
174       BEAM_NONE, {},                            // beam_effect & damage
175       true,                                     // opacity
176     },
177     // CLOUD_PETRIFY,
178     { "calcifying dust",  nullptr,              // terse, verbose name
179       WHITE,                                    // colour
180       { TILE_CLOUD_PETRIFY, CTVARY_RANDOM },    // tile
181       BEAM_PETRIFYING_CLOUD, {},                // beam_effect & damage
182       true,                                     // opacity
183     },
184     // CLOUD_HOLY,
185     { "blessed fire", nullptr,                  // terse, verbose name
186       ETC_HOLY,                                 // colour
187       { TILE_CLOUD_YELLOW_SMOKE },              // tile
188       BEAM_HOLY,                                // beam_effect
189       {4, 12, true},                            // base, random damage
190       true,                                     // opacity
191     },
192     // CLOUD_MIASMA,
193     { "foul pestilence", "dark miasma",         // terse, verbose name
194       DARKGREY,                                 // colour
195       { TILE_CLOUD_MIASMA, CTVARY_DUR },        // tile
196       BEAM_MIASMA,                              // beam_effect
197       { 0, 12 },                                // base, random damage
198     },
199     // CLOUD_MIST,
200     { "thin mist", nullptr,                     // terse, verbose name
201       ETC_MIST,                                 // colour
202       { TILE_CLOUD_MIST, CTVARY_NONE },         // tile
203     },
204     // CLOUD_CHAOS,
205     { "seething chaos", nullptr,                // terse, verbose name
206       ETC_RANDOM,                               // colour
207       { TILE_CLOUD_CHAOS, CTVARY_RANDOM },      // tile
208       BEAM_CHAOS,                               // beam_effect
209     },
210     // CLOUD_RAIN,
211     { "rain", "the rain",                       // terse, verbose name
212       ETC_MIST,                                 // colour
213       { TILE_CLOUD_RAIN, CTVARY_RANDOM },       // tile
214       BEAM_NONE,                                // unused
215       { 0, 9 },                                 // base, random damage
216                                                 // but only for fiery mons
217     },
218     // CLOUD_MUTAGENIC,
219     { "mutagenic fog",  nullptr,                // terse, verbose name
220       ETC_MUTAGENIC,                            // colour
221       { TILE_ERROR, CTVARY_NONE },              // tile
222     },
223     // CLOUD_MAGIC_TRAIL,
224     { "magical condensation", nullptr,          // terse, verbose name
225       ETC_MAGIC,                                // colour
226       { TILE_CLOUD_MAGIC_TRAIL, CTVARY_DUR },   // tile
227     },
228     // CLOUD_VORTEX,
229     { "whirling frost", nullptr,                // terse, verbose name
230       ETC_VORTEX,                               // colour
231       { TILE_ERROR },                           // tile
232     },
233     // CLOUD_DUST,
234     { "sparse dust",  nullptr,                  // terse, verbose name
235       ETC_EARTH,                                // colour
236       { TILE_CLOUD_DUST, CTVARY_DUR },          // tile
237     },
238     // CLOUD_SPECTRAL,
239     { "spectral mist", nullptr,                 // terse, verbose name
240       ETC_ELECTRICITY,                          // colour
241       { TILE_CLOUD_SPECTRAL, CTVARY_DUR },      // tile
242       BEAM_NONE,                                // beam_effect
243       { 4, 15 },                                // base, random damage
244     },
245     // CLOUD_ACID,
246     { "acidic fog", nullptr,                    // terse, verbose name
247       YELLOW,                                   // colour
248       { TILE_CLOUD_ACID, CTVARY_DUR },          // dur
249       BEAM_ACID,                                // beam_effect
250       NORMAL_CLOUD_DAM,                         // base, random damage
251     },
252     // CLOUD_STORM,
253     { "thunder", "a thunderstorm",              // terse, verbose name
254       ETC_DARK,                                 // colour
255       { TILE_CLOUD_STORM, CTVARY_RANDOM },      // tile
256       BEAM_ELECTRICITY,                         // beam_effect
257       {12, 12},
258     },
259     // CLOUD_NEGATIVE_ENERGY,
260     { "negative energy", nullptr,               // terse, verbose name
261       ETC_INCARNADINE,                          // colour
262       { TILE_CLOUD_NEG, CTVARY_DUR },           // tile
263       BEAM_NEG,                                 // beam_effect
264       NORMAL_CLOUD_DAM,                         // base, random damage
265     },
266     // CLOUD_FLUFFY,
267     { "white fluffiness",  nullptr,             // terse, verbose name
268       WHITE,                                    // colour
269       { TILE_CLOUD_WHITE_SMOKE, CTVARY_NONE },  // tile
270       BEAM_NONE, {},                            // beam & damage
271       true,                                     // opacity
272     },
273     // CLOUD_XOM_TRAIL,
274     { "magical condensation", nullptr,          // terse, verbose name
275       ETC_RANDOM,                               // colour
276       { TILE_CLOUD_MAGIC_TRAIL, CTVARY_DUR },   // tile
277       // TODO: another tile?
278     },
279     // CLOUD_SALT,
280     { "salt",  nullptr,                           // terse, verbose name
281       ETC_AIR,                                    // colour
282       { TILE_CLOUD_WHITE_SMOKE, CTVARY_NONE },    // tile
283       BEAM_NONE, {},                              // beam & damage
284       true,                                       // opacity
285     },
286     // CLOUD_GOLD_DUST,
287     { "golden dust",  nullptr,                    // terse, verbose name
288       ETC_HOLY,                                   // colour
289       { TILE_CLOUD_GOLD_DUST, CTVARY_DUR },       // tile
290       BEAM_NONE, {},                              // beam & damage
291       true,                                       // opacity
292     },
293     // CLOUD_EMBERS,
294     { "smouldering embers", "embers",
295         ETC_SMOKE,
296         { TILE_CLOUD_BLACK_SMOKE, CTVARY_NONE },
297     },
298     // CLOUD_FLAME,
299     { "wisps of flame", nullptr,          // terse, verbose name
300       ETC_FIRE,                                // colour
301       { TILE_CLOUD_FLAME, CTVARY_RANDOM },   // tile
302     },
303 };
304 COMPILE_CHECK(ARRAYSZ(clouds) == NUM_CLOUD_TYPES);
305 
306 static int _actor_cloud_damage(const actor *act, const cloud_struct &cloud,
307                                bool maximum_damage);
308 
_actual_spread_rate(cloud_type type,int spread_rate)309 static int _actual_spread_rate(cloud_type type, int spread_rate)
310 {
311     if (spread_rate >= 0)
312         return spread_rate;
313 
314     switch (type)
315     {
316 #if TAG_MAJOR_VERSION == 34
317     case CLOUD_GLOOM:
318         return 50;
319 #endif
320     case CLOUD_STEAM:
321     case CLOUD_GREY_SMOKE:
322     case CLOUD_BLACK_SMOKE:
323     case CLOUD_PURPLE_SMOKE:
324     case CLOUD_BLUE_SMOKE:
325     case CLOUD_FLUFFY:
326         return 22;
327     case CLOUD_RAIN:
328     case CLOUD_INK:
329         return 11;
330     default:
331         return 0;
332     }
333 }
334 
_cloud2beam(cloud_type flavour)335 static beam_type _cloud2beam(cloud_type flavour)
336 {
337     if (flavour == CLOUD_RANDOM)
338         return BEAM_RANDOM;
339     return clouds[flavour].beam_effect;
340 }
341 
342 #ifdef ASSERTS
_killer_whose_match(kill_category whose,killer_type killer)343 static bool _killer_whose_match(kill_category whose, killer_type killer)
344 {
345     switch (whose)
346     {
347         case KC_YOU:
348             return killer == KILL_YOU_MISSILE || killer == KILL_YOU_CONF;
349 
350         case KC_FRIENDLY:
351             return killer == KILL_MON_MISSILE || killer == KILL_YOU_CONF
352                    || killer == KILL_MON;
353 
354         case KC_OTHER:
355             return killer == KILL_MON_MISSILE || killer == KILL_MISCAST
356                    || killer == KILL_MISC || killer == KILL_MON;
357 
358         case KC_NCATEGORIES:
359             die("kill category not matching killer type");
360     }
361     return false;
362 }
363 #endif
364 
365 /*
366  * The LOS may have changed based on cloud changes at position `p`.
367  *
368  * @param p   The position that may have changed.
369  * @param t   The cloud type now there; CLOUD_NONE if there is no cloud there now.
370  * @param old The cloud type that was there; CLOUD_NONE if the was none.
371  */
_los_cloud_changed(const coord_def & p,const cloud_type t,const cloud_type old)372 static void _los_cloud_changed(const coord_def& p, const cloud_type t, const cloud_type old)
373 {
374     if (is_opaque_cloud(t) || is_opaque_cloud(old))
375         los_terrain_changed(p);
376 }
377 
cloud_struct(coord_def p,cloud_type c,int d,int spread,kill_category kc,killer_type kt,mid_t src,int excl)378 cloud_struct::cloud_struct(coord_def p, cloud_type c, int d, int spread,
379                            kill_category kc, killer_type kt, mid_t src,
380                            int excl)
381     : pos(p), type(c), decay(d), spread_rate(spread), whose(kc), killer(kt),
382       source(src), excl_rad(excl)
383 {
384     ASSERT(_killer_whose_match(whose, killer));
385 
386     if (type == CLOUD_RANDOM_SMOKE)
387         type = random_smoke_type();
388 }
389 
_spread_cloud(const cloud_struct & cloud)390 static int _spread_cloud(const cloud_struct &cloud)
391 {
392     const int spreadch = cloud.decay > 30 ? 80 :
393                          cloud.decay > 20 ? 50 :
394                                             30;
395     int extra_decay = 0;
396     for (adjacent_iterator ai(cloud.pos); ai; ++ai)
397     {
398         if (random2(100) >= spreadch)
399             continue;
400 
401         if (!in_bounds(*ai)
402             || cloud_at(*ai)
403             || cell_is_solid(*ai)
404             || is_sanctuary(*ai) && !is_harmless_cloud(cloud.type))
405         {
406             continue;
407         }
408 
409         if (cloud.type == CLOUD_INK && !feat_is_watery(env.grid(*ai)))
410             continue;
411 
412         int newdecay = cloud.decay / 2 + 1;
413         if (newdecay >= cloud.decay)
414             newdecay = cloud.decay - 1;
415 
416         env.cloud[*ai] = cloud;
417         env.cloud[*ai].pos = *ai;
418         env.cloud[*ai].decay = newdecay;
419         _los_cloud_changed(env.cloud[*ai].pos, env.cloud[*ai].type, CLOUD_NONE);
420 
421         extra_decay += 8;
422     }
423 
424     return extra_decay;
425 }
426 
_spread_fire(const cloud_struct & cloud)427 static void _spread_fire(const cloud_struct &cloud)
428 {
429     int make_flames = one_chance_in(5);
430 
431     for (adjacent_iterator ai(cloud.pos); ai; ++ai)
432     {
433         if (!in_bounds(*ai)
434             || cloud_at(*ai)
435             || is_sanctuary(*ai))
436         {
437             continue;
438         }
439 
440         // burning trees produce flames all around
441         if (!cell_is_solid(*ai) && make_flames)
442         {
443             env.cloud[*ai] = cloud;
444             env.cloud[*ai].type = CLOUD_FIRE;
445             env.cloud[*ai].pos = *ai;
446             env.cloud[*ai].decay = cloud.decay / 2 + 1;
447         }
448 
449         // forest fire doesn't spread in all directions at once,
450         // every neighbouring square gets a separate roll
451         if (!feat_is_flammable(env.grid(*ai)) || is_temp_terrain(*ai)
452             || x_chance_in_y(19, 20))
453         {
454             continue;
455         }
456 
457         if (env.markers.property_at(*ai, MAT_ANY, "veto_destroy") == "veto")
458             continue;
459 
460         if (you.see_cell(*ai))
461             mpr("The forest fire spreads!");
462         destroy_wall(*ai);
463         env.cloud[*ai] = cloud;
464         env.cloud[*ai].pos = *ai;
465         env.cloud[*ai].decay = random2(30) + 25;
466         if (cloud.whose == KC_YOU)
467             did_god_conduct(DID_KILL_PLANT, 1);
468         else if (cloud.whose == KC_FRIENDLY && !crawl_state.game_is_arena())
469             did_god_conduct(DID_KILL_PLANT, 1);
470 
471     }
472 }
473 
_cloud_interacts_with_terrain(const cloud_struct & cloud)474 static void _cloud_interacts_with_terrain(const cloud_struct &cloud)
475 {
476     if (cloud.type != CLOUD_FIRE && cloud.type != CLOUD_FOREST_FIRE)
477         return;
478 
479     for (adjacent_iterator ai(cloud.pos); ai; ++ai)
480     {
481         const coord_def p(*ai);
482         if (in_bounds(p)
483             && feat_is_watery(env.grid(p))
484             && !cell_is_solid(p)
485             && !cloud_at(p)
486             && one_chance_in(14))
487         {
488             const cloud_type old = cloud_type_at(p);
489             env.cloud[p] = cloud_struct(p, CLOUD_STEAM, 2 + random2(5),
490                                         11, cloud.whose, cloud.killer,
491                                         cloud.source, -1);
492             _los_cloud_changed(p, env.cloud[p].type, old);
493         }
494     }
495 }
496 
497 /**
498  * Convert timing out embers to conjured flames.
499  *
500  * @param cloud     The cloud in question.
501  * @return          Whether a flame cloud has been created.
502  */
_handle_conjure_flame(const cloud_struct & cloud)503 static bool _handle_conjure_flame(const cloud_struct &cloud)
504 {
505     if (cloud.type != CLOUD_EMBERS)
506         return false;
507 
508     if (you.pos() == cloud.pos)
509     {
510         mpr("You smother the flame.");
511         return false;
512     }
513     else if (monster_at(cloud.pos))
514     {
515         mprf("%s smothers the flame.",
516              monster_at(cloud.pos)->name(DESC_THE).c_str());
517         return false;
518     }
519     else
520     {
521         mpr("The fire ignites!");
522         place_cloud(CLOUD_FIRE, cloud.pos, you.props["cflame_dur"], &you);
523         return true;
524     }
525 }
526 
527 /**
528  * How fast should a given cloud fade away this turn?
529  *
530  * @param cloud_idx     The cloud in question.
531  * @return              The rate at which the cloud's "decay" should decrease
532  *                      this turn.
533  */
_cloud_dissipation_rate(const cloud_struct & cloud)534 static int _cloud_dissipation_rate(const cloud_struct &cloud)
535 {
536     int dissipate = you.time_taken;
537 
538     // Player-created non-opaque clouds vanish instantly when outside LOS.
539     // (Opaque clouds don't to prevent cloud suicide.)
540     if ((cloud.source == MID_PLAYER || cloud.source == MID_YOU_FAULTLESS)
541         && !you.see_cell_no_trans(cloud.pos)
542         && !is_opaque_cloud(cloud.type))
543     {
544         return cloud.decay;
545     }
546 
547     // Ink cloud shouldn't appear outside of water.
548     if (cloud.type == CLOUD_INK && !feat_is_watery(env.grid(cloud.pos)))
549         return cloud.decay;
550 
551     return dissipate;
552 }
553 
_dissipate_cloud(cloud_struct & cloud)554 static void _dissipate_cloud(cloud_struct& cloud)
555 {
556     // Apply calculated rate to the actual cloud.
557     cloud.decay -= _cloud_dissipation_rate(cloud);
558 
559     if (cloud.type == CLOUD_FOREST_FIRE)
560         _spread_fire(cloud);
561     else if (x_chance_in_y(cloud.spread_rate, 100))
562     {
563         cloud.spread_rate -= div_rand_round(cloud.spread_rate, 10);
564         cloud.decay       -= _spread_cloud(cloud);
565     }
566 
567     // Check for total dissipation and handle accordingly.
568     if (cloud.decay < 1 && !_handle_conjure_flame(cloud))
569         delete_cloud(cloud.pos);
570 }
571 
_handle_spectral_cloud(const cloud_struct & cloud)572 static void _handle_spectral_cloud(const cloud_struct& cloud)
573 {
574     if (actor_at(cloud.pos) || !actor_by_mid(cloud.source))
575         return;
576 
577     int countn = 0;
578     for (distance_iterator di(cloud.pos, false, false, 2); di; ++di)
579     {
580         if (monster_at(*di) && monster_at(*di)->type == MONS_SPECTRAL_THING)
581             countn++;
582     }
583 
584     int rate[5] = {650, 175, 45, 20, 0};
585     int chance = rate[(min(4, countn))];
586 
587     if (!x_chance_in_y(chance, you.time_taken * 600))
588         return;
589 
590     monster_type basetype =
591         random_choose_weighted(4,   MONS_ANACONDA,
592                                6,   MONS_HYDRA,
593                                3,   MONS_SNAPPING_TURTLE,
594                                2,   MONS_ALLIGATOR_SNAPPING_TURTLE,
595                                100, RANDOM_MONSTER);
596 
597     monster* agent = monster_by_mid(cloud.source);
598     create_monster(mgen_data(MONS_SPECTRAL_THING,
599                              (cloud.whose == KC_OTHER ?
600                                 BEH_HOSTILE :
601                                 BEH_FRIENDLY), cloud.pos,
602                              (agent ? agent->foe : short{MHITYOU}),
603                              MG_FORCE_PLACE)
604                     .set_base(basetype)
605                     .set_summoned(actor_by_mid(cloud.source), 1,
606                                   SPELL_SPECTRAL_CLOUD));
607 }
608 
manage_clouds()609 void manage_clouds()
610 {
611     // We can't iterate over env.cloud directly because _dissipate_cloud
612     // will remove this cloud and invalidate our iterator.
613     vector<cloud_struct *> cloud_ptrs;
614     for (auto& entry : env.cloud)
615         cloud_ptrs.push_back(&entry.second);
616 
617     for (auto ptr : cloud_ptrs)
618     {
619         cloud_struct& cloud = *ptr;
620 
621 #ifdef ASSERTS
622         if (cell_is_solid(cloud.pos))
623         {
624             die("cloud %s in %s at (%d,%d)", cloud_type_name(cloud.type).c_str(),
625                 dungeon_feature_name(env.grid(cloud.pos)), cloud.pos.x, cloud.pos.y);
626         }
627 #endif
628 
629         if (cloud.type == CLOUD_SPECTRAL)
630             _handle_spectral_cloud(cloud);
631 
632         _cloud_interacts_with_terrain(cloud);
633 
634         _dissipate_cloud(cloud);
635     }
636 
637     update_cloud_knowledge();
638 }
639 
_maybe_leave_water(const coord_def pos)640 static void _maybe_leave_water(const coord_def pos)
641 {
642     ASSERT_IN_BOUNDS(pos);
643 
644     // Rain clouds can occasionally leave shallow water or deepen it:
645     // If we're near lava, chance of leaving water is lower;
646     // if we're near deep water already, chance of leaving water
647     // is slightly higher.
648     if (!one_chance_in((5 + count_neighbours(pos, DNGN_LAVA)) -
649                             count_neighbours(pos, DNGN_DEEP_WATER)))
650     {
651         return;
652     }
653 
654     dungeon_feature_type feat = env.grid(pos);
655 
656     if (env.grid(pos) == DNGN_FLOOR)
657         feat = DNGN_SHALLOW_WATER;
658     else if (env.grid(pos) == DNGN_SHALLOW_WATER && you.pos() != pos
659              && one_chance_in(3) && !crawl_state.game_is_sprint())
660     {
661         // Don't drown the player!
662         feat = DNGN_DEEP_WATER;
663     }
664 
665     if (env.grid(pos) != feat)
666     {
667         if (you.pos() == pos && you.ground_level())
668             mpr("The rain has left you waist-deep in water!");
669         temp_change_terrain(pos, feat, random_range(500, 1000),
670                             TERRAIN_CHANGE_FLOOD);
671     }
672 }
673 
delete_cloud(coord_def p)674 void delete_cloud(coord_def p)
675 {
676     if (!cloud_at(p))
677         return;
678     const cloud_type type = cloud_at(p)->type;
679     env.cloud.erase(p);
680     if (type == CLOUD_RAIN)
681         _maybe_leave_water(p);
682     _los_cloud_changed(p, CLOUD_NONE, type);
683 }
684 
delete_all_clouds()685 void delete_all_clouds()
686 {
687     // We can't iterate over env.cloud directly because delete_cloud
688     // will remove this cloud and invalidate our iterator.
689     vector<coord_def> cloud_locs;
690     for (auto& entry : env.cloud)
691         cloud_locs.push_back(entry.first);
692 
693     for (auto pos : cloud_locs)
694         delete_cloud(pos);
695 }
696 
697 // The current use of this function is for shifting in the abyss, so
698 // that clouds get moved along with the rest of the map.
move_cloud(coord_def src,coord_def newpos)699 void move_cloud(coord_def src, coord_def newpos)
700 {
701     if (!cloud_at(src))
702         return;
703     ASSERT(!cell_is_solid(newpos));
704 
705     const cloud_type old = cloud_type_at(newpos);
706 
707     env.cloud[newpos] = env.cloud[src];
708     env.cloud.erase(src);
709     env.cloud[newpos].pos = newpos;
710     _los_cloud_changed(src, CLOUD_NONE, env.cloud[newpos].type);
711     _los_cloud_changed(newpos, env.cloud[newpos].type, old);
712 }
713 
swap_clouds(coord_def p1,coord_def p2)714 void swap_clouds(coord_def p1, coord_def p2)
715 {
716     if (p1 == p2)
717         return;
718     if (!cloud_at(p1))
719     {
720         move_cloud(p2, p1);
721         return;
722     }
723     else if (!cloud_at(p2))
724     {
725         move_cloud(p1, p2);
726         return;
727     }
728 
729     cloud_struct temp = env.cloud[p1];
730     env.cloud[p1] = env.cloud[p2];
731     env.cloud[p2] = temp;
732     env.cloud[p1].pos = p1;
733     env.cloud[p2].pos = p2;
734     _los_cloud_changed(p1, env.cloud[p1].type, env.cloud[p2].type);
735     _los_cloud_changed(p2, env.cloud[p2].type, env.cloud[p1].type);
736 }
737 
738 // Places a cloud with the given stats assuming one doesn't already
739 // exist at that point.
check_place_cloud(cloud_type cl_type,const coord_def & p,int lifetime,const actor * agent,int spread_rate,int excl_rad)740 void check_place_cloud(cloud_type cl_type, const coord_def& p, int lifetime,
741                        const actor *agent, int spread_rate, int excl_rad)
742 {
743     if (!in_bounds(p) || cloud_at(p))
744         return;
745 
746     place_cloud(cl_type, p, lifetime, agent, spread_rate, excl_rad);
747 }
748 
_cloud_is_stronger(cloud_type ct,const cloud_struct & cloud)749 static bool _cloud_is_stronger(cloud_type ct, const cloud_struct& cloud)
750 {
751     return (is_harmless_cloud(cloud.type) &&
752                 (!is_opaque_cloud(cloud.type) || is_opaque_cloud(ct)))
753            || cloud.type == CLOUD_STEAM
754            || ct == CLOUD_VORTEX; // soon gone
755 }
756 
757 /*
758  * Places a cloud with the given stats. Will overwrite an old cloud under some
759  * circumstances.
760  *
761  * @param cl_type     The type of cloud to place.
762  * @param ctarget     The location of the cloud.
763  * @param cl_range    How many turns the cloud will take to decay.
764  * @param agent       Any agent that may have caused the cloud. If this is the
765  *                    player, god conducts are applied.
766  * @param spread_rate How quickly the cloud spreads.
767  * @param excl_rad    How large of an exclusion radius to make around the
768  *                    cloud.
769  * @param do_conducts If true, apply any relevant god conducts for flame
770  *                    placement.
771 */
place_cloud(cloud_type cl_type,const coord_def & ctarget,int cl_range,const actor * agent,int spread_rate,int excl_rad,bool do_conducts)772 void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range,
773                  const actor *agent, int spread_rate, int excl_rad,
774                  bool do_conducts)
775 {
776     if (is_sanctuary(ctarget) && !is_harmless_cloud(cl_type))
777         return;
778 
779     if (cl_type == CLOUD_INK && !feat_is_watery(env.grid(ctarget)))
780         return;
781 
782     if (env.level_state & LSTATE_STILL_WINDS
783         && cl_type != CLOUD_VORTEX
784         && cl_type != CLOUD_INK)
785     {
786         return;
787     }
788 
789     const monster * const mons = monster_at(ctarget);
790 
791     // Fedhas protects plants from damaging clouds.
792     // XX demonic guardians? This logic mostly doesn't apply because protected
793     // monsters are also cloud immune, mostly
794     if (god_protects(agent, mons)
795         && !actor_cloud_immune(*mons, cl_type))
796     {
797         return;
798     }
799 
800     ASSERT(!cell_is_solid(ctarget));
801 
802     god_conduct_trigger conducts[3];
803     kill_category whose = KC_OTHER;
804     killer_type killer  = KILL_MISC;
805     mid_t source        = MID_NOBODY;
806     if (agent && agent->is_player())
807     {
808         if (do_conducts
809             && mons && mons->alive()
810             && !actor_cloud_immune(*mons, cl_type))
811         {
812             set_attack_conducts(conducts, *mons, you.can_see(*mons));
813         }
814 
815         whose = KC_YOU;
816         killer = KILL_YOU_MISSILE;
817         source = MID_PLAYER;
818     }
819     else if (agent && agent->is_monster())
820     {
821         if (agent->as_monster()->friendly())
822             whose = KC_FRIENDLY;
823         else
824             whose = KC_OTHER;
825         killer = KILL_MON_MISSILE;
826         source = agent->mid;
827     }
828 
829     // There's already a cloud here. See if we can overwrite it.
830     const cloud_struct *cloud = cloud_at(ctarget);
831     if (cloud && !_cloud_is_stronger(cl_type, *cloud))
832         return;
833 
834     // If the old cloud was opaque, may need to recalculate los. It *is*
835     // possible to overwrite an opaque cloud with a non-opaque one; OOD will do
836     // this.
837     const cloud_type old = cloud ? cloud->type : CLOUD_NONE;
838     env.cloud[ctarget] = cloud_struct(ctarget, cl_type, cl_range * 10,
839             _actual_spread_rate(cl_type, spread_rate), whose, killer, source,
840             excl_rad);
841     _los_cloud_changed(ctarget, env.cloud[ctarget].type, old);
842 }
843 
is_opaque_cloud(cloud_type ctype)844 bool is_opaque_cloud(cloud_type ctype)
845 {
846     return ctype >= CLOUD_NONE && ctype < NUM_CLOUD_TYPES
847            && clouds[ctype].opaque;
848 }
849 
cloud_type_at(const coord_def & c)850 cloud_type cloud_type_at(const coord_def &c)
851 {
852     return cloud_at(c) ? cloud_at(c)->type : CLOUD_NONE;
853 }
854 
cloud_is_yours_at(const coord_def & c)855 bool cloud_is_yours_at(const coord_def &c)
856 {
857     return cloud_at(c) ? YOU_KILL(cloud_at(c)->killer) : false;
858 }
859 
random_smoke_type()860 cloud_type random_smoke_type()
861 {
862     return random_choose(CLOUD_GREY_SMOKE, CLOUD_BLUE_SMOKE,
863                          CLOUD_BLACK_SMOKE, CLOUD_PURPLE_SMOKE);
864 }
max_cloud_damage(cloud_type cl_type,int power)865 int max_cloud_damage(cloud_type cl_type, int power)
866 {
867     cloud_struct cloud;
868     cloud.type = cl_type;
869     cloud.decay = power * 10;
870     return _actor_cloud_damage(&you, cloud, true);
871 }
872 
873 // Returns true if the cloud type has negative side effects beyond
874 // plain damage and inventory destruction effects.
_cloud_has_negative_side_effects(cloud_type cloud)875 static bool _cloud_has_negative_side_effects(cloud_type cloud)
876 {
877     switch (cloud)
878     {
879     case CLOUD_MEPHITIC:
880     case CLOUD_MIASMA:
881     case CLOUD_MUTAGENIC:
882     case CLOUD_CHAOS:
883     case CLOUD_PETRIFY:
884     case CLOUD_ACID:
885     case CLOUD_NEGATIVE_ENERGY:
886         return true;
887     default:
888         return false;
889     }
890 }
891 
_cloud_damage_calc(int size,int n_average,int extra,bool maximum_damage)892 static int _cloud_damage_calc(int size, int n_average, int extra,
893                               bool maximum_damage)
894 {
895     return maximum_damage?
896            extra + size - 1
897            : random2avg(size, n_average) + extra;
898 }
899 
_base_dam(const cloud_damage & dam,bool vs_player)900 static int _base_dam(const cloud_damage &dam, bool vs_player)
901 {
902     if (vs_player && dam.extra_player_dam)
903         return dam.base + 4;
904     return dam.base;
905 }
906 
_rand_dam(const cloud_damage & dam,bool vs_player)907 static int _rand_dam(const cloud_damage &dam, bool vs_player)
908 {
909     if (vs_player && dam.extra_player_dam)
910         return dam.random + 7;
911     return dam.random;
912 }
913 
914 // Calculates the base damage that the cloud does to an actor without
915 // considering resistances and time spent in the cloud.
_cloud_base_damage(const actor * act,cloud_type flavour,bool maximum_damage)916 static int _cloud_base_damage(const actor *act,
917                               cloud_type flavour,
918                               bool maximum_damage)
919 {
920     const cloud_damage &dam = clouds[flavour].damage;
921     const bool vs_player = act->is_player();
922     const int random_dam = _rand_dam(dam, vs_player);
923     const int base_dam = _base_dam(dam, vs_player);
924     const int trials = dam.random/15 + 1;
925 
926     return _cloud_damage_calc(random_dam, trials, base_dam, maximum_damage);
927 
928 }
929 
930 /**
931  * Is the given actor immune to cloud damage and other negative side effects
932  * (other than opaque clouds + invis) from all clouds of the given type?
933  */
actor_cloud_immune(const actor & act,cloud_type type)934 bool actor_cloud_immune(const actor &act, cloud_type type)
935 {
936     // Qazlalites and scarfwearers get immunity to clouds.
937     // and the Cloud Mage too!
938     if (is_harmless_cloud(type) || act.cloud_immune())
939         return true;
940 
941     switch (type)
942     {
943         case CLOUD_FIRE:
944         case CLOUD_FOREST_FIRE:
945             if (!act.is_player())
946                 return act.res_fire() >= 3;
947             return player_equip_unrand(UNRAND_SALAMANDER)
948 #if TAG_MAJOR_VERSION == 34
949                    || you.has_mutation(MUT_FLAME_CLOUD_IMMUNITY)
950 #endif
951                    || player_equip_unrand(UNRAND_FIRESTARTER)
952                    || you.has_mutation(MUT_IGNITE_BLOOD);
953         case CLOUD_HOLY:
954             return act.res_holy_energy() >= 3;
955         case CLOUD_COLD:
956             if (!act.is_player())
957                 return act.res_cold() >= 3;
958             return player_equip_unrand(UNRAND_FROSTBITE)
959 #if TAG_MAJOR_VERSION == 34
960                    || you.has_mutation(MUT_FREEZING_CLOUD_IMMUNITY)
961 #endif
962                    ;
963         case CLOUD_MEPHITIC:
964             return act.res_poison() > 0;
965         case CLOUD_POISON:
966             return act.res_poison() > 0;
967         case CLOUD_STEAM:
968             return act.res_steam() > 0;
969         case CLOUD_MIASMA:
970             return act.res_miasma();
971         case CLOUD_PETRIFY:
972             return act.res_petrify();
973         case CLOUD_SPECTRAL:
974             return bool(act.holiness() & MH_UNDEAD);
975         case CLOUD_ACID:
976             return act.res_acid() > 0;
977         case CLOUD_STORM:
978             return act.res_elec() >= 3;
979         case CLOUD_NEGATIVE_ENERGY:
980             return act.res_negative_energy() >= 3;
981         case CLOUD_VORTEX:
982             return act.res_polar_vortex();
983         case CLOUD_RAIN:
984             return !act.is_fiery();
985         default:
986             return false;
987     }
988 }
989 
990 // Returns true if the actor is immune to cloud damage and other negative
991 // side effects of the given cloud (other than opaque clouds + invis).
992 //
993 // Note that actor_cloud_immune may be false even if the actor will
994 // not be harmed by the cloud. The cloud may have positive
995 // side-effects on the actor.
actor_cloud_immune(const actor & act,const cloud_struct & cloud)996 bool actor_cloud_immune(const actor &act, const cloud_struct &cloud)
997 {
998     if (actor_cloud_immune(act, cloud.type))
999         return true;
1000 
1001     const bool player = act.is_player();
1002 
1003     if (!player
1004         && (god_protects(act.as_monster())
1005             || testbits(act.as_monster()->flags, MF_DEMONIC_GUARDIAN))
1006         && (cloud.whose == KC_YOU || cloud.whose == KC_FRIENDLY)
1007         && (act.as_monster()->friendly() || act.as_monster()->neutral())
1008         && (cloud.whose == KC_YOU || cloud.whose == KC_FRIENDLY))
1009     {
1010         return true;
1011     }
1012 
1013     int summon_type = 0;
1014     act.is_summoned(nullptr, &summon_type);
1015     if (!player && have_passive(passive_t::cloud_immunity)
1016         && (act.as_monster()->friendly() && summon_type == MON_SUMM_AID))
1017     {
1018         return true;
1019     }
1020 
1021     return false;
1022 }
1023 
1024 // Returns a numeric resistance value for the actor's resistance to
1025 // the cloud's effects. If the actor is immune to the cloud's damage,
1026 // returns WILL_INVULN.
_actor_cloud_resist(const actor * act,const cloud_struct & cloud)1027 static int _actor_cloud_resist(const actor *act, const cloud_struct &cloud)
1028 {
1029     if (actor_cloud_immune(*act, cloud))
1030         return WILL_INVULN;
1031     switch (cloud.type)
1032     {
1033     case CLOUD_RAIN:
1034         return act->is_fiery()? 0 : WILL_INVULN;
1035     case CLOUD_FIRE:
1036     case CLOUD_FOREST_FIRE:
1037         return act->res_fire();
1038     case CLOUD_HOLY:
1039         return act->res_holy_energy();
1040     case CLOUD_COLD:
1041         return act->res_cold();
1042     case CLOUD_PETRIFY:
1043         return act->res_petrify();
1044     case CLOUD_ACID:
1045         return act->res_acid();
1046     case CLOUD_STORM:
1047         return act->res_elec();
1048     case CLOUD_NEGATIVE_ENERGY:
1049         return act->res_negative_energy();
1050 
1051     default:
1052         return 0;
1053     }
1054 }
1055 
_mephitic_cloud_roll(const monster * mons)1056 static bool _mephitic_cloud_roll(const monster* mons)
1057 {
1058     return mons->get_hit_dice() >= MEPH_HD_CAP ? one_chance_in(50)
1059            : !x_chance_in_y(mons->get_hit_dice(), MEPH_HD_CAP);
1060 }
1061 
1062 // Applies cloud messages and side-effects and returns true if the
1063 // cloud had a side-effect. This function does not check for cloud immunity.
1064 // ... but it's only called if the actor isn't immune
_actor_apply_cloud_side_effects(actor * act,const cloud_struct & cloud,int final_damage)1065 static bool _actor_apply_cloud_side_effects(actor *act,
1066                                             const cloud_struct &cloud,
1067                                             int final_damage)
1068 {
1069     ASSERT(act); // XXX: change to actor &act
1070     const bool player = act->is_player();
1071     monster *mons = !player? act->as_monster() : nullptr;
1072     switch (cloud.type)
1073     {
1074     case CLOUD_FIRE:
1075     case CLOUD_STEAM:
1076         if (player)
1077             maybe_melt_player_enchantments(BEAM_FIRE, final_damage);
1078     case CLOUD_RAIN:
1079     case CLOUD_STORM:
1080         if (act->is_fiery() && final_damage > 0)
1081         {
1082             if (you.can_see(*act))
1083             {
1084                 mprf("%s %s in the rain.",
1085                      act->name(DESC_THE).c_str(),
1086                      act->conj_verb(silenced(act->pos())?
1087                                     "steam" : "sizzle").c_str());
1088             }
1089         }
1090         break;
1091 
1092     case CLOUD_MEPHITIC:
1093     {
1094         if (player)
1095         {
1096             if (1 + random2(27) >= you.experience_level)
1097             {
1098                 mpr("You choke on the stench!");
1099                 // effectively one or two turns, since it will be
1100                 // decremented right away
1101                 confuse_player(random_range(2, 3));
1102                 return true;
1103             }
1104         }
1105         else
1106         {
1107             bolt beam;
1108             beam.flavour = BEAM_CONFUSION;
1109             beam.thrower = cloud.killer;
1110 
1111             if (cloud.whose == KC_FRIENDLY)
1112                 beam.source_id = MID_ANON_FRIEND;
1113 
1114             if (_mephitic_cloud_roll(mons))
1115             {
1116                 beam.apply_enchantment_to_monster(mons);
1117                 return true;
1118             }
1119         }
1120         break;
1121     }
1122 
1123     case CLOUD_PETRIFY:
1124     {
1125         if (player)
1126         {
1127             if (random2(55) - 13 >= you.experience_level)
1128             {
1129                 you.petrify(cloud.agent());
1130                 return true;
1131             }
1132         }
1133         else
1134         {
1135             bolt beam;
1136             beam.flavour = BEAM_PETRIFY;
1137             beam.thrower = cloud.killer;
1138 
1139             if (cloud.whose == KC_FRIENDLY)
1140                 beam.source_id = MID_ANON_FRIEND;
1141 
1142             beam.apply_enchantment_to_monster(mons);
1143             return true;
1144         }
1145         break;
1146     }
1147 
1148     case CLOUD_POISON:
1149         if (player)
1150         {
1151             const actor* agent = cloud.agent();
1152             poison_player(5 + roll_dice(3, 8), agent ? agent->name(DESC_A) : "",
1153                           cloud.cloud_name());
1154         }
1155         else
1156             poison_monster(mons, cloud.agent());
1157         return true;
1158 
1159     case CLOUD_MIASMA:
1160         if (player)
1161             return miasma_player(cloud.agent(), cloud.cloud_name());
1162         else
1163             return miasma_monster(mons, cloud.agent());
1164 
1165     case CLOUD_MUTAGENIC:
1166         if (player)
1167         {
1168             mpr("The mutagenic energy flows into you.");
1169             // It's possible that you got trampled into the mutagenic cloud
1170             // and it's not your fault... so we'll say it's not intentional.
1171             // (it's quite bad in any case, so players won't scum, probably.)
1172             contaminate_player(1300 + random2(1250), false);
1173             // min 2 turns to yellow, max 4
1174             return true;
1175         }
1176         else if (coinflip() && mons->malmutate("mutagenic cloud"))
1177         {
1178             if (you_worship(GOD_ZIN) && cloud.whose == KC_YOU)
1179                 did_god_conduct(DID_DELIBERATE_MUTATING, 5 + random2(3));
1180             return true;
1181         }
1182         return false;
1183 
1184     case CLOUD_CHAOS:
1185         if (coinflip())
1186         {
1187             // TODO: Not have this in melee_attack
1188             melee_attack::chaos_affect_actor(act);
1189             return true;
1190         }
1191         break;
1192 
1193     case CLOUD_ACID:
1194     {
1195         const actor* agent = cloud.agent();
1196         act->splash_with_acid(agent, 5, true);
1197         return true;
1198     }
1199 
1200     case CLOUD_NEGATIVE_ENERGY:
1201     {
1202         actor* agent = cloud.agent();
1203         if (act->drain(agent, final_damage))
1204         {
1205             if (cloud.whose == KC_YOU)
1206                 did_god_conduct(DID_EVIL, 5 + random2(3));
1207             return true;
1208         }
1209         break;
1210     }
1211 
1212     default:
1213         break;
1214     }
1215     return false;
1216 }
1217 
_actor_cloud_base_damage(const actor * act,const cloud_struct & cloud,int resist,bool maximum_damage)1218 static int _actor_cloud_base_damage(const actor *act,
1219                                     const cloud_struct &cloud,
1220                                     int resist,
1221                                     bool maximum_damage)
1222 {
1223     if (actor_cloud_immune(*act, cloud))
1224         return 0;
1225 
1226     const int cloud_raw_base_damage =
1227         _cloud_base_damage(act, cloud.type, maximum_damage);
1228     const int cloud_base_damage = (resist == WILL_INVULN ?
1229                                    0 : cloud_raw_base_damage);
1230     return cloud_base_damage;
1231 }
1232 
_cloud_damage_output(const actor * actor,beam_type flavour,int base_damage,bool maximum_damage=false)1233 static int _cloud_damage_output(const actor *actor,
1234                                 beam_type flavour,
1235                                 int base_damage,
1236                                 bool maximum_damage = false)
1237 {
1238     if (maximum_damage)
1239         return resist_adjust_damage(actor, flavour, base_damage);
1240 
1241     int dam = actor->apply_ac(base_damage);
1242     dam = resist_adjust_damage(actor, flavour, dam);
1243     return max(0, dam);
1244 }
1245 
1246 /**
1247  * How much damage will this cloud do to the given actor?
1248  *
1249  * @param act               The actor in question.
1250  * @param cloud             The cloud in question.
1251  * @param maximum_damage    Whether to return the maximum possible damage.
1252  */
_actor_cloud_damage(const actor * act,const cloud_struct & cloud,bool maximum_damage)1253 static int _actor_cloud_damage(const actor *act,
1254                                const cloud_struct &cloud,
1255                                bool maximum_damage)
1256 {
1257     const int resist = _actor_cloud_resist(act, cloud);
1258     const int cloud_base_damage = _actor_cloud_base_damage(act, cloud,
1259                                                            resist,
1260                                                            maximum_damage);
1261     int final_damage = cloud_base_damage;
1262 
1263     switch (cloud.type)
1264     {
1265     case CLOUD_FIRE:
1266     case CLOUD_FOREST_FIRE:
1267     case CLOUD_HOLY:
1268     case CLOUD_COLD:
1269     case CLOUD_STEAM:
1270     case CLOUD_SPECTRAL:
1271     case CLOUD_ACID:
1272     case CLOUD_NEGATIVE_ENERGY:
1273         final_damage =
1274             _cloud_damage_output(act, _cloud2beam(cloud.type),
1275                                  cloud_base_damage,
1276                                  maximum_damage);
1277         break;
1278     case CLOUD_STORM:
1279     {
1280 
1281         // if we don't have thunder, there's always rain
1282         cloud_struct raincloud = cloud;
1283         raincloud.type = CLOUD_RAIN;
1284         const int rain_damage = _actor_cloud_damage(act, raincloud,
1285                                                     maximum_damage);
1286 
1287         // if this isn't just a test run, and no time passed, don't trigger
1288         // lightning. (just rain.)
1289         if (!maximum_damage && !(you.turn_is_over && you.time_taken > 0))
1290             return rain_damage;
1291 
1292         // only announce ourselves if this isn't a test run.
1293         if (!maximum_damage)
1294             cloud.announce_actor_engulfed(act);
1295 
1296         const int turns_per_lightning = 3;
1297         const int aut_per_lightning = turns_per_lightning * BASELINE_DELAY;
1298 
1299         // if we fail our lightning roll, again, just rain.
1300         if (!maximum_damage && !x_chance_in_y(you.time_taken,
1301                                               aut_per_lightning))
1302         {
1303             return rain_damage;
1304         }
1305 
1306         const int lightning_dam = _cloud_damage_output(act,
1307                                                        _cloud2beam(cloud.type),
1308                                                        cloud_base_damage,
1309                                                        maximum_damage);
1310 
1311         if (maximum_damage)
1312         {
1313             // Average maximum damage over time.
1314             const int avg_dam = lightning_dam / turns_per_lightning;
1315             if (avg_dam > 0)
1316                 return avg_dam;
1317             return rain_damage; // vs relec+++ or w/e
1318         }
1319 
1320         if (act->is_player())
1321             mpr("You are struck by lightning!");
1322         else if (you.can_see(*act))
1323         {
1324             simple_monster_message(*act->as_monster(),
1325                                    " is struck by lightning.");
1326         }
1327         else if (you.see_cell(act->pos()))
1328         {
1329             mpr("Lightning from the thunderstorm strikes something you cannot "
1330                 "see.");
1331         }
1332 
1333         return lightning_dam;
1334 
1335     }
1336     default:
1337         break;
1338     }
1339 
1340     return timescale_damage(act, final_damage);
1341 }
1342 
1343 // Applies damage and side effects for an actor in a cloud and returns
1344 // the damage dealt.
actor_apply_cloud(actor * act)1345 int actor_apply_cloud(actor *act)
1346 {
1347     const cloud_struct* cl = cloud_at(act->pos());
1348     if (!cl)
1349         return 0;
1350 
1351     const cloud_struct &cloud(*cl);
1352     const bool player = act->is_player();
1353     monster *mons = act->as_monster();
1354     const beam_type cloud_flavour = _cloud2beam(cloud.type);
1355 
1356     if (actor_cloud_immune(*act, cloud))
1357         return 0;
1358 
1359     const int resist = _actor_cloud_resist(act, cloud);
1360     const int cloud_max_base_damage =
1361         _actor_cloud_base_damage(act, cloud, resist, true);
1362     const int final_damage = _actor_cloud_damage(act, cloud, false);
1363 
1364     if ((player || final_damage > 0
1365          || _cloud_has_negative_side_effects(cloud.type))
1366         && cloud.type != CLOUD_STORM) // handled elsewhere
1367     {
1368         cloud.announce_actor_engulfed(act);
1369     }
1370     if (player && cloud_max_base_damage > 0 && resist > 0
1371         && (cloud.type != CLOUD_STORM || final_damage > 0))
1372     {
1373         canned_msg(MSG_YOU_RESIST);
1374     }
1375 
1376     if (cloud_flavour != BEAM_NONE)
1377         act->expose_to_element(cloud_flavour, 7);
1378 
1379     const bool side_effects =
1380         _actor_apply_cloud_side_effects(act, cloud, final_damage);
1381 
1382     if (!player && (side_effects || final_damage > 0))
1383         behaviour_event(mons, ME_DISTURB, 0, act->pos());
1384 
1385     if (final_damage)
1386     {
1387         actor *oppressor = cloud.agent();
1388         const string oppr_name =
1389             oppressor ? " "+apostrophise(oppressor->name(DESC_THE))
1390                       : "";
1391         dprf("%s %s %d damage from%s cloud: %s.",
1392              act->name(DESC_THE).c_str(),
1393              act->conj_verb("take").c_str(),
1394              final_damage,
1395              oppr_name.c_str(),
1396              cloud.cloud_name().c_str());
1397 
1398         act->hurt(oppressor, final_damage, BEAM_MISSILE,
1399                   KILLED_BY_CLOUD, "", cloud.cloud_name(true));
1400     }
1401 
1402     return final_damage;
1403 }
1404 
1405 // Describe cloud damage in the form "3-18". If vs_player is set,
1406 // extra anti-player damage is included.
desc_cloud_damage(cloud_type cl_type,bool vs_player)1407 string desc_cloud_damage(cloud_type cl_type, bool vs_player)
1408 {
1409     const cloud_damage &dam_info = clouds[cl_type].damage;
1410     const int base = _base_dam(dam_info, vs_player);
1411     const int rand = _rand_dam(dam_info, vs_player);
1412     if (rand == 0) {
1413         if (base == 0)
1414             return "";
1415         return make_stringf("%d", base);
1416     }
1417     return make_stringf("%d-%d", base, base + rand - 1);
1418 }
1419 
_cloud_is_harmful(actor * act,cloud_struct & cloud,int maximum_negligible_damage)1420 static bool _cloud_is_harmful(actor *act, cloud_struct &cloud,
1421                               int maximum_negligible_damage)
1422 {
1423     return !actor_cloud_immune(*act, cloud)
1424            && (_cloud_has_negative_side_effects(cloud.type)
1425                || (_actor_cloud_damage(act, cloud, true) >
1426                    maximum_negligible_damage));
1427 }
1428 
1429 /**
1430  * Is this cloud type dangerous to you?
1431  *
1432  * @param type the type of cloud to look at.
1433  * @param accept_temp_resistances whether to look at resistances from your form
1434  *        or durations; items and gods are used regardless of this parameter's value.
1435  * @param yours whether to treat this cloud as being made by you.
1436  */
is_damaging_cloud(cloud_type type,bool accept_temp_resistances,bool yours)1437 bool is_damaging_cloud(cloud_type type, bool accept_temp_resistances, bool yours)
1438 {
1439     // If you're immune to clouds, then no clouds are damaging. Bing bong so simple!
1440     if (you.cloud_immune())
1441         return false;
1442 
1443     // A nasty hack; map_knowledge doesn't preserve whom the cloud belongs to.
1444     if (type == CLOUD_VORTEX)
1445         return !you.duration[DUR_VORTEX] && !you.duration[DUR_VORTEX_COOLDOWN];
1446 
1447     if (accept_temp_resistances)
1448     {
1449         cloud_struct cloud;
1450         cloud.type = type;
1451         cloud.decay = 100;
1452         if (yours)
1453             cloud.set_killer(KILL_YOU);
1454         return _cloud_is_harmful(&you, cloud, 0);
1455     }
1456     else
1457     {
1458         // [ds] Yes, this is an ugly kludge: temporarily hide
1459         // durations and transforms.
1460         unwind_var<durations_t> old_durations(you.duration);
1461         unwind_var<transformation> old_form(you.form, transformation::none);
1462         you.duration.init(0);
1463         return is_damaging_cloud(type, true, yours);
1464     }
1465 }
1466 
1467 /**
1468  * Will the given monster refuse to walk into the given cloud?
1469  *
1470  * @param mons              The monster in question.
1471  * @param cloud             The cloud in question.
1472  * @param extra_careful     Whether the monster could suffer any harm from the
1473  *                          cloud at all, even if it would normally be brave
1474  *                          enough (based on e.g. hp) to enter the cloud.
1475  * @return                  Whether the monster is NOT ok to enter the cloud.
1476  */
_mons_avoids_cloud(const monster * mons,const cloud_struct & cloud,bool extra_careful)1477 static bool _mons_avoids_cloud(const monster* mons, const cloud_struct& cloud,
1478                                bool extra_careful)
1479 {
1480     // Friendlies avoid snuffing the player's conjured flames
1481     if (mons->attitude == ATT_FRIENDLY && cloud.type == CLOUD_EMBERS)
1482         return true;
1483 
1484     // clouds you're immune to are inherently safe.
1485     if (actor_cloud_immune(*mons, cloud))
1486         return false;
1487 
1488     // harmless clouds, likewise.
1489     if (is_harmless_cloud(cloud.type))
1490         return false;
1491 
1492     // Berserk monsters are less careful and will blindly plow through any
1493     // dangerous cloud, just to kill you. {due}
1494     if (!extra_careful && mons->berserk_or_insane())
1495         return false;
1496 
1497     switch (cloud.type)
1498     {
1499     case CLOUD_MIASMA:
1500         // Even the dumbest monsters will avoid miasma if they can.
1501         return true;
1502 
1503     case CLOUD_RAIN:
1504         // Fiery monsters dislike the rain.
1505         if (mons->is_fiery() && extra_careful)
1506             return true;
1507 
1508         // We don't care about what's underneath the rain cloud if we can fly.
1509         if (mons->airborne())
1510             return false;
1511 
1512         // These don't care about deep water.
1513         if (monster_habitable_grid(mons, DNGN_DEEP_WATER))
1514             return false;
1515 
1516         // This position could become deep water, and they might drown.
1517         if (env.grid(cloud.pos) == DNGN_SHALLOW_WATER
1518             && mons_intel(*mons) > I_BRAINLESS)
1519         {
1520             return true;
1521         }
1522         break;
1523 
1524     default:
1525     {
1526         if (extra_careful)
1527             return true;
1528 
1529         // calc damage here instead of using _cloud_base_damage() so we can
1530         // set our own # of trials, to try to make the AI more consistent
1531         // XXX: add a param instead?
1532         const cloud_damage &dam_info = clouds[cloud.type].damage;
1533         const int base_damage = _cloud_damage_calc(dam_info.random,
1534                                                    max(1, dam_info.random / 9),
1535                                                    dam_info.base, false);
1536         const int damage = resist_adjust_damage(mons,
1537                                                 clouds[cloud.type].beam_effect,
1538                                                 base_damage);
1539         const int hp_threshold = damage * 3;
1540 
1541         // intelligent monsters want a larger margin of safety
1542         const int safety_mult = (mons_intel(*mons) > I_ANIMAL) ? 2 : 1;
1543         // dare we risk the damage?
1544         const bool hp_ok = mons->hit_points > safety_mult * hp_threshold;
1545         // dare we risk the status effects?
1546         const bool sfx_ok = cloud.type != CLOUD_MEPHITIC
1547                             || x_chance_in_y(mons->get_hit_dice() - 1, 5);
1548         if (hp_ok && sfx_ok)
1549             return false;
1550         break;
1551     }
1552     }
1553 
1554     // Exceedingly dumb creatures will wander into harmful clouds.
1555     if (mons_intel(*mons) == I_BRAINLESS && !extra_careful)
1556         return false;
1557 
1558     // If we get here, the cloud is potentially harmful.
1559     return true;
1560 }
1561 
1562 // Like the above, but allow a monster to move from one damaging cloud
1563 // to another, even if they're of different types.
mons_avoids_cloud(const monster * mons,coord_def pos,bool placement)1564 bool mons_avoids_cloud(const monster* mons, coord_def pos, bool placement)
1565 {
1566     if (!cloud_at(pos))
1567         return false;
1568 
1569     // Is the target cloud okay?
1570     if (!_mons_avoids_cloud(mons, *cloud_at(pos), placement))
1571         return false;
1572 
1573     // If we're already in a cloud that we'd want to avoid then moving
1574     // from one to the other is okay.
1575     if (!in_bounds(mons->pos()) || mons->pos() == pos)
1576         return true;
1577 
1578     if (!cloud_at(mons->pos()))
1579         return true;
1580 
1581     return !_mons_avoids_cloud(mons, *cloud_at(mons->pos()), true);
1582 }
1583 
is_harmless_cloud(cloud_type type)1584 bool is_harmless_cloud(cloud_type type)
1585 {
1586     return clouds[type].beam_effect == BEAM_NONE
1587            && clouds[type].damage.base == 0
1588            && clouds[type].damage.random == 0
1589            && !_cloud_has_negative_side_effects(type)
1590            && type != CLOUD_VORTEX;
1591 }
1592 
cloud_type_name(cloud_type type,bool terse)1593 string cloud_type_name(cloud_type type, bool terse)
1594 {
1595     if (type <= CLOUD_NONE || type >= NUM_CLOUD_TYPES)
1596         return "buggy goodness";
1597 
1598     ASSERT(clouds[type].terse_name);
1599     if (terse || clouds[type].verbose_name == nullptr)
1600         return clouds[type].terse_name;
1601     return clouds[type].verbose_name;
1602 }
1603 
cloud_name_to_type(const string & name)1604 cloud_type cloud_name_to_type(const string &name)
1605 {
1606     const string lower_name = lowercase_string(name);
1607 
1608     if (lower_name == "random")
1609         return CLOUD_RANDOM;
1610     else if (lower_name == "debugging")
1611         return CLOUD_DEBUGGING;
1612 
1613     for (int i = CLOUD_NONE; i < CLOUD_RANDOM; i++)
1614         if (cloud_type_name(static_cast<cloud_type>(i)) == lower_name)
1615             return static_cast<cloud_type>(i);
1616 
1617     return CLOUD_NONE;
1618 }
1619 
random_walk(coord_def start,int dist)1620 coord_def random_walk(coord_def start, int dist)
1621 {
1622     ASSERT(in_bounds(start));
1623     ASSERT(dist >= 1);
1624 
1625     int moves_left = dist;
1626     coord_def pos = start;
1627     while (moves_left-- > 0)
1628     {
1629         int okay_dirs = 0;
1630         int dir       = -1;
1631         for (int j = 0; j < 8; j++)
1632         {
1633             const coord_def new_pos   = pos + Compass[j];
1634 
1635             if (in_bounds(new_pos) && !feat_is_solid(env.grid(new_pos))
1636                 && one_chance_in(++okay_dirs))
1637             {
1638                 dir = j;
1639             }
1640         }
1641 
1642         if (okay_dirs == 0)
1643             break;
1644 
1645         if (one_chance_in(++okay_dirs))
1646             continue;
1647 
1648         pos       += Compass[dir];
1649     }
1650 
1651     return pos;
1652 }
1653 
1654 ////////////////////////////////////////////////////////////////////////
1655 // cloud_struct
1656 
killer_to_whose(killer_type _killer)1657 kill_category cloud_struct::killer_to_whose(killer_type _killer)
1658 {
1659     switch (_killer)
1660     {
1661         case KILL_YOU:
1662         case KILL_YOU_MISSILE:
1663         case KILL_YOU_CONF:
1664             return KC_YOU;
1665 
1666         case KILL_MON:
1667         case KILL_MON_MISSILE:
1668         case KILL_MISC:
1669             return KC_OTHER;
1670 
1671         default:
1672             die("invalid killer type");
1673     }
1674     return KC_OTHER;
1675 }
1676 
whose_to_killer(kill_category _whose)1677 killer_type cloud_struct::whose_to_killer(kill_category _whose)
1678 {
1679     switch (_whose)
1680     {
1681         case KC_YOU:         return KILL_YOU_MISSILE;
1682         case KC_FRIENDLY:    return KILL_MON_MISSILE;
1683         case KC_OTHER:       return KILL_MISC;
1684         case KC_NCATEGORIES: die("invalid kill category");
1685     }
1686     return KILL_NONE;
1687 }
1688 
set_whose(kill_category _whose)1689 void cloud_struct::set_whose(kill_category _whose)
1690 {
1691     whose  = _whose;
1692     killer = whose_to_killer(whose);
1693 }
1694 
set_killer(killer_type _killer)1695 void cloud_struct::set_killer(killer_type _killer)
1696 {
1697     killer = _killer;
1698     whose  = killer_to_whose(killer);
1699 
1700     switch (killer)
1701     {
1702     case KILL_YOU:
1703         killer = KILL_YOU_MISSILE;
1704         break;
1705 
1706     case KILL_MON:
1707         killer = KILL_MON_MISSILE;
1708         break;
1709 
1710     default:
1711         break;
1712     }
1713 }
1714 
agent() const1715 actor *cloud_struct::agent() const
1716 {
1717     return find_agent(source, whose);
1718 }
1719 
cloud_name(bool terse) const1720 string cloud_struct::cloud_name(bool terse) const
1721 {
1722     return cloud_type_name(type, terse);
1723 }
1724 
announce_actor_engulfed(const actor * act,bool beneficial) const1725 void cloud_struct::announce_actor_engulfed(const actor *act,
1726                                            bool beneficial) const
1727 {
1728     ASSERT(act); // XXX: change to const actor &act
1729     if (!you.can_see(*act))
1730         return;
1731 
1732     // Normal clouds. (Unmodified rain clouds have a different message.)
1733     if (type != CLOUD_RAIN && type != CLOUD_STORM)
1734     {
1735         mprf("%s %s in %s.",
1736              act->name(DESC_THE).c_str(),
1737              beneficial ? act->conj_verb("bask").c_str()
1738                         : (act->conj_verb("are") + " engulfed").c_str(),
1739              cloud_name().c_str());
1740         return;
1741     }
1742 
1743     // Don't produce monster-in-rain messages in the interests
1744     // of spam reduction.
1745     if (act->is_player())
1746     {
1747         mprf("%s %s standing in %s.",
1748              act->name(DESC_THE).c_str(),
1749              act->conj_verb("are").c_str(),
1750              type == CLOUD_STORM ? "a thunderstorm" : "the rain");
1751     }
1752 }
1753 
1754 /**
1755  * What colour is the given cloud?
1756  *
1757  * @param cloudno       The cloud in question.
1758  * @return              An appropriate colour for the cloud.
1759  *                      May vary from call to call (randomized for some cloud
1760  *                      types).
1761  */
get_cloud_colour(const cloud_struct & cloud)1762 colour_t get_cloud_colour(const cloud_struct &cloud)
1763 {
1764     // if we have the colour in data, use that.
1765     if (clouds[cloud.type].colour)
1766         return clouds[cloud.type].colour;
1767 
1768     // weird clouds
1769     switch (cloud.type)
1770     {
1771     case CLOUD_FIRE:
1772     case CLOUD_FOREST_FIRE:
1773         if (cloud.decay <= 20)
1774             return RED;
1775         if (cloud.decay <= 40)
1776             return LIGHTRED;
1777 
1778         // total weight 16
1779         return random_choose_weighted(9, YELLOW,
1780                                       4, RED,
1781                                       3, LIGHTRED);
1782 
1783     case CLOUD_COLD:
1784         if (cloud.decay <= 20)
1785             return BLUE;
1786         if (cloud.decay <= 40)
1787             return LIGHTBLUE;
1788 
1789         // total weight 16
1790         return random_choose_weighted(9, WHITE,
1791                                       4, BLUE,
1792                                       3, LIGHTBLUE);
1793         break;
1794 
1795     default:
1796         return LIGHTGREY;
1797     }
1798 }
1799 
get_cloud_originator(const coord_def & pos)1800 coord_def get_cloud_originator(const coord_def& pos)
1801 {
1802     const cloud_struct* cloud = cloud_at(pos);
1803     if (!cloud)
1804         return coord_def();
1805     const actor *agent = actor_by_mid(cloud->source);
1806     if (!agent)
1807         return coord_def();
1808     return agent->pos();
1809 }
1810 
remove_vortex_clouds(mid_t whose)1811 void remove_vortex_clouds(mid_t whose)
1812 {
1813     // Needed to clean up after the end of tornado cooldown, so we can again
1814     // assume all "raging winds" clouds are harmful. This is needed only
1815     // because map_knowledge doesn't preserve the knowledge about whom the
1816     // cloud belongs to. If this changes, please remove this function. For
1817     // example, this approach doesn't work if we ever make Tornado a monster
1818     // spell (excluding immobile and mindless casters).
1819     // XXX: this comment seems impossibly out of date? ^
1820 
1821     // We can't iterate over env.cloud directly because delete_cloud
1822     // will remove this cloud and invalidate our iterator.
1823     vector<coord_def> vortices;
1824     for (auto& entry : env.cloud)
1825         if (entry.second.type == CLOUD_VORTEX && entry.second.source == whose)
1826             vortices.push_back(entry.first);
1827 
1828     for (auto pos : vortices)
1829         delete_cloud(pos);
1830 }
1831 
_spread_cloud(coord_def pos,cloud_type type,int radius,int pow,int & remaining,int ratio=10,mid_t agent_mid=0,kill_category kcat=KC_OTHER)1832 static void _spread_cloud(coord_def pos, cloud_type type, int radius, int pow,
1833                           int &remaining, int ratio = 10,
1834                           mid_t agent_mid = 0, kill_category kcat = KC_OTHER)
1835 {
1836     bolt beam;
1837     beam.target = pos;
1838     beam.use_target_as_pos = true;
1839     explosion_map exp_map;
1840     exp_map.init(INT_MAX);
1841     beam.determine_affected_cells(exp_map, coord_def(), 0,
1842                                   radius, true, true);
1843 
1844     coord_def centre(9,9);
1845     for (distance_iterator di(pos, true, false); di; ++di)
1846     {
1847         if (di.radius() > radius)
1848             return;
1849 
1850         if ((exp_map(*di - pos + centre) < INT_MAX) && !cloud_at(*di)
1851             && (di.radius() < radius || x_chance_in_y(ratio, 100)))
1852         {
1853             place_cloud(type, *di, pow + random2(pow), nullptr);
1854             --remaining;
1855 
1856             // Setting this way since the agent of the cloud may be dead before
1857             // cloud is placed, so no agent exists to pass to place_cloud (though
1858             // proper blame should still be assigned)
1859             if (cloud_struct* cloud = cloud_at(*di))
1860             {
1861                 cloud->source = agent_mid;
1862                 cloud->whose = kcat;
1863             }
1864         }
1865 
1866         // Placed all clouds for this spreader
1867         if (remaining == 0)
1868             return;
1869     }
1870 }
1871 
run_cloud_spreaders(int dur)1872 void run_cloud_spreaders(int dur)
1873 {
1874     if (!dur)
1875         return;
1876 
1877     for (map_marker *marker : env.markers.get_all(MAT_CLOUD_SPREADER))
1878     {
1879         map_cloud_spreader_marker * const mark
1880             = dynamic_cast<map_cloud_spreader_marker*>(marker);
1881 
1882         mark->speed_increment += dur;
1883         int rad = min(mark->speed_increment / mark->speed, mark->max_rad - 1) + 1;
1884         int ratio = (mark->speed_increment - ((rad - 1) * mark->speed))
1885                     * 100 / mark->speed;
1886 
1887         if (ratio == 0)
1888         {
1889             rad--;
1890             ratio = 100;
1891         }
1892 
1893         _spread_cloud(mark->pos, mark->ctype, rad, mark->duration,
1894                         mark->remaining, ratio, mark->agent_mid, mark->kcat);
1895         if ((rad >= mark->max_rad && ratio >= 100) || mark->remaining == 0)
1896         {
1897             env.markers.remove(mark);
1898             break;
1899         }
1900     }
1901 }
1902 
cloud_type_tile_info(cloud_type type)1903 const cloud_tile_info& cloud_type_tile_info(cloud_type type)
1904 {
1905     return clouds[type].tile_info;
1906 }
1907 
1908 /// Knock out clouds & set the still winds level flag; also message.
start_still_winds()1909 void start_still_winds()
1910 {
1911     delete_all_clouds();
1912     env.level_state |= LSTATE_STILL_WINDS;
1913     mprf(MSGCH_WARN, "%s", "The air becomes perfectly still.");
1914 }
1915 
end_still_winds()1916 void end_still_winds()
1917 {
1918     env.level_state &= ~LSTATE_STILL_WINDS;
1919     mpr("The air resumes its normal movements.");
1920 }
1921 
1922 /**
1923  * Surround a monster with clouds of a certain type (excepting squares with an
1924  * allied monster). This also deletes the same cloud if it's on top of the
1925  * monster (which will happen as they walk around).
1926  */
surround_actor_with_cloud(const actor * a,cloud_type cloud)1927 void surround_actor_with_cloud(const actor* a, cloud_type cloud)
1928 {
1929     const coord_def pos = a->pos();
1930     const cloud_struct* overhead = cloud_at(pos);
1931     if (overhead && overhead->type == cloud)
1932         delete_cloud(pos);
1933     for (adjacent_iterator ai(pos); ai; ++ai)
1934     {
1935         const cloud_struct* existing = cloud_at(*ai);
1936         // dprf("surround_actor_with_cloud x:%d y:%d solid:%d cloud_at:%s",
1937         //      ai->x, ai->y, cell_is_solid(*ai), existing ? "y" : "n");
1938         if (cell_is_solid(*ai))
1939             continue;
1940         if (existing && existing->type != cloud)
1941             continue;
1942         const monster* mons = monster_at(*ai);
1943         if (mons && mons->alive() && mons_aligned(a, mons))
1944             continue;
1945         place_cloud(cloud, *ai, 2 + random2(6), a);
1946     }
1947 }
1948