summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/docs/crawl_manual.txt21
-rw-r--r--crawl-ref/source/acr.cc42
-rw-r--r--crawl-ref/source/command.cc7
-rw-r--r--crawl-ref/source/enum.h2
-rw-r--r--crawl-ref/source/item_use.cc347
-rw-r--r--crawl-ref/source/item_use.h8
6 files changed, 231 insertions, 196 deletions
diff --git a/crawl-ref/docs/crawl_manual.txt b/crawl-ref/docs/crawl_manual.txt
index f964d0d4ed..dd66420e93 100644
--- a/crawl-ref/docs/crawl_manual.txt
+++ b/crawl-ref/docs/crawl_manual.txt
@@ -542,7 +542,7 @@ you might find in the course of your adventures, how these are
displayed, and what commands there are to use them:
) weapons (use 'w'ield)
-( ammunition (use 't'hrow or 'f'ire)
+( ammunition (use 'f'ire)
[ armour (use 'W'ear and 'T'ake off)
% food (use 'e'at; also 'D'issect for corpses)
? scrolls (use 'r'ead)
@@ -663,16 +663,17 @@ thrown; other kinds require you to wield an appropriate device to
inflict worthwhile damage. Ammunition has only one "plus" value, which
affects both accuracy and damage.
-The 'f' command fires a piece of ammunition, chosen from lots suitable
-for your weapon and defaulting to your preferred lot (or "quiver"),
-typically the last lot you fired. Use the '(' command if you want to
-change your quiver without firing. Use the 't' command to throw
-anything.
+The 'f' command fires or throws a piece of ammunition, typically
+chosen from lots suitable for your weapon and defaulting to the last
+lot you fired (your "quiver").
-Regardless of their mnemonics, if you are using the right kind of hand
-weapon both 'f' and 't' will make you "shoot" the ammunition.
-Otherwise, you will "throw" it. At times it is also sensible to throw
-weapons like spears, daggers, or hand axes.
+The firing interface also allows you to manually select an item to
+throw; but it may not be very effective if you lack the correct
+launcher. At times it is sensible to throw weapons like spears,
+daggers, or hand axes.
+
+Use the '(' command if you want to change your quiver without
+firing.
The interface for shooting or throwing things is also used for zapping
wands and casting certain spells, and is described in detail in
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index fbbcff2fa5..4023971c9a 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -1902,47 +1902,8 @@ void process_command( command_type cmd )
wield_weapon(false);
break;
- case CMD_THROW:
- if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
- {
- canned_msg(MSG_PRESENT_FORM);
- break;
- }
- else if (you.attribute[ATTR_HELD])
- {
- mpr("You cannot throw anything while held in a net!");
- break;
- }
- if (Options.tutorial_left)
- Options.tut_throw_counter++;
- throw_anything();
- break;
-
case CMD_FIRE:
- if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
- {
- canned_msg(MSG_PRESENT_FORM);
- break;
- }
- else if (you.attribute[ATTR_HELD])
- {
- const item_def *weapon = you.weapon();
- if (!weapon || !is_range_weapon(*weapon))
- {
- mpr("You cannot throw anything while held in a net!");
- break;
- }
- else if (weapon->sub_type != WPN_BLOWGUN)
- {
- mprf("You cannot shoot with your %s while held in a net!",
- weapon->name(DESC_BASENAME).c_str());
- break;
- }
- // else shooting is possible
- }
- if (Options.tutorial_left)
- Options.tut_throw_counter++;
- shoot_thing();
+ fire_thing();
break;
case CMD_WEAR_ARMOUR:
@@ -3261,7 +3222,6 @@ command_type keycode_to_command( keycode_type key )
case 'q': return CMD_QUAFF;
case 'r': return CMD_READ;
case 's': return CMD_SEARCH;
- case 't': return CMD_THROW;
case 'v': return CMD_EXAMINE_OBJECT;
case 'w': return CMD_WIELD_WEAPON;
case 'x': return CMD_LOOK_AROUND;
diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc
index f6a42f6382..7f0f9e3f45 100644
--- a/crawl-ref/source/command.cc
+++ b/crawl-ref/source/command.cc
@@ -593,7 +593,9 @@ static const char *targeting_help_1 =
static const char *targeting_help_2 =
"<h>Firing or throwing a missile:\n"
"<w>Ctrl-P</w> : cycle to previous missile.\n"
- "<w>Ctrl-N</w> : cycle to next missile.\n";
+ "<w>Ctrl-N</w> : cycle to next missile.\n"
+ "<w>i</w> : choose from inventory.\n"
+;
// Add the contents of the file fp to the scroller menu m.
@@ -1523,8 +1525,7 @@ void list_commands(bool wizard, int hotkey, bool do_redraw_screen)
"<w>]</w> : show inventory of equipped items\n"
"<w>v</w> : View item description\n"
"<w>{</w> : inscribe item\n"
- "<w>t</w> : Throw/shoot an item\n"
- "<w>f</w> : Fire first available missile\n"
+ "<w>f</w> : Fire or throw an item\n"
"<w>e</w> : Eat food (but tries floor first)\n"
"<w>q</w> : Quaff a potion\n"
"<w>z</w> : Zap a wand\n"
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 52527c6979..4b003358e2 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -440,7 +440,7 @@ enum command_type
CMD_EVOKE,
CMD_WIELD_WEAPON,
CMD_WEAPON_SWAP,
- CMD_THROW,
+ CMD_THROW, // unused now
CMD_FIRE,
CMD_WEAR_ARMOUR,
CMD_REMOVE_ARMOUR,
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index 710940109a..0348abecfd 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -28,6 +28,7 @@
#include "AppHdr.h"
#include "item_use.h"
+#include <sstream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
@@ -82,6 +83,10 @@
static bool drink_fountain();
static bool enchant_armour();
+static int _fire_prompt_for_item(std::string& err);
+static bool _fire_validate_item(int selected, std::string& err);
+static std::string _fire_get_noitem_reason();
+
// Rather messy - we've gathered all the can't-wield logic from wield_weapon()
// here.
bool can_wield(const item_def *weapon, bool say_reason,
@@ -1194,53 +1199,6 @@ bool takeoff_armour(int item)
return true;
} // end takeoff_armour()
-void throw_anything( int slot )
-{
- struct bolt beam;
- int throw_slot;
-
- if (you.duration[DUR_BERSERKER])
- {
- canned_msg(MSG_TOO_BERSERK);
- return;
- }
- else if (inv_count() < 1)
- {
- canned_msg(MSG_NOTHING_CARRIED);
- return;
- }
-
- if (slot != -1)
- throw_slot = slot;
- else
- throw_slot = prompt_invent_item( "Throw which item? (* to show all)",
- MT_INVLIST,
- OBJ_MISSILES, true, true, true, 0, NULL,
- OPER_THROW );
- if (throw_slot == PROMPT_ABORT)
- {
- canned_msg( MSG_OK );
- return;
- }
-
- if (throw_slot == you.equip[EQ_WEAPON]
- && (item_cursed( you.inv[you.equip[EQ_WEAPON]] )))
- {
- mpr("That thing is stuck to your hand!");
- return;
- }
- else
- {
- if ( wearing_slot(throw_slot) )
- {
- mpr("You are wearing that object!");
- return;
- }
- }
-
- throw_it(beam, throw_slot);
-} // end throw_anything()
-
static bool fire_item_matches(const item_def &item, unsigned fire_type)
{
if (!is_valid_item(item))
@@ -1399,32 +1357,71 @@ int get_fire_item_index(int start_from, bool forward, bool check_quiver)
return (item);
}
-static void announce_ammo(int item)
-{
- mesclr();
- mprf("Firing%s: %s",
- get_fire_item_index((item + 1) % ENDOFPACK, true, false) != item?
- " (^N/^P - change)" : "",
- you.inv[item].name(DESC_INVENTORY_EQUIP).c_str());
-}
-
class fire_target_behaviour : public targeting_behaviour
{
public:
- fire_target_behaviour(int it) : item(it), need_prompt(false) { }
- command_type get_command(int key = -1);
- bool should_redraw();
- void announce_new_ammo(bool redraw = true);
+ fire_target_behaviour()
+ : item(ENDOFPACK), selected_from_inventory(false), need_prompt(false)
+ {
+ item = get_fire_item_index();
+ }
+
+ // targeting_behaviour API
+ virtual command_type get_command(int key = -1);
+ virtual bool should_redraw();
+
+ void message_ammo_prompt(const std::string* pre_text=0);
public:
int item;
+ bool selected_from_inventory;
bool need_prompt;
-
-private:
- void find_next_ammo();
- void find_prev_ammo();
};
+void fire_target_behaviour::message_ammo_prompt(const std::string* pre_text)
+{
+ bool no_other_items;
+ {
+ const int next_item = get_fire_item_index((item + 1) % ENDOFPACK, true, false);
+ no_other_items = (next_item == ENDOFPACK || next_item == item);
+ }
+
+ mesclr();
+
+ if (pre_text)
+ {
+ mpr(pre_text->c_str());
+ }
+
+ {
+ std::ostringstream msg;
+ if (item == ENDOFPACK) msg << "Firing ";
+ else {
+ const item_def& item_def = you.inv[item];
+ const launch_retval projected = is_launched(&you, you.weapon(), item_def);
+ if (projected == LRET_FUMBLED) msg << "Awkwardly throwing ";
+ else if (projected == LRET_LAUNCHED) msg << "Firing ";
+ else if (projected == LRET_THROWN) msg << "Throwing ";
+ else msg << "Buggy ";
+ }
+
+ msg << (no_other_items ? "(i - change)" : "(^n ^p i - change)")
+ << ": ";
+
+ if (item == ENDOFPACK) {
+ msg << "<red>" << _fire_get_noitem_reason() << "</red>";
+ } else {
+ const char* color = (selected_from_inventory ? "grey" : "w");
+ msg << "<" << color << ">"
+ << you.inv[item].name(DESC_INVENTORY_EQUIP)
+ << "</" << color << ">";
+ }
+
+ // XXX: use another channel that doesn't spam the message log?
+ formatted_message_history(msg.str(), MSGCH_PROMPT);
+ }
+}
+
bool fire_target_behaviour::should_redraw()
{
if (need_prompt)
@@ -1444,15 +1441,38 @@ command_type fire_target_behaviour::get_command(int key)
{
case '(':
case CONTROL('N'):
- find_next_ammo();
- break;
- case CONTROL('P'):
- find_prev_ammo();
+ case CONTROL('P'): {
+ const int direction = (key == CONTROL('N')) ? +1 : -1;
+ const int start = (item + ENDOFPACK + direction) % ENDOFPACK;
+ const int next = get_fire_item_index(start, (direction==1), false);
+ if (next != item && next != ENDOFPACK)
+ {
+ item = next;
+ selected_from_inventory = false;
+ }
+ // Do this stuff unconditionally to make the prompt redraw
+ message_ammo_prompt();
+ need_prompt = true;
break;
+ }
+
+ case 'i': {
+ std::string err;
+ const int selected = _fire_prompt_for_item(err);
+ if (selected != ENDOFPACK &&
+ _fire_validate_item(selected, err))
+ {
+ item = selected;
+ selected_from_inventory = true;
+ }
+ message_ammo_prompt( err.length() ? &err : NULL );
+ need_prompt = true;
+ return CMD_NO_CMD;
+ }
case '?':
show_targeting_help();
redraw_screen();
- announce_new_ammo(item);
+ message_ammo_prompt();
need_prompt = true;
return (CMD_NO_CMD);
}
@@ -1460,46 +1480,18 @@ command_type fire_target_behaviour::get_command(int key)
return targeting_behaviour::get_command(key);
}
-void fire_target_behaviour::announce_new_ammo(bool redraw)
+static bool _fire_choose_item_and_target(int& item, dist& target)
{
- announce_ammo(item);
- need_prompt = redraw;
-}
-
-void fire_target_behaviour::find_next_ammo()
-{
- const int start = (item == ENDOFPACK - 1)? 0 : item + 1;
- const int next = get_fire_item_index(start, true, false);
-
- // We should never get back ENDOFPACK.
- if (next != item)
- {
- item = next;
- announce_new_ammo();
- }
-}
-
-void fire_target_behaviour::find_prev_ammo()
-{
- const int start = (item == 0)? ENDOFPACK - 1 : item - 1;
- const int next = get_fire_item_index(start, false, false);
-
- // We should never get back ENDOFPACK.
- if (next != item)
- {
- item = next;
- announce_new_ammo();
- }
-}
-
-static bool choose_fire_target(dist &target, int &item)
-{
- announce_ammo(item);
- message_current_target();
-
- fire_target_behaviour beh(item);
+ fire_target_behaviour beh;
+ beh.message_ammo_prompt();
+ message_current_target(); // XXX: this stuff should be done by direction()
direction( target, DIR_NONE, TARG_ENEMY, false, true, NULL, &beh );
+ if (beh.item == ENDOFPACK)
+ {
+ canned_msg(MSG_OK);
+ return false;
+ }
if (!target.isValid)
{
if (target.isCancel)
@@ -1521,48 +1513,136 @@ static bool choose_fire_target(dist &target, int &item)
return (true);
}
-void shoot_thing(void)
+// Bring up an inventory screen and have user choose an item.
+// Returns an item slot, or ENDOFPACK on abort/failure
+// On failure, returns error text, if any.
+static int _fire_prompt_for_item(std::string& err)
{
- if (you.duration[DUR_BERSERKER])
+ if (inv_count() < 1)
{
- canned_msg(MSG_TOO_BERSERK);
- flush_input_buffer( FLUSH_ON_FAILURE );
- return;
+ // canned_msg(MSG_NOTHING_CARRIED); // Hmmm...
+ err = "You aren't carrying anything.";
+ return ENDOFPACK;
}
- int item = get_fire_item_index();
+ int slot = prompt_invent_item( "Fire/throw which item? (* to show all)",
+ MT_INVLIST,
+ OBJ_MISSILES, true, true, true, 0, NULL,
+ OPER_THROW );
+ if (slot == PROMPT_ABORT)
+ {
+ err = "Nothing selected.";
+ return ENDOFPACK;
+ }
+ return slot;
+}
+
+// Return false and err text if this item can't be fired.
+static bool _fire_validate_item(int slot, std::string& err)
+{
+ if (slot == you.equip[EQ_WEAPON]
+ && item_cursed(you.inv[slot]))
+ {
+ err = "That thing is stuck to your hand!";
+ return false;
+ }
+ else if ( wearing_slot(slot) )
+ {
+ err = "You are wearing that object!";
+ return false;
+ }
+ return true;
+}
- if (item == ENDOFPACK)
+// Return true if warning is given
+static bool _fire_warn_if_impossible()
+{
+ if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
{
- // Tell the user why we might have skipped their missile
- unwind_var<int> unwind_festart(Options.fire_items_start, 0);
- unwind_var<bool> unwind_inscription(Hack_Ignore_F_Inscription, true);
- const int skipped_item = get_fire_item_index();
- if (skipped_item == ENDOFPACK)
+ canned_msg(MSG_PRESENT_FORM);
+ return true;
+ }
+ if (you.attribute[ATTR_HELD])
+ {
+ const item_def *weapon = you.weapon();
+ if (!weapon || !is_range_weapon(*weapon))
{
- mpr("No suitable missiles.");
+ mpr("You cannot throw anything while held in a net!");
+ return true;
}
- else if (skipped_item < unwind_festart.original_value())
+ else if (weapon->sub_type != WPN_BLOWGUN)
{
- mprf("No suitable missiles (fire_items_start = '%c', "
- "ignoring item on '%c').",
- index_to_letter(unwind_festart.original_value()),
- index_to_letter(skipped_item));
+ mprf("You cannot shoot with your %s while held in a net!",
+ weapon->name(DESC_BASENAME).c_str());
+ return true;
}
- else
- {
- mprf("No suitable missiles (ignoring '=f'-inscribed item on '%c').",
+ // else shooting is possible
+ }
+ if (you.duration[DUR_BERSERKER])
+ {
+ canned_msg(MSG_TOO_BERSERK);
+ return true;
+ }
+ return false;
+}
+
+static std::string _fire_get_noitem_reason()
+{
+ const int cur_item = get_fire_item_index();
+ if (cur_item != ENDOFPACK)
+ {
+ // Shouldn't be calling this if there is a good default item!
+ return std::string("Buggy.");
+ }
+
+ // Tell the user why we might have skipped their missile
+ unwind_var<int> unwind_festart(Options.fire_items_start, 0);
+ unwind_var<bool> unwind_inscription(Hack_Ignore_F_Inscription, true);
+ const int skipped_item = get_fire_item_index();
+ char buf[200];
+ if (skipped_item == ENDOFPACK)
+ {
+ return std::string("No suitable missiles.");
+ }
+ else if (skipped_item < unwind_festart.original_value())
+ {
+ // no room for showing index_to_letter(skipped_item);
+ snprintf(buf, 200,
+ "Nothing suitable (fire_items_start = '%c').",
+ index_to_letter(unwind_festart.original_value()));
+ return std::string(buf);
+ }
+ else
+ {
+ snprintf(buf, 200,
+ "Nothing suitable (ignored '=f'-inscribed item on '%c').",
index_to_letter(skipped_item));
- }
- flush_input_buffer( FLUSH_ON_FAILURE );
+ return std::string(buf);
+ }
+}
+
+// if item == -1, prompt the user.
+void fire_thing(int item)
+{
+ if (_fire_warn_if_impossible())
+ {
+ flush_input_buffer( FLUSH_ON_FAILURE );
return;
}
+
+ if (Options.tutorial_left)
+ Options.tut_throw_counter++;
dist target;
- bolt beam;
- if (choose_fire_target(target, item)
- && check_warning_inscriptions(you.inv[item], OPER_FIRE))
+ if (item == -1)
+ {
+ if (! _fire_choose_item_and_target(item, target))
+ return;
+ }
+
+ if (check_warning_inscriptions(you.inv[item], OPER_FIRE))
{
+ bolt beam;
throw_it( beam, item, false, 0, &target );
}
}
@@ -4602,8 +4682,7 @@ void tile_use_item(int idx, InvAction act)
return;
case OBJ_MISSILES:
- if (check_warning_inscriptions(you.inv[idx], OPER_THROW))
- throw_anything(idx);
+ fire_thing(idx);
return;
case OBJ_ARMOUR:
diff --git a/crawl-ref/source/item_use.h b/crawl-ref/source/item_use.h
index cc1c5b8820..0efea498a3 100644
--- a/crawl-ref/source/item_use.h
+++ b/crawl-ref/source/item_use.h
@@ -104,15 +104,9 @@ bool remove_ring(int slot = -1, bool announce = false);
* *********************************************************************** */
int get_fire_item_index(int start_from = 0, bool forward = true,
bool check_quiver = true);
-void shoot_thing(void);
+void fire_thing(int item=-1);
-// last updated 12may2000 {dlb}
-/* ***********************************************************************
- * called from: acr
- * *********************************************************************** */
-void throw_anything(int slot = -1);
-
quiver_type get_quiver_type(void);
// last updated 12may2000 {dlb}