3 * Summary: Cloud creating spells.
8 #include "spl-clouds.h"
20 #include "godconduct.h"
34 #include "transform.h"
38 // Returns whether the spell was actually cast.
39 bool conjure_flame(int pow
, const coord_def
& where
)
41 // FIXME: This would be better handled by a flag to enforce max range.
42 if (distance(where
, you
.pos()) > dist_range(spell_range(SPELL_CONJURE_FLAME
,
46 mpr("That's too far away.");
50 if (you
.trans_wall_blocking(where
))
52 mpr("A translucent wall is in the way.");
56 if (cell_is_solid(where
))
61 mpr("The flames aren't hot enough to melt wax walls!");
64 mpr("You can't ignite solid metal!");
66 case DNGN_GREEN_CRYSTAL_WALL
:
67 mpr("You can't ignite solid crystal!");
71 mpr("The flames aren't hot enough to burn down trees!");
74 mpr("You can't ignite solid rock!");
80 const int cloud
= env
.cgrid(where
);
82 if (cloud
!= EMPTY_CLOUD
&& env
.cloud
[cloud
].type
!= CLOUD_FIRE
)
84 mpr("There's already a cloud there!");
88 // Note that self-targeting is handled by SPFLAG_NOT_SELF.
89 monster
* mons
= monster_at(where
);
92 if (you
.can_see(mons
) && !mons_is_unknown_mimic(mons
))
94 mpr("You can't place the cloud on a creature.");
99 // FIXME: maybe should do _paranoid_option_disable() here?
100 mpr("You see a ghostly outline there, and the spell fizzles.");
101 return (true); // Don't give free detection!
105 if (cloud
!= EMPTY_CLOUD
)
107 // Reinforce the cloud - but not too much.
108 // It must be a fire cloud from a previous test.
109 mpr("The fire roars with new energy!");
110 const int extra_dur
= 2 + std::min(random2(pow
) / 2, 20);
111 env
.cloud
[cloud
].decay
+= extra_dur
* 5;
112 env
.cloud
[cloud
].set_whose(KC_YOU
);
116 const int durat
= std::min(5 + (random2(pow
)/2) + (random2(pow
)/2), 23);
117 place_cloud(CLOUD_FIRE
, where
, durat
, &you
);
118 mpr("The fire roars!");
125 // Assumes beem.range has already been set. -cao
126 bool stinking_cloud(int pow
, bolt
&beem
)
128 beem
.name
= "stinking cloud";
130 beem
.damage
= dice_def(1, 0);
132 beem
.glyph
= dchar_glyph(DCHAR_FIRED_ZAP
);
133 beem
.flavour
= BEAM_POTION_STINKING_CLOUD
;
134 beem
.ench_power
= pow
;
135 beem
.beam_source
= MHITYOU
;
136 beem
.thrower
= KILL_YOU
;
137 beem
.is_beam
= false;
138 beem
.is_explosion
= true;
139 beem
.aux_source
.clear();
142 beem
.source
= you
.pos();
143 beem
.can_see_invis
= you
.can_see_invisible();
144 beem
.smart_monster
= true;
145 beem
.attitude
= ATT_FRIENDLY
;
146 beem
.friend_info
.count
= 0;
147 beem
.is_tracer
= true;
150 if (beem
.beam_cancelled
)
152 // We don't want to fire through friendlies.
158 beem
.is_tracer
= false;
164 bool cast_big_c(int pow
, cloud_type cty
, const actor
*caster
, bolt
&beam
)
166 if (distance(beam
.target
, you
.pos()) > dist_range(beam
.range
)
167 || !in_bounds(beam
.target
))
169 mpr("That is beyond the maximum range.");
173 if (cell_is_solid(beam
.target
))
175 mpr("You can't place clouds on a wall.");
179 big_cloud(cty
, caster
, beam
.target
, pow
, 8 + random2(3), -1);
180 noisy(2, beam
.target
);
184 void big_cloud(cloud_type cl_type
, const actor
*agent
,
185 const coord_def
& where
, int pow
, int size
, int spread_rate
,
186 int colour
, std::string name
, std::string tile
)
188 apply_area_cloud(make_a_normal_cloud
, where
, pow
, size
,
189 cl_type
, agent
, spread_rate
, colour
, name
, tile
);
192 void cast_ring_of_flames(int power
)
194 // You shouldn't be able to cast this in the rain. {due}
195 if (in_what_cloud(CLOUD_RAIN
))
197 mpr("Your spell sizzles in the rain.");
200 you
.increase_duration(DUR_FIRE_SHIELD
,
201 5 + (power
/ 10) + (random2(power
) / 5), 50,
202 "The air around you leaps into flame!");
203 manage_fire_shield(1);
206 void manage_fire_shield(int delay
)
208 ASSERT(you
.duration
[DUR_FIRE_SHIELD
]);
210 int old_dur
= you
.duration
[DUR_FIRE_SHIELD
];
212 you
.duration
[DUR_FIRE_SHIELD
]-= delay
;
213 if (you
.duration
[DUR_FIRE_SHIELD
] < 0)
214 you
.duration
[DUR_FIRE_SHIELD
] = 0;
216 if (!you
.duration
[DUR_FIRE_SHIELD
])
218 mpr("Your ring of flames gutters out.", MSGCH_DURATION
);
222 int threshold
= get_expiration_threshold(DUR_FIRE_SHIELD
);
225 if (old_dur
> threshold
&& you
.duration
[DUR_FIRE_SHIELD
] < threshold
)
226 mpr("Your ring of flames is guttering out.", MSGCH_WARN
);
228 // Place fire clouds all around you
229 for (adjacent_iterator
ai(you
.pos()); ai
; ++ai
)
230 if (!feat_is_solid(grd(*ai
)) && env
.cgrid(*ai
) == EMPTY_CLOUD
)
231 place_cloud(CLOUD_FIRE
, *ai
, 1 + random2(6), &you
);
234 void corpse_rot(actor
* caster
)
236 for (radius_iterator
ri(caster
->pos(), 6, C_ROUND
, caster
->atype() == ACT_PLAYER
? you
.get_los_no_trans()
237 : caster
->get_los());
240 if (!is_sanctuary(*ri
) && env
.cgrid(*ri
) == EMPTY_CLOUD
)
241 for (stack_iterator
si(*ri
); si
; ++si
)
242 if (si
->base_type
== OBJ_CORPSES
&& si
->sub_type
== CORPSE_BODY
)
244 // Found a corpse. Skeletonise it if possible.
245 if (!mons_skeleton(si
->plus
))
246 destroy_item(si
->index());
248 turn_corpse_into_skeleton(*si
);
250 place_cloud(CLOUD_MIASMA
, *ri
, 4+random2avg(16, 3),caster
);
252 // Don't look for more corpses here.
257 if (you
.can_smell() && you
.can_see(caster
))
258 mpr("You smell decay.");
260 // Should make zombies decay into skeletons?
263 int make_a_normal_cloud(coord_def where
, int pow
, int spread_rate
,
264 cloud_type ctype
, const actor
*agent
, int colour
,
265 std::string name
, std::string tile
)
267 place_cloud(ctype
, where
,
268 (3 + random2(pow
/ 4) + random2(pow
/ 4) + random2(pow
/ 4)),
269 agent
, spread_rate
, colour
, name
, tile
);
274 // Returns a vector of cloud types created by this potion type.
275 // FIXME: Heavily duplicated code.
276 static std::vector
<int> _get_evaporate_result(int potion
)
278 std::vector
<int> beams
;
279 bool random_potion
= false;
282 case POT_STRONG_POISON
:
284 beams
.push_back(BEAM_POTION_POISON
);
287 case POT_DEGENERATION
:
288 beams
.push_back(BEAM_POTION_POISON
);
289 beams
.push_back(BEAM_POTION_MIASMA
);
293 beams
.push_back(BEAM_POTION_MIASMA
);
299 beams
.push_back(BEAM_POTION_STINKING_CLOUD
);
304 beams
.push_back(BEAM_POTION_STEAM
);
308 case POT_BLOOD_COAGULATED
:
309 beams
.push_back(BEAM_POTION_STINKING_CLOUD
);
310 // deliberate fall through
311 case POT_BERSERK_RAGE
:
312 beams
.push_back(BEAM_POTION_FIRE
);
313 beams
.push_back(BEAM_POTION_STEAM
);
317 beams
.push_back(BEAM_POTION_MUTAGENIC
);
318 // deliberate fall-through
319 case POT_GAIN_STRENGTH
:
320 case POT_GAIN_DEXTERITY
:
321 case POT_GAIN_INTELLIGENCE
:
324 beams
.push_back(BEAM_POTION_FIRE
);
325 beams
.push_back(BEAM_POTION_COLD
);
326 beams
.push_back(BEAM_POTION_POISON
);
327 beams
.push_back(BEAM_POTION_MIASMA
);
328 random_potion
= true;
332 beams
.push_back(BEAM_POTION_FIRE
);
333 beams
.push_back(BEAM_POTION_STINKING_CLOUD
);
334 beams
.push_back(BEAM_POTION_COLD
);
335 beams
.push_back(BEAM_POTION_POISON
);
336 beams
.push_back(BEAM_POTION_BLUE_SMOKE
);
337 beams
.push_back(BEAM_POTION_STEAM
);
338 random_potion
= true;
342 std::vector
<int> clouds
;
343 for (unsigned int k
= 0; k
< beams
.size(); ++k
)
344 clouds
.push_back(beam2cloud((beam_type
) beams
[k
]));
348 // handled in beam.cc
349 clouds
.push_back(CLOUD_FIRE
);
350 clouds
.push_back(CLOUD_STINK
);
351 clouds
.push_back(CLOUD_COLD
);
352 clouds
.push_back(CLOUD_POISON
);
353 clouds
.push_back(CLOUD_BLUE_SMOKE
);
354 clouds
.push_back(CLOUD_STEAM
);
360 // Returns a comma-separated list of all cloud types potentially created
361 // by this potion type. Doesn't respect the different probabilities.
362 std::string
get_evaporate_result_list(int potion
)
364 std::vector
<int> clouds
= _get_evaporate_result(potion
);
365 std::sort(clouds
.begin(), clouds
.end());
367 std::vector
<std::string
> clouds_list
;
370 for (unsigned int k
= 0; k
< clouds
.size(); ++k
)
372 const int new_cloud
= clouds
[k
];
373 if (new_cloud
== old_cloud
)
376 // This relies on all smoke types being handled as blue.
377 if (new_cloud
== CLOUD_BLUE_SMOKE
)
378 clouds_list
.push_back("coloured smoke");
380 clouds_list
.push_back(cloud_type_name((cloud_type
) new_cloud
));
382 old_cloud
= new_cloud
;
385 return comma_separated_line(clouds_list
.begin(), clouds_list
.end(),
390 // Assumes beem.range is already set -cao
391 bool cast_evaporate(int pow
, bolt
& beem
, int pot_idx
)
393 ASSERT(you
.inv
[pot_idx
].base_type
== OBJ_POTIONS
);
394 item_def
& potion
= you
.inv
[pot_idx
];
396 beem
.name
= "potion";
397 beem
.colour
= potion
.colour
;
398 beem
.glyph
= dchar_glyph(DCHAR_FIRED_FLASK
);
399 beem
.beam_source
= MHITYOU
;
400 beem
.thrower
= KILL_YOU_MISSILE
;
401 beem
.is_beam
= false;
402 beem
.aux_source
.clear();
404 beem
.auto_hit
= true;
405 beem
.damage
= dice_def(1, 0); // no damage, just producing clouds
406 beem
.ench_power
= pow
; // used for duration only?
407 beem
.is_explosion
= true;
409 beem
.flavour
= BEAM_POTION_STINKING_CLOUD
;
410 beam_type tracer_flavour
= BEAM_MMISSILE
;
412 switch (potion
.sub_type
)
414 case POT_STRONG_POISON
:
415 beem
.ench_power
*= 2;
416 // deliberate fall-through
418 beem
.flavour
= BEAM_POTION_POISON
;
419 tracer_flavour
= BEAM_POISON
;
422 case POT_DEGENERATION
:
423 beem
.effect_known
= false;
424 beem
.flavour
= (coinflip() ? BEAM_POTION_POISON
: BEAM_POTION_MIASMA
);
425 tracer_flavour
= BEAM_MIASMA
;
426 beem
.ench_power
*= 2;
430 beem
.flavour
= BEAM_POTION_MIASMA
;
431 tracer_flavour
= BEAM_MIASMA
;
432 beem
.ench_power
*= 2;
436 beem
.ench_power
*= 2;
440 tracer_flavour
= beem
.flavour
= BEAM_POTION_STINKING_CLOUD
;
445 tracer_flavour
= beem
.flavour
= BEAM_POTION_STEAM
;
449 case POT_BLOOD_COAGULATED
:
450 if (one_chance_in(3))
451 break; // stinking cloud
452 // deliberate fall through
453 case POT_BERSERK_RAGE
:
454 beem
.effect_known
= false;
455 beem
.flavour
= (coinflip() ? BEAM_POTION_FIRE
: BEAM_POTION_STEAM
);
456 if (potion
.sub_type
== POT_BERSERK_RAGE
)
457 tracer_flavour
= BEAM_FIRE
;
459 tracer_flavour
= BEAM_RANDOM
;
463 // Maybe we'll get a mutagenic cloud.
466 beem
.effect_known
= true;
467 tracer_flavour
= beem
.flavour
= BEAM_POTION_MUTAGENIC
;
470 // if not, deliberate fall through for something random
472 case POT_GAIN_STRENGTH
:
473 case POT_GAIN_DEXTERITY
:
474 case POT_GAIN_INTELLIGENCE
:
477 beem
.effect_known
= false;
480 case 0: beem
.flavour
= BEAM_POTION_FIRE
; break;
481 case 1: beem
.flavour
= BEAM_POTION_COLD
; break;
482 case 2: beem
.flavour
= BEAM_POTION_POISON
; break;
483 case 3: beem
.flavour
= BEAM_POTION_MIASMA
; break;
484 default: beem
.flavour
= BEAM_POTION_RANDOM
; break;
486 tracer_flavour
= BEAM_RANDOM
;
490 beem
.effect_known
= false;
493 case 0: beem
.flavour
= BEAM_POTION_FIRE
; break;
494 case 1: beem
.flavour
= BEAM_POTION_STINKING_CLOUD
; break;
495 case 2: beem
.flavour
= BEAM_POTION_COLD
; break;
496 case 3: beem
.flavour
= BEAM_POTION_POISON
; break;
497 case 4: beem
.flavour
= BEAM_POTION_RANDOM
; break;
498 case 5: beem
.flavour
= BEAM_POTION_BLUE_SMOKE
; break;
499 case 6: beem
.flavour
= BEAM_POTION_BLACK_SMOKE
; break;
500 default: beem
.flavour
= BEAM_POTION_STEAM
; break;
502 tracer_flavour
= BEAM_RANDOM
;
506 // Fire tracer. FIXME: use player_tracer() here!
507 beem
.source
= you
.pos();
508 beem
.can_see_invis
= you
.can_see_invisible();
509 beem
.smart_monster
= true;
510 beem
.attitude
= ATT_FRIENDLY
;
511 beem
.beam_cancelled
= false;
512 beem
.is_tracer
= true;
513 beem
.friend_info
.reset();
515 beam_type real_flavour
= beem
.flavour
;
516 beem
.flavour
= tracer_flavour
;
519 if (beem
.beam_cancelled
)
521 // We don't want to fire through friendlies or at ourselves.
527 beem
.flavour
= real_flavour
;
528 beem
.is_tracer
= false;
532 if (is_blood_potion(potion
))
533 remove_oldest_blood_potion(potion
);
535 dec_inv_item_quantity(pot_idx
, 1);
540 int holy_flames(monster
* caster
, actor
* defender
)
542 const coord_def pos
= defender
->pos();
545 for (adjacent_iterator
ai(pos
); ai
; ++ai
)
548 || env
.cgrid(*ai
) != EMPTY_CLOUD
549 || feat_is_solid(grd(*ai
))
556 place_cloud(CLOUD_HOLY_FLAMES
, *ai
, caster
->hit_dice
* 5, caster
);