summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/wiz-you.cc
diff options
context:
space:
mode:
authorMatthew Cline <zelgadis@sourceforge.net>2009-11-06 19:26:31 -0800
committerMatthew Cline <zelgadis@sourceforge.net>2009-11-06 19:26:31 -0800
commitbe871d682e8087ab38c5e9e054190daf6f81fff2 (patch)
treee31a44ad0f851c3946f6d665021bb9b206dd5a66 /crawl-ref/source/wiz-you.cc
parent8051b93a756f55ba7985f4e6ffe4a833ce0b41cb (diff)
downloadcrawl-ref-be871d682e8087ab38c5e9e054190daf6f81fff2.tar.gz
crawl-ref-be871d682e8087ab38c5e9e054190daf6f81fff2.zip
Split up debug.cc
Diffstat (limited to 'crawl-ref/source/wiz-you.cc')
-rw-r--r--crawl-ref/source/wiz-you.cc882
1 files changed, 882 insertions, 0 deletions
diff --git a/crawl-ref/source/wiz-you.cc b/crawl-ref/source/wiz-you.cc
new file mode 100644
index 0000000000..ad1fb6eb15
--- /dev/null
+++ b/crawl-ref/source/wiz-you.cc
@@ -0,0 +1,882 @@
+/*
+ * File: wiz-you.cc
+ * Summary: Player related debugging functions.
+ * Written by: Linley Henzell and Jesse Jones
+ */
+
+#include "AppHdr.h"
+
+#include "wiz-you.h"
+
+#include "cio.h"
+#include "dbg-util.h"
+#include "food.h"
+#include "message.h"
+#include "mutation.h"
+#include "newgame.h"
+#include "output.h"
+#include "player.h"
+#include "random.h"
+#include "religion.h"
+#include "skills.h"
+#include "skills2.h"
+#include "spl-cast.h"
+#include "spl-util.h"
+#include "stuff.h"
+#include "terrain.h"
+#include "xom.h"
+
+#ifdef WIZARD
+void wizard_change_species( void )
+{
+ char specs[80];
+ int i;
+
+ mpr("What species would you like to be now? " , MSGCH_PROMPT);
+ get_input_line( specs, sizeof( specs ) );
+
+ if (specs[0] == '\0')
+ return;
+ strlwr(specs);
+
+ species_type sp = SP_UNKNOWN;
+
+ for (i = 0; i < NUM_SPECIES; ++i)
+ {
+ const species_type si = static_cast<species_type>(i);
+ const std::string sp_name =
+ lowercase_string(species_name(si, you.experience_level));
+
+ std::string::size_type pos = sp_name.find(specs);
+ if (pos != std::string::npos)
+ {
+ if (pos == 0 && *specs)
+ {
+ // We prefer prefixes over partial matches.
+ sp = si;
+ break;
+ }
+ else
+ sp = si;
+ }
+ }
+
+ if (sp == SP_UNKNOWN)
+ {
+ mpr("That species isn't available.");
+ return;
+ }
+
+ // Re-scale skill-points.
+ for (i = 0; i < NUM_SKILLS; ++i)
+ {
+ you.skill_points[i] *= species_skills( i, sp );
+ you.skill_points[i] /= species_skills( i, you.species );
+ }
+
+ you.species = sp;
+ you.is_undead = get_undead_state(sp);
+
+ // Change permanent mutations, but preserve non-permanent ones.
+ unsigned char prev_muts[NUM_MUTATIONS];
+ for (i = 0; i < NUM_MUTATIONS; ++i)
+ {
+ if (you.demon_pow[i] > 0)
+ {
+ if (you.demon_pow[i] > you.mutation[i])
+ you.mutation[i] = 0;
+ else
+ you.mutation[i] -= you.demon_pow[i];
+
+ you.demon_pow[i] = 0;
+ }
+ prev_muts[i] = you.mutation[i];
+ }
+ give_basic_mutations(sp);
+ for (i = 0; i < NUM_MUTATIONS; ++i)
+ {
+ if (prev_muts[i] > you.demon_pow[i])
+ you.demon_pow[i] = 0;
+ else
+ you.demon_pow[i] -= prev_muts[i];
+ }
+
+ switch (sp)
+ {
+ case SP_GREEN_DRACONIAN:
+ if (you.experience_level >= 7)
+ perma_mutate(MUT_POISON_RESISTANCE, 1);
+ break;
+
+ case SP_RED_DRACONIAN:
+ if (you.experience_level >= 14)
+ perma_mutate(MUT_HEAT_RESISTANCE, 1);
+ break;
+
+ case SP_WHITE_DRACONIAN:
+ if (you.experience_level >= 14)
+ perma_mutate(MUT_COLD_RESISTANCE, 1);
+ break;
+
+
+ case SP_BLACK_DRACONIAN:
+ if (you.experience_level >= 18)
+ perma_mutate(MUT_SHOCK_RESISTANCE, 1);
+ break;
+
+ case SP_DEMONSPAWN:
+ {
+ roll_demonspawn_mutations();
+ for (i = 2; i <= you.experience_level; ++i)
+ {
+ mutation_type m = you.demon_trait[i-2];
+
+ if (m == NUM_MUTATIONS)
+ continue;
+
+ ++you.mutation[m];
+ ++you.demon_pow[m];
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+#ifdef USE_TILE
+ init_player_doll();
+#endif
+ redraw_screen();
+}
+#endif
+
+#ifdef WIZARD
+// Casts a specific spell by number.
+void wizard_cast_spec_spell(void)
+{
+ int spell = debug_prompt_for_int( "Cast which spell by number? ", true );
+
+ if (spell == -1)
+ canned_msg( MSG_OK );
+ else if (your_spells( static_cast<spell_type>(spell), 0, false )
+ == SPRET_ABORT)
+ {
+ crawl_state.cancel_cmd_repeat();
+ }
+}
+#endif
+
+
+#ifdef WIZARD
+// Casts a specific spell by name.
+void wizard_cast_spec_spell_name(void)
+{
+ char specs[80];
+
+ mpr("Cast which spell by name? ", MSGCH_PROMPT);
+ if (cancelable_get_line_autohist( specs, sizeof( specs ) )
+ || specs[0] == '\0')
+ {
+ canned_msg( MSG_OK );
+ crawl_state.cancel_cmd_repeat();
+ return;
+ }
+
+ spell_type type = spell_by_name(specs, true);
+ if (type == SPELL_NO_SPELL)
+ {
+ mpr((one_chance_in(20)) ? "Maybe you should go back to WIZARD school."
+ : "I couldn't find that spell.");
+ crawl_state.cancel_cmd_repeat();
+ return;
+ }
+
+ if (your_spells(type, 0, false) == SPRET_ABORT)
+ crawl_state.cancel_cmd_repeat();
+}
+#endif
+
+void wizard_heal(bool super_heal)
+{
+ if (super_heal)
+ {
+ // Clear more stuff and give a HP boost.
+ you.magic_contamination = 0;
+ you.duration[DUR_LIQUID_FLAMES] = 0;
+ you.clear_beholders();
+
+ // If we're repeating then do the HP increase all at once.
+ int amount = 10;
+ if (crawl_state.cmd_repeat_goal > 0)
+ {
+ amount *= crawl_state.cmd_repeat_goal;
+ crawl_state.cancel_cmd_repeat();
+ }
+
+ inc_hp(amount, true);
+ }
+
+ // Clear most status ailments.
+ you.rotting = 0;
+ you.disease = 0;
+ you.duration[DUR_CONF] = 0;
+ you.duration[DUR_POISONING] = 0;
+ set_hp(you.hp_max, false);
+ set_mp(you.max_magic_points, false);
+ set_hunger(10999, true);
+ you.redraw_hit_points = true;
+}
+
+void wizard_set_hunger_state()
+{
+ std::string hunger_prompt =
+ "Set hunger state to s(T)arving, (N)ear starving, (H)ungry";
+ if (you.species == SP_GHOUL)
+ hunger_prompt += " or (S)atiated";
+ else
+ hunger_prompt += ", (S)atiated, (F)ull or (E)ngorged";
+ hunger_prompt += "? ";
+
+ mprf(MSGCH_PROMPT, "%s", hunger_prompt.c_str());
+
+ const int c = tolower(getch());
+
+ // Values taken from food.cc.
+ switch (c)
+ {
+ case 't': you.hunger = 500; break;
+ case 'n': you.hunger = 1200; break;
+ case 'h': you.hunger = 2400; break;
+ case 's': you.hunger = 5000; break;
+ case 'f': you.hunger = 8000; break;
+ case 'e': you.hunger = 12000; break;
+ default: canned_msg(MSG_OK); break;
+ }
+ food_change();
+ if (you.species == SP_GHOUL && you.hunger_state >= HS_SATIATED)
+ mpr("Ghouls can never be full or above!");
+}
+
+void wizard_gain_piety()
+{
+ if (you.religion == GOD_NO_GOD)
+ {
+ mpr("You are not religious!");
+ return;
+ }
+ else if (you.religion == GOD_XOM)
+ {
+ you.piety = random2(MAX_PIETY+1); // reroll mood
+ if (one_chance_in(5))
+ you.gift_timeout = 0; // 20% chance to make Xom bored.
+ else
+ you.gift_timeout = random2(40) + random2(40); // reroll interest
+
+ const std::string new_xom_favour = describe_xom_favour();
+ const std::string msg = "You are now " + new_xom_favour;
+ god_speaks(you.religion, msg.c_str());
+ return;
+ }
+
+ const int old_piety = you.piety;
+ const int old_penance = you.penance[you.religion];
+ if (old_piety >= MAX_PIETY && !old_penance)
+ {
+ mprf("Your piety (%d) is already %s maximum.",
+ old_piety, old_piety == MAX_PIETY ? "at" : "above");
+ }
+
+ // Even at maximum, you can still gain gifts.
+ // Try at least once for maximum, or repeat until something
+ // happens. Rarely, this might result in several gifts during the
+ // same round!
+ do
+ {
+ gain_piety(50);
+ }
+ while (old_piety < MAX_PIETY && old_piety == you.piety
+ && old_penance == you.penance[you.religion]);
+
+ if (old_penance)
+ {
+ mprf("Congratulations, your penance was decreased from %d to %d!",
+ old_penance, you.penance[you.religion]);
+ }
+ else if (you.piety > old_piety)
+ {
+ mprf("Congratulations, your piety went from %d to %d!",
+ old_piety, you.piety);
+ }
+}
+
+//---------------------------------------------------------------
+//
+// debug_add_skills
+//
+//---------------------------------------------------------------
+#ifdef WIZARD
+void wizard_exercise_skill(void)
+{
+ int skill = debug_prompt_for_skill( "Which skill (by name)? " );
+
+ if (skill == -1)
+ mpr("That skill doesn't seem to exist.");
+ else
+ {
+ mpr("Exercising...");
+ exercise(skill, 100);
+ }
+}
+#endif
+
+#ifdef WIZARD
+void wizard_set_skill_level(void)
+{
+ int skill = debug_prompt_for_skill( "Which skill (by name)? " );
+
+ if (skill == -1)
+ mpr("That skill doesn't seem to exist.");
+ else
+ {
+ mpr(skill_name(skill));
+ int amount = debug_prompt_for_int( "To what level? ", true );
+
+ if (amount < 0)
+ canned_msg( MSG_OK );
+ else
+ {
+ const int old_amount = you.skills[skill];
+ const int points = (skill_exp_needed( amount )
+ * species_skills( skill, you.species )) / 100;
+
+ you.skill_points[skill] = points + 1;
+ you.skills[skill] = amount;
+
+ calc_total_skill_points();
+
+ redraw_skill(you.your_name, player_title());
+
+ switch (skill)
+ {
+ case SK_FIGHTING:
+ calc_hp();
+ break;
+
+ case SK_SPELLCASTING:
+ case SK_INVOCATIONS:
+ case SK_EVOCATIONS:
+ calc_mp();
+ break;
+
+ case SK_DODGING:
+ you.redraw_evasion = true;
+ break;
+
+ case SK_ARMOUR:
+ you.redraw_armour_class = true;
+ you.redraw_evasion = true;
+ break;
+
+ default:
+ break;
+ }
+
+ mprf("%s %s to skill level %d.",
+ (old_amount < amount ? "Increased" :
+ old_amount > amount ? "Lowered"
+ : "Reset"),
+ skill_name(skill), amount);
+
+ if (skill == SK_STEALTH && amount == 27)
+ {
+ mpr("If you set the stealth skill to a value higher than 27, "
+ "hide mode is activated, and monsters won't notice you.");
+ }
+ }
+ }
+}
+#endif
+
+
+#ifdef WIZARD
+void wizard_set_all_skills(void)
+{
+ int i;
+ int amount =
+ debug_prompt_for_int( "Set all skills to what level? ", true );
+
+ if (amount < 0) // cancel returns -1 -- bwr
+ canned_msg( MSG_OK );
+ else
+ {
+ if (amount > 27)
+ amount = 27;
+
+ for (i = SK_FIGHTING; i < NUM_SKILLS; ++i)
+ {
+ if (is_invalid_skill(i))
+ continue;
+
+ const int points = (skill_exp_needed( amount )
+ * species_skills( i, you.species )) / 100;
+
+ you.skill_points[i] = points + 1;
+ you.skills[i] = amount;
+ }
+
+ redraw_skill(you.your_name, player_title());
+
+ calc_total_skill_points();
+
+ calc_hp();
+ calc_mp();
+
+ you.redraw_armour_class = true;
+ you.redraw_evasion = true;
+ }
+}
+#endif
+
+#ifdef WIZARD
+extern mutation_def mutation_defs[];
+
+bool wizard_add_mutation()
+{
+ bool success = false;
+ char specs[80];
+
+ if (player_mutation_level(MUT_MUTATION_RESISTANCE) > 0
+ && !crawl_state.is_replaying_keys())
+ {
+ const char* msg;
+
+ if (you.mutation[MUT_MUTATION_RESISTANCE] == 3)
+ msg = "You are immune to mutations, remove immunity?";
+ else
+ msg = "You are resistant to mutations, remove resistance?";
+
+ if (yesno(msg, true, 'n'))
+ {
+ you.mutation[MUT_MUTATION_RESISTANCE] = 0;
+ crawl_state.cancel_cmd_repeat();
+ }
+ }
+
+ bool force = yesno("Force mutation to happen?", true, 'n');
+
+ if (player_mutation_level(MUT_MUTATION_RESISTANCE) == 3 && !force)
+ {
+ mpr("Can't mutate when immune to mutations without forcing it.");
+ crawl_state.cancel_cmd_repeat();
+ return (false);
+ }
+
+ bool god_gift = yesno("Treat mutation as god gift?", true, 'n');
+
+ mpr("Which mutation (name, 'good', 'bad', 'any', 'xom')? ", MSGCH_PROMPT);
+ get_input_line( specs, sizeof( specs ) );
+
+ if (specs[0] == '\0')
+ return (false);
+
+ strlwr(specs);
+
+ mutation_type mutat = NUM_MUTATIONS;
+
+
+ if (strcmp(specs, "good") == 0)
+ mutat = RANDOM_GOOD_MUTATION;
+ else if (strcmp(specs, "bad") == 0)
+ mutat = RANDOM_BAD_MUTATION;
+ else if (strcmp(specs, "any") == 0)
+ mutat = RANDOM_MUTATION;
+ else if (strcmp(specs, "xom") == 0)
+ mutat = RANDOM_XOM_MUTATION;
+
+ if (mutat != NUM_MUTATIONS)
+ {
+ int old_resist = player_mutation_level(MUT_MUTATION_RESISTANCE);
+
+ success = mutate(mutat, true, force, god_gift);
+
+ if (old_resist < player_mutation_level(MUT_MUTATION_RESISTANCE)
+ && !force)
+ {
+ crawl_state.cancel_cmd_repeat("Your mutation resistance has "
+ "increased.");
+ }
+ return (success);
+ }
+
+ std::vector<mutation_type> partial_matches;
+
+ for (unsigned i = 0; true; ++i)
+ {
+ if (strcmp(specs, mutation_defs[i].wizname) == 0)
+ {
+ mutat = mutation_defs[i].mutation;
+ break;
+ }
+
+ if (strstr(mutation_defs[i].wizname, specs))
+ partial_matches.push_back(mutation_defs[i].mutation);
+
+ // FIXME: hack, but I don't want to export the size
+ // of the array...this is even worse.
+ if (mutation_defs[i].mutation + 1 == NUM_MUTATIONS)
+ break;
+ }
+
+ // If only one matching mutation, use that.
+ if (mutat == NUM_MUTATIONS && partial_matches.size() == 1)
+ mutat = partial_matches[0];
+
+ if (mutat == NUM_MUTATIONS)
+ {
+ crawl_state.cancel_cmd_repeat();
+
+ if (partial_matches.size() == 0)
+ mpr("No matching mutation names.");
+ else
+ {
+ std::vector<std::string> matches;
+
+ for (unsigned int i = 0; i < partial_matches.size(); ++i)
+ matches.push_back(get_mutation_def(partial_matches[i]).wizname);
+
+ std::string prefix = "No exact match for mutation '" +
+ std::string(specs) + "', possible matches are: ";
+
+ // Use mpr_comma_separated_list() because the list
+ // might be *LONG*.
+ mpr_comma_separated_list(prefix, matches, " and ", ", ",
+ MSGCH_DIAGNOSTICS);
+ }
+
+ return (false);
+ }
+ else
+ {
+ mprf("Found #%d: %s (\"%s\")", (int) mutat,
+ get_mutation_def(mutat).wizname,
+ mutation_name(mutat, 1, false).c_str());
+
+ const int levels =
+ debug_prompt_for_int("How many levels to increase or decrease? ",
+ false);
+
+ if (levels == 0)
+ {
+ canned_msg(MSG_OK);
+ success = false;
+ }
+ else if (levels > 0)
+ {
+ for (int i = 0; i < levels; ++i)
+ if (mutate(mutat, true, force, god_gift))
+ success = true;
+ }
+ else
+ {
+ for (int i = 0; i < -levels; ++i)
+ if (delete_mutation(mutat, true, force, god_gift))
+ success = true;
+ }
+ }
+
+ return (success);
+}
+#endif
+
+#ifdef WIZARD
+void wizard_get_religion(void)
+{
+ char specs[80];
+
+ mpr("Which god (by name)? ", MSGCH_PROMPT);
+ get_input_line( specs, sizeof( specs ) );
+
+ if (specs[0] == '\0')
+ return;
+
+ strlwr(specs);
+
+ god_type god = GOD_NO_GOD;
+
+ for (int i = 1; i < NUM_GODS; ++i)
+ {
+ const god_type gi = static_cast<god_type>(i);
+ if (lowercase_string(god_name(gi)).find(specs) != std::string::npos)
+ {
+ god = gi;
+ break;
+ }
+ }
+
+ if (god == GOD_NO_GOD)
+ mpr("That god doesn't seem to be taking followers today.");
+ else
+ {
+ dungeon_feature_type feat =
+ static_cast<dungeon_feature_type>( DNGN_ALTAR_FIRST_GOD + god - 1 );
+ dungeon_terrain_changed(you.pos(), feat, false);
+
+ pray();
+ }
+}
+#endif
+
+void wizard_set_stats()
+{
+ char buf[80];
+ mprf(MSGCH_PROMPT, "Enter values for Str, Int, Dex (space separated): ");
+ if (cancelable_get_line_autohist(buf, sizeof buf))
+ return;
+
+ int sstr = you.strength,
+ sdex = you.dex,
+ sint = you.intel;
+
+ sscanf(buf, "%d %d %d", &sstr, &sint, &sdex);
+
+ you.max_strength = you.strength = debug_cap_stat(sstr);
+ you.max_dex = you.dex = debug_cap_stat(sdex);
+ you.max_intel = you.intel = debug_cap_stat(sint);
+
+ you.redraw_strength = true;
+ you.redraw_dexterity = true;
+ you.redraw_intelligence = true;
+ you.redraw_evasion = true;
+}
+
+static const char* dur_names[NUM_DURATIONS] =
+{
+ "invis",
+ "conf",
+ "paralysis",
+ "slow",
+ "mesmerised",
+ "haste",
+ "might",
+ "levitation",
+ "berserker",
+ "poisoning",
+ "confusing touch",
+ "sure blade",
+ "backlight",
+ "deaths door",
+ "fire shield",
+ "building rage",
+ "exhausted",
+ "liquid flames",
+ "icy armour",
+ "repel missiles",
+ "prayer",
+ "piety pool",
+ "divine vigour",
+ "divine stamina",
+ "divine shield",
+ "regeneration",
+ "swiftness",
+ "stonemail",
+ "controlled flight",
+ "teleport",
+ "control teleport",
+ "breath weapon",
+ "transformation",
+ "death channel",
+ "deflect missiles",
+ "phase shift",
+ "see invisible",
+ "weapon brand",
+ "silence",
+ "condensation shield",
+ "stoneskin",
+ "gourmand",
+ "bargain",
+ "insulation",
+ "resist poison",
+ "resist fire",
+ "resist cold",
+ "slaying",
+ "stealth",
+ "magic shield",
+ "sleep",
+ "sage",
+ "telepathy",
+ "petrified",
+ "lowered mr",
+ "repel stairs move",
+ "repel stairs climb"
+};
+
+void wizard_edit_durations( void )
+{
+ std::vector<int> durs;
+ size_t max_len = 0;
+
+ for (int i = 0; i < NUM_DURATIONS; ++i)
+ {
+ if (!you.duration[i])
+ continue;
+
+ max_len = std::max(strlen(dur_names[i]), max_len);
+ durs.push_back(i);
+ }
+
+ if (durs.size() > 0)
+ {
+ for (unsigned int i = 0; i < durs.size(); ++i)
+ {
+ int dur = durs[i];
+ mprf(MSGCH_PROMPT, "%c) %-*s : %d", 'a' + i, max_len,
+ dur_names[dur], you.duration[dur]);
+ }
+ mpr("", MSGCH_PROMPT);
+ mpr("Edit which duration (letter or name)? ", MSGCH_PROMPT);
+ }
+ else
+ mpr("Edit which duration (name)? ", MSGCH_PROMPT);
+
+ char buf[80];
+
+ if (cancelable_get_line_autohist(buf, sizeof buf) || strlen(buf) == 0)
+ {
+ canned_msg( MSG_OK );
+ return;
+ }
+
+ strcpy(buf, lowercase_string(trimmed_string(buf)).c_str());
+
+ if (strlen(buf) == 0)
+ {
+ canned_msg( MSG_OK );
+ return;
+ }
+
+ int choice = -1;
+
+ if (strlen(buf) == 1)
+ {
+ if (durs.size() == 0)
+ {
+ mpr("No existing durations to choose from.", MSGCH_PROMPT);
+ return;
+ }
+ choice = buf[0] - 'a';
+
+ if (choice < 0 || choice >= (int) durs.size())
+ {
+ mpr("Invalid choice.", MSGCH_PROMPT);
+ return;
+ }
+ choice = durs[choice];
+ }
+ else
+ {
+ std::vector<int> matches;
+ std::vector<std::string> match_names;
+ max_len = 0;
+
+ for (int i = 0; i < NUM_DURATIONS; ++i)
+ {
+ if (strcmp(dur_names[i], buf) == 0)
+ {
+ choice = i;
+ break;
+ }
+ if (strstr(dur_names[i], buf) != NULL)
+ {
+ matches.push_back(i);
+ match_names.push_back(dur_names[i]);
+ }
+ }
+ if (choice != -1)
+ ;
+ else if (matches.size() == 1)
+ choice = matches[0];
+ else if (matches.size() == 0)
+ {
+ mprf(MSGCH_PROMPT, "No durations matching '%s'.", buf);
+ return;
+ }
+ else
+ {
+ std::string prefix = "No exact match for duration '";
+ prefix += buf;
+ prefix += "', possible matches are: ";
+
+ mpr_comma_separated_list(prefix, match_names, " and ", ", ",
+ MSGCH_DIAGNOSTICS);
+ return;
+ }
+ }
+
+ sprintf(buf, "Set '%s' to: ", dur_names[choice]);
+ int num = debug_prompt_for_int(buf, false);
+
+ if (num == 0)
+ {
+ mpr("Can't set duration directly to 0, setting it to 1 instead.",
+ MSGCH_PROMPT);
+ num = 1;
+ }
+ you.duration[choice] = num;
+}
+
+static void debug_uptick_xl(int newxl)
+{
+ while (newxl > you.experience_level)
+ {
+ you.experience = 1 + exp_needed( 2 + you.experience_level );
+ level_change(true);
+ }
+}
+
+static void debug_downtick_xl(int newxl)
+{
+ you.hp = you.hp_max;
+ while (newxl < you.experience_level)
+ {
+ // Each lose_level() subtracts 4 HP, so do this to avoid death
+ // and/or negative HP when going from a high level to a low level.
+ you.hp = std::max(5, you.hp);
+ you.hp_max = std::max(5, you.hp_max);
+
+ lose_level();
+ }
+
+ you.hp = std::max(1, you.hp);
+ you.hp_max = std::max(1, you.hp_max);
+
+ you.base_hp = std::max(5000, you.base_hp);
+ you.base_hp2 = std::max(5000 + you.hp_max, you.base_hp2);
+}
+
+void wizard_set_xl()
+{
+ mprf(MSGCH_PROMPT, "Enter new experience level: ");
+ char buf[30];
+ if (cancelable_get_line_autohist(buf, sizeof buf))
+ {
+ canned_msg(MSG_OK);
+ return;
+ }
+
+ const int newxl = atoi(buf);
+ if (newxl < 1 || newxl > 27 || newxl == you.experience_level)
+ {
+ canned_msg(MSG_OK);
+ return;
+ }
+
+ no_messages mx;
+ if (newxl < you.experience_level)
+ debug_downtick_xl(newxl);
+ else
+ debug_uptick_xl(newxl);
+}
+
+
+