/** * @file * @brief Potion and potion-like effects. **/ #include "AppHdr.h" #include "potion.h" #include #include #include "externs.h" #include "areas.h" #include "artefact.h" #include "beam.h" #include "env.h" #include "food.h" #include "godconduct.h" #include "hints.h" #include "item_use.h" #include "itemname.h" #include "itemprop.h" #include "libutil.h" #include "message.h" #include "misc.h" #include "mutation.h" #include "player.h" #include "player-stats.h" #include "skill_menu.h" #include "spl-goditem.h" #include "spl-miscast.h" #include "strings.h" #include "terrain.h" #include "transform.h" #include "xom.h" /** * Apply the effect of a potion to the player. * * This is called when the player quaffs a potion, but also for some cards, * beams, god effects and miscasts. * * @param pot_eff The potion type. * @param pow The power of the effect. 40 for actual potions. * @param was_known Whether the potion was already identified. * * @return If the potion was used. */ bool potion_effect(potion_type pot_eff, int pow, item_def *potion, bool was_known) { pow = min(pow, 150); // Reduced effect for healing potions. int mut_factor = 3 - you.mutation[MUT_NO_DEVICE_HEAL]; // Knowingly drinking bad potions is much less amusing. int xom_factor = 1; if (potion && was_known) { xom_factor *= 2; if (!player_in_a_dangerous_place()) xom_factor *= 3; } switch (pot_eff) { case POT_CURING: { const bool ddoor = you.duration[DUR_DEATHS_DOOR]; if ((!you.can_device_heal() || ddoor) && potion && was_known && you.duration[DUR_CONF] == 0 && you.duration[DUR_POISONING] == 0 && you.rotting == 0 && you.disease == 0 // The potion won't heal us, so don't count rot unless at max HP. && (!player_rotted() || you.hp != you.hp_max)) { mprf("You have no ailments to cure%s.", ddoor && you.can_device_heal() ? ", and can't heal while in Death's door" : ""); return false; } if (you.can_device_heal() && !ddoor) inc_hp((5 + random2(7)) * mut_factor / 3); mprf("You feel %s.", ddoor ? "queasy" : "better"); // Only fix rot when healed to full. if (you.hp == you.hp_max) { unrot_hp(1); set_hp(you.hp_max); } // need to redraw from yellow to green even if no hp was gained if (you.duration[DUR_POISONING]) you.redraw_hit_points = true; you.duration[DUR_POISONING] = 0; you.rotting = 0; you.disease = 0; you.duration[DUR_CONF] = 0; break; } case POT_HEAL_WOUNDS: if (you.duration[DUR_DEATHS_DOOR]) { if (potion && was_known) { mpr("You can't heal while in Death's door!"); return false; } mpr("You feel queasy."); break; } if (!you.can_device_heal()) { if (potion && was_known) { mpr("That would not heal you."); return false; } mpr("That seemed strangely inert."); break; } inc_hp((10 + random2avg(28, 3)) * mut_factor / 3); mpr("You feel much better."); // only fix rot when healed to full if (you.hp == you.hp_max) { unrot_hp(2 + random2avg(5, 2)); set_hp(you.hp_max); } break; case POT_BLOOD: case POT_BLOOD_COAGULATED: if (you.species == SP_VAMPIRE) { // No healing anymore! (jpeg) int value = 840; if (pot_eff == POT_BLOOD) { mpr("Yummy - fresh blood!"); value += 200; } else // Coagulated. mpr("This tastes delicious!"); lessen_hunger(value, true); } else { const int value = pot_eff == POT_BLOOD ? 1040 : 840; const int herbivorous = player_mutation_level(MUT_HERBIVOROUS); if (herbivorous < 3 && player_likes_chunks()) { // Likes it. mpr("This tastes like blood."); if (you.species == SP_GHOUL && player_rotted()) { mpr("You feel more resilient."); unrot_hp(1); } lessen_hunger(value, true); } else { mpr("Yuck - this tastes like blood."); if (x_chance_in_y(herbivorous + 1, 4)) { // Full herbivores always become ill from blood. you.sicken(50 + random2(100)); xom_is_stimulated(25 / xom_factor); } else lessen_hunger(value, true); } } did_god_conduct(DID_DRINK_BLOOD, 1 + random2(3), was_known); break; case POT_HASTE: if (potion && was_known && you.stasis(false)) { mpr("This potion can't work under stasis."); return false; } if (haste_player(40 + random2(pow))) did_god_conduct(DID_HASTY, 10, was_known); break; case POT_MIGHT: { const bool were_mighty = you.duration[DUR_MIGHT] > 0; mprf(MSGCH_DURATION, "You feel %s all of a sudden.", were_mighty ? "mightier" : "very mighty"); you.increase_duration(DUR_MIGHT, 35 + random2(pow), 80); if (!were_mighty) notify_stat_change(STAT_STR, 5, true, ""); break; } case POT_BRILLIANCE: { const bool were_brilliant = you.duration[DUR_BRILLIANCE] > 0; mprf(MSGCH_DURATION, "You feel %s all of a sudden.", were_brilliant ? "more clever" : "clever"); you.increase_duration(DUR_BRILLIANCE, 35 + random2(pow), 80); if (!were_brilliant) notify_stat_change(STAT_INT, 5, true, ""); break; } case POT_AGILITY: { const bool were_agile = you.duration[DUR_AGILITY] > 0; mprf(MSGCH_DURATION, "You feel %s all of a sudden.", were_agile ? "more agile" : "agile"); you.increase_duration(DUR_AGILITY, 35 + random2(pow), 80); if (!were_agile) notify_stat_change(STAT_DEX, 5, true, ""); break; } #if TAG_MAJOR_VERSION == 34 case POT_GAIN_STRENGTH: if (mutate(MUT_STRONG, "potion of gain strength", true, false, false, true)) learned_something_new(HINT_YOU_MUTATED); break; case POT_GAIN_DEXTERITY: if (mutate(MUT_AGILE, "potion of gain dexterity", true, false, false, true)) learned_something_new(HINT_YOU_MUTATED); break; case POT_GAIN_INTELLIGENCE: if (mutate(MUT_CLEVER, "potion of gain intelligence", true, false, false, true)) learned_something_new(HINT_YOU_MUTATED); break; #endif case POT_FLIGHT: if (!flight_allowed()) { if (potion && was_known) return false; break; } you.attribute[ATTR_FLIGHT_UNCANCELLABLE] = 1; fly_player(pow); break; case POT_POISON: #if TAG_MAJOR_VERSION == 34 case POT_STRONG_POISON: #endif if (player_res_poison() >= 1) mpr("You feel slightly nauseous."); else { mprf(MSGCH_WARN, "That liquid tasted very nasty..."); int amount = 10 + random2avg(15, 2); string msg = "a potion of poison"; poison_player(amount, "", msg); xom_is_stimulated(100 / xom_factor); } break; // Potions of slowing no longer exist, but tons of other effects use // potion_effect(POT_SLOWING) so this code stays in. case POT_SLOWING: if (slow_player(10 + random2(pow))) xom_is_stimulated(50 / xom_factor); break; case POT_CANCELLATION: debuff_player(); break; case POT_CONFUSION: if (confuse_player(3 + random2(8))) xom_is_stimulated(100 / xom_factor); break; case POT_INVISIBILITY: if (you.haloed() || you.glows_naturally()) { if (potion && was_known) { mpr("You cannot become invisible while glowing."); return false; } // You can't turn invisible while haloed or glowing // naturally, but identify the effect anyway. mpr("You briefly turn translucent."); return true; } else if (you.backlit()) { vector afflictions; if (get_contamination_level() > 1) afflictions.push_back("magical contamination"); if (you.duration[DUR_CORONA]) afflictions.push_back("corona"); if (you.duration[DUR_LIQUID_FLAMES]) afflictions.push_back("liquid flames"); mprf(MSGCH_DURATION, "You become %stransparent, but the glow from your " "%s prevents you from becoming " "completely invisible.", you.duration[DUR_INVIS] ? "more " : "", comma_separated_line(afflictions.begin(), afflictions.end()).c_str()); } else { mprf(MSGCH_DURATION, !you.duration[DUR_INVIS] ? "You fade into invisibility!" : "You fade further into invisibility."); } // Now multiple invisiblity casts aren't as good. -- bwr if (!you.duration[DUR_INVIS]) you.set_duration(DUR_INVIS, 15 + random2(pow), 100); else you.increase_duration(DUR_INVIS, random2(pow), 100); if (potion) you.attribute[ATTR_INVIS_UNCANCELLABLE] = 1; break; case POT_PORRIDGE: if (you.species == SP_VAMPIRE || player_mutation_level(MUT_CARNIVOROUS) == 3) { mpr("Blech - that potion was really gluggy!"); } else { mpr("That potion was really gluggy!"); lessen_hunger(6040, true); } break; case POT_DEGENERATION: if (potion) mpr("There was something very wrong with that liquid!"); if (lose_stat(STAT_RANDOM, 1 + random2avg(4, 2), false, "drinking a potion of degeneration")) { xom_is_stimulated(50 / xom_factor); } break; case POT_DECAY: if (you.rot(&you, 0, 3 + random2(3))) xom_is_stimulated(50 / xom_factor); break; #if TAG_MAJOR_VERSION == 34 case POT_WATER: #endif case NUM_POTIONS: if (you.species == SP_VAMPIRE) mpr("Blech - this tastes like water."); else mpr("This tastes like water."); break; case POT_EXPERIENCE: if (you.experience_level < 27) { mpr("You feel more experienced!"); adjust_level(1, true); // Deferred calling level_change() into item_use.cc:3919, after // dec_inv_item_quantity. This prevents using SIGHUP to get infinite // potions of experience. Confer Mantis #3245. [due] } else mpr("A flood of memories washes over you."); more(); skill_menu(SKMF_EXPERIENCE_POTION, 750 * you.experience_level); break; case POT_MAGIC: // Allow repairing rot, disallow going through Death's Door. #if TAG_MAJOR_VERSION == 34 if (you.species == SP_DJINNI) return potion_effect(POT_HEAL_WOUNDS, pow, potion, was_known); #endif inc_mp(10 + random2avg(28, 3)); mpr("Magic courses through your body."); break; case POT_RESTORE_ABILITIES: { bool nothing_happens = true; if (you.duration[DUR_BREATH_WEAPON]) { mprf(MSGCH_RECOVERY, "You have got your breath back."); you.duration[DUR_BREATH_WEAPON] = 0; nothing_happens = false; } // Give a message if no message otherwise. if (!restore_stat(STAT_ALL, 0, false) && nothing_happens) mpr("You feel refreshed."); break; } case POT_BERSERK_RAGE: if (potion && was_known && !you.can_go_berserk(true, potion, false)) return false; if (you.species == SP_VAMPIRE && you.hunger_state <= HS_SATIATED) { mpr("You feel slightly irritated."); make_hungry(100, false); } else { if (you.go_berserk(was_known, true)) xom_is_stimulated(50); } break; case POT_CURE_MUTATION: if (potion && was_known && undead_mutation_rot()) { mpr(you.form == TRAN_LICH ? "You cannot mutate at present." : "You cannot mutate."); return false; } mpr("It has a very clean taste."); for (int i = 0; i < 7; i++) if (random2(9) >= i) delete_mutation(RANDOM_MUTATION, "potion of cure mutation", false); break; case POT_MUTATION: if (potion && was_known && undead_mutation_rot()) { mpr(you.form == TRAN_LICH ? "You cannot mutate at present." : "You cannot mutate."); return false; } mpr("You feel extremely strange."); for (int i = 0; i < 3; i++) mutate(RANDOM_MUTATION, "potion of mutation", false); learned_something_new(HINT_YOU_MUTATED); did_god_conduct(DID_DELIBERATE_MUTATING, 10, was_known); break; case POT_BENEFICIAL_MUTATION: if (undead_mutation_rot()) { if (potion && was_known) { mpr(you.form == TRAN_LICH ? "You cannot mutate at present." : "You cannot mutate."); return false; } mpr("You feel dead inside."); mutate(RANDOM_GOOD_MUTATION, "potion of beneficial mutation", true, false, false, true); break; } if (mutate(RANDOM_GOOD_MUTATION, "potion of beneficial mutation", true, false, false, true)) { mpr("You feel fantastic!"); did_god_conduct(DID_DELIBERATE_MUTATING, 10, was_known); } else mpr("You feel fantastic for a moment."); learned_something_new(HINT_YOU_MUTATED); break; case POT_RESISTANCE: mprf(MSGCH_DURATION, "You feel protected."); you.increase_duration(DUR_RESISTANCE, random2(pow) + 35); break; case POT_LIGNIFY: if (potion && was_known && !transform(0, TRAN_TREE, false, true)) return false; if (transform(30, TRAN_TREE, !was_known)) { you.transform_uncancellable = true; did_god_conduct(DID_CHAOS, 10, was_known); } else mpr("You feel woody for a moment."); break; } if (potion && !was_known) { set_ident_flags(*potion, ISFLAG_IDENT_MASK); set_ident_type(*potion, ID_KNOWN_TYPE); mprf("It was a %s.", potion->name(DESC_QUALNAME).c_str()); identify_healing_pots(); } return true; }