Apply the new ground_level method.
[crawl.git] / crawl-ref / source / spl-clouds.cc
blobbe87c474559c117af761e1e7faec0c1dd4bed2fc
1 /*
2 * File: spl-clouds.cc
3 * Summary: Cloud creating spells.
4 */
6 #include "AppHdr.h"
8 #include "spl-clouds.h"
9 #include "externs.h"
11 #include <algorithm>
13 #include "beam.h"
14 #include "cloud.h"
15 #include "coord.h"
16 #include "coordit.h"
17 #include "env.h"
18 #include "exercise.h"
19 #include "fprop.h"
20 #include "godconduct.h"
21 #include "itemprop.h"
22 #include "items.h"
23 #include "libutil.h"
24 #include "message.h"
25 #include "misc.h"
26 #include "mon-behv.h"
27 #include "mon-util.h"
28 #include "ouch.h"
29 #include "player.h"
30 #include "skills.h"
31 #include "spl-util.h"
32 #include "stuff.h"
33 #include "terrain.h"
34 #include "transform.h"
35 #include "viewchar.h"
36 #include "shout.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,
43 pow, true))
44 || !in_bounds(where))
46 mpr("That's too far away.");
47 return (false);
50 if (you.trans_wall_blocking(where))
52 mpr("A translucent wall is in the way.");
53 return (false);
56 if (cell_is_solid(where))
58 switch (grd(where))
60 case DNGN_WAX_WALL:
61 mpr("The flames aren't hot enough to melt wax walls!");
62 break;
63 case DNGN_METAL_WALL:
64 mpr("You can't ignite solid metal!");
65 break;
66 case DNGN_GREEN_CRYSTAL_WALL:
67 mpr("You can't ignite solid crystal!");
68 break;
69 case DNGN_TREE:
70 case DNGN_SWAMP_TREE:
71 mpr("The flames aren't hot enough to burn down trees!");
72 break;
73 default:
74 mpr("You can't ignite solid rock!");
75 break;
77 return (false);
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!");
85 return (false);
88 // Note that self-targeting is handled by SPFLAG_NOT_SELF.
89 monster* mons = monster_at(where);
90 if (mons)
92 if (you.can_see(mons) && !mons_is_unknown_mimic(mons))
94 mpr("You can't place the cloud on a creature.");
95 return (false);
97 else
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);
114 else
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!");
120 noisy(2, where);
122 return (true);
125 // Assumes beem.range has already been set. -cao
126 bool stinking_cloud(int pow, bolt &beem)
128 beem.name = "stinking cloud";
129 beem.colour = GREEN;
130 beem.damage = dice_def(1, 0);
131 beem.hit = 20;
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();
141 // Fire tracer.
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;
148 beem.fire();
150 if (beem.beam_cancelled)
152 // We don't want to fire through friendlies.
153 canned_msg(MSG_OK);
154 return (false);
157 // Really fire.
158 beem.is_tracer = false;
159 beem.fire();
161 return (true);
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.");
170 return false;
173 if (cell_is_solid(beam.target))
175 mpr("You can't place clouds on a wall.");
176 return false;
179 big_cloud(cty, caster, beam.target, pow, 8 + random2(3), -1);
180 noisy(2, beam.target);
181 return true;
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.");
198 return;
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);
219 return;
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());
238 ri; ++ri)
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());
247 else
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.
253 break;
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);
271 return 1;
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;
280 switch (potion)
282 case POT_STRONG_POISON:
283 case POT_POISON:
284 beams.push_back(BEAM_POTION_POISON);
285 break;
287 case POT_DEGENERATION:
288 beams.push_back(BEAM_POTION_POISON);
289 beams.push_back(BEAM_POTION_MIASMA);
290 break;
292 case POT_DECAY:
293 beams.push_back(BEAM_POTION_MIASMA);
294 break;
296 case POT_PARALYSIS:
297 case POT_CONFUSION:
298 case POT_SLOWING:
299 beams.push_back(BEAM_POTION_STINKING_CLOUD);
300 break;
302 case POT_WATER:
303 case POT_PORRIDGE:
304 beams.push_back(BEAM_POTION_STEAM);
305 break;
307 case POT_BLOOD:
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);
314 break;
316 case POT_MUTATION:
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:
322 case POT_EXPERIENCE:
323 case POT_MAGIC:
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;
329 break;
331 default:
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;
339 break;
342 std::vector<int> clouds;
343 for (unsigned int k = 0; k < beams.size(); ++k)
344 clouds.push_back(beam2cloud((beam_type) beams[k]));
346 if (random_potion)
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);
357 return (clouds);
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;
369 int old_cloud = -1;
370 for (unsigned int k = 0; k < clouds.size(); ++k)
372 const int new_cloud = clouds[k];
373 if (new_cloud == old_cloud)
374 continue;
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");
379 else
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(),
386 " or ", ", ");
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
417 case POT_POISON:
418 beem.flavour = BEAM_POTION_POISON;
419 tracer_flavour = BEAM_POISON;
420 break;
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;
427 break;
429 case POT_DECAY:
430 beem.flavour = BEAM_POTION_MIASMA;
431 tracer_flavour = BEAM_MIASMA;
432 beem.ench_power *= 2;
433 break;
435 case POT_PARALYSIS:
436 beem.ench_power *= 2;
437 // fall through
438 case POT_CONFUSION:
439 case POT_SLOWING:
440 tracer_flavour = beem.flavour = BEAM_POTION_STINKING_CLOUD;
441 break;
443 case POT_WATER:
444 case POT_PORRIDGE:
445 tracer_flavour = beem.flavour = BEAM_POTION_STEAM;
446 break;
448 case POT_BLOOD:
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;
458 else
459 tracer_flavour = BEAM_RANDOM;
460 break;
462 case POT_MUTATION:
463 // Maybe we'll get a mutagenic cloud.
464 if (coinflip())
466 beem.effect_known = true;
467 tracer_flavour = beem.flavour = BEAM_POTION_MUTAGENIC;
468 break;
470 // if not, deliberate fall through for something random
472 case POT_GAIN_STRENGTH:
473 case POT_GAIN_DEXTERITY:
474 case POT_GAIN_INTELLIGENCE:
475 case POT_EXPERIENCE:
476 case POT_MAGIC:
477 beem.effect_known = false;
478 switch (random2(5))
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;
487 break;
489 default:
490 beem.effect_known = false;
491 switch (random2(12))
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;
503 break;
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;
517 beem.fire();
519 if (beem.beam_cancelled)
521 // We don't want to fire through friendlies or at ourselves.
522 canned_msg(MSG_OK);
523 return (false);
526 // Really fire.
527 beem.flavour = real_flavour;
528 beem.is_tracer = false;
529 beem.fire();
531 // Use up a potion.
532 if (is_blood_potion(potion))
533 remove_oldest_blood_potion(potion);
535 dec_inv_item_quantity(pot_idx, 1);
537 return (true);
540 int holy_flames(monster* caster, actor* defender)
542 const coord_def pos = defender->pos();
543 int cloud_count = 0;
545 for (adjacent_iterator ai(pos); ai; ++ai)
547 if (!in_bounds(*ai)
548 || env.cgrid(*ai) != EMPTY_CLOUD
549 || feat_is_solid(grd(*ai))
550 || is_sanctuary(*ai)
551 || monster_at(*ai))
553 continue;
556 place_cloud(CLOUD_HOLY_FLAMES, *ai, caster->hit_dice * 5, caster);
558 cloud_count++;
561 return cloud_count;