/* * File: debug.cc * Summary: Debug and wizard related functions. * Written by: Linley Henzell and Jesse Jones * * Modified for Crawl Reference by $Author$ on $Date$ * * Change History (most recent first): * * <4> 14/12/99 LRH Added cast_spec_spell_name() * <3> 5/06/99 JDJ Added TRACE. * <2> -/--/-- JDJ Added a bunch od debugging macros. * Old code is now #if WIZARD. * <1> -/--/-- LRH Created */ #include "AppHdr.h" #include "debug.h" #include #include #include #include #include #include #ifdef DOS #include #endif #include "externs.h" #include "direct.h" #include "describe.h" #include "dungeon.h" #include "fight.h" #include "invent.h" #include "itemname.h" #include "itemprop.h" #include "item_use.h" #include "items.h" #include "misc.h" #include "monplace.h" #include "monstuff.h" #include "mon-util.h" #include "mutation.h" #include "player.h" #include "randart.h" #include "religion.h" #include "skills.h" #include "skills2.h" #include "spl-cast.h" #include "spl-util.h" #include "stuff.h" #include "travel.h" #include "version.h" #ifndef WIZARD #define WIZARD #endif #if DEBUG && WIN #define MyDebugBreak() _asm {int 3} #endif //----------------------------------- // Internal Variables // #if WIN static HANDLE sConsole = NULL; #endif // ======================================================================== // Internal Functions // ======================================================================== //--------------------------------------------------------------- // // BreakStrToDebugger // //--------------------------------------------------------------- #if DEBUG static void BreakStrToDebugger(const char *mesg) { #if OSX fprintf(stderr, mesg); // raise(SIGINT); // this is what DebugStr() does on OS X according to Tech Note 2030 int* p = NULL; // but this gives us a stack crawl... *p = 0; #elif WIN MSG msg; // remove pending quit messages so the message box displays bool quitting = (bool)::PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE); char text[2500]; int flags = MB_YESNO + // want abort and ignore buttons // (too bad we can't ditch the retry button...) MB_ICONERROR + // display the icon for errors MB_TASKMODAL + // don't let the user do anything else in the app MB_SETFOREGROUND; // bring the app to the front strcpy(text, mesg); strcat(text, "\nDo you want to drop into the debugger?"); int result = MessageBoxA(NULL, text, "Debug Break", flags); if (result == IDYES) MyDebugBreak(); if (quitting) PostQuitMessage(msg.wParam); #else fprintf(stderr, "%s\n", mesg); abort(); #endif } #endif //--------------------------------------------------------------- // // IsDebuggerPresent95 // // From March 1999 Windows Developer's Journal. This should only // be called if we're running on Win 95 (normally I'd add an // ASSERT, but that's a bit dicy since this is called by ASSERT...) // //--------------------------------------------------------------- #if WIN static bool IsDebuggerPresent95() { bool present = false; const DWORD kDebuggerPresentFlag = 0x000000001; const DWORD kProcessDatabaseBytes = 190; const DWORD kOffsetFlags = 8; DWORD threadID = GetCurrentThreadId(); DWORD processID = GetCurrentProcessId(); DWORD obfuscator = 0; #if __MWERKS__ asm { mov ax, fs mov es, ax mov eax, 0x18 mov eax, es:[eax] sub eax, 0x10 xor eax,[threadID] mov[obfuscator], eax } #else _asm { mov ax, fs mov es, ax mov eax, 18 h mov eax, es:[eax] sub eax, 10 h xor eax,[threadID] mov[obfuscator], eax } #endif const DWORD *processDatabase = reinterpret_cast< const DWORD * >(processID ^ obfuscator); if (!IsBadReadPtr(processDatabase, kProcessDatabaseBytes)) { DWORD flags = processDatabase[kOffsetFlags]; present = (flags & kDebuggerPresentFlag) != 0; } return present; } #endif //--------------------------------------------------------------- // // IsDebuggerPresent // //--------------------------------------------------------------- #if WIN bool IsDebuggerPresent() { bool present = false; typedef BOOL(WINAPI * IsDebuggerPresentProc) (); HINSTANCE kernelH = LoadLibrary("KERNEL32.DLL"); if (kernelH != NULL) { // should never fail IsDebuggerPresentProc proc = (IsDebuggerPresentProc)::GetProcAddress( kernelH, "IsDebuggerPresent" ); if (proc != NULL) // only present in NT and Win 98 present = proc() != 0; else present = IsDebuggerPresent95(); } return present; } #endif //--------------------------------------------------------------- // // CreateConsoleWindow // //--------------------------------------------------------------- #if WIN static void CreateConsoleWindow() { ASSERT(sConsole == NULL); // Create the console window if (::AllocConsole()) { // Get the console window's handle sConsole =::GetStdHandle(STD_ERROR_HANDLE); if (sConsole == INVALID_HANDLE_VALUE) sConsole = NULL; // Set some options if (sConsole != NULL) { VERIFY(::SetConsoleTextAttribute(sConsole, FOREGROUND_GREEN)); // green text on a black background (there doesn't appear to // be a way to get black text) VERIFY(::SetConsoleTitle("Debug Log")); COORD size = { 80, 120 }; VERIFY(::SetConsoleScreenBufferSize(sConsole, size)); } else DEBUGSTR(L "Couldn't get the console window's handle!"); } else DEBUGSTR(L "Couldn't allocate the console window!"); } #endif #if DEBUG //--------------------------------------------------------------- // // TraceString // //--------------------------------------------------------------- static void TraceString(const char *mesg) { // Write the string to the debug window #if WIN if (IsDebuggerPresent()) { OutputDebugStringA(mesg); // if you're using CodeWarrior you'll need to enable the "Log System Messages" checkbox to get this working } else { if (sConsole == NULL) // otherwise we'll use a console window CreateConsoleWindow(); if (sConsole != NULL) { unsigned long written; VERIFY(WriteConsoleA(sConsole, mesg, strlen(mesg), &written, NULL)); } } #else fprintf(stderr, "%s", mesg); #endif // Write the string to the debug log static bool inited = false; static FILE *file = NULL; if (!inited) { ASSERT(file == NULL); const char *fileName = "DebugLog.txt"; file = fopen(fileName, "w"); ASSERT(file != NULL); inited = true; } if (file != NULL) { fputs(mesg, file); fflush(file); // make sure all the output makes it to the file } } #endif // ======================================================================== // Global Functions // ======================================================================== //--------------------------------------------------------------- // // AssertFailed // //--------------------------------------------------------------- #if DEBUG void AssertFailed(const char *expr, const char *file, int line) { char mesg[512]; const char *fileName = file + strlen(file); // strip off path while (fileName > file && fileName[-1] != '\\') --fileName; sprintf(mesg, "ASSERT(%s) in '%s' at line %d failed.", expr, fileName, line); BreakStrToDebugger(mesg); } #endif //--------------------------------------------------------------- // // DEBUGSTR // //--------------------------------------------------------------- #if DEBUG void DEBUGSTR(const char *format, ...) { char mesg[2048]; va_list args; va_start(args, format); vsprintf(mesg, format, args); va_end(args); BreakStrToDebugger(mesg); } #endif //--------------------------------------------------------------- // // TRACE // //--------------------------------------------------------------- #if DEBUG void TRACE(const char *format, ...) { char mesg[2048]; va_list args; va_start(args, format); vsprintf(mesg, format, args); va_end(args); TraceString(mesg); } #endif // DEBUG //--------------------------------------------------------------- // // debug_prompt_for_monster // //--------------------------------------------------------------- #ifdef WIZARD static int get_monnum(const char *name) { char search[ITEMNAME_SIZE], mname[ITEMNAME_SIZE]; strncpy(search, name, sizeof search); search[ITEMNAME_SIZE - 1] = 0; strlwr(search); int mon = -1; for (int i = 0; i < NUM_MONSTERS; i++) { moname( i, true, DESC_PLAIN, mname ); const char *ptr = strstr( strlwr(mname), search ); if (ptr != NULL) { if (ptr == mname) { // we prefer prefixes over partial matches mon = i; break; } else mon = i; } } return (mon); } static int debug_prompt_for_monster( void ) { char specs[80]; mpr( "(Hint: 'generated' names, eg 'orc zombie', won't work)", MSGCH_PROMPT ); mpr( "Which monster by name? ", MSGCH_PROMPT ); get_input_line( specs, sizeof( specs ) ); if (specs[0] == '\0') return (-1); return (get_monnum(specs)); } #endif //--------------------------------------------------------------- // // debug_prompt_for_skill // //--------------------------------------------------------------- #ifdef WIZARD static int debug_prompt_for_skill( const char *prompt ) { char specs[80]; mpr( prompt, MSGCH_PROMPT ); get_input_line( specs, sizeof( specs ) ); if (specs[0] == '\0') return (-1); int skill = -1; for (int i = 0; i < NUM_SKILLS; i++) { // avoid the bad values: if (i == SK_UNUSED_1 || (i > SK_UNARMED_COMBAT && i < SK_SPELLCASTING)) continue; char sk_name[80]; strncpy( sk_name, skill_name(i), sizeof( sk_name ) ); char *ptr = strstr( strlwr(sk_name), strlwr(specs) ); if (ptr != NULL) { if (ptr == sk_name && strlen(specs) > 0) { // we prefer prefixes over partial matches skill = i; break; } else skill = i; } } return (skill); } #endif //--------------------------------------------------------------- // // debug_change_species // //--------------------------------------------------------------- #ifdef WIZARD void debug_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; int sp = -1; for (i = SP_HUMAN; i < NUM_SPECIES; i++) { char sp_name[80]; strncpy( sp_name, species_name(i, you.experience_level), sizeof( sp_name ) ); char *ptr = strstr( strlwr(sp_name), strlwr(specs) ); if (ptr != NULL) { if (ptr == sp_name && strlen(specs) > 0) { // we prefer prefixes over partial matches sp = i; break; } else sp = i; } } if (sp == -1) mpr( "That species isn't available." ); else { 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; redraw_screen(); } } #endif //--------------------------------------------------------------- // // debug_prompt_for_int // // If nonneg, then it returns a non-negative number or -1 on fail // If !nonneg, then it returns an integer, and 0 on fail // //--------------------------------------------------------------- #ifdef WIZARD static int debug_prompt_for_int( const char *prompt, bool nonneg ) { char specs[80]; mpr( prompt, MSGCH_PROMPT ); get_input_line( specs, sizeof( specs ) ); if (specs[0] == '\0') return (nonneg ? -1 : 0); char *end; int ret = strtol( specs, &end, 10 ); if ((ret < 0 && nonneg) || (ret == 0 && end == specs)) ret = (nonneg ? -1 : 0); return (ret); } #endif /* Some debugging functions, accessable through keys like %, $, &, ) etc when a section of code in input() in acr.cc is uncommented. */ //--------------------------------------------------------------- // // cast_spec_spell // //--------------------------------------------------------------- #ifdef WIZARD void cast_spec_spell(void) { int spell = debug_prompt_for_int( "Cast which spell by number? ", true ); if (spell == -1) canned_msg( MSG_OK ); else your_spells( spell, 0, false ); } #endif //--------------------------------------------------------------- // // cast_spec_spell_name // //--------------------------------------------------------------- #ifdef WIZARD void cast_spec_spell_name(void) { int i = 0; char specs[80]; char spname[80]; mpr( "Cast which spell by name? ", MSGCH_PROMPT ); get_input_line( specs, sizeof( specs ) ); for (i = 0; i < NUM_SPELLS; i++) { strncpy( spname, spell_title(i), sizeof( spname ) ); if (strstr( strlwr(spname), strlwr(specs) ) != NULL) { your_spells(i, 0, false); return; } } mpr((one_chance_in(20)) ? "Maybe you should go back to WIZARD school." : "I couldn't find that spell."); } #endif //--------------------------------------------------------------- // // create_spec_monster // //--------------------------------------------------------------- #ifdef WIZARD void create_spec_monster(void) { int mon = debug_prompt_for_int( "Create which monster by number? ", true ); if (mon == -1) canned_msg( MSG_OK ); else create_monster( mon, 0, BEH_SLEEP, you.x_pos, you.y_pos, MHITNOT, 250, true ); } // end create_spec_monster() #endif //--------------------------------------------------------------- // // create_spec_monster_name // //--------------------------------------------------------------- #ifdef WIZARD void create_spec_monster_name(void) { int mon = debug_prompt_for_monster(); if (mon == -1) { mpr("I couldn't find that monster."); if (one_chance_in(20)) mpr("Maybe it's hiding."); } else { create_monster(mon, 0, BEH_SLEEP, you.x_pos, you.y_pos, MHITNOT, 250, true); } } // end create_spec_monster_name() #endif //--------------------------------------------------------------- // // level_travel // //--------------------------------------------------------------- #ifdef WIZARD void level_travel( int delta ) { int old_level = you.your_level; int new_level = you.your_level + delta; if (delta == 0) { new_level = debug_prompt_for_int( "Travel to which level? ", true ) - 1; } if (new_level < 0 || new_level >= 50) { mpr( "That level is out of bounds." ); return; } you.your_level = new_level - 1; grd[you.x_pos][you.y_pos] = DNGN_STONE_STAIRS_DOWN_I; down_stairs(true, old_level); untag_followers(); } // end level_travel() #endif //--------------------------------------------------------------- // // create_spec_object // //--------------------------------------------------------------- #ifdef WIZARD void create_spec_object(void) { static int max_subtype[] = { NUM_WEAPONS, NUM_MISSILES, NUM_ARMOURS, NUM_WANDS, NUM_FOODS, 0, // unknown I NUM_SCROLLS, NUM_JEWELLERY, NUM_POTIONS, 0, // unknown II NUM_BOOKS, NUM_STAVES, 0, // Orbs -- only one, handled specially NUM_MISCELLANY, 0, // corpses -- handled specially 0, // gold -- handled specially 0, // "gemstones" -- no items of type }; char specs[80]; char obj_name[ ITEMNAME_SIZE ]; char keyin; char * ptr; int best_index; int mon; int i; int class_wanted = OBJ_UNASSIGNED; int type_wanted = -1; int special_wanted = 0; int thing_created; for (;;) { mpr(") - weapons ( - missiles [ - armour / - wands ? - scrolls", MSGCH_PROMPT); mpr("= - jewellery ! - potions : - books | - staves 0 - The Orb", MSGCH_PROMPT); mpr("} - miscellany X - corpses %% - food $ - gold ESC - exit", MSGCH_PROMPT); mpr("What class of item? ", MSGCH_PROMPT); keyin = toupper( get_ch() ); if (keyin == ')') class_wanted = OBJ_WEAPONS; else if (keyin == '(') class_wanted = OBJ_MISSILES; else if (keyin == '[' || keyin == ']') class_wanted = OBJ_ARMOUR; else if (keyin == '/' || keyin == '\\') class_wanted = OBJ_WANDS; else if (keyin == '?') class_wanted = OBJ_SCROLLS; else if (keyin == '=' || keyin == '"') class_wanted = OBJ_JEWELLERY; else if (keyin == '!') class_wanted = OBJ_POTIONS; else if (keyin == ':') class_wanted = OBJ_BOOKS; else if (keyin == '|') class_wanted = OBJ_STAVES; else if (keyin == '0' || keyin == 'O') class_wanted = OBJ_ORBS; else if (keyin == '}' || keyin == '{') class_wanted = OBJ_MISCELLANY; else if (keyin == 'X') class_wanted = OBJ_CORPSES; else if (keyin == '%') class_wanted = OBJ_FOOD; else if (keyin == '$') class_wanted = OBJ_GOLD; else if (keyin == ESCAPE || keyin == ' ' || keyin == '\r' || keyin == '\n') { canned_msg( MSG_OK ); return; } if (class_wanted != OBJ_UNASSIGNED) break; } // allocate an item to play with: thing_created = get_item_slot(); if (thing_created == NON_ITEM) { mpr( "Could not allocate item." ); return; } // turn item into appropriate kind: if (class_wanted == OBJ_ORBS) { mitm[thing_created].base_type = OBJ_ORBS; mitm[thing_created].sub_type = ORB_ZOT; mitm[thing_created].quantity = 1; } else if (class_wanted == OBJ_GOLD) { int amount = debug_prompt_for_int( "How much gold? ", true ); if (amount <= 0) { canned_msg( MSG_OK ); return; } mitm[thing_created].base_type = OBJ_GOLD; mitm[thing_created].sub_type = 0; mitm[thing_created].quantity = amount; } else if (class_wanted == OBJ_CORPSES) { mon = debug_prompt_for_monster(); if (mon == -1) { mpr( "No such monster." ); return; } mitm[thing_created].base_type = OBJ_CORPSES; mitm[thing_created].sub_type = CORPSE_BODY; mitm[thing_created].plus = mon; mitm[thing_created].plus2 = 0; mitm[thing_created].special = 210; mitm[thing_created].colour = mons_colour(mon);; mitm[thing_created].quantity = 1; mitm[thing_created].flags = 0; } else { mpr( "What type of item? ", MSGCH_PROMPT ); get_input_line( specs, sizeof( specs ) ); if (specs[0] == '\0') { canned_msg( MSG_OK ); return; } // In order to get the sub-type, we'll fill out the base type... // then we're going to iterate over all possible subtype values // and see if we get a winner. -- bwr mitm[thing_created].base_type = class_wanted; mitm[thing_created].sub_type = 0; mitm[thing_created].plus = 0; mitm[thing_created].plus2 = 0; mitm[thing_created].special = 0; mitm[thing_created].flags = 0; mitm[thing_created].quantity = 1; set_ident_flags( mitm[thing_created], ISFLAG_IDENT_MASK ); if (class_wanted == OBJ_ARMOUR) { if (strstr( "naga barding", specs )) { mitm[thing_created].sub_type = ARM_NAGA_BARDING; } else if (strstr( "centaur barding", specs )) { mitm[thing_created].sub_type = ARM_CENTAUR_BARDING; } else if (strstr( "wizard's hat", specs )) { mitm[thing_created].sub_type = ARM_HELMET; mitm[thing_created].plus2 = THELM_WIZARD_HAT; } else if (strstr( "cap", specs )) { mitm[thing_created].sub_type = ARM_HELMET; mitm[thing_created].plus2 = THELM_CAP; } else if (strstr( "helm", specs )) { mitm[thing_created].sub_type = ARM_HELMET; mitm[thing_created].plus2 = THELM_HELM; } } if (!mitm[thing_created].sub_type) { type_wanted = -1; best_index = 10000; for (i = 0; i < max_subtype[ mitm[thing_created].base_type ]; i++) { mitm[thing_created].sub_type = i; item_name( mitm[thing_created], DESC_PLAIN, obj_name ); ptr = strstr( strlwr(obj_name), strlwr(specs) ); if (ptr != NULL) { // earliest match is the winner if (ptr - obj_name < best_index) { mpr( obj_name ); type_wanted = i; best_index = ptr - obj_name; } } } if (type_wanted == -1) { // ds -- if specs is a valid int, try using that. // Since zero is atoi's copout, the wizard // must enter (subtype + 1). if (!(type_wanted = atoi(specs))) { mpr( "No such item." ); return; } type_wanted--; } mitm[thing_created].sub_type = type_wanted; } switch (mitm[thing_created].base_type) { case OBJ_MISSILES: mitm[thing_created].quantity = 30; // intentional fall-through case OBJ_WEAPONS: case OBJ_ARMOUR: mpr( "What ego type? ", MSGCH_PROMPT ); get_input_line( specs, sizeof( specs ) ); if (specs[0] != '\0') { special_wanted = 0; best_index = 10000; for (i = 1; i < 25; i++) { mitm[thing_created].special = i; item_name( mitm[thing_created], DESC_PLAIN, obj_name ); ptr = strstr( strlwr(obj_name), strlwr(specs) ); if (ptr != NULL) { // earliest match is the winner if (ptr - obj_name < best_index) { mpr( obj_name ); special_wanted = i; best_index = ptr - obj_name; } } } mitm[thing_created].special = special_wanted; } break; case OBJ_BOOKS: if (mitm[thing_created].sub_type == BOOK_MANUAL) { special_wanted = debug_prompt_for_skill( "A manual for which skill? " ); if (special_wanted != -1) mitm[thing_created].plus = special_wanted; else mpr( "Sorry, no books on that skill today." ); } break; case OBJ_WANDS: mitm[thing_created].plus = 24; break; case OBJ_STAVES: if (item_is_rod( mitm[thing_created] )) { mitm[thing_created].plus = MAX_ROD_CHARGE * ROD_CHARGE_MULT; mitm[thing_created].plus2 = MAX_ROD_CHARGE * ROD_CHARGE_MULT; } break; case OBJ_MISCELLANY: // Runes to "demonic", decks have 50 cards, ignored elsewhere? mitm[thing_created].plus = 50; break; case OBJ_FOOD: case OBJ_SCROLLS: case OBJ_POTIONS: mitm[thing_created].quantity = 12; break; default: break; } } item_colour( mitm[thing_created] ); move_item_to_grid( &thing_created, you.x_pos, you.y_pos ); if (thing_created != NON_ITEM) { origin_acquired( mitm[thing_created], AQ_WIZMODE ); canned_msg( MSG_SOMETHING_APPEARS ); } } #endif //--------------------------------------------------------------- // // create_spec_object // //--------------------------------------------------------------- #ifdef WIZARD void tweak_object(void) { char specs[50]; char keyin; int item = prompt_invent_item( "Tweak which item? ", -1 ); if (item == PROMPT_ABORT) { canned_msg( MSG_OK ); return; } if (item == you.equip[EQ_WEAPON]) you.wield_change = true; for (;;) { void *field_ptr = NULL; for (;;) { item_name( you.inv[item], DESC_INVENTORY_EQUIP, info ); mpr( info ); mpr( "a - plus b - plus2 c - special d - quantity ESC - exit", MSGCH_PROMPT ); mpr( "Which field? ", MSGCH_PROMPT ); keyin = tolower( get_ch() ); if (keyin == 'a') field_ptr = &(you.inv[item].plus); else if (keyin == 'b') field_ptr = &(you.inv[item].plus2); else if (keyin == 'c') field_ptr = &(you.inv[item].special); else if (keyin == 'd') field_ptr = &(you.inv[item].quantity); else if (keyin == ESCAPE || keyin == ' ' || keyin == '\r' || keyin == '\n') { canned_msg( MSG_OK ); return; } if (keyin >= 'a' && keyin <= 'd') break; } if (keyin != 'c') { const short *const ptr = static_cast< short * >( field_ptr ); snprintf( info, INFO_SIZE, "Old value: %d (0x%04x)", *ptr, *ptr ); } else { const long *const ptr = static_cast< long * >( field_ptr ); snprintf( info, INFO_SIZE, "Old value: %ld (0x%08lx)", *ptr, *ptr ); } mpr( info ); mpr( "New value? ", MSGCH_PROMPT ); get_input_line( specs, sizeof( specs ) ); if (specs[0] == '\0') return; char *end; int new_value = strtol( specs, &end, 10 ); if (new_value == 0 && end == specs) return; if (keyin != 'c') { short *ptr = static_cast< short * >( field_ptr ); *ptr = new_value; } else { long *ptr = static_cast< long * >( field_ptr ); *ptr = new_value; } } } #endif //--------------------------------------------------------------- // // stethoscope // //--------------------------------------------------------------- #if DEBUG_DIAGNOSTICS static const char *enchant_names[] = { "None", "Slow", "Haste", "*BUG-3*", "Fear", "Conf", "Invis", "YPois-1", "YPois-2", "YPois-3", "YPois-4", "YShug-1", "YShug-2", "YShug-3", "YShug-4", "YRot-1", "YRot-2", "YRot-3", "YRot-4", "Summon", "Abj-1", "Abj-2", "Abj-3", "Abj-4", "Abj-5", "Abj-6", "Corona-1", "Corona-2", "Corona-3", "Corona-4", "Charm", "YSticky-1", "YSticky-2", "YSticky-3", "YSticky-4", "*BUG-35*", "*BUG-36*", "*BUG-37*", "GlowShapeshifter", "Shapeshifter", "Tele-1", "Tele-2", "Tele-3", "Tele-4", "*BUG-44*", "*BUG-45*", "*BUG-46*", "*BUG-47*", "*BUG-48*", "*BUG-49*", "*BUG-50*", "*BUG-51*", "*BUG-52*", "*BUG-53*", "*BUG-54*", "*BUG-55*", "*BUG-56*", "Pois-1", "Pois-2", "Pois-3", "Pois-4", "Sticky-1", "Sticky-2", "Sticky-3", "Sticky-4", "OldAbj-1", "OldAbj-2", "OldAbj-3", "OldAbj-4", "OldAbj-5", "OldAbj-6", "OldCreatedFriendly", "SleepWary", "Submerged", "Short Lived", "*BUG-too big*" }; void stethoscope(int mwh) { struct dist stth; int steth_x, steth_y; int i, j; if (mwh != RANDOM_MONSTER) i = mwh; else { mpr( "Which monster?", MSGCH_PROMPT ); direction( stth ); if (!stth.isValid) return; if (stth.isTarget) { steth_x = stth.tx; steth_y = stth.ty; } else { steth_x = you.x_pos + stth.dx; steth_y = you.x_pos + stth.dy; } if (env.cgrid[steth_x][steth_y] != EMPTY_CLOUD) { snprintf( info, INFO_SIZE, "cloud type: %d delay: %d", env.cloud[ env.cgrid[steth_x][steth_y] ].type, env.cloud[ env.cgrid[steth_x][steth_y] ].decay ); mpr( info, MSGCH_DIAGNOSTICS ); } if (mgrd[steth_x][steth_y] == NON_MONSTER) { snprintf( info, INFO_SIZE, "item grid = %d", igrd[steth_x][steth_y] ); mpr( info, MSGCH_DIAGNOSTICS ); return; } i = mgrd[steth_x][steth_y]; } // print type of monster snprintf( info, INFO_SIZE, "%s (id #%d; type=%d loc=(%d,%d) align=%s)", monam( menv[i].number, menv[i].type, true, DESC_CAP_THE ), i, menv[i].type, menv[i].x, menv[i].y, ((menv[i].attitude == ATT_FRIENDLY) ? "friendly" : (menv[i].attitude == ATT_HOSTILE) ? "hostile" : (menv[i].attitude == ATT_NEUTRAL) ? "neutral" : "unknown alignment") ); mpr( info, MSGCH_DIAGNOSTICS ); // print stats and other info snprintf( info, INFO_SIZE,"HD=%d HP=%d/%d AC=%d EV=%d MR=%d SP=%d energy=%d num=%d flags=%02x", menv[i].hit_dice, menv[i].hit_points, menv[i].max_hit_points, menv[i].armour_class, menv[i].evasion, mons_resist_magic( &menv[i] ), menv[i].speed, menv[i].speed_increment, menv[i].number, menv[i].flags ); mpr( info, MSGCH_DIAGNOSTICS ); // print behaviour information const int hab = monster_habitat( menv[i].type ); snprintf( info, INFO_SIZE, "hab=%s beh=%s(%d) foe=%s(%d) mem=%d target=(%d,%d)", ((hab == DNGN_DEEP_WATER) ? "water" : (hab == DNGN_LAVA) ? "lava" : "floor"), ((menv[i].behaviour == BEH_SLEEP) ? "sleep" : (menv[i].behaviour == BEH_WANDER) ? "wander" : (menv[i].behaviour == BEH_SEEK) ? "seek" : (menv[i].behaviour == BEH_FLEE) ? "flee" : (menv[i].behaviour == BEH_CORNERED) ? "cornered" : "unknown"), menv[i].behaviour, ((menv[i].foe == MHITYOU) ? "you" : (menv[i].foe == MHITNOT) ? "none" : (menv[menv[i].foe].type == -1) ? "unassigned monster" : monam( menv[menv[i].foe].number, menv[menv[i].foe].type, true, DESC_PLAIN )), menv[i].foe, menv[i].foe_memory, menv[i].target_x, menv[i].target_y ); mpr( info, MSGCH_DIAGNOSTICS ); // print resistances snprintf( info, INFO_SIZE, "resist: fire=%d cold=%d elec=%d pois=%d neg=%d", mons_res_fire( &menv[i] ), mons_res_cold( &menv[i] ), mons_res_elec( &menv[i] ), mons_res_poison( &menv[i] ), mons_res_negative_energy( &menv[i] ) ); mpr( info, MSGCH_DIAGNOSTICS ); // print enchantments strncpy( info, "ench: ", INFO_SIZE ); for (j = 0; j < 6; j++) { if (menv[i].enchantment[j] >= NUM_ENCHANTMENTS) strncat( info, enchant_names[ NUM_ENCHANTMENTS ], INFO_SIZE ); else strncat( info, enchant_names[ menv[i].enchantment[j] ], INFO_SIZE ); if (strlen( info ) <= 70) strncat( info, " ", INFO_SIZE ); else if (j < 5) { mpr( info, MSGCH_DIAGNOSTICS ); strncpy( info, "ench: ", INFO_SIZE ); } } mpr( info, MSGCH_DIAGNOSTICS ); if (menv[i].type == MONS_PLAYER_GHOST || menv[i].type == MONS_PANDEMONIUM_DEMON) { snprintf( info, INFO_SIZE, "Ghost damage: %d; brand: %d", ghost.values[ GVAL_DAMAGE ], ghost.values[ GVAL_BRAND ] ); mpr( info, MSGCH_DIAGNOSTICS ); } } // end stethoscope() #endif #if DEBUG_ITEM_SCAN //--------------------------------------------------------------- // // dump_item // //--------------------------------------------------------------- static void dump_item( const char *name, int num, const item_def &item ) { mpr( name, MSGCH_WARN ); snprintf( info, INFO_SIZE, " item #%d: base: %d; sub: %d; plus: %d; plus2: %d; special: %ld", num, item.base_type, item.sub_type, item.plus, item.plus2, item.special ); mpr( info ); snprintf( info, INFO_SIZE, " quant: %d; colour: %d; ident: 0x%08lx; ident_type: %d", item.quantity, item.colour, item.flags, get_ident_type( item.base_type, item.sub_type ) ); mpr( info ); snprintf( info, INFO_SIZE, " x: %d; y: %d; link: %d", item.x, item.y, item.link ); mpr( info ); } //--------------------------------------------------------------- // // debug_item_scan // //--------------------------------------------------------------- void debug_item_scan( void ) { int i; char name[256]; // unset marks for (i = 0; i < MAX_ITEMS; i++) mitm[i].flags &= (~ISFLAG_DEBUG_MARK); // First we're going to check all the stacks on the level: for (int x = 0; x < GXM; x++) { for (int y = 0; y < GYM; y++) { // These are unlinked monster inventory items -- skip them: if (x == 0 && y == 0) continue; // Looking for infinite stacks (ie more links than tems allowed) // and for items which have bad coordinates (can't find their stack) for (int obj = igrd[x][y]; obj != NON_ITEM; obj = mitm[obj].link) { // Check for invalid (zero quantity) items that are linked in if (!is_valid_item( mitm[obj] )) { snprintf( info, INFO_SIZE, "Linked invalid item at (%d,%d)!", x, y); mpr( info, MSGCH_WARN ); item_name( mitm[obj], DESC_PLAIN, name ); dump_item( name, obj, mitm[obj] ); } // Check that item knows what stack it's in if (mitm[obj].x != x || mitm[obj].y != y) { snprintf( info, INFO_SIZE, "Item position incorrect at (%d,%d)!", x, y); mpr( info, MSGCH_WARN ); item_name( mitm[obj], DESC_PLAIN, name ); dump_item( name, obj, mitm[obj] ); } // If we run into a premarked item we're in real trouble, // this will also keep this from being an infinite loop. if (mitm[obj].flags & ISFLAG_DEBUG_MARK) { snprintf( info, INFO_SIZE, "Potential INFINITE STACK at (%d, %d)", x, y); mpr( info, MSGCH_WARN ); break; } mitm[obj].flags |= ISFLAG_DEBUG_MARK; } } } // Now scan all the items on the level: for (i = 0; i < MAX_ITEMS; i++) { if (!is_valid_item( mitm[i] )) continue; item_name( mitm[i], DESC_PLAIN, name ); // Don't check (-1,-1) player items or (0,0) monster items if ((mitm[i].x > 0 || mitm[i].y > 0) && !(mitm[i].flags & ISFLAG_DEBUG_MARK)) { mpr( "Unlinked item:", MSGCH_WARN ); dump_item( name, i, mitm[i] ); snprintf( info, INFO_SIZE, "igrd(%d,%d) = %d", mitm[i].x, mitm[i].y, igrd[ mitm[i].x ][ mitm[i].y ] ); mpr( info ); // Let's check to see if it's an errant monster object: for (int j = 0; j < MAX_MONSTERS; j++) { for (int k = 0; k < NUM_MONSTER_SLOTS; k++) { if (menv[j].inv[k] == i) { snprintf( info, INFO_SIZE, "Held by monster #%d: %s at (%d,%d)", j, ptr_monam( &menv[j], DESC_CAP_A ), menv[j].x, menv[j].y ); mpr( info ); } } } } // Current bad items of interest: // -- armour and weapons with large enchantments/illegal special vals // // -- items described as questionable (the class 100 bug) // // -- eggplant is an illegal throwing weapon // // -- bola is an illegal fixed artefact // // -- items described as buggy (typically adjectives out of range) // (note: covers buggy, bugginess, buggily, whatever else) // if (strstr( name, "questionable" ) != NULL || strstr( name, "eggplant" ) != NULL || strstr( name, "bola" ) != NULL || strstr( name, "bugg" ) != NULL) { mpr( "Bad item:", MSGCH_WARN ); dump_item( name, i, mitm[i] ); } else if ((mitm[i].base_type == OBJ_WEAPONS && (abs(mitm[i].plus) > 30 || abs(mitm[i].plus2) > 30 || (!is_random_artefact( mitm[i] ) && (mitm[i].special >= 30 && mitm[i].special < 181)))) || (mitm[i].base_type == OBJ_MISSILES && (abs(mitm[i].plus) > 25 || (!is_random_artefact( mitm[i] ) && mitm[i].special >= 30))) || (mitm[i].base_type == OBJ_ARMOUR && (abs(mitm[i].plus) > 25 || (!is_random_artefact( mitm[i] ) && mitm[i].sub_type != ARM_HELMET && mitm[i].special >= 30)))) { mpr( "Bad plus or special value:", MSGCH_WARN ); dump_item( name, i, mitm[i] ); } } // Don't want debugging marks interfering with anything else. for (i = 0; i < MAX_ITEMS; i++) mitm[i].flags &= (~ISFLAG_DEBUG_MARK); // Quickly scan monsters for "program bug"s. for (i = 0; i < MAX_MONSTERS; i++) { const struct monsters *const monster = &menv[i]; if (monster->type == -1) continue; moname( monster->type, true, DESC_PLAIN, name ); if (strcmp( name, "program bug" ) == 0) { mpr( "Program bug detected!", MSGCH_WARN ); snprintf( info, INFO_SIZE, "Buggy monster detected: monster #%d; position (%d,%d)", i, monster->x, monster->y ); mpr( info, MSGCH_WARN ); } } } #endif //--------------------------------------------------------------- // // debug_add_skills // //--------------------------------------------------------------- #ifdef WIZARD void debug_add_skills(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); } } // end debug_add_skills() #endif //--------------------------------------------------------------- // // debug_set_skills // //--------------------------------------------------------------- #ifdef WIZARD void debug_set_skills(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 == -1) canned_msg( MSG_OK ); else { const int points = (skill_exp_needed( amount + 1 ) * 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 = 1; break; case SK_ARMOUR: you.redraw_armour_class = 1; you.redraw_evasion = 1; break; default: break; } } } } // end debug_add_skills() #endif //--------------------------------------------------------------- // // debug_set_all_skills // //--------------------------------------------------------------- #ifdef WIZARD void debug_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 (i == SK_UNUSED_1 || (i > SK_UNARMED_COMBAT && i < SK_SPELLCASTING)) { continue; } const int points = (skill_exp_needed( amount + 1 ) * 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 = 1; you.redraw_evasion = 1; } } // end debug_add_skills() #endif //--------------------------------------------------------------- // // debug_add_mutation // //--------------------------------------------------------------- #ifdef WIZARD bool debug_add_mutation(void) { bool success = false; char specs[80]; // Yeah, the gaining message isn't too good for this... but // there isn't an array of simple mutation names. -- bwr mpr( "Which mutation (by message when getting mutation)? ", MSGCH_PROMPT ); get_input_line( specs, sizeof( specs ) ); if (specs[0] == '\0') return (false); int mutation = -1; for (int i = 0; i < NUM_MUTATIONS; i++) { char mut_name[80]; strncpy( mut_name, mutation_name( i, 1 ), sizeof( mut_name ) ); char *ptr = strstr( strlwr(mut_name), strlwr(specs) ); if (ptr != NULL) { // we take the first mutation that matches mutation = i; break; } } if (mutation == -1) mpr("I can't warp you that way!"); else { snprintf( info, INFO_SIZE, "Found: %s", mutation_name( mutation, 1 ) ); mpr( info ); int levels = debug_prompt_for_int( "How many levels? ", false ); if (levels == 0) { canned_msg( MSG_OK ); success = false; } else if (levels > 0) { for (int i = 0; i < levels; i++) { if (mutate( mutation )) success = true; } } else { for (int i = 0; i < -levels; i++) { if (delete_mutation( mutation )) success = true; } } } return (success); } // end debug_add_mutation() #endif //--------------------------------------------------------------- // // debug_get_religion // //--------------------------------------------------------------- #ifdef WIZARD void debug_get_religion(void) { char specs[80]; mpr( "Which god (by name)? ", MSGCH_PROMPT ); get_input_line( specs, sizeof( specs ) ); if (specs[0] == '\0') return; int god = -1; for (int i = 1; i < NUM_GODS; i++) { char name[80]; strncpy( name, god_name(i), sizeof( name ) ); char *ptr = strstr( strlwr(name), strlwr(specs) ); if (ptr != NULL) { god = i; break; } } if (god == -1) mpr( "That god doesn't seem to be taking followers today." ); else { grd[you.x_pos][you.y_pos] = 179 + god; god_pitch(god); } } // end debug_add_skills() #endif void error_message_to_player(void) { mpr("Oh dear. There appears to be a bug in the program."); mpr("I suggest you leave this level then save as soon as possible."); } // end error_message_to_player() #ifdef WIZARD static int create_fsim_monster(int mtype, int hp) { const int mi = create_monster( mtype, 0, BEH_HOSTILE, you.x_pos, you.y_pos, MHITNOT, 250 ); if (mi == -1) return (mi); monsters *mon = &menv[mi]; mon->hit_points = mon->max_hit_points = hp; return (mi); } static skill_type fsim_melee_skill(const item_def *item) { skill_type sk = SK_UNARMED_COMBAT; if (item) sk = weapon_skill(*item); return (sk); } static void fsim_set_melee_skill(int skill, const item_def *item) { you.skills[fsim_melee_skill(item)] = skill; you.skills[SK_FIGHTING] = skill * 15 / 27; } static void fsim_set_ranged_skill(int skill, const item_def *item) { you.skills[range_skill(*item)] = skill; you.skills[SK_RANGED_COMBAT] = skill * 15 / 27; } static void fsim_item(FILE *out, bool melee, const item_def *weap, int wskill, unsigned long damage, long iterations, long hits, int maxdam, unsigned long time) { double hitdam = hits? double(damage) / hits : 0.0; int avspeed = (int) (time / iterations); fprintf(out, " %2d | %3ld%% | %5.2f | %5.2f | %5.2f | %3d | %2ld\n", wskill, 100 * hits / iterations, double(damage) / iterations, hitdam, double(damage) * player_speed() / avspeed / iterations, maxdam, time / iterations); } static bool fsim_ranged_combat(FILE *out, int wskill, int mi, const item_def *item, int missile_slot) { monsters &mon = menv[mi]; unsigned long cumulative_damage = 0L; unsigned long time_taken = 0L; long hits = 0L; int maxdam = 0; const int thrown = missile_slot == -1? get_fire_item_index() : missile_slot; if (thrown == ENDOFPACK || thrown == -1) { mprf("No suitable missiles for combat simulation."); return (false); } fsim_set_ranged_skill(wskill, item); no_messages mx; const long iter_limit = Options.fsim_rounds; const int hunger = you.hunger; for (long i = 0; i < iter_limit; ++i) { mon.hit_points = mon.max_hit_points; bolt beam; you.time_taken = player_speed(); if (throw_it(beam, thrown, &mon)) hits++; you.hunger = hunger; time_taken += you.time_taken; int damage = (mon.max_hit_points - mon.hit_points); cumulative_damage += damage; if (damage > maxdam) maxdam = damage; } fsim_item(out, false, item, wskill, cumulative_damage, iter_limit, hits, maxdam, time_taken); return (true); } static bool fsim_melee_combat(FILE *out, int wskill, int mi, const item_def *item) { monsters &mon = menv[mi]; unsigned long cumulative_damage = 0L; unsigned long time_taken = 0L; long hits = 0L; int maxdam = 0; fsim_set_melee_skill(wskill, item); no_messages mx; const long iter_limit = Options.fsim_rounds; const int hunger = you.hunger; for (long i = 0; i < iter_limit; ++i) { mon.hit_points = mon.max_hit_points; you.time_taken = player_speed(); if (you_attack(mi, true)) hits++; you.hunger = hunger; time_taken += you.time_taken; int damage = (mon.max_hit_points - mon.hit_points); cumulative_damage += damage; if (damage > maxdam) maxdam = damage; } fsim_item(out, true, item, wskill, cumulative_damage, iter_limit, hits, maxdam, time_taken); return (true); } static bool debug_fight_simulate(FILE *out, int wskill, int mi, int miss_slot) { int weapon = you.equip[EQ_WEAPON]; const item_def *iweap = weapon != -1? &you.inv[weapon] : NULL; if (iweap && iweap->base_type == OBJ_WEAPONS && is_range_weapon(*iweap)) return fsim_ranged_combat(out, wskill, mi, iweap, miss_slot); else return fsim_melee_combat(out, wskill, mi, iweap); } static const item_def *fsim_weap_item() { const int weap = you.equip[EQ_WEAPON]; if (weap == -1) return NULL; return &you.inv[weap]; } static std::string fsim_wskill() { const item_def *iweap = fsim_weap_item(); return iweap && iweap->base_type == OBJ_WEAPONS && is_range_weapon(*iweap)? skill_name( range_skill(*iweap) ) : iweap? skill_name( fsim_melee_skill(iweap) ) : skill_name( SK_UNARMED_COMBAT ); } static std::string fsim_weapon(int missile_slot) { char item_buf[ITEMNAME_SIZE]; if (you.equip[EQ_WEAPON] != -1) { const item_def &weapon = you.inv[ you.equip[EQ_WEAPON] ]; item_name(weapon, DESC_PLAIN, item_buf, true); if (is_range_weapon(weapon)) { const int missile = missile_slot == -1? get_fire_item_index() : missile_slot; if (missile < ENDOFPACK) { std::string base = item_buf; base += " with "; in_name(missile, DESC_PLAIN, item_buf, true); return (base + item_buf); } } } else { strncpy(item_buf, "unarmed", sizeof item_buf); } return (item_buf); } static std::string fsim_time_string() { time_t curr_time = time(NULL); struct tm *ltime = localtime(&curr_time); if (ltime) { char buf[100]; snprintf(buf, sizeof buf, "%4d%02d%02d/%2d:%02d:%02d", ltime->tm_year + 1900, ltime->tm_mon + 1, ltime->tm_mday, ltime->tm_hour, ltime->tm_min, ltime->tm_sec); return (buf); } return (""); } static void fsim_mon_stats(FILE *o, const monsters &mon) { char buf[ITEMNAME_SIZE]; fprintf(o, "Monster : %s\n", moname(mon.type, true, DESC_PLAIN, buf)); fprintf(o, "HD : %d\n", mon.hit_dice); fprintf(o, "AC : %d\n", mon.armour_class); fprintf(o, "EV : %d\n", mon.evasion); } static void fsim_title(FILE *o, int mon, int ms) { char buf[ITEMNAME_SIZE]; fprintf(o, CRAWL " version " VERSION "\n\n"); fprintf(o, "Combat simulation: %s %s vs. %s (%ld rounds) (%s)\n", species_name(you.species, you.experience_level), you.class_name, moname(menv[mon].type, true, DESC_PLAIN, buf), Options.fsim_rounds, fsim_time_string().c_str()); fprintf(o, "Experience: %d\n", you.experience_level); fprintf(o, "Strength : %d\n", you.strength); fprintf(o, "Intel. : %d\n", you.intel); fprintf(o, "Dexterity : %d\n", you.dex); fprintf(o, "Base speed: %d\n", player_speed()); fprintf(o, "\n"); fsim_mon_stats(o, menv[mon]); fprintf(o, "\n"); fprintf(o, "Weapon : %s\n", fsim_weapon(ms).c_str()); fprintf(o, "Skill : %s\n", fsim_wskill().c_str()); fprintf(o, "\n"); fprintf(o, "Skill | Accuracy | Av.Dam | Av.HitDam | Eff.Dam | Max.Dam | Av.Time\n"); } static int fsim_stat(int stat) { return (stat < 1 ? 1 : stat > 60 ? 60 : stat); } static bool debug_fight_sim(int mindex, int missile_slot) { FILE *ostat = fopen("fight.stat", "a"); if (!ostat) { // I'm not sure what header provides errno on djgpp, // and it's insufficiently important for a wizmode-only // feature. #ifndef DOS mprf("Can't write fight.stat: %s", strerror(errno)); #endif return (false); } bool success = true; FixedVector skill_backup = you.skills; int ystr = you.strength, yint = you.intel, ydex = you.dex; int yxp = you.experience_level; for (int i = SK_FIGHTING; i < NUM_SKILLS; ++i) you.skills[i] = 0; you.experience_level = Options.fsim_xl; if (you.experience_level < 1) you.experience_level = 1; if (you.experience_level > 27) you.experience_level = 27; you.strength = fsim_stat(Options.fsim_str); you.intel = fsim_stat(Options.fsim_int); you.dex = fsim_stat(Options.fsim_dex); fsim_title(ostat, mindex, missile_slot); for (int wskill = 0; wskill <= 27; ++wskill) { mesclr(); mprf("Calculating average damage for %s at skill %d", fsim_weapon(missile_slot).c_str(), wskill); if (!debug_fight_simulate(ostat, wskill, mindex, missile_slot)) goto done_combat_sim; fflush(ostat); // Not checking in the combat loop itself; that would be more responsive // for the user, but slow down the sim with all the calls to kbhit(). if (kbhit() && getch() == 27) { success = false; mprf("Canceling simulation\n"); goto done_combat_sim; } } you.skills = skill_backup; you.strength = ystr; you.intel = yint; you.dex = ydex; you.experience_level = yxp; mprf("Done fight simulation with %s", fsim_weapon(missile_slot).c_str()); done_combat_sim: fprintf(ostat, "-----------------------------------\n\n"); fclose(ostat); return (success); } int fsim_kit_equip(const std::string &kit) { int missile_slot = -1; char item_buf[ITEMNAME_SIZE]; std::string::size_type ammo_div = kit.find("/"); std::string weapon = kit; std::string missile; if (ammo_div != std::string::npos) { weapon = kit.substr(0, ammo_div); missile = kit.substr(ammo_div + 1); trim_string(weapon); trim_string(missile); } for (int i = 0; i < ENDOFPACK; ++i) { if (!is_valid_item(you.inv[i])) continue; in_name(i, DESC_PLAIN, item_buf, true); if (std::string(item_buf).find(weapon) != std::string::npos) { if (i != you.equip[EQ_WEAPON]) { wield_weapon(true, i, false); if (i != you.equip[EQ_WEAPON]) return -100; } break; } } if (!missile.empty()) { for (int i = 0; i < ENDOFPACK; ++i) { if (!is_valid_item(you.inv[i])) continue; in_name(i, DESC_PLAIN, item_buf, true); if (std::string(item_buf).find(missile) != std::string::npos) { missile_slot = i; break; } } } return (missile_slot); } // Writes statistics about a fight to fight.stat in the current directory. // For fight purposes, a punching bag is summoned and given lots of hp, and the // average damage the player does to the p. bag over 10000 hits is noted, // advancing the weapon skill from 0 to 27, and keeping fighting skill to 2/5 // of current weapon skill. void debug_fight_statistics(bool use_defaults) { int punching_bag = get_monnum(Options.fsim_mons.c_str()); if (punching_bag == -1) punching_bag = MONS_WORM; int mindex = create_fsim_monster(punching_bag, 500); if (mindex == -1) { mprf("Failed to create punching bag"); return; } if (!use_defaults) { debug_fight_sim(mindex, -1); goto fsim_mcleanup; } for (int i = 0, size = Options.fsim_kit.size(); i < size; ++i) { int missile = fsim_kit_equip(Options.fsim_kit[i]); if (missile == -100) { mprf("Aborting sim on %s", Options.fsim_kit[i].c_str()); goto fsim_mcleanup; } if (!debug_fight_sim(mindex, missile)) break; } fsim_mcleanup: monster_die(&menv[mindex], KILL_DISMISSED, 0); } static int find_trap_slot() { for (int i = 0; i < MAX_TRAPS; ++i) { if (env.trap[i].type == TRAP_UNASSIGNED) return (i); } return (-1); } void debug_make_trap() { char requested_trap[80]; int trap_slot = find_trap_slot(); trap_type trap = TRAP_UNASSIGNED; int gridch = grd[you.x_pos][you.y_pos]; if (trap_slot == -1) { mpr("Sorry, this level can't take any more traps."); return; } if (gridch != DNGN_FLOOR) { mpr("You need to be on a floor square to make a trap."); return; } mprf(MSGCH_PROMPT, "What kind of trap? "); get_input_line( requested_trap, sizeof( requested_trap ) ); if (!*requested_trap) return; strlwr(requested_trap); for (int t = TRAP_DART; t < NUM_TRAPS; ++t) { if (strstr(requested_trap, trap_name(trap_type(t)))) { trap = trap_type(t); break; } } if (trap == TRAP_UNASSIGNED) { mprf("I know no traps named \"%s\"", requested_trap); return; } place_specific_trap(you.x_pos, you.y_pos, trap); mprf("Created a %s trap, marked it undiscovered", trap_name(trap)); // Also tell travel that its world-view must change. travel_init_new_level(); } static const char *shop_types[] = { "weapon", "armour", "antique weapon", "antique armour", "antiques", "jewellery", "wand", "book", "food", "distillery", "scroll", "general" }; void debug_make_shop() { char requested_shop[80]; int gridch = grd[you.x_pos][you.y_pos]; bool have_shop_slots = false; int new_shop_type = SHOP_UNASSIGNED; bool representative = false; if (gridch != DNGN_FLOOR) { mpr("Insufficient floor-space for new Wal-Mart."); return; } for (int i = 0; i < MAX_SHOPS; ++i) { if (env.shop[i].type == SHOP_UNASSIGNED) { have_shop_slots = true; break; } } if (!have_shop_slots) { mpr("There are too many shops on this level."); return; } mprf(MSGCH_PROMPT, "What kind of shop? "); get_input_line( requested_shop, sizeof( requested_shop ) ); if (!*requested_shop) return; strlwr(requested_shop); for (unsigned i = 0; i < sizeof(shop_types) / sizeof (*shop_types); ++i) { if (strstr(requested_shop, shop_types[i])) { new_shop_type = i; break; } } if (new_shop_type == SHOP_UNASSIGNED) { mprf("Bad shop type: \"%s\"", requested_shop); return; } representative = !!strchr(requested_shop, '*'); place_spec_shop(you.your_level, you.x_pos, you.y_pos, new_shop_type, representative); link_items(); mprf("Done."); } #endif