diff options
Diffstat (limited to 'trunk/source/debug.cc')
-rw-r--r-- | trunk/source/debug.cc | 1701 |
1 files changed, 1701 insertions, 0 deletions
diff --git a/trunk/source/debug.cc b/trunk/source/debug.cc new file mode 100644 index 0000000000..265db7e1e1 --- /dev/null +++ b/trunk/source/debug.cc @@ -0,0 +1,1701 @@ +/* + * File: debug.cc + * Summary: Debug and wizard related functions. + * Written by: Linley Henzell and Jesse Jones + * + * 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> + +#ifdef DOS +#include <conio.h> +#endif + +#include "externs.h" + +#include "direct.h" +#include "dungeon.h" +#include "invent.h" +#include "itemname.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" + +#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 MAC + unsigned char s[50]; + + int len = strlen(mesg); + + if (len > 255) + len = 255; + + s[0] = (Byte) len; + BlockMoveData(mesg, s + 1, len); + + DebugStr(s); + +#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 + +#if MAC +#pragma mark - +#endif + +// ======================================================================== +// Global Functions +// ======================================================================== + +//--------------------------------------------------------------- +// +// AssertFailed +// +//--------------------------------------------------------------- +#if DEBUG +void AssertFailed(const char *expr, const char *file, int line) +{ + char mesg[512]; + +#if MAC + sprintf(mesg, "ASSERT(%s) in %s at line %d failed.", expr, file, line); + +#else + 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); +#endif + + 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 debug_prompt_for_monster( void ) +{ + char specs[80]; + char obj_name[ ITEMNAME_SIZE ]; + char *ptr; + + 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); + + int mon = -1; + + for (int i = 0; i < NUM_MONSTERS; i++) + { + moname( i, true, DESC_PLAIN, obj_name ); + + ptr = strstr( strlwr(obj_name), strlwr(specs) ); + if (ptr != NULL) + { + mpr( obj_name ); + if (ptr == obj_name) + { + // we prefer prefixes over partial matches + mon = i; + break; + } + else + mon = i; + } + } + + return (mon); +} +#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 (int 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 ); +} // 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); + } +} // 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_BOOTS; + mitm[thing_created].plus2 = TBOOT_NAGA_BARDING; + } + else if (strstr( "centaur barding", specs )) + { + mitm[thing_created].sub_type = ARM_BOOTS; + mitm[thing_created].plus2 = TBOOT_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) + { + mpr( "No such item." ); + return; + } + + 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_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) + 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() |