diff options
Diffstat (limited to 'src/cmd3.cc')
-rw-r--r-- | src/cmd3.cc | 2111 |
1 files changed, 2111 insertions, 0 deletions
diff --git a/src/cmd3.cc b/src/cmd3.cc new file mode 100644 index 00000000..06300982 --- /dev/null +++ b/src/cmd3.cc @@ -0,0 +1,2111 @@ +/* + * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke + * + * This software may be copied and distributed for educational, research, and + * not for profit purposes provided that this copyright and statement are + * included in all such copies. + */ +#include "cmd3.hpp" + +#include "artifact_type.hpp" +#include "cave.hpp" +#include "cave_type.hpp" +#include "cli_comm.hpp" +#include "files.hpp" +#include "gods.hpp" +#include "hook_drop_in.hpp" +#include "hook_wield_in.hpp" +#include "hooks.hpp" +#include "monster1.hpp" +#include "monster_race.hpp" +#include "object1.hpp" +#include "object2.hpp" +#include "object_kind.hpp" +#include "object_type.hpp" +#include "options.hpp" +#include "player_type.hpp" +#include "quark.hpp" +#include "squeltch.hpp" +#include "store.hpp" +#include "store_type.hpp" +#include "tables.hpp" +#include "town_type.hpp" +#include "util.hpp" +#include "util.h" +#include "variable.h" +#include "variable.hpp" +#include "xtra1.hpp" +#include "xtra2.hpp" + +#include <cassert> +#include <algorithm> +#include <memory> +#include <utility> + +/* + * Display p_ptr->inventory + */ +void do_cmd_inven(void) +{ + char out_val[160]; + + + /* Note that we are in "p_ptr->inventory" mode */ + command_wrk = FALSE; + + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Show the inventory */ + show_inven_full(); + + /* Show prompt */ + { + s32b total_weight = calc_total_weight(); + + strnfmt(out_val, 160, + "Inventory: carrying %ld.%ld pounds (%ld%% of capacity). Command: ", + total_weight / 10, total_weight % 10, + (total_weight * 100) / ((weight_limit()) / 2)); + } + + /* Get a command */ + prt(out_val, 0, 0); + + /* Get a new command */ + command_new = inkey(); + + /* Restore the screen */ + Term_load(); + character_icky = FALSE; + + + /* Process "Escape" */ + if (command_new == ESCAPE) + { + /* Reset stuff */ + command_new = 0; + } + + /* Process normal keys */ + else + { + /* Mega-Hack -- Don't disable keymaps for this key */ + request_command_inven_mode = TRUE; + } +} + + +/* + * Display equipment + */ +void do_cmd_equip(void) +{ + char out_val[160]; + + + /* Note that we are in "equipment" mode */ + command_wrk = TRUE; + + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Display the equipment */ + show_equip_full(); + + /* Show prompt */ + { + s32b total_weight = calc_total_weight(); + + /* Build a prompt */ + strnfmt(out_val, 160, + "Equipment: carrying %ld.%ld pounds (%ld%% of capacity). Command: ", + total_weight / 10, total_weight % 10, + (total_weight * 100) / ((weight_limit()) / 2)); + } + + /* Get a command */ + prt(out_val, 0, 0); + + /* Get a new command */ + command_new = inkey(); + + /* Restore the screen */ + Term_load(); + character_icky = FALSE; + + + /* Process "Escape" */ + if (command_new == ESCAPE) + { + /* Reset stuff */ + command_new = 0; + } + + /* Process normal keys */ + else + { + /* Mega-Hack -- Don't disable keymaps for this key */ + request_command_inven_mode = TRUE; + } +} + + +/* + * The "wearable" tester + */ +static bool item_tester_hook_wear(object_type const *o_ptr) +{ + u32b f1, f2, f3, f4, f5, esp; + int slot = wield_slot(o_ptr); + + + /* Extract the flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Only one ultimate at a time */ + if (f4 & TR4_ULTIMATE) + { + int i; + + for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) + { + object_type *q_ptr = &p_ptr->inventory[i]; + + /* Extract the flags */ + object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if (!q_ptr->k_idx) continue; + + if (f4 & TR4_ULTIMATE) return (FALSE); + } + } + + if ((slot < INVEN_WIELD) || ((p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_WIELD) && (p_ptr->melee_style != SKILL_MASTERY))) + return (FALSE); + + /* Check for a usable slot */ + if (slot >= INVEN_WIELD) return (TRUE); + + /* Assume not wearable */ + return (FALSE); +} + + +bool_ is_slot_ok(int slot) +{ + if ((slot >= INVEN_WIELD) && (slot < INVEN_TOTAL)) + { + return (TRUE); + } + else + { + return (FALSE); + } +} + + +/* + * Wield or wear a single item from the pack or floor + */ +void do_cmd_wield(void) +{ + int item, slot, num = 1; + + object_type forge; + + object_type *q_ptr; + + object_type *i_ptr; + + cptr act; + + char o_name[80]; + + u32b f1, f2, f3, f4, f5, esp; + + + /* Get an item */ + if (!get_item(&item, + "Wear/Wield which item? ", + "You have nothing you can wear or wield.", + (USE_INVEN | USE_FLOOR), + item_tester_hook_wear)) + { + return; + } + + /* Get the item */ + object_type *o_ptr = get_object(item); + + /* Check the slot */ + slot = wield_slot(o_ptr); + + /* Prevent wielding into a cursed slot */ + if (cursed_p(&p_ptr->inventory[slot])) + { + /* Describe it */ + object_desc(o_name, &p_ptr->inventory[slot], FALSE, 0); + + /* Message */ + msg_format("The %s you are %s appears to be cursed.", + o_name, describe_use(slot)); + + /* Cancel the command */ + return; + } + + if ((cursed_p(o_ptr)) && (wear_confirm) + && (object_known_p(o_ptr) || (o_ptr->ident & (IDENT_SENSE)))) + { + char dummy[512]; + + /* Describe it */ + object_desc(o_name, o_ptr, FALSE, 0); + + strnfmt(dummy, 512, "Really use the %s {cursed}? ", o_name); + if (!(get_check(dummy))) + return; + } + + /* Can we wield */ + { + struct hook_wield_in in = { o_ptr }; + if (process_hooks_new(HOOK_WIELD, &in, NULL)) + { + return; + } + } + + /* Extract the flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Two handed weapons can't be wielded with a shield */ + if ((is_slot_ok(slot - INVEN_WIELD + INVEN_ARM)) && + (f4 & TR4_MUST2H) && + (p_ptr->inventory[slot - INVEN_WIELD + INVEN_ARM].k_idx != 0)) + { + object_desc(o_name, o_ptr, FALSE, 0); + msg_format("You cannot wield your %s with a shield.", o_name); + return; + } + + if (is_slot_ok(slot - INVEN_ARM + INVEN_WIELD)) + { + i_ptr = &p_ptr->inventory[slot - INVEN_ARM + INVEN_WIELD]; + + /* Extract the flags */ + object_flags(i_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Prevent shield from being put on if wielding 2H */ + if ((f4 & TR4_MUST2H) && (i_ptr->k_idx) && + (p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_ARM)) + { + object_desc(o_name, o_ptr, FALSE, 0); + msg_format("You cannot wield your %s with a two-handed weapon.", o_name); + return; + } + + if ((p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_ARM) && + (f4 & TR4_COULD2H)) + { + if (!get_check("Are you sure you want to restrict your fighting? ")) + { + return; + } + } + } + + + /* Extract the flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if ((is_slot_ok(slot - INVEN_WIELD + INVEN_ARM)) && + (p_ptr->inventory[slot - INVEN_WIELD + INVEN_ARM].k_idx != 0) && + (f4 & TR4_COULD2H)) + { + if (!get_check("Are you sure you want to use this weapon with a shield?")) + { + return; + } + } + + /* Take a turn */ + energy_use = 100; + + /* Get local object */ + q_ptr = &forge; + + /* Obtain local object */ + object_copy(q_ptr, o_ptr); + + if (slot == INVEN_AMMO) num = o_ptr->number; + + /* Modify quantity */ + q_ptr->number = num; + + /* Decrease the item */ + inc_stack_size_ex(item, -num, OPTIMIZE, NO_DESCRIBE); + + /* Access the wield slot */ + o_ptr = &p_ptr->inventory[slot]; + + /* Take off existing item */ + if (slot != INVEN_AMMO) + { + if (o_ptr->k_idx) + { + /* Take off existing item */ + (void)inven_takeoff(slot, 255, FALSE); + } + } + else + { + if (o_ptr->k_idx) + { + if (!object_similar(o_ptr, q_ptr)) + { + /* Take off existing item */ + (void)inven_takeoff(slot, 255, FALSE); + } + else + { + q_ptr->number += o_ptr->number; + } + } + } + + + /* Wear the new stuff */ + object_copy(o_ptr, q_ptr); + + /* Increment the equip counter by hand */ + equip_cnt++; + + /* Where is the item now */ + if (slot == INVEN_WIELD) + { + act = "You are wielding"; + } + else if (( slot == INVEN_BOW ) && (o_ptr->tval == TV_INSTRUMENT)) + { + act = "You are holding"; + } + else if (slot == INVEN_BOW) + { + act = "You are shooting with"; + } + else if (slot == INVEN_LITE) + { + act = "Your light source is"; + } + else if (slot == INVEN_AMMO) + { + act = "In your quiver you have"; + } + else if (slot == INVEN_TOOL) + { + act = "You are using"; + } + else + { + act = "You are wearing"; + } + + /* Describe the result */ + object_desc(o_name, o_ptr, TRUE, 3); + + /* Message */ + msg_format("%s %s (%c).", act, o_name, index_to_label(slot)); + + /* Cursed! */ + if (cursed_p(o_ptr)) + { + /* Warn the player */ + msg_print("Oops! It feels deathly cold!"); + + /* Note the curse */ + o_ptr->ident |= (IDENT_SENSE); + o_ptr->sense = SENSE_CURSED; + } + + /* Take care of item sets */ + if (o_ptr->name1) + { + wield_set(o_ptr->name1, a_info[o_ptr->name1].set, FALSE); + } + + /* Recalculate bonuses */ + p_ptr->update |= (PU_BONUS); + + /* Recalculate torch */ + p_ptr->update |= (PU_TORCH); + + /* Recalculate hitpoint */ + p_ptr->update |= (PU_HP); + + /* Recalculate mana */ + p_ptr->update |= (PU_MANA | PU_SPELLS); + + /* Redraw monster hitpoint */ + p_ptr->redraw |= (PR_FRAME); + + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); +} + + + +/* + * Take off an item + */ +void do_cmd_takeoff(void) +{ + /* Get an item */ + int item; + if (!get_item(&item, + "Take off which item? ", + "You are not wearing anything to take off.", + (USE_EQUIP))) + { + return; + } + + /* Get the item */ + object_type *o_ptr = get_object(item); + + /* Item is cursed */ + if (cursed_p(o_ptr) && (!wizard)) + { + /* Oops */ + msg_print("Hmmm, it seems to be cursed."); + + /* Nope */ + return; + } + + + /* Take a partial turn */ + energy_use = 50; + + /* Take off the item */ + (void)inven_takeoff(item, 255, FALSE); + + /* Recalculate hitpoint */ + p_ptr->update |= (PU_HP); + + p_ptr->redraw |= (PR_FRAME); +} + + +/* + * Drop an item + */ +void do_cmd_drop(void) +{ + /* Get an item */ + int item; + if (!get_item(&item, + "Drop which item? ", + "You have nothing to drop.", + (USE_EQUIP | USE_INVEN))) + { + return; + } + + /* Get the item */ + object_type *o_ptr = get_object(item); + u32b f1, f2, f3, f4, f5, esp; + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Can we drop */ + struct hook_drop_in in = { item }; + if (process_hooks_new(HOOK_DROP, &in, NULL)) return; + + /* Hack -- Cannot remove cursed items */ + if (cursed_p(o_ptr)) + { + if (item >= INVEN_WIELD) + { + /* Oops */ + msg_print("Hmmm, it seems to be cursed."); + + /* Nope */ + return; + } + else + { + if (f4 & TR4_CURSE_NO_DROP) + { + /* Oops */ + msg_print("Hmmm, you seem to be unable to drop it."); + + /* Nope */ + return; + } + } + } + + /* See how many items */ + int amt = 1; + if (o_ptr->number > 1) + { + /* Get a quantity */ + amt = get_quantity(NULL, o_ptr->number); + + /* Allow user abort */ + if (amt <= 0) return; + } + + /* Take a partial turn */ + energy_use = 50; + + /* Drop (some of) the item */ + inven_drop(item, amt, p_ptr->py, p_ptr->px, FALSE); +} + + +/* + * Destroy an item + */ +void do_cmd_destroy(void) +{ + int old_number; + + bool_ force = FALSE; + + char o_name[80]; + + char out_val[160]; + + /* Hack -- force destruction */ + if (command_arg > 0) force = TRUE; + + + /* Get an item */ + int item; + if (!get_item(&item, + "Destroy which item? ", + "You have nothing to destroy.", + (USE_INVEN | USE_FLOOR | USE_AUTO))) + { + return; + } + + /* Get the item */ + object_type *o_ptr = get_object(item); + + /* See how many items */ + int amt = 1; + if (o_ptr->number > 1) + { + /* Get a quantity */ + amt = get_quantity(NULL, o_ptr->number); + + /* Allow user abort */ + if (amt <= 0) return; + } + + + /* Describe the object */ + old_number = o_ptr->number; + o_ptr->number = amt; + object_desc(o_name, o_ptr, TRUE, 3); + o_ptr->number = old_number; + + /* Verify unless quantity given */ + if (!force) + { + /* Make a verification */ + strnfmt(out_val, 160, "Really destroy %s? ", o_name); + if (!get_check(out_val)) return; + } + + /* Take no time, just like the automatizer */ + energy_use = 0; + + u32b f1, f2, f3, f4, f5, esp; + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if ((f4 & TR4_CURSE_NO_DROP) && cursed_p(o_ptr)) + { + /* Oops */ + msg_print("Hmmm, you seem to be unable to destroy it."); + + /* Nope */ + return; + } + + + /* Artifacts cannot be destroyed */ + if (artifact_p(o_ptr) || o_ptr->art_name) + { + byte feel = SENSE_SPECIAL; + + energy_use = 0; + + /* Message */ + msg_format("You cannot destroy %s.", o_name); + + /* Hack -- Handle icky artifacts */ + if (cursed_p(o_ptr)) feel = SENSE_TERRIBLE; + + /* Hack -- inscribe the artifact */ + o_ptr->sense = feel; + + /* We have "felt" it (again) */ + o_ptr->ident |= (IDENT_SENSE); + + /* Combine the pack */ + p_ptr->notice |= (PN_COMBINE); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + /* Done */ + return; + } + + /* Message */ + msg_format("You destroy %s.", o_name); + sound(SOUND_DESTITEM); + + /* Create an automatizer rule */ + if (automatizer_create) + { + automatizer_add_rule(o_ptr); + } + + /* + * Hack -- If rods or wand are destroyed, the total maximum timeout or + * charges of the stack needs to be reduced, unless all the items are + * being destroyed. -LM- + */ + if ((o_ptr->tval == TV_WAND) && (amt < o_ptr->number)) + { + o_ptr->pval -= o_ptr->pval * amt / o_ptr->number; + } + + /* Eru wont be happy */ + if (f3 & TR3_BLESSED) + inc_piety(GOD_ERU, -10 * k_info[o_ptr->k_idx].level); + + /* Eliminate the item */ + inc_stack_size(item, -amt); +} + + +/* + * Observe an item which has been *identify*-ed + */ +void do_cmd_observe(void) +{ + /* Get an item */ + int item; + if (!get_item(&item, + "Examine which item? ", + "You have nothing to examine.", + (USE_EQUIP | USE_INVEN | USE_FLOOR))) + { + return; + } + + /* Get the item */ + object_type *o_ptr = get_object(item); + + /* Description */ + char o_name[80]; + object_desc(o_name, o_ptr, TRUE, 3); + + /* Describe */ + cmsg_format(TERM_L_BLUE, "%s", o_name); + + /* Describe it fully */ + if (!object_out_desc(o_ptr, NULL, FALSE, TRUE)) msg_print("You see nothing special."); +} + + + +/* + * Remove the inscription from an object + * XXX Mention item (when done)? + */ +void do_cmd_uninscribe(void) +{ + /* Get an item */ + int item; + if (!get_item(&item, + "Un-inscribe which item? ", + "You have nothing to un-inscribe.", + (USE_EQUIP | USE_INVEN | USE_FLOOR))) + { + return; + } + + /* Get the item */ + object_type *o_ptr = get_object(item); + + /* Nothing to remove */ + if (!o_ptr->note) + { + msg_print("That item had no inscription to remove."); + return; + } + + /* Message */ + msg_print("Inscription removed."); + + /* Remove the incription */ + o_ptr->note = 0; + + /* Combine the pack */ + p_ptr->notice |= (PN_COMBINE); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); +} + + +/* + * Inscribe an object with a comment + */ +void do_cmd_inscribe(void) +{ + /* Get an item */ + int item; + if (!get_item(&item, + "Inscribe which item? ", + "You have nothing to inscribe.", + (USE_EQUIP | USE_INVEN | USE_FLOOR))) + { + return; + } + + /* Get the item */ + object_type *o_ptr = get_object(item); + + /* Describe the activity */ + char o_name[80]; + object_desc(o_name, o_ptr, TRUE, 3); + + /* Message */ + msg_format("Inscribing %s.", o_name); + msg_print(NULL); + + /* Start with nothing */ + char out_val[80]; + strcpy(out_val, ""); + + /* Use old inscription */ + if (o_ptr->note) + { + /* Start with the old inscription */ + strcpy(out_val, quark_str(o_ptr->note)); + } + + /* Get a new inscription (possibly empty) */ + if (get_string("Inscription: ", out_val, sizeof(out_val))) + { + /* Save the inscription */ + o_ptr->note = quark_add(out_val); + + /* Combine the pack */ + p_ptr->notice |= (PN_COMBINE); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + } +} + + + +/* + * An "item_tester_hook" for refilling lanterns + */ +static object_filter_t const &item_tester_refill_lantern() +{ + using namespace object_filter; + static auto instance = Or( + TVal(TV_FLASK), + And( + TVal(TV_LITE), + SVal(SV_LITE_LANTERN))); + return instance; +} + + +/* + * Refill the players lamp (from the pack or floor) + */ +static void do_cmd_refill_lamp(void) +{ + /* Get an item */ + int item; + if (!get_item(&item, + "Refill with which flask? ", + "You have no flasks of oil.", + (USE_INVEN | USE_FLOOR), + item_tester_refill_lantern())) + { + return; + } + + /* Get the item */ + object_type *o_ptr = get_object(item); + + /* Take a partial turn */ + energy_use = 50; + + /* Access the lantern */ + object_type *j_ptr = &p_ptr->inventory[INVEN_LITE]; + + /* Refuel */ + if (o_ptr->tval == TV_FLASK) + j_ptr->timeout += o_ptr->pval; + else + j_ptr->timeout += o_ptr->timeout; + + /* Message */ + msg_print("You fuel your lamp."); + + /* Comment */ + if (j_ptr->timeout >= FUEL_LAMP) + { + j_ptr->timeout = FUEL_LAMP; + msg_print("Your lamp is full."); + } + + /* Decrease the item stack */ + inc_stack_size(item, -1); + + /* Recalculate torch */ + p_ptr->update |= (PU_TORCH); +} + + +/* + * An "item_tester_hook" for refilling torches + */ +static object_filter_t const &item_tester_refill_torch() +{ + using namespace object_filter; + static auto instance = + And( + TVal(TV_LITE), + SVal(SV_LITE_TORCH)); + return instance; +} + + +/* + * Refuel the players torch (from the pack or floor) + */ +static void do_cmd_refill_torch(void) +{ + /* Get an item */ + int item; + if (!get_item(&item, + "Refuel with which torch? ", + "You have no extra torches.", + (USE_INVEN | USE_FLOOR), + item_tester_refill_torch())) + { + return; + } + + /* Get the item */ + object_type *o_ptr = get_object(item); + + /* Take a partial turn */ + energy_use = 50; + + /* Access the primary torch */ + object_type *j_ptr = &p_ptr->inventory[INVEN_LITE]; + + /* Refuel */ + j_ptr->timeout += o_ptr->timeout + 5; + + /* Message */ + msg_print("You combine the torches."); + + /* Over-fuel message */ + if (j_ptr->timeout >= FUEL_TORCH) + { + j_ptr->timeout = FUEL_TORCH; + msg_print("Your torch is fully fueled."); + } + + /* Refuel message */ + else + { + msg_print("Your torch glows more brightly."); + } + + /* Decrease the item stack */ + inc_stack_size(item, -1); + + /* Recalculate torch */ + p_ptr->update |= (PU_TORCH); +} + + +/* + * Refill the players lamp, or restock his torches + */ +void do_cmd_refill(void) +{ + object_type *o_ptr; + + u32b f1, f2, f3, f4, f5, esp; + + + /* Get the light */ + o_ptr = &p_ptr->inventory[INVEN_LITE]; + + /* It is nothing */ + if (o_ptr->tval != TV_LITE) + { + msg_print("You are not wielding a light."); + return; + } + + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if (f4 & TR4_FUEL_LITE) + { + /* It's a torch */ + if (o_ptr->sval == SV_LITE_TORCH || + o_ptr->sval == SV_LITE_TORCH_EVER) + { + do_cmd_refill_torch(); + } + + /* It's a lamp */ + else if (o_ptr->sval == SV_LITE_LANTERN || + o_ptr->sval == SV_LITE_DWARVEN || + o_ptr->sval == SV_LITE_FEANORIAN) + { + do_cmd_refill_lamp(); + } + } + + /* No torch to refill */ + else + { + msg_print("Your light cannot be refilled."); + } +} + + +/* + * Target command + */ +void do_cmd_target(void) +{ + /* Target set */ + if (target_set(TARGET_KILL)) + { + msg_print("Target Selected."); + } + + /* Target aborted */ + else + { + msg_print("Target Aborted."); + } +} + + + +/* + * Look command + */ +void do_cmd_look(void) +{ + /* Look around */ + if (target_set(TARGET_LOOK)) + { + msg_print("Target Selected."); + } +} + + + +/* + * Allow the player to examine other sectors on the map + */ +void do_cmd_locate(void) +{ + int dir, y1, x1, y2, x2; + int panel_hgt, panel_wid; + char tmp_val[80]; + char out_val[160]; + + + /* Retrieve size of the Angband window */ + Term_get_size(&panel_wid, &panel_hgt); + + /* Calcurate size of the dungeon map area */ + panel_hgt = (panel_hgt - (ROW_MAP + 1)) / 2; + panel_wid = (panel_wid - (COL_MAP + 1)) / 2; + + /* Start at current panel */ + y2 = y1 = panel_row_min; + x2 = x1 = panel_col_min; + + /* Show panels until done */ + while (1) + { + /* Describe the location */ + if ((y2 == y1) && (x2 == x1)) + { + tmp_val[0] = '\0'; + } + else + { + strnfmt(tmp_val, 80, "%s%s of", + ((y2 < y1) ? " North" : (y2 > y1) ? " South" : ""), + ((x2 < x1) ? " West" : (x2 > x1) ? " East" : "")); + } + + /* Prepare to ask which way to look */ + if ((panel_hgt == PANEL_HGT) && (panel_wid == PANEL_WID)) + { + /* Avoid surprising the standard screen users */ + strnfmt(out_val, 160, + "Map sector [%d,%d], which is%s your sector. Direction?", + y2 / panel_hgt, x2 / panel_wid, tmp_val); + } + + /* Big screen */ + else + { + /* Panels are measured by current map area size */ + strnfmt(out_val, 160, + "Map sector [%d(%02d),%d(%02d)], which is%s your sector. Direction?", + y2 / panel_hgt, y2 % panel_hgt, + x2 / panel_wid, x2 % panel_wid, tmp_val); + } + + /* Assume no direction */ + dir = 0; + + /* Get a direction */ + while (!dir) + { + char ch; + + /* Get a command (or cancel) */ + if (!get_com(out_val, &ch)) break; + + /* Extract the action (if any) */ + dir = get_keymap_dir(ch); + + /* Error */ + if (!dir) bell(); + } + + /* No direction */ + if (!dir) break; + + /* Apply the motion */ + if (change_panel(ddy[dir], ddx[dir])) + { + y2 = panel_row_min; + x2 = panel_col_min; + } + } + + /* Recenter the map around the player */ + verify_panel(); + + /* Update stuff */ + p_ptr->update |= (PU_MONSTERS); + + /* Redraw map */ + p_ptr->redraw |= (PR_MAP); + + /* Window stuff */ + p_ptr->window |= (PW_OVERHEAD); + + /* Handle stuff */ + handle_stuff(); +} + + + + + + +/* + * The table of "symbol info" -- each entry is a string of the form + * "X:desc" where "X" is the trigger, and "desc" is the "info". + */ +static cptr ident_info[] = +{ + " :A dark grid", + "!:A potion (or oil)", + "\":An amulet (or necklace)", + "#:A wall (or secret door)", + "$:Treasure (gold or gems)", + "%:A vein (magma or quartz)", + /* "&:unused", */ + "':An open door", + "(:Soft armor", + "):A shield", + "*:A vein with treasure", + "+:A closed door", + ",:Food (or mushroom patch)", + "-:A wand (or rod)", + ".:Floor", + "/:A polearm (Axe/Pike/etc)", + "0:An altar", + "1:Entrance to General Store", + "2:Entrance to Armory", + "3:Entrance to Weaponsmith", + "4:Entrance to Temple", + "5:Entrance to Alchemy shop", + "6:Entrance to Magic store", + "7:Entrance to Black Market", + "8:Entrance to your home", + "9:Entrance to Bookstore", + "::Rubble", + ";:A glyph of warding / explosive rune", + "<:An up staircase", + "=:A ring", + ">:A down staircase", + "?:A scroll", + "@:You", + "A:Angel", + "B:Bird", + "C:Canine", + "D:Ancient Dragon/Wyrm", + "E:Elemental", + "F:Dragon Fly", + "G:Ghost", + "H:Hybrid", + "I:Insect", + "J:Snake", + "K:Killer Beetle", + "L:Lich", + "M:Multi-Headed Reptile", + /* "N:unused", */ + "O:Ogre", + "P:Giant Humanoid", + "Q:Quylthulg (Pulsing Flesh Mound)", + "R:Reptile/Amphibian", + "S:Spider/Scorpion/Tick", + "T:Troll", + "U:Major Demon", + "V:Vampire", + "W:Wight/Wraith/etc", + "X:Xorn/Xaren/etc", + "Y:Yeti", + "Z:Zephyr Hound", + "[:Hard armor", + "\\:A hafted weapon (mace/whip/etc)", + "]:Misc. armor", + "^:A trap", + "_:A staff", + /* "`:unused", */ + "a:Ant", + "b:Bat", + "c:Centipede", + "d:Dragon", + "e:Floating Eye", + "f:Feline", + "g:Golem", + "h:Hobbit/Elf/Dwarf", + "i:Icky Thing", + "j:Jelly", + "k:Kobold", + "l:Louse", + "m:Mold", + "n:Naga", + "o:Orc", + "p:Person/Human", + "q:Quadruped", + "r:Rodent", + "s:Skeleton", + "t:Townsperson", + "u:Minor Demon", + "v:Vortex", + "w:Worm/Worm-Mass", + /* "x:unused", */ + "y:Yeek", + "z:Zombie/Mummy", + "{:A missile (arrow/bolt/shot)", + "|:An edged weapon (sword/dagger/etc)", + "}:A launcher (bow/crossbow/sling)", + "~:A tool (or miscellaneous item)", + NULL +}; + + +/** + * Sort by monster experience. + */ +static bool compare_monster_experience(int w1, int w2) +{ + /* Extract experience */ + s32b z1 = r_info[w1].mexp; + s32b z2 = r_info[w2].mexp; + + /* Compare experience */ + if (z1 < z2) return true; + if (z1 > z2) return false; + + /* Punt to index */ + return w1 < w2; +} + +/** + * Sort by monster level. + */ +static bool compare_monster_level(int w1, int w2) +{ + /* Extract levels */ + byte z1 = r_info[w1].level; + byte z2 = r_info[w2].level; + + /* Compare levels */ + if (z1 < z2) return true; + if (z1 > z2) return false; + + /* Punt to monster experience. */ + return compare_monster_experience(w1, w2); +} + +/** + * Sort by total number of kills + */ +static bool compare_total_kills(int w1, int w2) +{ + /* Extract total kills */ + s16b z1 = r_info[w1].r_tkills; + s16b z2 = r_info[w2].r_tkills; + + /* Compare total kills */ + if (z1 < z2) return true; + if (z1 > z2) return false; + + /* Punt to monster level. */ + return compare_monster_level(w1, w2); +} + +/* + * Sort by player kills + */ +static bool compare_player_kills(int w1, int w2) +{ + /* Extract player kills */ + s16b z1 = r_info[w1].r_pkills; + s16b z2 = r_info[w2].r_pkills; + + /* Compare player kills */ + if (z1 < z2) return true; + if (z1 > z2) return false; + + /* Punt to total number of kills. */ + return compare_total_kills(w1, w2); +} + + +/* + * Hack -- Display the "name" and "attr/chars" of a monster race + */ +static void roff_top(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + byte a1, a2; + + char c1, c2; + + + /* Access the chars */ + c1 = r_ptr->d_char; + c2 = r_ptr->x_char; + + /* Access the attrs */ + a1 = r_ptr->d_attr; + a2 = r_ptr->x_attr; + + + /* Clear the top line */ + Term_erase(0, 0, 255); + + /* Reset the cursor */ + Term_gotoxy(0, 0); + + /* A title (use "The" for non-uniques) */ + if (!(r_ptr->flags1 & (RF1_UNIQUE))) + { + Term_addstr( -1, TERM_WHITE, "The "); + } + + /* Dump the name */ + Term_addstr( -1, TERM_WHITE, r_ptr->name); + + /* Append the "standard" attr/char info */ + Term_addstr( -1, TERM_WHITE, " ('"); + Term_addch(a1, c1); + Term_addstr( -1, TERM_WHITE, "')"); + + /* Append the "optional" attr/char info */ + Term_addstr( -1, TERM_WHITE, "/('"); + Term_addch(a2, c2); + Term_addstr( -1, TERM_WHITE, "'):"); +} + + +/* + * Identify a character, allow recall of monsters + * + * Several "special" responses recall "multiple" monsters: + * ^A (all monsters) + * ^U (all unique monsters) + * ^N (all non-unique monsters) + * ^M (case insensitive name search) + * + * The responses may be sorted in several ways, see below. + * + * Note that the player ghosts are ignored. XXX XXX XXX + */ +void do_cmd_query_symbol(void) +{ + int i, r_idx; + + char sym, query; + + char buf[128]; + + + bool_ all = FALSE; + + bool_ uniq = FALSE; + + bool_ norm = FALSE; + + + bool_ name = FALSE; + + char temp[80] = ""; + + + bool_ recall = FALSE; + + bool (*sort_by)(int,int) = nullptr; + + /* Get a character, or abort */ + if (!get_com("Enter character to be identified, " + "or (Ctrl-A, Ctrl-U, Ctrl-N, Ctrl-M):", &sym)) return; + + /* Find that character info, and describe it */ + for (i = 0; ident_info[i]; ++i) + { + if (sym == ident_info[i][0]) break; + } + + /* Describe */ + if (sym == KTRL('A')) + { + all = TRUE; + strcpy(buf, "Full monster list."); + } + else if (sym == KTRL('U')) + { + all = uniq = TRUE; + strcpy(buf, "Unique monster list."); + } + else if (sym == KTRL('N')) + { + all = norm = TRUE; + strcpy(buf, "Non-unique monster list."); + } + else if (sym == KTRL('M')) + { + all = name = TRUE; + if (!get_string("Name:", temp, 70)) return; + strnfmt(buf, 128, "Monsters with a name \"%s\"", temp); + strlower(temp); + } + else if (ident_info[i]) + { + strnfmt(buf, 128, "%c - %s.", sym, ident_info[i] + 2); + } + else + { + strnfmt(buf, 128, "%c - %s.", sym, "Unknown Symbol"); + } + + /* Display the result */ + prt(buf, 0, 0); + + /* Collect matching monsters */ + std::vector<u16b> who; + for (i = 1; i < max_r_idx; i++) + { + monster_race *r_ptr = &r_info[i]; + + /* Nothing to recall */ + if (!cheat_know && !r_ptr->r_sights) continue; + + /* Require non-unique monsters if needed */ + if (norm && (r_ptr->flags1 & (RF1_UNIQUE))) continue; + + /* Require unique monsters if needed */ + if (uniq && !(r_ptr->flags1 & (RF1_UNIQUE))) continue; + + /* Require monsters with the name requested if needed */ + if (name) + { + char mon_name[80]; + + strcpy(mon_name, r_ptr->name); + strlower(mon_name); + + if (!strstr(mon_name, temp)) continue; + } + + /* Collect "appropriate" monsters */ + if (all || (r_ptr->d_char == sym)) { + who.push_back(i); + } + } + + /* Nothing to recall */ + if (who.empty()) + { + return; + } + + + /* Prompt XXX XXX XXX */ + put_str("Recall details? (k/p/y/n): ", 0, 40); + + /* Query */ + query = inkey(); + + /* Restore */ + prt(buf, 0, 0); + + + /* Sort by kills (and level) */ + if (query == 'k') + { + sort_by = compare_player_kills; + query = 'y'; + } + + /* Sort by level */ + if (query == 'p') + { + sort_by = compare_monster_level; + query = 'y'; + } + + /* Catch "escape" */ + if (query != 'y') + { + return; + } + + + /* Sort if needed */ + if (sort_by) + { + /* Sort the array */ + std::sort(std::begin(who), std::end(who), sort_by); + } + + + /* Start at the end */ + i = who.size() - 1; + + /* Scan the monster memory */ + while (1) + { + /* Extract a race */ + r_idx = who[i]; + + /* Hack -- Auto-recall */ + monster_race_track(r_idx, 0); + + /* Hack -- Handle stuff */ + handle_stuff(); + + /* Hack -- Begin the prompt */ + roff_top(r_idx); + + /* Hack -- Complete the prompt */ + Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC]"); + + /* Interact */ + while (1) + { + /* Recall */ + if (recall) + { + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Recall on screen */ + screen_roff(who[i], 0, 0); + + /* Hack -- Complete the prompt (again) */ + Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC]"); + } + + /* Command */ + query = inkey(); + + /* Unrecall */ + if (recall) + { + /* Restore */ + Term_load(); + character_icky = FALSE; + } + + /* Normal commands */ + if (query != 'r') break; + + /* Toggle recall */ + recall = !recall; + } + + /* Stop scanning */ + if (query == ESCAPE) break; + + /* Move to "prev" monster */ + if (query == '-') + { + i++; + assert(i >= 0); + if (static_cast<size_t>(i) == who.size()) + { + i = 0; + } + } + + /* Move to "next" monster */ + else + { + if (i-- == 0) + { + i = who.size() - 1; + } + } + } + + /* Re-display the identity */ + prt(buf, 0, 0); +} + + +/* + * research_mon + * -KMW- + */ +bool_ research_mon() +{ + int i, r_idx; + + char sym, query; + + char buf[128]; + + + s16b oldkills; + + byte oldwake; + + bool_ oldcheat; + + + bool_ all = FALSE; + + bool_ uniq = FALSE; + + bool_ norm = FALSE; + + bool_ notpicked; + + + bool_ recall = FALSE; + + monster_race *r2_ptr; + + /* Hack -- Remember "cheat_know" flag */ + oldcheat = cheat_know; + + + /* Get a character, or abort */ + if (!get_com("Enter character of monster: ", &sym)) return (TRUE); + + /* Find that character info, and describe it */ + for (i = 0; ident_info[i]; ++i) + { + if (sym == ident_info[i][0]) break; + } + + if (ident_info[i]) + { + strnfmt(buf, 128, "%c - %s.", sym, ident_info[i] + 2); + } + else + { + strnfmt(buf, 128, "%c - %s.", sym, "Unknown Symbol"); + } + + /* Display the result */ + prt(buf, 16, 10); + + + /* Collect matching monsters */ + std::vector<u16b> who; + for (i = 1; i < max_r_idx; i++) + { + monster_race *r_ptr = &r_info[i]; + + /* Hack -- Force "cheat_know" */ + cheat_know = TRUE; + + /* Nothing to recall */ + if (!cheat_know && !r_ptr->r_sights) continue; + + /* Require non-unique monsters if needed */ + if (norm && (r_ptr->flags1 & (RF1_UNIQUE))) continue; + + /* Require unique monsters if needed */ + if (uniq && !(r_ptr->flags1 & (RF1_UNIQUE))) continue; + + /* Collect "appropriate" monsters */ + if (all || (r_ptr->d_char == sym)) { + who.push_back(i); + } + } + + /* Nothing to recall */ + if (who.empty()) + { + /* Restore the "cheat_know" flag */ + cheat_know = oldcheat; + + return (TRUE); + } + + + query = 'y'; + + /* Sort by level */ + std::sort(std::begin(who), std::end(who), compare_monster_level); + + + /* Start at the end */ + i = who.size() - 1; + + notpicked = TRUE; + + /* Scan the monster memory */ + while (notpicked) + { + /* Extract a race */ + r_idx = who[i]; + + /* Hack -- Auto-recall */ + monster_race_track(r_idx, 0); + + /* Hack -- Handle stuff */ + handle_stuff(); + + /* Hack -- Begin the prompt */ + roff_top(r_idx); + + /* Hack -- Complete the prompt */ + Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC, space to continue]"); + + /* Interact */ + while (1) + { + /* Recall */ + if (recall) + { + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Recall on screen */ + r2_ptr = &r_info[r_idx]; + + oldkills = r2_ptr->r_tkills; + oldwake = r2_ptr->r_wake; + screen_roff(who[i], 0, 1); + r2_ptr->r_tkills = oldkills; + r2_ptr->r_wake = oldwake; + r2_ptr->r_sights = 1; + cheat_know = oldcheat; + notpicked = FALSE; + break; + + } + + /* Command */ + query = inkey(); + + /* Unrecall */ + if (recall) + { + /* Restore */ + Term_load(); + character_icky = FALSE; + } + + /* Normal commands */ + if (query != 'r') break; + + /* Toggle recall */ + recall = !recall; + } + + /* Stop scanning */ + if (query == ESCAPE) break; + + /* Move to "prev" monster */ + if (query == '-') + { + i++; + assert(i >= 0); + if (static_cast<size_t>(i) == who.size()) + { + i = 0; + } + } + + /* Move to "next" monster */ + else + { + if (i-- == 0) + { + i = who.size() - 1; + } + } + } + + + /* Re-display the identity */ + /* prt(buf, 5, 5);*/ + + /* Restore the "cheat_know" flag */ + cheat_know = oldcheat; + + return (notpicked); +} + + +/* + * Try to "sense" the grid's mana + */ +bool_ do_cmd_sense_grid_mana() +{ + int chance, i; + + + /* Take (a lot of) time */ + energy_use = 200; + + /* Base chance of success */ + chance = p_ptr->skill_dev; + + /* Confusion hurts skill */ + if (p_ptr->confused) chance = chance / 2; + + /* Hight mana grids are harder */ + chance = chance - (cave[p_ptr->py][p_ptr->px].mana / 10); + + /* Give everyone a (slight) chance */ + if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0)) + { + chance = USE_DEVICE; + } + + /* Roll for usage */ + if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE)) + { + if (flush_failure) flush(); + msg_print("You failed to sense the grid's mana."); + sound(SOUND_FAIL); + return FALSE; + } + + /* Try to give an "average" value */ + i = (101 - p_ptr->skill_dev) / 2; + i = (i < 1) ? 1 : (i > 50) ? 50 : i; + + if (wizard) + { + msg_format("Grid's mana: %d.", cave[p_ptr->py][p_ptr->px].mana); + msg_format("Average grid's mana: %d.", (cave[p_ptr->py][p_ptr->px].mana / i) * i); + } + else + { + msg_format("Average Area's mana: %d", (cave[p_ptr->py][p_ptr->px].mana / i) * i); + } + return TRUE; +} + + +/* + * Try to add a CLI action. + */ +void cli_add(cptr active, cptr trigger, cptr descr) +{ + s16b num; + cli_comm *cli_ptr, *old_ptr; + + /* Too many macros. */ + if (cli_total >= CLI_MAX) return; + + /* First try to read active as a number. */ + if (strtol(active, 0, 0)) + { + num = strtol(active, 0, 0); + } + /* Then try to read it as a character. */ + else if (strlen(active) == 1) + { + num = active[0]; + } + /* Give up if it doesn't work. */ + else + { + return; + } + + /* Dump the macro. */ + cli_ptr = cli_info + cli_total; + old_ptr = cli_info + cli_total - 1; + + /* + * Trim 's from the ends of a token. This turns '@' into @ and + * ''' into '. This may be the intent of the code in tokenize(), + * but I've left it for lack of comments to back me up. + */ + if (strchr(trigger, '\'')) + { + char temp[80], *t; + cptr s; + for (s = trigger, t = temp; ; s++, t++) + { + /* tokenize() causes each ' to be followed by another character, + * and then another '. Trim the 's here. */ + if (*s == '\'') + { + *t = *(++s); + s++; + } + else + { + *t = *s; + } + if (*t == '\0') break; + } + cli_ptr->comm = strdup(temp); + } + else + { + cli_ptr->comm = strdup(trigger); + } + + /* First try copying everything across. */ + cli_ptr->key = num; + cli_ptr->descrip = nullptr; + if (descr) { + cli_ptr->descrip = strdup(descr); + } + + /* Take description for the previous record if appropriate. */ + if ((cli_total > 0) && (old_ptr->key == cli_ptr->key) && (cli_ptr->descrip == 0)) + { + cli_ptr->descrip = old_ptr->descrip; + } + + /* Accept the macro. */ + if (cli_ptr->key && cli_ptr->comm && cli_ptr->descrip) cli_total++; +} + + + +/* + * Get a string using CLI completion. + */ +bool_ get_string_cli(cptr prompt, char *buf, int len) +{ + bool_ res; + + + /* Paranoia XXX XXX XXX */ + msg_print(NULL); + + /* Display prompt */ + prt(prompt, 0, 0); + + /* Ask the user for a string */ + askfor_aux_complete = TRUE; + res = askfor_aux(buf, len); + askfor_aux_complete = FALSE; + + /* Clear prompt */ + prt("", 0, 0); + + /* Result */ + return (res); +} + + +/* + * Do a command line command + * + * This is a wrapper around process command to provide a "reverse keymap" + * whereby a set of keypresses is mapped to one. + * + * This is useful because command_cmd is a s16b, and so allows each command a + * unique representation. + * + * See defines.h for a list of the codes used. + */ +void do_cmd_cli(void) +{ + char buff[80]; + + cli_comm *cli_ptr; + + /* Clear the input buffer */ + strcpy(buff, ""); + + /* Accept command */ + if (!get_string_cli("Command: ", buff, 30)) return; + + + /* Analyse the input */ + for (cli_ptr = cli_info; cli_ptr->comm; cli_ptr++) + { + if (!strcmp(buff, cli_ptr->comm)) + { + /* Process the command without keymaps or macros. */ + command_new = cli_ptr->key; + return; + } + } + + msg_format("No such command: %s", buff); +} + + +/* + * Display on-line help for the CLI commands + */ +void do_cmd_cli_help() +{ + int i, j; + + FILE *fff; + + char file_name[1024]; + + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + for (i = 0, j = -1; i < cli_total; i++) + { + if (j < i - 1) fprintf(fff, "/"); + fprintf(fff, "[[[[[G%s]", cli_info[i].comm); + if (cli_info[i].descrip != cli_info[i + 1].descrip) + { + fprintf(fff, " %s\n", cli_info[i].descrip); + j = i; + } + } + + /* Close the file */ + my_fclose(fff); + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + /* Display the file contents */ + show_file(file_name, "Command line help", 0, 0); + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; + + /* Remove the file */ + fd_kill(file_name); +} + + +/* + * Dump screen shot in HTML + */ +void do_cmd_html_dump() +{ + char tmp_val[81]; + bool_ html = TRUE; + term_win *save; + + /* Save the screen */ + save = Term_save_to(); + + if (wizard && get_check("WIZARD MODE: Do an help file dump?")) + html = FALSE; + + /* Ask for a file */ + if (html) + { + strcpy(tmp_val, "dummy.htm"); + if (!get_string("File(you can post it to http://angband.oook.cz/): ", tmp_val, 80)) + { + /* Now restore the screen to initial state */ + Term_load_from(save, TRUE); + Term_fresh(); + return; + } + } + else + { + strcpy(tmp_val, "dummy.txt"); + if (!get_string("File: ", tmp_val, 80)) + { + /* Now restore the screen to initial state */ + Term_load_from(save, TRUE); + Term_fresh(); + return; + } + } + + /* Now restore the screen to dump it */ + Term_load_from(save, TRUE); + + if (html) + html_screenshot(tmp_val); + else + help_file_screenshot(tmp_val); + + Term_erase(0, 0, 255); + msg_print("Dump saved."); + Term_fresh(); + fix_message(); +} |