Apply the new ground_level method.
[crawl.git] / crawl-ref / source / spl-other.cc
blobc1fd896519fa64f553954f9e665a8bfb5bc6d93e
1 /*
2 * File: spl-other.cc
3 * Summary: Non-enchantment spells that didn't fit anywhere else.
4 * Mostly Transmutations.
5 */
7 #include "AppHdr.h"
9 #include "spl-other.h"
10 #include "externs.h"
12 #include "coord.h"
13 #include "delay.h"
14 #include "env.h"
15 #include "food.h"
16 #include "godconduct.h"
17 #include "it_use2.h"
18 #include "itemname.h"
19 #include "itemprop.h"
20 #include "items.h"
21 #include "libutil.h"
22 #include "makeitem.h"
23 #include "message.h"
24 #include "misc.h"
25 #include "mon-place.h"
26 #include "mon-util.h"
27 #include "player.h"
28 #include "player-stats.h"
29 #include "religion.h"
30 #include "spl-util.h"
31 #include "stuff.h"
32 #include "terrain.h"
33 #include "transform.h"
35 void cast_cure_poison(int pow)
37 if (you.duration[DUR_POISONING] > 0)
38 reduce_poison_player(2 + random2(pow) + random2(3));
39 else
40 canned_msg(MSG_NOTHING_HAPPENS);
43 bool cast_sublimation_of_blood(int pow)
45 bool success = false;
47 int wielded = you.equip[EQ_WEAPON];
49 if (wielded != -1)
51 if (you.inv[wielded].base_type == OBJ_FOOD
52 && you.inv[wielded].sub_type == FOOD_CHUNK)
54 mpr("The chunk of flesh you are holding crumbles to dust.");
56 mpr("A flood of magical energy pours into your mind!");
58 inc_mp(7 + random2(7), false);
60 dec_inv_item_quantity(wielded, 1);
62 if (mons_genus(you.inv[wielded].plus) == MONS_ORC)
63 did_god_conduct(DID_DESECRATE_ORCISH_REMAINS, 2);
65 else if (is_blood_potion(you.inv[wielded]))
67 mprf("The blood within %s froths and boils.",
68 you.inv[wielded].quantity > 1 ? "one of your flasks"
69 : "the flask you are holding");
71 mpr("A flood of magical energy pours into your mind!");
73 inc_mp(7 + random2(7), false);
75 remove_oldest_blood_potion(you.inv[wielded]);
76 dec_inv_item_quantity(wielded, 1);
78 else
79 wielded = -1;
82 if (wielded == -1)
84 if (you.duration[DUR_DEATHS_DOOR])
86 mpr("A conflicting enchantment prevents the spell from "
87 "coming into effect.");
89 else if (you.species == SP_VAMPIRE && you.hunger_state <= HS_SATIATED)
91 mpr("You don't have enough blood to draw power from your "
92 "own body.");
94 else if (!enough_hp(2, true))
95 mpr("Your attempt to draw power from your own body fails.");
96 else
98 // For vampires.
99 int food = 0;
101 mpr("You draw magical energy from your own body!");
103 while (you.magic_points < you.max_magic_points && you.hp > 1
104 && (you.species != SP_VAMPIRE || you.hunger - food >= 7000))
106 success = true;
108 inc_mp(1, false);
109 dec_hp(1, false);
111 if (you.species == SP_VAMPIRE)
112 food += 15;
114 for (int loopy = 0; loopy < (you.hp > 1 ? 3 : 0); ++loopy)
115 if (x_chance_in_y(6, pow))
116 dec_hp(1, false);
118 if (x_chance_in_y(6, pow))
119 break;
122 make_hungry(food, false);
126 return (success);
129 bool cast_death_channel(int pow, god_type god)
131 bool success = false;
133 if (you.duration[DUR_DEATH_CHANNEL] < 30 * BASELINE_DELAY)
135 success = true;
137 mpr("Malign forces permeate your being, awaiting release.");
139 you.increase_duration(DUR_DEATH_CHANNEL, 15 + random2(1 + pow/3), 100);
141 if (god != GOD_NO_GOD)
142 you.attribute[ATTR_DIVINE_DEATH_CHANNEL] = static_cast<int>(god);
144 else
145 canned_msg(MSG_NOTHING_HAPPENS);
147 return (success);
150 // Type recalled:
151 // 0 = anything
152 // 1 = undead only (Yred religion ability)
153 // 2 = orcs only (Beogh religion ability)
154 bool recall(int type_recalled)
156 int loopy = 0; // general purpose looping variable {dlb}
157 bool success = false; // more accurately: "apparent success" {dlb}
158 int start_count = 0;
159 int step_value = 1;
160 int end_count = (MAX_MONSTERS - 1);
162 monster* mons = NULL;
164 // someone really had to make life difficult {dlb}:
165 // sometimes goes through monster list backwards
166 if (coinflip())
168 start_count = (MAX_MONSTERS - 1);
169 end_count = 0;
170 step_value = -1;
173 for (loopy = start_count; loopy != end_count + step_value;
174 loopy += step_value)
176 mons = &menv[loopy];
178 if (mons->type == MONS_NO_MONSTER)
179 continue;
181 if (!mons->friendly())
182 continue;
184 if (mons_class_is_stationary(mons->type)
185 || mons_is_conjured(mons->type))
187 continue;
190 if (!monster_habitable_grid(mons, DNGN_FLOOR))
191 continue;
193 if (type_recalled == 1) // undead
195 if (mons->holiness() != MH_UNDEAD)
196 continue;
198 else if (type_recalled == 2) // Beogh
200 if (!is_orcish_follower(mons))
201 continue;
204 coord_def empty;
205 if (empty_surrounds(you.pos(), DNGN_FLOOR, 3, false, empty)
206 && mons->move_to_pos(empty))
208 // only informed if monsters recalled are visible {dlb}:
209 if (simple_monster_message(mons, " is recalled."))
210 success = true;
212 else
213 break; // no more room to place monsters {dlb}
216 if (!success)
217 mpr("Nothing appears to have answered your call.");
219 return (success);
222 // Cast_phase_shift: raises evasion (by 8 currently) via Translocations.
223 void cast_phase_shift(int pow)
225 if (!you.duration[DUR_PHASE_SHIFT])
226 mpr("You feel the strange sensation of being on two planes at once.");
227 else
228 mpr("You feel the material plane grow further away.");
230 you.increase_duration(DUR_PHASE_SHIFT, 5 + random2(pow), 30);
231 you.redraw_evasion = true;
234 static bool _feat_is_passwallable(dungeon_feature_type feat)
236 // Irony: you can passwall through a secret door but not a door.
237 // Worked stone walls are out, they're not diggable and
238 // are used for impassable walls...
239 switch (feat)
241 case DNGN_ROCK_WALL:
242 case DNGN_SLIMY_WALL:
243 case DNGN_CLEAR_ROCK_WALL:
244 case DNGN_SECRET_DOOR:
245 return (true);
246 default:
247 return (false);
251 bool cast_passwall(const coord_def& delta, int pow)
253 int shallow = 1 + (you.skills[SK_EARTH_MAGIC] / 8);
254 int range = shallow + random2(pow) / 25;
255 int maxrange = shallow + pow / 25;
257 coord_def dest;
258 for (dest = you.pos() + delta;
259 in_bounds(dest) && _feat_is_passwallable(grd(dest));
260 dest += delta) ;
262 int walls = (dest - you.pos()).rdist() - 1;
263 if (walls == 0)
265 mpr("That's not a passable wall.");
266 return (false);
269 // Below here, failing to cast yields information to the
270 // player, so we don't make the spell abort (return true).
271 if (!in_bounds(dest))
272 mpr("You sense an overwhelming volume of rock.");
273 else if (feat_is_solid(grd(dest)))
274 mpr("Something is blocking your path through the rock.");
275 else if (is_feat_dangerous(grd(dest), true))
277 if (grd(dest) == DNGN_DEEP_WATER)
278 mpr("You sense a large body of water on the other side of the rock.");
279 else if (grd(dest) == DNGN_LAVA)
280 mpr("You sense an intense heat on the other side of the rock.");
281 else
282 mprf(MSGCH_ERROR, "Unhandled dangerous feature: ",
283 feature_description(dest, false, DESC_PLAIN).c_str());
285 else if (walls > maxrange)
286 mpr("This rock feels extremely deep.");
287 else if (walls > range)
288 mpr("You fail to penetrate the rock.");
289 else
291 // Passwall delay is reduced, and the delay cannot be interrupted.
292 start_delay(DELAY_PASSWALL, 1 + walls, dest.x, dest.y);
294 return (true);
297 static int _intoxicate_monsters(coord_def where, int pow, int, actor *)
299 UNUSED(pow);
301 monster* mons = monster_at(where);
302 if (mons == NULL
303 || mons_intel(mons) < I_NORMAL
304 || mons->holiness() != MH_NATURAL
305 || mons->res_poison() > 0)
307 return 0;
310 mons->add_ench(mon_enchant(ENCH_CONFUSION, 0, KC_YOU));
311 return 1;
314 void cast_intoxicate(int pow)
316 potion_effect(POT_CONFUSION, 10 + (100 - pow) / 10);
318 if (one_chance_in(20)
319 && lose_stat(STAT_INT, 1 + random2(3), false,
320 "casting intoxication"))
322 mpr("Your head spins!");
325 apply_area_visible(_intoxicate_monsters, pow, true);
328 // The intent of this spell isn't to produce helpful potions
329 // for drinking, but rather to provide ammo for the Evaporate
330 // spell out of corpses, thus potentially making it useful.
331 // Producing helpful potions would break game balance here...
332 // and producing more than one potion from a corpse, or not
333 // using up the corpse might also lead to game balance problems. - bwr
334 bool cast_fulsome_distillation(int pow, bool check_range)
336 int num_corpses = 0;
337 item_def *corpse = corpse_at(you.pos(), &num_corpses);
338 if (num_corpses && you.flight_mode() == FL_LEVITATE)
339 num_corpses = -1;
341 // If there is only one corpse, distill it; otherwise, ask the player
342 // which corpse to use.
343 switch (num_corpses)
345 case 0: case -1:
346 // Allow using Z to victory dance fulsome.
347 if (!check_range)
349 mpr("The spell fizzles.");
350 return (true);
353 if (num_corpses == -1)
354 mpr("You can't reach the corpse!");
355 else
356 mpr("There aren't any corpses here.");
357 return (false);
358 case 1:
359 // Use the only corpse available without prompting.
360 break;
361 default:
362 // Search items at the player's location for corpses.
363 // The last corpse detected earlier is irrelevant.
364 corpse = NULL;
365 for (stack_iterator si(you.pos(), true); si; ++si)
367 if (item_is_corpse(*si))
369 const std::string corpsedesc =
370 get_menu_colour_prefix_tags(*si, DESC_NOCAP_THE);
371 const std::string prompt =
372 make_stringf("Distill a potion from %s?",
373 corpsedesc.c_str());
375 if (yesno(prompt.c_str(), true, 0, false))
377 corpse = &*si;
378 break;
384 if (!corpse)
386 canned_msg(MSG_OK);
387 return (false);
390 potion_type pot_type = POT_WATER;
392 switch (mons_corpse_effect(corpse->plus))
394 case CE_CLEAN:
395 pot_type = POT_WATER;
396 break;
398 case CE_CONTAMINATED:
399 pot_type = (mons_weight(corpse->plus) >= 900)
400 ? POT_DEGENERATION : POT_CONFUSION;
401 break;
403 case CE_POISONOUS:
404 case CE_POISON_CONTAM:
405 pot_type = POT_POISON;
406 break;
408 case CE_MUTAGEN_RANDOM:
409 case CE_MUTAGEN_GOOD: // unused
410 case CE_RANDOM: // unused
411 pot_type = POT_MUTATION;
412 break;
414 case CE_MUTAGEN_BAD: // unused
415 case CE_ROTTEN: // actually this only occurs via mangling
416 case CE_HCL: // necrophage
417 pot_type = POT_DECAY;
418 break;
420 case CE_NOCORPSE: // shouldn't occur
421 default:
422 break;
425 switch (corpse->plus)
427 case MONS_RED_WASP: // paralysis attack
428 pot_type = POT_PARALYSIS;
429 break;
431 case MONS_YELLOW_WASP: // slowing attack
432 pot_type = POT_SLOWING;
433 break;
435 default:
436 break;
439 struct monsterentry* smc = get_monster_data(corpse->plus);
441 for (int nattk = 0; nattk < 4; ++nattk)
443 if (smc->attack[nattk].flavour == AF_POISON_MEDIUM
444 || smc->attack[nattk].flavour == AF_POISON_STRONG
445 || smc->attack[nattk].flavour == AF_POISON_STR
446 || smc->attack[nattk].flavour == AF_POISON_INT
447 || smc->attack[nattk].flavour == AF_POISON_DEX
448 || smc->attack[nattk].flavour == AF_POISON_STAT)
450 pot_type = POT_STRONG_POISON;
454 const bool was_orc = (mons_genus(corpse->plus) == MONS_ORC);
456 // We borrow the corpse's object to make our potion.
457 corpse->base_type = OBJ_POTIONS;
458 corpse->sub_type = pot_type;
459 corpse->quantity = 1;
460 corpse->plus = 0;
461 corpse->plus2 = 0;
462 corpse->flags = 0;
463 corpse->inscription.clear();
464 item_colour(*corpse); // sets special as well
466 // Always identify said potion.
467 set_ident_type(*corpse, ID_KNOWN_TYPE);
469 mprf("You extract %s from the corpse.",
470 corpse->name(DESC_NOCAP_A).c_str());
472 // Try to move the potion to the player (for convenience).
473 if (move_item_to_player(corpse->index(), 1) != 1)
474 mpr("Unfortunately, you can't carry it right now!");
476 if (was_orc)
477 did_god_conduct(DID_DESECRATE_ORCISH_REMAINS, 2);
479 return (true);
482 void remove_condensation_shield()
484 mpr("Your icy shield evaporates.", MSGCH_DURATION);
485 you.duration[DUR_CONDENSATION_SHIELD] = 0;
486 you.redraw_armour_class = true;
489 void cast_condensation_shield(int pow)
491 if (you.shield() || you.duration[DUR_FIRE_SHIELD])
492 canned_msg(MSG_SPELL_FIZZLES);
493 else
495 if (you.duration[DUR_CONDENSATION_SHIELD] > 0)
497 mpr("The disc of vapour around you crackles some more.");
498 you.increase_duration(DUR_CONDENSATION_SHIELD,
499 5 + roll_dice(2,3), 30);
501 else
503 mpr("A crackling disc of dense vapour forms in the air!");
504 you.increase_duration(DUR_CONDENSATION_SHIELD,
505 10 + roll_dice(2, pow / 5), 30);
506 you.redraw_armour_class = true;
511 void cast_stoneskin(int pow)
513 if (you.is_undead
514 && (you.species != SP_VAMPIRE || you.hunger_state < HS_SATIATED))
516 mpr("This spell does not affect your undead flesh.");
517 return;
520 if (you.form != TRAN_NONE
521 && you.form != TRAN_STATUE
522 && you.form != TRAN_BLADE_HANDS)
524 mpr("This spell does not affect your current form.");
525 return;
528 if (you.duration[DUR_STONEMAIL] || you.duration[DUR_ICY_ARMOUR])
530 mpr("This spell conflicts with another spell still in effect.");
531 return;
534 if (you.duration[DUR_STONESKIN])
535 mpr("Your skin feels harder.");
536 else
538 if (you.form == TRAN_STATUE)
539 mpr("Your stone body feels more resilient.");
540 else
541 mpr("Your skin hardens.");
543 you.redraw_armour_class = true;
546 you.increase_duration(DUR_STONESKIN, 10 + random2(pow) + random2(pow), 50);