From cd4f718dbcb7974ef1762c87b5e13f0a38df0961 Mon Sep 17 00:00:00 2001 From: zelgadis Date: Sat, 20 Dec 2008 02:26:25 +0000 Subject: Some wizard/debugging commands: * The &" command now shows the total experience value of all monster on the level. * &G now prevents dismissed monsters from dropping their original equipment, except for unrandarts and fixed arts. You can force the dismissed monsters to drop all of their non-summoned items by adding "keepitem" to the regexp string. * &R can now either change the random monster spawn rate for the current level, or immediately spawn a given number of random monsters. * &^D can be used to examine and alter player enchantments/durations. * The targeting command "S" can be used to turn a permanent monster into a summoned one. * The targeting command "~" can be used to polymorph a monster into one of a particular type. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7870 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/acr.cc | 192 +++++++----------- crawl-ref/source/command.cc | 3 + crawl-ref/source/debug.cc | 485 +++++++++++++++++++++++++++++++++++++++++++- crawl-ref/source/debug.h | 7 + crawl-ref/source/directn.cc | 21 ++ 5 files changed, 592 insertions(+), 116 deletions(-) diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc index c64a720a74..b4b200708d 100644 --- a/crawl-ref/source/acr.cc +++ b/crawl-ref/source/acr.cc @@ -426,27 +426,10 @@ static void _startup_tutorial() } #ifdef WIZARD -// Returns whether an item of this type can be an artefact, or cursed. -static bool _item_type_can_be_artefact( int type) +// Returns whether an item of this type can be cursed. +static bool _item_type_can_be_cursed( int type) { - return (type == OBJ_WEAPONS || type == OBJ_ARMOUR || type == OBJ_JEWELLERY - || type == OBJ_BOOKS); -} - -static bool _make_book_randart(item_def &book) -{ - char type; - - do - { - mpr("Make book fixed [t]heme or fixed [l]evel? ", MSGCH_PROMPT); - type = tolower(getch()); - } while (type != 't' && type != 'l'); - - if (type == 'l') - return make_book_level_randart(book); - else - return make_book_theme_randart(book); + return (type == OBJ_WEAPONS || type == OBJ_ARMOUR || type == OBJ_JEWELLERY); } static void _do_wizard_command(int wiz_command, bool silent_fail) @@ -522,95 +505,11 @@ static void _do_wizard_command(int wiz_command, bool silent_fail) break; } - case '+': - { - int i = prompt_invent_item( "Make an artefact out of which item?", - MT_INVLIST, -1 ); - - if (prompt_failed(i)) - break; - - item_def &item(you.inv[i]); - - if (!_item_type_can_be_artefact(item.base_type)) - { - mpr("That item cannot be turned into an artefact."); - break; - } - - int j; - // Set j == equipment slot of chosen item, remove old randart benefits. - for (j = 0; j < NUM_EQUIP; j++) - { - if (you.equip[j] == i) - { - if (j == EQ_WEAPON) - you.wield_change = true; - - if (is_random_artefact( item )) - unuse_randart( i ); - break; - } - } - if (is_random_artefact(item)) - { - if (!yesno("Is already a randart; wipe and re-use?")) - { - canned_msg( MSG_OK ); - // If equipped, re-apply benefits. - if (j != NUM_EQUIP) - use_randart( i ); - return; - } - - item.special = 0; - item.flags &= ~ISFLAG_RANDART; - item.props.clear(); - } - - mpr("Fake item as gift from which god (ENTER to leave alone): ", - MSGCH_PROMPT); - char name[80]; - if (!cancelable_get_line( name, sizeof( name ) ) && name[0]) - { - god_type god = string_to_god(name, false); - if (god == GOD_NO_GOD) - mpr("No such god, leaving item origin alone."); - else - { - mprf("God gift of %s.", god_name(god, false).c_str()); - item.orig_monnum = -god; - } - } - - if (item.base_type == OBJ_BOOKS) - { - if (!_make_book_randart(item)) - { - mpr("Failed to turn book into randart."); - break; - } - } - else if (!make_item_randart( item )) - { - mpr("Failed to turn item into randart."); - break; - } - - if (Options.autoinscribe_randarts) - { - add_autoinscription(item, - randart_auto_inscription(you.inv[i])); - } - - // If equipped, apply new randart benefits. - if (j != NUM_EQUIP) - use_randart( i ); - - mpr( item.name(DESC_INVENTORY_EQUIP).c_str() ); + case '+': + wizard_make_object_randart(); break; - } + case '|': // Create all unrandarts. for (tmp = 1; tmp < NO_UNRANDARTS; tmp++) @@ -688,10 +587,10 @@ static void _do_wizard_command(int wiz_command, bool silent_fail) if (item_cursed(item)) do_uncurse_item(item); - else if (_item_type_can_be_artefact(item.base_type)) + else if (_item_type_can_be_cursed(item.base_type)) do_curse_item(item); else - mpr("That item cannot be cursed."); + mpr("That type of item cannot be cursed."); break; } @@ -805,16 +704,77 @@ static void _do_wizard_command(int wiz_command, bool silent_fail) break; case 'R': - sprintf(specs, "Set monster spawn rate to what? (now %d) ", - env.spawn_random_rate); - mpr( specs, MSGCH_PROMPT ); + mpr( "(c)hange spawn rate or (s)pawn monsters? ", MSGCH_PROMPT ); + tmp = tolower(getch()); - get_input_line( specs, sizeof( specs ) ); - if (specs[0] != '\0') + if (tmp != 'c' && tmp != 's') { + canned_msg( MSG_OK ); + break; + } + + if (tmp == 'c') + { + sprintf(specs, "Set monster spawn rate to what? (now %d) ", + env.spawn_random_rate); + mpr(specs, MSGCH_PROMPT ); + + if (cancelable_get_line( specs, sizeof( specs ) ) + || strlen(specs) == 0) + { + canned_msg( MSG_OK ); + break; + } + if (tmp = atoi(specs)) env.spawn_random_rate = tmp; } + else + { + // 50 spots are reserved for non-wandering monsters. + int max_spawn = MAX_MONSTERS - 50; + for (int i = 0; i < MAX_MONSTERS; i++) + { + if (menv[i].alive()) + max_spawn--; + } + + if (max_spawn <= 0) + { + mpr("Level already filled with monsters, get rid of some " + "of them first.", MSGCH_PROMPT); + return; + } + + sprintf(specs, "Spawn how many random monsters (max %d)? ", + max_spawn); + mpr(specs, MSGCH_PROMPT ); + + if (cancelable_get_line( specs, sizeof( specs ) ) + || strlen(specs) == 0) + { + canned_msg( MSG_OK ); + break; + } + + int num = atoi(specs); + if (num <= 0) + { + canned_msg( MSG_OK ); + break; + } + num = std::max(num, max_spawn); + + int curr_rate = env.spawn_random_rate; + // Each call to spawn_random_monsters() will spawn one with + // the rate at 5 or less. + env.spawn_random_rate = 5; + + for (int i = 0; i < num; i++) + spawn_random_monsters(); + + env.spawn_random_rate = curr_rate; + } break; case 'r': @@ -1065,6 +1025,10 @@ static void _do_wizard_command(int wiz_command, bool silent_fail) wizard_set_stats(); break; + case CONTROL('D'): + wizard_edit_durations(); + break; + case '^': { if (you.religion == GOD_NO_GOD) diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc index eec9382a82..e7d5dbb441 100644 --- a/crawl-ref/source/command.cc +++ b/crawl-ref/source/command.cc @@ -763,11 +763,13 @@ static const char *targeting_help_1 = "Wizard targeting comands:\n" "g: give item to monster\n" "s: force monster to shout or speak\n" + "S: make monster a summoned monster\n" "F: cycle monster friendly/good neutral/neutral/hostile\n" "P: apply divine blessing to monster\n" "m: move monster or player\n" "M: cause spell miscast for monster or player\n" "w: calculate shortest path to any point on the map\n" + "~: polymorph monster to specific type\n" #endif ; @@ -2260,6 +2262,7 @@ int list_wizard_commands(bool do_redraw_screen) "^ : gain piety\n" "_ : gain religion\n" "@ : set Str Int Dex\n" + "Ctrl-D : change enchantments/durations\n" "\n" "Other player related effects\n" "c : card effect\n" diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc index 51bb7ceb5c..806be6e4ab 100644 --- a/crawl-ref/source/debug.cc +++ b/crawl-ref/source/debug.cc @@ -837,6 +837,8 @@ void debug_list_monsters() std::sort(mon_nums, mon_nums + MAX_MONSTERS, _sort_monster_list); + int total_exp = 0, total_adj_exp = 0; + std::string prev_name = ""; int count = 0; @@ -862,6 +864,19 @@ void debug_list_monsters() nfound++; count++; prev_name = name; + + int exp = exper_value(m); + total_exp += exp; + + if ((m->flags & (MF_WAS_NEUTRAL | MF_CREATED_FRIENDLY)) + || m->has_ench(ENCH_ABJ)) + { + continue; + } + if (m->flags & MF_GOT_HALF_XP) + exp /= 2; + + total_adj_exp += exp; } char buf[80]; @@ -872,7 +887,13 @@ void debug_list_monsters() mons.push_back(buf); mpr_comma_separated_list("Monsters: ", mons); - mprf("%d monsters", nfound); + + if (total_adj_exp == total_exp) + mprf("%d monsters, %d total exp value", + nfound, total_exp); + else + mprf("%d monsters, %d total exp value (%d adjusted)", + nfound, total_exp, total_adj_exp); } #endif @@ -1824,8 +1845,119 @@ void wizard_tweak_object(void) } } } -#endif +// Returns whether an item of this type can be an artefact. +static bool _item_type_can_be_artefact( int type) +{ + return (type == OBJ_WEAPONS || type == OBJ_ARMOUR || type == OBJ_JEWELLERY + || type == OBJ_BOOKS); +} + +static bool _make_book_randart(item_def &book) +{ + char type; + + do + { + mpr("Make book fixed [t]heme or fixed [l]evel? ", MSGCH_PROMPT); + type = tolower(getch()); + } while (type != 't' && type != 'l'); + + if (type == 'l') + return make_book_level_randart(book); + else + return make_book_theme_randart(book); +} + +void wizard_make_object_randart() +{ + int i = prompt_invent_item( "Make an artefact out of which item?", + MT_INVLIST, -1 ); + + if (prompt_failed(i)) + return; + + item_def &item(you.inv[i]); + + if (!_item_type_can_be_artefact(item.base_type)) + { + mpr("That item cannot be turned into an artefact."); + return; + } + + int j; + // Set j == equipment slot of chosen item, remove old randart benefits. + for (j = 0; j < NUM_EQUIP; j++) + { + if (you.equip[j] == i) + { + if (j == EQ_WEAPON) + you.wield_change = true; + + if (is_random_artefact( item )) + unuse_randart( i ); + break; + } + } + + if (is_random_artefact(item)) + { + if (!yesno("Is already a randart; wipe and re-use?")) + { + canned_msg( MSG_OK ); + // If equipped, re-apply benefits. + if (j != NUM_EQUIP) + use_randart( i ); + return; + } + + item.special = 0; + item.flags &= ~ISFLAG_RANDART; + item.props.clear(); + } + + mpr("Fake item as gift from which god (ENTER to leave alone): ", + MSGCH_PROMPT); + char name[80]; + if (!cancelable_get_line( name, sizeof( name ) ) && name[0]) + { + god_type god = string_to_god(name, false); + if (god == GOD_NO_GOD) + mpr("No such god, leaving item origin alone."); + else + { + mprf("God gift of %s.", god_name(god, false).c_str()); + item.orig_monnum = -god; + } + } + + if (item.base_type == OBJ_BOOKS) + { + if (!_make_book_randart(item)) + { + mpr("Failed to turn book into randart."); + return; + } + } + else if (!make_item_randart( item )) + { + mpr("Failed to turn item into randart."); + return; + } + + if (Options.autoinscribe_randarts) + { + add_autoinscription(item, + randart_auto_inscription(you.inv[i])); + } + + // If equipped, apply new randart benefits. + if (j != NUM_EQUIP) + use_randart( i ); + + mpr( item.name(DESC_INVENTORY_EQUIP).c_str() ); +} +#endif #ifdef DEBUG_DIAGNOSTICS // Prints a number of useful (for debugging, that is) stats on monsters. @@ -3750,6 +3882,182 @@ void wizard_set_stats() 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", + "forescry", + "see invisible", + "weapon brand", + "silence", + "condensation shield", + "stoneskin", + "repel undead", + "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 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 matches; + std::vector 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; +} + void wizard_draw_card() { msg::streams(MSGCH_PROMPT) << "Which card? " << std::endl; @@ -3890,6 +4198,33 @@ void debug_place_map() debug_load_map_by_name(what); } +// Make all of the monster's original equipment disappear, unless it's a fixed +// artefact or unrand artefact. +static void _vanish_orig_eq(monsters* mons) +{ + for (int i = 0; i < NUM_MONSTER_SLOTS; i++) + { + if (mons->inv[i] == NON_ITEM) + continue; + + item_def &item(mitm[mons->inv[i]]); + + if (!is_valid_item(item)) + continue; + + if (item.orig_place != 0 || item.orig_monnum != 0 + || !item.inscription.empty() + || is_unrandom_artefact(item) + || is_fixed_artefact(item) + || (item.flags & (ISFLAG_DROPPED | ISFLAG_THROWN | ISFLAG_NOTED_GET + | ISFLAG_BEEN_IN_INV) ) ) + { + continue; + } + item.flags |= ISFLAG_SUMMONED; + } +} + // Dismisses all monsters on the level or all monsters that match a user // specified regex. void wizard_dismiss_all_monsters(bool force_all) @@ -3907,6 +4242,18 @@ void wizard_dismiss_all_monsters(bool force_all) } } + // Make all of the monsters' original equipment disappear unless "keepitem" + // is found in the regex (except for fixed arts and unrand arts). + bool keep_item = false; + if (strstr(buf, "keepitem") != NULL) + { + std::string str = replace_all(buf, "keepitem", ""); + trim_string(str); + strcpy(buf, str.c_str()); + + keep_item = true; + } + // Dismiss all if (buf[0] == '\0' || force_all) { @@ -3916,7 +4263,11 @@ void wizard_dismiss_all_monsters(bool force_all) monsters *monster = &menv[mon]; if (monster->alive()) + { + if (!keep_item) + _vanish_orig_eq(monster); monster_die(monster, KILL_DISMISSED, NON_MONSTER, false, true); + } } return; } @@ -3928,7 +4279,11 @@ void wizard_dismiss_all_monsters(bool force_all) monsters *monster = &menv[mon]; if (monster->alive() && tpat.matches(monster->name(DESC_PLAIN))) + { + if (!keep_item) + _vanish_orig_eq(monster); monster_die(monster, KILL_DISMISSED, NON_MONSTER, false, true); + } } } @@ -4376,6 +4731,132 @@ void wizard_move_player_or_monster(const coord_def& where) already_moving = false; } +void wizard_make_monster_summoned(monsters* mon) +{ + int summon_type = 0; + if (mon->is_summoned(NULL, &summon_type) || summon_type != 0) + { + mpr("Monster is already summoned.", MSGCH_PROMPT); + return; + } + + if (mons_is_unique(mon->type)) + { + mpr("Can't make unique monsters summoned."); + return; + } + + int dur = _debug_prompt_for_int("What summon longevity (1 to 6)? ", true); + + if (dur < 1 || dur > 6) + { + canned_msg( MSG_OK ); + return; + } + + mpr("[a] clone [b] animated [c] chaos [d] miscast [e] zot", MSGCH_PROMPT); + mpr("[f] wrath [g] aid [m] misc [s] spell", + MSGCH_PROMPT); + + mpr("Which summon type? ", MSGCH_PROMPT); + + char choice = tolower(getch()); + + if (!(choice >= 'a' && choice <= 'g') && choice != 'm' && choice != 's') + { + canned_msg( MSG_OK ); + return; + } + + int type = 0; + + switch (choice) + { + case 'a': type = MON_SUMM_CLONE; break; + case 'b': type = MON_SUMM_ANIMATE; break; + case 'c': type = MON_SUMM_CHAOS; break; + case 'd': type = MON_SUMM_MISCAST; break; + case 'e': type = MON_SUMM_ZOT; break; + case 'f': type = MON_SUMM_WRATH; break; + case 'g': type = MON_SUMM_AID; break; + case 'm': type = 0; break; + + case 's': + { + char specs[80]; + + mpr( "Cast which spell by name? ", MSGCH_PROMPT ); + get_input_line( specs, sizeof( specs ) ); + + if (specs[0] == '\0') + { + canned_msg( MSG_OK ); + return; + } + + spell_type spell = spell_by_name(specs, true); + if (spell == SPELL_NO_SPELL) + { + mpr("No such spell.", MSGCH_PROMPT); + return; + } + type = (int) spell; + break; + } + + default: + DEBUGSTR("Invalid summon type choice."); + break; + } + + mon->mark_summoned(dur, true, type); +} + +void wizard_polymorph_monster(monsters* mon) +{ + int old_type = mon->type; + int type = _debug_prompt_for_monster(); + + if (type == -1) + { + canned_msg( MSG_OK ); + return; + } + + if (invalid_monster_class(type) || type == MONS_PROGRAM_BUG) + { + mpr("Invalid monster type.", MSGCH_PROMPT); + return; + } + + if (type == old_type) + { + mpr("Old type and new type are the same, not polymorphing."); + return; + } + + if (mons_species(type) == mons_species(old_type)) + { + mpr("Target species must be different than current species."); + return; + } + + monster_polymorph(mon, (monster_type) type, PPT_SAME, true); + + if (!mon->alive()) + { + mpr("Polymorph killed monster?", MSGCH_ERROR); + return; + } + + mon->check_redraw(mon->pos()); + + if (mon->type == old_type) + mpr("Polymorph failed."); + else if (mon->type != type) + mpr("Monster turned into something other than the desired type."); +} + void debug_pathfind(int mid) { if (mid == NON_MONSTER) diff --git a/crawl-ref/source/debug.h b/crawl-ref/source/debug.h index 7fe31e996c..22a62a0333 100644 --- a/crawl-ref/source/debug.h +++ b/crawl-ref/source/debug.h @@ -73,6 +73,7 @@ void wizard_create_spec_monster(void); void wizard_create_spec_monster_name(void); void wizard_create_spec_object(void); void wizard_tweak_object(void); +void wizard_make_object_randart(void); void wizard_exercise_skill(void); void wizard_set_skill_level(void); void wizard_set_all_skills(void); @@ -81,6 +82,7 @@ void wizard_change_species( void ); void wizard_set_xl(); void wizard_get_religion( void ); void wizard_set_stats( void ); +void wizard_edit_durations( void ); void wizard_draw_card(); void wizard_dismiss_all_monsters(bool force_all = false); void wizard_place_stairs( bool down ); @@ -89,6 +91,7 @@ void wizard_interlevel_travel(); void debug_make_trap( void ); void debug_make_shop( void ); void debug_place_map(); + // Honest debugging functions. void debug_item_scan( void ); void debug_mons_scan(); @@ -109,10 +112,14 @@ void debug_stethoscope(int mon); class monsters; class coord_def; + void wizard_apply_monster_blessing(monsters* mon); void wizard_give_monster_item(monsters* mon); void wizard_move_player_or_monster(const coord_def& where); +void wizard_make_monster_summoned(monsters* mon); +void wizard_polymorph_monster(monsters* mon); void debug_make_monster_shout(monsters* mon); + void debug_pathfind(int mid); void debug_miscast( int target ); diff --git a/crawl-ref/source/directn.cc b/crawl-ref/source/directn.cc index d6d09117d8..f07d04864c 100644 --- a/crawl-ref/source/directn.cc +++ b/crawl-ref/source/directn.cc @@ -1415,6 +1415,27 @@ void direction(dist& moves, targeting_type restricts, debug_miscast(mid); break; + + case CMD_TARGET_WIZARD_MAKE_SUMMONED: + if (!you.wizard || !in_bounds(moves.target)) + break; + mid = mgrd(moves.target); + if (mid == NON_MONSTER) // can put in terrain description here + break; + + wizard_make_monster_summoned(&menv[mid]); + break; + + case CMD_TARGET_WIZARD_POLYMORPH: + if (!you.wizard || !in_bounds(moves.target)) + break; + mid = mgrd(moves.target); + if (mid == NON_MONSTER) // can put in terrain description here + break; + + wizard_polymorph_monster(&menv[mid]); + break; + #endif case CMD_TARGET_DESCRIBE: full_describe_square(moves.target); -- cgit v1.2.3-54-g00ecf