#include "AppHdr.h"
#include "item_use.h"
#include <sstream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "externs.h"
#include "abl-show.h"
#include "artefact.h"
#include "beam.h"
#include "cio.h"
#include "cloud.h"
#include "colour.h"
#include "command.h"
#include "debug.h"
#include "decks.h"
#include "delay.h"
#include "describe.h"
#include "directn.h"
#include "effects.h"
#include "fight.h"
#include "food.h"
#include "goditem.h"
#include "invent.h"
#include "it_use2.h"
#include "it_use3.h"
#include "items.h"
#include "itemname.h"
#include "itemprop.h"
#include "los.h"
#include "macro.h"
#include "message.h"
#include "misc.h"
#include "monplace.h"
#include "monstuff.h"
#include "mstuff2.h"
#include "mon-util.h"
#include "notes.h"
#include "ouch.h"
#include "player.h"
#include "quiver.h"
#include "religion.h"
#include "shopping.h"
#include "skills.h"
#include "skills2.h"
#include "spells1.h"
#include "spells2.h"
#include "spells3.h"
#include "spells4.h"
#include "spl-book.h"
#include "spl-cast.h"
#include "spl-mis.h"
#include "spl-util.h"
#include "state.h"
#include "stuff.h"
#include "transfor.h"
#include "traps.h"
#include "tutorial.h"
#include "view.h"
#include "xom.h"
static bool _drink_fountain();
static bool _handle_enchant_weapon( enchant_stat_type which_stat,
bool quiet = false, int item_slot = -1 );
static bool _handle_enchant_armour( int item_slot = -1 );
static int _fire_prompt_for_item(std::string& err);
static bool _fire_validate_item(int selected, std::string& err);
bool can_wield(item_def *weapon, bool say_reason,
bool ignore_temporary_disability)
{
#define SAY(x) if (say_reason) { x; } else
if (!ignore_temporary_disability && you.duration[DUR_BERSERKER])
{
SAY(canned_msg(MSG_TOO_BERSERK));
return (false);
}
if (!can_equip( EQ_WEAPON, ignore_temporary_disability ))
{
SAY(mpr("You can't wield anything in your present form."));
return (false);
}
if (!ignore_temporary_disability
&& you.weapon()
&& you.weapon()->base_type == OBJ_WEAPONS
&& item_cursed(*you.weapon()))
{
SAY(mpr("You can't unwield your weapon to draw a new one!"));
return (false);
}
if (!weapon)
return (true);
for (int i = EQ_CLOAK; i <= EQ_AMULET; i++)
{
if (you.equip[i] != -1 && &you.inv[you.equip[i]] == weapon)
{
SAY(mpr("You are wearing that object!"));
return (false);
}
}
if (weapon->base_type != OBJ_WEAPONS)
{
if (!ignore_temporary_disability && is_shield_incompatible(*weapon))
{
SAY(mpr("You can't wield that with a shield."));
return (false);
}
else
return (true);
}
if (you.body_size(PSIZE_TORSO) < SIZE_LARGE && item_mass(*weapon) >= 300)
{
SAY(mpr("That's too large and heavy for you to wield."));
return (false);
}
if (you.body_size(PSIZE_BODY) < SIZE_MEDIUM
&& !check_weapon_wieldable_size(*weapon, you.body_size(PSIZE_BODY)))
{
SAY(mpr("That's too large for you to wield."));
return (false);
}
if (you.is_unholy() && is_holy_item(*weapon))
{
if (say_reason)
{
mpr("This weapon is holy and will not allow you to wield it.");
if (!is_artefact(*weapon) && !is_blessed_blade(*weapon)
&& !item_type_known(*weapon))
{
set_ident_flags(*weapon, ISFLAG_KNOW_TYPE);
if (in_inventory(*weapon))
mpr(weapon->name(DESC_INVENTORY_EQUIP).c_str());
}
}
return (false);
}
if (!ignore_temporary_disability && is_shield_incompatible(*weapon))
{
SAY(mpr("You can't wield that with a shield."));
return (false);
}
return (true);
#undef SAY
}
static bool _valid_weapon_swap(const item_def &item)
{
if (item.base_type == OBJ_WEAPONS || item.base_type == OBJ_STAVES)
return (true);
if (is_deck(item) || item.base_type == OBJ_MISCELLANY
&& item.sub_type == MISC_LANTERN_OF_SHADOWS)
{
return (true);
}
if (item.base_type == OBJ_MISSILES)
{
if (item.sub_type == MI_STONE)
return (player_knows_spell(SPELL_SANDBLAST));
if (item.sub_type == MI_ARROW)
return (player_knows_spell(SPELL_STICKS_TO_SNAKES));
return (false);
}
if (item.base_type == OBJ_CORPSES)
{
return (item.sub_type == CORPSE_SKELETON
&& player_knows_spell(SPELL_BONE_SHARDS));
}
if (!player_knows_spell(SPELL_SUBLIMATION_OF_BLOOD))
return (false);
if (item.base_type == OBJ_FOOD)
return (item.sub_type == FOOD_CHUNK);
if (item.base_type == OBJ_POTIONS && item_type_known(item))
{
return (item.sub_type == POT_BLOOD
|| item.sub_type == POT_BLOOD_COAGULATED);
}
return (false);
}
bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages,
bool force, bool show_unwield_msg)
{
if (inv_count() < 1)
{
canned_msg(MSG_NOTHING_CARRIED);
return (false);
}
if (!can_wield(NULL, true))
return (false);
int item_slot = 0;
if (auto_wield)
{
if (item_slot == you.equip[EQ_WEAPON])
item_slot = 1;
if (slot != -1) item_slot = slot;
}
const bool good_swap = (item_slot == PROMPT_GOT_SPECIAL
|| _valid_weapon_swap(you.inv[item_slot]));
if (item_slot != PROMPT_GOT_SPECIAL
&& (!auto_wield || !is_valid_item(you.inv[item_slot]) || !good_swap))
{
if (!auto_wield)
{
item_slot = prompt_invent_item(
"Wield which item (- for none, * to show all)?",
MT_INVLIST, OSEL_WIELD,
true, true, true, '-', -1, NULL, OPER_WIELD);
}
else
item_slot = PROMPT_GOT_SPECIAL;
}
if (prompt_failed(item_slot))
return (false);
else if (item_slot == you.equip[EQ_WEAPON])
{
mpr("You are already wielding that!");
return (true);
}
if (you.duration[DUR_SURE_BLADE])
{
mpr("The bond with your blade fades away.");
you.duration[DUR_SURE_BLADE] = 0;
}
you.received_weapon_warning = false;
if (item_slot == PROMPT_GOT_SPECIAL) {
if (you.equip[EQ_WEAPON] != -1)
{
item_def& wpn = *you.weapon();
if (has_warning_inscription(wpn, OPER_WIELD))
{
std::string prompt = "Really unwield ";
prompt += wpn.name(DESC_INVENTORY);
prompt += '?';
if (!yesno(prompt.c_str(), false, 'n'))
return (false);
}
if (!unwield_item(show_weff_messages))
return (false);
if (show_unwield_msg)
canned_msg(MSG_EMPTY_HANDED);
you.turn_is_over = true;
you.time_taken *= 3;
you.time_taken /= 10;
}
else
mpr("You are already empty-handed.");
return (true);
}
item_def& new_wpn(you.inv[item_slot]);
if (!can_wield(&new_wpn, true))
return (false);
if (auto_wield && !force
&& !check_warning_inscriptions(new_wpn, OPER_WIELD))
{
return (false);
}
if (!safe_to_remove_or_wear(new_wpn, false))
return (false);
if (you.equip[EQ_WEAPON] != -1 && !unwield_item(show_weff_messages))
return (false);
const unsigned int old_talents = your_talents(false).size();
you.equip[EQ_WEAPON] = item_slot;
wield_effects(item_slot, show_weff_messages);
mpr(new_wpn.name(DESC_INVENTORY_EQUIP).c_str());
if (show_weff_messages)
wield_warning();
if (Options.tutorial_left && your_talents(false).size() > old_talents)
learned_something_new(TUT_NEW_ABILITY_ITEM);
you.time_taken /= 2;
you.wield_change = true;
you.m_quiver->on_weapon_changed();
you.turn_is_over = true;
return (true);
}
static const char *shield_base_name(const item_def *shield)
{
return (shield->sub_type == ARM_BUCKLER? "buckler"
: "shield");
}
static const char *shield_impact_degree(int impact)
{
return (impact > 160 ? "severely " :
impact > 130 ? "significantly " :
impact > 110 ? ""
: NULL);
}
static void warn_rod_shield_interference(const item_def &)
{
const int leakage = rod_shield_leakage();
const char *leak_degree = shield_impact_degree(leakage);
if (leak_degree)
{
mprf(MSGCH_WARN,
"Your %s %sreduces the effectiveness of your rod.",
shield_base_name(player_shield()),
leak_degree);
}
}
static void warn_launcher_shield_slowdown(const item_def &launcher)
{
const int slowspeed =
launcher_final_speed(launcher, player_shield()) * player_speed() / 100;
const int normspeed =
launcher_final_speed(launcher, NULL) * player_speed() / 100;
if (slowspeed > normspeed)
{
const char *slow_degree =
shield_impact_degree(slowspeed * 100 / normspeed);
if (slow_degree)
{
mprf(MSGCH_WARN,
"Your %s %sslows your rate of fire.",
shield_base_name(player_shield()),
slow_degree);
}
}
}
void warn_shield_penalties()
{
if (!player_shield())
return;
const item_def *weapon = player_weapon();
if (!weapon)
return;
if (item_is_rod(*weapon))
warn_rod_shield_interference(*weapon);
else if (is_range_weapon(*weapon))
warn_launcher_shield_slowdown(*weapon);
else if (weapon->base_type == OBJ_WEAPONS
&& weapon_skill(*weapon) == SK_STAVES)
{
mprf(MSGCH_WARN, "Your %s severely limits your weapon's effectiveness.",
shield_base_name(player_shield()));
}
}
void wield_effects(int item_wield_2, bool showMsgs)
{
unsigned char special = 0;
item_def &item = you.inv[item_wield_2];
const bool artefact = is_artefact(item);
const bool known_cursed = item_known_cursed(item);
switch (item.base_type)
{
case OBJ_MISCELLANY:
{
if (item.sub_type == MISC_LANTERN_OF_SHADOWS)
{
if (showMsgs)
mpr("The area is filled with flickering shadows.");
you.current_vision -= 2;
set_los_radius(you.current_vision);
you.attribute[ATTR_SHADOWS] = 1;
}
else if (item.sub_type == MISC_HORN_OF_GERYON)
set_ident_flags(item, ISFLAG_IDENT_MASK);
break;
}
case OBJ_STAVES:
{
if (item.sub_type == STAFF_POWER)
{
calc_mp();
set_ident_type(item, ID_KNOWN_TYPE);
set_ident_flags(item, ISFLAG_EQ_WEAPON_MASK);
mpr("You feel your mana capacity increase.");
}
else if (!maybe_identify_staff(item))
{
set_ident_flags(item, ISFLAG_KNOW_CURSE);
}
break;
}
case OBJ_WEAPONS:
{
if (showMsgs)
{
if (is_holy_item(item) && you.religion == GOD_YREDELEMNUL)
mpr("You really shouldn't be using a holy item like this.");
else if (is_evil_item(item) && is_good_god(you.religion))
mpr("You really shouldn't be using an evil item like this.");
else if (is_chaotic_item(item) && you.religion == GOD_ZIN)
mpr("You really shouldn't be using a chaotic item like this.");
}
if (artefact)
use_artefact(item_wield_2, &showMsgs);
const bool was_known = item_type_known(item);
bool known_recurser = false;
set_ident_flags(item, ISFLAG_EQ_WEAPON_MASK);
special = item.special;
if (artefact)
{
special = artefact_wpn_property(item, ARTP_BRAND);
if (!was_known)
{
item.flags |= ISFLAG_NOTED_ID;
if (Options.autoinscribe_artefacts)
add_autoinscription(item, artefact_auto_inscription(item));
take_note(Note(NOTE_ID_ITEM, 0, 0, item.name(DESC_NOCAP_A).c_str(),
origin_desc(item).c_str()));
}
else
known_recurser = artefact_known_wpn_property(item,
ARTP_CURSED);
}
if (special != SPWPN_NORMAL)
{
if (showMsgs)
{
switch (special)
{
case SPWPN_FLAMING:
mpr("It bursts into flame!");
break;
case SPWPN_FREEZING:
mpr("It glows with a cold blue light!");
break;
case SPWPN_HOLY_WRATH:
mpr("It softly glows with a divine radiance!");
break;
case SPWPN_ELECTROCUTION:
if (!silenced(you.pos()))
{
mpr("You hear the crackle of electricity.",
MSGCH_SOUND);
}
else
mpr("You see sparks fly.");
break;
case SPWPN_ORC_SLAYING:
mpr((you.species == SP_HILL_ORC)
? "You feel a sudden desire to commit suicide."
: "You feel a sudden desire to kill orcs!");
break;
case SPWPN_DRAGON_SLAYING:
mpr(player_genus(GENPC_DRACONIAN)
|| you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON
? "You feel a sudden desire to commit suicide."
: "You feel a sudden desire to slay dragons!");
break;
case SPWPN_VENOM:
mpr("It begins to drip with poison!");
break;
case SPWPN_PROTECTION:
mpr("You feel protected!");
break;
case SPWPN_DRAINING:
mpr("You sense an unholy aura.");
break;
case SPWPN_SPEED:
mpr("Your hands tingle!");
break;
case SPWPN_FLAME:
mpr("It bursts into flame!");
break;
case SPWPN_FROST:
mpr("It is covered in frost.");
break;
case SPWPN_VAMPIRICISM:
if (you.is_undead != US_UNDEAD)
mpr("You feel a strange hunger.");
else
mpr("You feel strangely empty.");
break;
case SPWPN_RETURNING:
mpr("It wiggles slightly.");
break;
case SPWPN_PAIN:
mpr("A searing pain shoots up your arm!");
break;
case SPWPN_CHAOS:
mpr("It is briefly surrounded by a scintillating aura "
"of random colours.");
break;
case SPWPN_PENETRATION:
mprf("Your %s briefly pass through it before you manage "
"to get a firm grip on it.",
you.hand_name(true).c_str());
break;
case SPWPN_REAPING:
mpr("It is briefly surrounded by shifting shadows.");
break;
default:
break;
}
}
switch (special)
{
case SPWPN_PROTECTION:
you.redraw_armour_class = true;
break;
case SPWPN_DISTORTION:
mpr("Space warps around you for a moment!");
if (!was_known)
{
god_type god;
if (origin_is_god_gift(item, &god) && god == GOD_XOM)
xom_is_stimulated(255);
else
xom_is_stimulated(128);
}
break;
default:
break;
}
}
if (item_cursed(item))
{
mpr("It sticks to your hand!");
int amusement = 16;
if (!known_cursed && !known_recurser)
{
amusement *= 2;
god_type god;
if (origin_is_god_gift(item, &god) && god == GOD_XOM)
amusement *= 2;
}
const int wpn_skill = weapon_skill(item.base_type, item.sub_type);
if (wpn_skill != SK_FIGHTING && you.skills[wpn_skill] == 0)
amusement *= 2;
xom_is_stimulated(amusement);
}
break;
}
default:
break;
}
if (showMsgs)
warn_shield_penalties();
you.attribute[ATTR_WEAPON_SWAP_INTERRUPTED] = 0;
}
bool armour_prompt(const std::string & mesg, int *index, operation_types oper)
{
ASSERT(index != NULL);
bool succeeded = false;
int slot;
if (inv_count() < 1)
canned_msg(MSG_NOTHING_CARRIED);
else if (you.duration[DUR_BERSERKER])
canned_msg(MSG_TOO_BERSERK);
else
{
int selector = OBJ_ARMOUR;
if (oper == OPER_TAKEOFF && !Options.equip_unequip)
selector = OSEL_WORN_ARMOUR;
slot = prompt_invent_item( mesg.c_str(), MT_INVLIST, selector,
true, true, true, 0, -1, NULL,
oper );
if (!prompt_failed(slot))
{
*index = slot;
succeeded = true;
}
}
return (succeeded);
}
static bool cloak_is_being_removed( void )
{
if (current_delay_action() != DELAY_ARMOUR_OFF)
return (false);
if (you.delay_queue.front().parm1 != you.equip[ EQ_CLOAK ])
return (false);
return (true);
}
void wear_armour(int slot) {
if (player_in_bat_form())
{
mpr("You can't wear anything in your present form.");
return;
}
int armour_wear_2 = 0;
if (slot != -1)
armour_wear_2 = slot;
else if (!armour_prompt("Wear which item?", &armour_wear_2, OPER_WEAR))
return;
if (safe_to_remove_or_wear( you.inv[armour_wear_2],
wearing_slot(armour_wear_2) ))
{
do_wear_armour( armour_wear_2, false );
}
}
static int armour_equip_delay(const item_def &item)
{
int delay = property( item, PARM_AC );
if (is_shield( item ))
delay = delay / 2 + 1;
if (delay < 1)
delay = 1;
return (delay);
}
bool can_wear_armour(const item_def &item, bool verbose, bool ignore_temporary)
{
const object_class_type base_type = item.base_type;
if (base_type != OBJ_ARMOUR)
{
if (verbose)
mpr("You can't wear that.");
return (false);
}
bool can_wear = true;
const int sub_type = item.sub_type;
const equipment_type slot = get_armour_slot(item);
if (sub_type == ARM_NAGA_BARDING)
can_wear = (you.species == SP_NAGA);
else if (sub_type == ARM_CENTAUR_BARDING)
can_wear = (you.species == SP_CENTAUR);
else
{
can_wear = (fit_armour_size(item,
you.body_size(PSIZE_TORSO, ignore_temporary)) == 0);
}
if (!can_wear)
{
if (verbose)
mpr("You can't wear that!");
return (false);
}
if (sub_type == ARM_GLOVES)
{
if (you.has_claws(false) >= 3)
{
if (verbose)
mpr( "You can't wear gloves with your huge claws!" );
return (false);
}
}
if (sub_type == ARM_BOOTS)
{
if (player_mutation_level(MUT_HOOVES))
{
if (verbose)
mpr("You can't wear boots with hooves!");
return (false);
}
if (player_mutation_level(MUT_TALONS))
{
if (verbose)
mpr("Boots don't fit your talons!");
return (false);
}
if (you.species == SP_NAGA)
{
if (verbose)
mpr("You can't wear that!");
return (false);
}
if (!ignore_temporary && player_is_swimming()
&& you.species == SP_MERFOLK)
{
if (verbose)
mpr("You don't currently have feet!");
return (false);
}
}
if (you.species == SP_NAGA && sub_type == ARM_NAGA_BARDING
&& (ignore_temporary || !player_is_shapechanged()))
{
return (true);
}
else if (you.species == SP_CENTAUR
&& sub_type == ARM_CENTAUR_BARDING
&& (ignore_temporary || !player_is_shapechanged()))
{
return (true);
}
else if (slot == EQ_HELMET)
{
if (!is_hard_helmet( item ))
return (true);
if (player_mutation_level(MUT_HORNS))
{
if (verbose)
mpr("You can't wear that with your horns!");
return (false);
}
if (player_mutation_level(MUT_BEAK))
{
if (verbose)
mpr("You can't wear that with your beak!");
return (false);
}
}
if (!can_equip( slot, ignore_temporary ))
{
if (verbose)
mpr("You can't wear that in your present form.");
return (false);
}
if (you.body_size(PSIZE_TORSO, ignore_temporary) >= SIZE_LARGE
|| player_genus(GENPC_DRACONIAN))
{
if (sub_type >= ARM_LEATHER_ARMOUR
&& sub_type <= ARM_PLATE_MAIL
|| sub_type == ARM_GLOVES
|| sub_type == ARM_BOOTS
|| sub_type == ARM_BUCKLER
|| sub_type == ARM_CRYSTAL_PLATE_MAIL
|| is_hard_helmet(item))
{
if (verbose)
mpr("This armour doesn't fit on your body.");
return (false);
}
}
if (you.body_size(PSIZE_TORSO, ignore_temporary) <= SIZE_LITTLE)
{
if ((sub_type >= ARM_LEATHER_ARMOUR
&& sub_type <= ARM_PLATE_MAIL)
|| sub_type == ARM_GLOVES
|| sub_type == ARM_BOOTS
|| sub_type == ARM_SHIELD
|| sub_type == ARM_LARGE_SHIELD
|| sub_type == ARM_CRYSTAL_PLATE_MAIL
|| is_hard_helmet(item))
{
if (verbose)
mpr("This armour doesn't fit on your body.");
return (false);
}
}
return (true);
}
bool do_wear_armour(int item, bool quiet)
{
const item_def &invitem = you.inv[item];
if (!is_valid_item(invitem))
{
if (!quiet)
mpr("You don't have any such object.");
return (false);
}
if (!can_wear_armour(invitem, !quiet, false))
return (false);
const equipment_type slot = get_armour_slot(invitem);
if (item == you.equip[EQ_WEAPON])
{
if (!quiet)
mpr("You are wielding that object!");
return (false);
}
if (wearing_slot(item))
{
if (Options.equip_unequip)
return (!takeoff_armour(item));
else
{
mpr("You're already wearing that object!");
return (false);
}
}
if (you.weapon()
&& is_shield(invitem)
&& is_shield_incompatible(*you.weapon(), &invitem))
{
if (!quiet)
mpr("You'd need three hands to do that!");
return (false);
}
bool removed_cloak = false;
int cloak = -1;
if (slot == EQ_BODY_ARMOUR
&& you.equip[EQ_CLOAK] != -1 && !cloak_is_being_removed())
{
if (you.equip[EQ_BODY_ARMOUR] != -1 &&
item_cursed(you.inv[you.equip[EQ_BODY_ARMOUR]]))
{
if (!quiet)
{
mprf("%s is stuck to your body!",
you.inv[you.equip[EQ_BODY_ARMOUR]].name(DESC_CAP_YOUR)
.c_str());
}
return (false);
}
if (!item_cursed(you.inv[you.equip[EQ_CLOAK]]))
{
cloak = you.equip[EQ_CLOAK];
if (!takeoff_armour(you.equip[EQ_CLOAK]))
return (false);
removed_cloak = true;
}
else
{
if (!quiet)
mpr("Your cloak prevents you from wearing the armour.");
return (false);
}
}
if ((slot == EQ_CLOAK
|| slot == EQ_HELMET
|| slot == EQ_GLOVES
|| slot == EQ_BOOTS
|| slot == EQ_SHIELD
|| slot == EQ_BODY_ARMOUR)
&& you.equip[slot] != -1)
{
if (!takeoff_armour(you.equip[slot]))
return (false);
}
if (!safe_to_remove_or_wear(invitem, false))
return (false);
you.turn_is_over = true;
const int delay = armour_equip_delay(invitem);
if (delay)
start_delay(DELAY_ARMOUR_ON, delay, item);
if (removed_cloak)
start_delay(DELAY_ARMOUR_ON, 1, cloak);
return (true);
}
bool takeoff_armour(int item)
{
const item_def& invitem = you.inv[item];
if (invitem.base_type != OBJ_ARMOUR)
{
mpr("You aren't wearing that!");
return (false);
}
if (you.duration[DUR_BERSERKER])
{
canned_msg(MSG_TOO_BERSERK);
return (false);
}
const equipment_type slot = get_armour_slot(invitem);
if (!you_tran_can_wear(invitem) && invitem.link == you.equip[slot])
{
mprf("%s is melded into your body!",
invitem.name(DESC_CAP_YOUR).c_str());
return (false);
}
if (!wearing_slot(item))
{
if (Options.equip_unequip)
return do_wear_armour(item, true);
else
{
mpr("You aren't wearing that object!");
return (false);
}
}
if (item_cursed(invitem))
{
mprf("%s is stuck to your body!", invitem.name(DESC_CAP_YOUR).c_str());
return (false);
}
if (!safe_to_remove_or_wear(invitem, true))
return (false);
bool removed_cloak = false;
int cloak = -1;
if (slot == EQ_BODY_ARMOUR)
{
if (you.equip[EQ_CLOAK] != -1 && !cloak_is_being_removed())
{
if (!item_cursed(you.inv[you.equip[EQ_CLOAK]]))
{
cloak = you.equip[ EQ_CLOAK ];
if (!takeoff_armour(you.equip[EQ_CLOAK]))
return (false);
removed_cloak = true;
}
else
{
mpr("Your cloak prevents you from removing the armour.");
return (false);
}
}
}
else
{
switch (slot)
{
case EQ_SHIELD:
case EQ_CLOAK:
case EQ_HELMET:
case EQ_GLOVES:
case EQ_BOOTS:
if (item != you.equip[slot])
{
mpr("You aren't wearing that!");
return (false);
}
break;
default:
break;
}
}
you.turn_is_over = true;
const int delay = armour_equip_delay(invitem);
start_delay(DELAY_ARMOUR_OFF, delay, item);
if (removed_cloak)
start_delay(DELAY_ARMOUR_ON, 1, cloak);
return (true);
}
bool item_is_quivered(const item_def &item)
{
return (item.link == you.m_quiver->get_fire_item());
}
int get_next_fire_item(int current, int direction)
{
std::vector<int> fire_order;
you.m_quiver->get_fire_order(fire_order);
if (fire_order.size() == 0)
return -1;
if (current == -1)
return fire_order[0];
for (unsigned i = 0; i < fire_order.size(); i++)
{
if (fire_order[i] == current)
{
unsigned next =
(i + direction + fire_order.size()) % fire_order.size();
return fire_order[next];
}
}
return fire_order[0];
}
class fire_target_behaviour : public targetting_behaviour
{
public:
fire_target_behaviour()
: m_slot(-1), selected_from_inventory(false), need_prompt(false),
chosen_ammo(false)
{
m_slot = you.m_quiver->get_fire_item(&m_noitem_reason);
}
virtual command_type get_command(int key = -1);
virtual bool should_redraw();
virtual void mark_ammo_nonchosen();
void message_ammo_prompt(const std::string* pre_text = 0);
public:
int m_slot;
std::string m_noitem_reason;
bool selected_from_inventory;
bool need_prompt;
bool chosen_ammo;
};
void fire_target_behaviour::message_ammo_prompt(const std::string* pre_text)
{
const int next_item = get_next_fire_item(m_slot, +1);
bool no_other_items = (next_item == -1 || next_item == m_slot);
mesclr();
if (pre_text)
mpr(pre_text->c_str());
std::ostringstream msg;
if (m_slot == -1)
msg << "Firing ";
else
{
const item_def& item_def = you.inv[m_slot];
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 - inventory)"
: "(i - inventory. (,) - cycle)")
<< ": ";
if (m_slot == -1)
{
msg << "<red>" << m_noitem_reason << "</red>";
}
else
{
const char* colour = (selected_from_inventory ? "lightgrey" : "w");
msg << "<" << colour << ">"
<< you.inv[m_slot].name(DESC_INVENTORY_EQUIP)
<< "</" << colour << ">";
}
formatted_message_history(tagged_string_substr(msg.str(),
0, crawl_view.msgsz.x),
MSGCH_PROMPT);
}
bool fire_target_behaviour::should_redraw()
{
if (need_prompt)
{
need_prompt = false;
return (true);
}
return (false);
}
void fire_target_behaviour::mark_ammo_nonchosen()
{
chosen_ammo = false;
}
command_type fire_target_behaviour::get_command(int key)
{
if (key == -1)
key = get_key();
switch (key)
{
case '(':
case CONTROL('N'):
case ')':
case CONTROL('P'):
{
const int direction = (key == CONTROL('P') || key == ')') ? -1 : +1;
const int next = get_next_fire_item(m_slot, direction);
if (next != m_slot && next != -1)
{
m_slot = next;
selected_from_inventory = false;
chosen_ammo = true;
}
message_ammo_prompt();
need_prompt = true;
return (CMD_NO_CMD);
}
case 'i':
{
std::string err;
const int selected = _fire_prompt_for_item(err);
if (selected >= 0 && _fire_validate_item(selected, err))
{
m_slot = selected;
selected_from_inventory = true;
chosen_ammo = true;
}
message_ammo_prompt( err.length() ? &err : NULL );
need_prompt = true;
return (CMD_NO_CMD);
}
case '?':
show_targetting_help();
redraw_screen();
message_ammo_prompt();
need_prompt = true;
return (CMD_NO_CMD);
}
return targetting_behaviour::get_command(key);
}
static bool _fire_choose_item_and_target(int& slot, dist& target,
bool teleport = false)
{
fire_target_behaviour beh;
const bool was_chosen = (slot != -1);
if (was_chosen)
{
std::string warn;
if (!_fire_validate_item(slot, warn))
{
mpr(warn.c_str());
return (false);
}
beh.m_slot = slot;
}
beh.message_ammo_prompt();
message_current_target();
direction( target, DIR_NONE, TARG_HOSTILE, -1, false, !teleport, true, false,
NULL, &beh );
if (beh.m_slot == -1)
{
canned_msg(MSG_OK);
return (false);
}
if (!target.isValid)
{
if (target.isCancel)
canned_msg(MSG_OK);
return (false);
}
you.m_quiver->on_item_fired(you.inv[beh.m_slot], beh.chosen_ammo);
you.redraw_quiver = true;
slot = beh.m_slot;
return (true);
}
static int _fire_prompt_for_item(std::string& err)
{
if (inv_count() < 1)
{
err = "You aren't carrying anything.";
return -1;
}
int slot = prompt_invent_item( "Fire/throw which item? (* to show all)",
MT_INVLIST,
OSEL_THROWABLE, true, true, true, 0, -1,
NULL, OPER_FIRE );
if (slot == PROMPT_ABORT || slot == PROMPT_NOTHING)
{
err = "Nothing selected.";
return -1;
}
return slot;
}
static bool _fire_validate_item(int slot, std::string &err)
{
if (slot == you.equip[EQ_WEAPON]
&& you.inv[slot].base_type == OBJ_WEAPONS
&& item_cursed(you.inv[slot]))
{
err = "That weapon is stuck to your hand!";
return (false);
}
else if (wearing_slot(slot))
{
err = "You are wearing that object!";
return (false);
}
return (true);
}
static bool _fire_warn_if_impossible()
{
const int trans = you.attribute[ATTR_TRANSFORMATION];
if (trans == TRAN_SPIDER
|| trans == TRAN_BLADE_HANDS
|| trans == TRAN_ICE_BEAST
|| trans == TRAN_DRAGON
|| trans == TRAN_BAT)
{
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("You cannot throw anything while held in a net!");
return (true);
}
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());
return (true);
}
}
if (you.duration[DUR_BERSERKER])
{
canned_msg(MSG_TOO_BERSERK);
return (true);
}
return (false);
}
int get_ammo_to_shoot(int item, dist &target, bool teleport)
{
if (_fire_warn_if_impossible())
{
flush_input_buffer( FLUSH_ON_FAILURE );
return (-1);
}
if (!_fire_choose_item_and_target(item, target, teleport))
return (-1);
std::string warn;
if (!_fire_validate_item(item, warn))
{
mpr(warn.c_str());
return (-1);
}
return (item);
}
void fire_thing(int item)
{
dist target;
item = get_ammo_to_shoot(item, target);
if (item == -1)
return;
if (check_warning_inscriptions(you.inv[item], OPER_FIRE))
{
bolt beam;
throw_it( beam, item, false, 0, &target );
}
}
void throw_item_no_quiver()
{
if (_fire_warn_if_impossible())
{
flush_input_buffer( FLUSH_ON_FAILURE );
return;
}
if (inv_count() < 1)
{
canned_msg(MSG_NOTHING_CARRIED);
return;
}
std::string warn;
int slot = _fire_prompt_for_item(warn);
if (slot == -1)
{
canned_msg(MSG_OK);
return;
}
if (!_fire_validate_item(slot, warn))
{
mpr(warn.c_str());
return;
}
bolt beam;
throw_it( beam, slot );
}
int launcher_shield_slowdown(const item_def &launcher, const item_def *shield)
{
int speed_adjust = 100;
if (!shield)
return (speed_adjust);
const int shield_type = shield->sub_type;
hands_reqd_type hands = hands_reqd(launcher, you.body_size());
switch (hands)
{
default:
case HANDS_ONE:
case HANDS_HALF:
speed_adjust = shield_type == ARM_BUCKLER ? 105 :
shield_type == ARM_SHIELD ? 125 :
150;
break;
case HANDS_TWO:
speed_adjust = shield_type == ARM_BUCKLER ? 125 :
shield_type == ARM_SHIELD ? 150 :
200;
break;
}
if (speed_adjust > 100)
speed_adjust -= ((speed_adjust - 100) * 5 / 10)
* you.skills[SK_SHIELDS] / 27;
return (speed_adjust);
}
int launcher_final_speed(const item_def &launcher, const item_def *shield)
{
const int str_weight = weapon_str_weight( launcher );
const int dex_weight = 10 - str_weight;
const skill_type launcher_skill = range_skill( launcher );
const int shoot_skill = you.skills[launcher_skill];
const int bow_brand = get_weapon_brand( launcher );
int speed_base = 10 * property( launcher, PWPN_SPEED );
int speed_min = 70;
int speed_stat = str_weight * you.strength + dex_weight * you.dex;
if (launcher_skill == SK_BOWS)
speed_min = 60;
if (shield)
{
const int speed_adjust = launcher_shield_slowdown(launcher, shield);
speed_base = speed_base * speed_adjust / 100;
speed_min = speed_min * speed_adjust / 100;
}
if (you.attribute[ATTR_HELD])
{
int speed_adjust = 105; speed_adjust -= ((speed_adjust - 100) * 5 / 10)
* you.skills[SK_THROWING] / 27;
speed_base = speed_base * speed_adjust / 100;
speed_min = speed_min * speed_adjust / 100;
}
int speed = speed_base - 4 * shoot_skill * speed_stat / 250;
if (speed < speed_min)
speed = speed_min;
if (bow_brand == SPWPN_SPEED)
{
speed = 2 * speed / 3;
}
return (speed);
}
bool elemental_missile_beam(int launcher_brand, int ammo_brand)
{
if (launcher_brand == SPWPN_CHAOS || ammo_brand == SPMSL_CHAOS)
return (true);
int element = (launcher_brand == SPWPN_FROST
+ ammo_brand == SPMSL_FROST
- launcher_brand == SPWPN_FLAME
- ammo_brand == SPMSL_FLAME);
return (element != 0);
}
static int _item_to_skill_level(const item_def *item)
{
skill_type type = range_skill(*item);
if (type == SK_DARTS || type == SK_SLINGS)
return (you.skills[type] + you.skills[SK_THROWING]);
return (2 * you.skills[type]);
}
static bool _poison_hit_victim(bolt& beam, actor* victim, int dmg, int corpse)
{
if (!victim->alive() || victim->res_poison() > 0)
return (false);
if (beam.is_tracer)
return (true);
int levels = 0;
actor* agent = beam.agent();
if (agent->atype() == ACT_MONSTER)
{
if (dmg > 0 || beam.ench_power == AUTOMATIC_HIT
&& x_chance_in_y(90 - 3 * victim->armour_class(), 100))
{
levels = 1 + random2(3);
}
}
else
{
if (beam.ench_power == AUTOMATIC_HIT
&& x_chance_in_y(90 - 3 * victim->armour_class(), 100))
{
levels = 2;
}
else if (random2(dmg) > random2(victim->armour_class()))
levels = 1;
int num_success = 0;
if (YOU_KILL(beam.thrower))
{
const int skill_level = _item_to_skill_level(beam.item);
if (x_chance_in_y(skill_level + 25, 50))
num_success++;
if (x_chance_in_y(skill_level, 50))
num_success++;
}
else
num_success = 1;
if (num_success == 0)
return (false);
else
{
if (num_success == 2)
levels++;
}
}
if (levels <= 0)
return (false);
victim->poison(agent, levels);
return (true);
}
static bool _item_penetrates_victim(const bolt &beam, const actor *victim,
int &used)
{
if (beam.aimed_at_feet)
return (false);
used = 0;
if (!beam.is_tracer && you.can_see(victim))
{
mprf("The %s passes through %s!", beam.name.c_str(),
victim->name(DESC_NOCAP_THE).c_str());
}
return (true);
}
static bool _silver_damages_victim(bolt &beam, actor* victim, int &dmg,
std::string &dmg_msg)
{
bool shifter;
if (victim->atype() == ACT_MONSTER)
{
monsters* mon = dynamic_cast<monsters*>(victim);
shifter = mons_is_shapeshifter(mon);
}
else
shifter = transform_changed_physiology();
if (shifter || victim->is_unholy())
{
dmg *= 2;
if (!beam.is_tracer && you.can_see(victim))
dmg_msg = "The silver sears " + victim->name(DESC_NOCAP_THE) + "!";
}
return (false);
}
static bool _reaping_hit_victim(bolt& beam, actor* victim, int dmg, int corpse)
{
if (beam.is_tracer || victim->alive() || corpse == -1
|| corpse == NON_ITEM)
{
return (false);
}
actor* agent = beam.agent();
beh_type beh;
unsigned short hitting;
if (agent->atype() == ACT_PLAYER)
{
hitting = MHITYOU;
beh = BEH_FRIENDLY;
}
else
{
monsters *mon = dynamic_cast<monsters*>(agent);
beh = SAME_ATTITUDE(mon);
behaviour_event(mon, ME_EVAL);
hitting = mon->foe;
}
int midx = NON_MONSTER;
if (animate_remains(victim->pos(), CORPSE_BODY, beh, hitting, GOD_NO_GOD,
true, true, true, &midx) <= 0)
{
return (false);
}
monsters *zombie = &menv[midx];
if (you.can_see(victim))
mprf("%s turns into a zombie!", victim->name(DESC_CAP_THE).c_str());
else if (you.can_see(zombie))
mprf("%s appears out of thin air!", zombie->name(DESC_CAP_THE).c_str());
player_angers_monster(zombie);
return (true);
}
static bool _dispersal_hit_victim(bolt& beam, actor* victim, int dmg,
int corpse)
{
const actor* agent = beam.agent();
if (!victim->alive() || victim == agent)
return (false);
if (beam.is_tracer)
return (true);
const bool was_seen = you.can_see(victim);
const bool no_sanct = victim->kill_alignment() == KC_OTHER;
coord_def pos, pos2;
int tries = 0;
do
{
if (!random_near_space(victim->pos(), pos, false, true, false,
no_sanct))
{
return (false);
}
}
while (!victim->is_habitable(pos) && tries++ < 100);
if (!victim->is_habitable(pos))
return (false);
tries = 0;
do
random_near_space(victim->pos(), pos2, false, true, false, no_sanct);
while (!victim->is_habitable(pos2) && tries++ < 100);
if (!victim->is_habitable(pos2))
return (false);
const coord_def from = agent->pos();
if (in_bounds(pos2)
&& grid_distance(pos2, from) > grid_distance(pos, from))
{
pos = pos2;
}
if (pos == victim->pos())
return (false);
const coord_def oldpos = victim->pos();
if (victim->atype() == ACT_PLAYER)
{
place_cloud(CLOUD_PURP_SMOKE, you.pos(), 1 + random2(3), KC_YOU);
victim->moveto(pos);
mpr("You blink!");
}
else
{
monsters *mon = dynamic_cast<monsters*>(victim);
if (!(mon->flags & MF_WAS_IN_VIEW))
mon->seen_context = "thin air";
mon->move_to_pos(pos);
place_cloud(CLOUD_PURP_SMOKE, oldpos, 1 + random2(3),
victim->kill_alignment());
mon->apply_location_effects(oldpos);
mon->check_redraw(oldpos);
const bool seen = you.can_see(mon);
const std::string name = mon->name(DESC_CAP_THE);
if (was_seen && seen)
mprf("%s blinks!", name.c_str());
else if (was_seen && !seen)
mprf("%s vanishes!", name.c_str());
}
return (true);
}
bool setup_missile_beam(const actor *agent, bolt &beam, item_def &item,
std::string &ammo_name, bool &returning)
{
dungeon_char_type zapsym = DCHAR_SPACE;
switch (item.base_type)
{
case OBJ_WEAPONS: zapsym = DCHAR_FIRED_WEAPON; break;
case OBJ_MISSILES: zapsym = DCHAR_FIRED_MISSILE; break;
case OBJ_ARMOUR: zapsym = DCHAR_FIRED_ARMOUR; break;
case OBJ_WANDS: zapsym = DCHAR_FIRED_STICK; break;
case OBJ_FOOD: zapsym = DCHAR_FIRED_CHUNK; break;
case OBJ_UNKNOWN_I: zapsym = DCHAR_FIRED_BURST; break;
case OBJ_SCROLLS: zapsym = DCHAR_FIRED_SCROLL; break;
case OBJ_JEWELLERY: zapsym = DCHAR_FIRED_TRINKET; break;
case OBJ_POTIONS: zapsym = DCHAR_FIRED_FLASK; break;
case OBJ_UNKNOWN_II: zapsym = DCHAR_FIRED_ZAP; break;
case OBJ_BOOKS: zapsym = DCHAR_FIRED_BOOK; break;
case OBJ_STAVES: zapsym = DCHAR_FIRED_STICK; break;
default: break;
}
beam.type = dchar_glyph(zapsym);
returning = get_weapon_brand(item) == SPWPN_RETURNING
|| get_ammo_brand(item) == SPMSL_RETURNING;
if (agent->atype() == ACT_PLAYER)
{
beam.attitude = ATT_FRIENDLY;
beam.beam_source = NON_MONSTER;
beam.smart_monster = true;
beam.thrower = KILL_YOU_MISSILE;
}
else
{
const monsters *mon = dynamic_cast<const monsters*>(agent);
beam.attitude = mons_attitude(mon);
beam.beam_source = mon->mindex();
beam.smart_monster = (mons_intel(mon) >= I_NORMAL);
beam.thrower = KILL_MON_MISSILE;
}
beam.item = &item;
beam.source = agent->pos();
beam.colour = item.colour;
beam.flavour = BEAM_MISSILE;
beam.is_beam = false;
beam.aux_source.clear();
beam.can_see_invis = agent->can_see_invisible();
item_def *launcher = const_cast<actor*>(agent)->weapon(0);
if (launcher && !item.launched_by(*launcher))
launcher = NULL;
const unrandart_entry* entry = launcher && is_unrandom_artefact(*launcher)
? get_unrand_entry(launcher->special) : NULL;
if (entry && entry->fight_func.launch)
{
setup_missile_type sm =
entry->fight_func.launch(launcher, &beam, &ammo_name,
&returning);
switch(sm)
{
case SM_CONTINUE:
break;
case SM_FINISHED:
return (false);
case SM_CANCEL:
return (true);
}
}
int bow_brand = SPWPN_NORMAL;
if (launcher != NULL)
bow_brand = get_weapon_brand(*launcher);
int ammo_brand = get_ammo_brand(item);
bool poisoned = ammo_brand == SPMSL_POISONED;
if (bow_brand == SPWPN_VENOM && ammo_brand != SPMSL_CURARE)
{
if (ammo_brand == SPMSL_NORMAL)
item.special = SPMSL_POISONED;
poisoned = true;
}
const bool exploding = ammo_brand == SPMSL_EXPLODING;
const bool penetrating = (!exploding
&& (bow_brand == SPWPN_PENETRATION
|| ammo_brand == SPMSL_PENETRATION));
const bool silver = (ammo_brand == SPMSL_SILVER);
const bool disperses = (ammo_brand == SPMSL_DISPERSAL);
const bool reaping = (bow_brand == SPWPN_REAPING
|| ammo_brand == SPMSL_REAPING);
ASSERT(!exploding || !is_artefact(item));
beam.name = item.name(DESC_PLAIN, false, false, false);
item_def ammo = item;
if (bow_brand == SPWPN_CHAOS || ammo_brand == SPMSL_CHAOS)
{
poisoned = false;
if (item.special == SPWPN_VENOM || item.special == SPMSL_CURARE)
item.special = SPMSL_NORMAL;
beam.effect_known = false;
beam.flavour = BEAM_CHAOS;
beam.name = "chaos";
beam.colour = ETC_RANDOM;
ammo.special = SPMSL_CHAOS;
}
else if ((bow_brand == SPWPN_FLAME || ammo_brand == SPMSL_FLAME)
&& ammo_brand != SPMSL_FROST && bow_brand != SPWPN_FROST)
{
beam.flavour = BEAM_FIRE;
beam.name = "flame";
beam.colour = RED;
ammo.special = SPMSL_FLAME;
}
else if ((bow_brand == SPWPN_FROST || ammo_brand == SPMSL_FROST)
&& ammo_brand != SPMSL_FLAME && bow_brand != SPWPN_FLAME)
{
beam.flavour = BEAM_COLD;
beam.name = "frost";
beam.colour = WHITE;
ammo.special = SPMSL_FROST;
}
ASSERT(beam.flavour == BEAM_MISSILE || !is_artefact(item));
ammo_name = ammo.name(DESC_PLAIN);
if (silver)
beam.damage_funcs.push_back(_silver_damages_victim);
if (poisoned)
beam.hit_funcs.push_back(_poison_hit_victim);
if (penetrating)
beam.range_funcs.push_back(_item_penetrates_victim);
if (reaping)
beam.hit_funcs.push_back(_reaping_hit_victim);
if (disperses)
beam.hit_funcs.push_back(_dispersal_hit_victim);
if (reaping && ammo.special != SPMSL_REAPING)
{
beam.name = "shadowy " + beam.name;
ammo_name = "shadowy " + ammo_name;
}
if (disperses && ammo.special != SPMSL_DISPERSAL)
{
beam.name = "dispersing " + beam.name;
ammo_name = "dispersing " + ammo_name;
}
if (poisoned && ammo.special != SPMSL_POISONED)
{
beam.name = "poison " + beam.name;
ammo_name = "poisoned " + ammo_name;
}
if (penetrating && ammo.special != SPMSL_PENETRATION)
{
beam.name = "penetrating " + beam.name;
ammo_name = "penetrating " + ammo_name;
}
if (silver && ammo.special != SPMSL_SILVER)
{
beam.name = "silvery " + beam.name;
ammo_name = "silvery " + ammo_name;
}
if (exploding)
{
bolt *expl = new bolt(beam);
expl->is_explosion = true;
expl->damage = dice_def(2, 5);
expl->ex_size = 1;
if (beam.flavour == BEAM_MISSILE)
{
expl->flavour = BEAM_FRAG;
expl->name += " fragments";
const std::string short_name =
ammo.name(DESC_PLAIN, false, false, false, false,
ISFLAG_IDENT_MASK | ISFLAG_COSMETIC_MASK
| ISFLAG_RACIAL_MASK);
expl->name = replace_all(expl->name, ammo.name(DESC_PLAIN),
short_name);
}
expl->name = "explosion of " + expl->name;
beam.special_explosion = expl;
}
if (exploding && ammo.special != SPMSL_EXPLODING)
{
beam.name = "exploding " + beam.name;
ammo_name = "exploding " + ammo_name;
}
if (beam.flavour != BEAM_MISSILE)
{
returning = false;
beam.type = dchar_glyph(DCHAR_FIRED_BOLT);
beam.name = "bolt of " + beam.name;
}
if (!is_artefact(item))
ammo_name = article_a(ammo_name, true);
else
ammo_name = "the " + ammo_name;
return (false);
}
static bool determines_ammo_brand(int bow_brand, int ammo_brand)
{
if (bow_brand == SPWPN_FLAME && ammo_brand == SPMSL_FLAME)
return (false);
if (bow_brand == SPWPN_FROST && ammo_brand == SPMSL_FROST)
return (false);
if (bow_brand == SPWPN_VENOM && ammo_brand == SPMSL_POISONED)
return (false);
if (bow_brand == SPWPN_CHAOS && ammo_brand == SPMSL_CHAOS)
return (false);
if (bow_brand == SPWPN_PENETRATION && ammo_brand == SPMSL_PENETRATION)
return (false);
if (bow_brand == SPWPN_REAPING && ammo_brand == SPMSL_REAPING)
return (false);
return (true);
}
static int stat_adjust(int value, int stat, int statbase,
const int maxmult = 160, const int minmult = 40)
{
int multiplier = (statbase + (stat - statbase) / 2) * 100 / statbase;
if (multiplier > maxmult)
multiplier = maxmult;
else if (multiplier < minmult)
multiplier = minmult;
if (multiplier > 100)
value = value * (100 + random2avg(multiplier - 100, 2)) / 100;
else if (multiplier < 100)
value = value * (100 - random2avg(100 - multiplier, 2)) / 100;
return (value);
}
static int str_adjust_thrown_damage(int dam)
{
return stat_adjust(dam, you.strength, 15, 160, 90);
}
static int dex_adjust_thrown_tohit(int hit)
{
return stat_adjust(hit, you.dex, 13, 160, 90);
}
static void identify_floor_missiles_matching(item_def mitem, int idflags)
{
mitem.flags &= ~idflags;
for (int y = 0; y < GYM; ++y)
for (int x = 0; x < GXM; ++x)
for (stack_iterator si(coord_def(x,y)); si; ++si)
{
if ((si->flags & ISFLAG_THROWN) && items_stack(*si, mitem))
si->flags |= idflags;
}
}
void _merge_ammo_in_inventory(int slot)
{
if (!is_valid_item(you.inv[slot]))
return;
bool done_anything = false;
for (int i = 0; i < ENDOFPACK; ++i)
{
if (i == slot || !is_valid_item(you.inv[i]))
continue;
if (items_stack(you.inv[i], you.inv[slot]))
{
if (!done_anything)
mpr("You combine your ammunition.");
inc_inv_item_quantity(slot, you.inv[i].quantity, true);
dec_inv_item_quantity(i, you.inv[i].quantity);
done_anything = true;
}
}
}
bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
dist *target)
{
dist thr;
int shoot_skill = 0;
bool ammo_ided = false;
weapon_type lnchType;
int baseHit = 0, baseDam = 0; int ammoHitBonus = 0, ammoDamBonus = 0; int lnchHitBonus = 0, lnchDamBonus = 0; int exHitBonus = 0, exDamBonus = 0; int effSkill = 0; int dice_mult = 100;
bool returning = false; bool did_return = false; int slayDam = 0;
if (target)
thr = *target;
else
{
message_current_target();
direction(thr, DIR_NONE, TARG_HOSTILE);
if (!thr.isValid)
{
if (thr.isCancel)
canned_msg(MSG_OK);
return (false);
}
}
pbolt.set_target(thr);
item_def& thrown = you.inv[throw_2];
ASSERT(is_valid_item(thrown));
const launch_retval projected = is_launched(&you, you.weapon(), thrown);
item_def item = thrown;
item.quantity = 1;
item.slot = index_to_letter(item.link);
if (you.duration[DUR_WEAPON_BRAND] && projected != LRET_LAUNCHED
&& throw_2 == you.equip[EQ_WEAPON])
{
set_item_ego_type(item, OBJ_WEAPONS, SPWPN_NORMAL);
}
std::string ammo_name;
if (setup_missile_beam(&you, pbolt, item, ammo_name, returning))
{
you.turn_is_over = false;
return (false);
}
const bool ammo_brand_known = item_type_known(thrown);
const object_class_type wepClass = thrown.base_type;
const int wepType = thrown.sub_type;
int max_range = 0;
int range = 0;
if (projected)
{
if (wepType == MI_LARGE_ROCK)
{
range = 1 + random2( you.strength / 5 );
max_range = you.strength / 5;
if (you.can_throw_large_rocks())
{
range += random_range(4, 7);
max_range += 7;
}
}
else if (wepType == MI_THROWING_NET)
{
max_range = range = 2 + you.body_size(PSIZE_BODY);
}
else
{
max_range = range = LOS_RADIUS;
}
}
else
{
max_range = range = std::max(you.strength-item_mass(thrown)/10 + 3, 1);
}
range = std::min(range, LOS_RADIUS);
max_range = std::min(max_range, LOS_RADIUS);
pbolt.range = max_range;
if (!teleport && !you.confused())
{
pbolt.hit = 100;
pbolt.damage = dice_def(1, 100);
pbolt.foe_info.reset();
pbolt.friend_info.reset();
pbolt.foe_ratio = 100;
pbolt.is_tracer = true;
pbolt.fire();
if (pbolt.beam_cancelled)
{
canned_msg(MSG_OK);
you.turn_is_over = false;
if (pbolt.special_explosion != NULL)
delete pbolt.special_explosion;
return (false);
}
pbolt.hit = 0;
pbolt.damage = dice_def();
}
pbolt.is_tracer = false;
pbolt.range = range;
bool unwielded = false;
if (throw_2 == you.equip[EQ_WEAPON] && thrown.quantity == 1)
{
if (!wield_weapon(true, PROMPT_GOT_SPECIAL, true, false, false))
return (false);
unwielded = true;
}
origin_set_unknown(item);
if (is_blood_potion(item) && thrown.quantity > 1)
{
long val = remove_oldest_blood_potion(thrown);
val -= you.num_turns;
item.props.clear();
init_stack_blood_potions(item, val);
}
if (you.confused())
{
thr.isTarget = true;
thr.target = you.pos() + coord_def(random2(13)-6, random2(13)-6);
}
if (!teleport)
pbolt.set_target(thr);
if (!you.weapon())
lnchType = NUM_WEAPONS;
else
lnchType = static_cast<weapon_type>(you.weapon()->sub_type);
baseHit = std::min(0, you.strength - item_mass(item) / 10);
baseDam = item_mass(item) / 100;
if (wepClass == OBJ_WEAPONS)
baseDam = std::max(0, property(item, PWPN_DAMAGE) - 4);
ammoHitBonus = item.plus;
ammoDamBonus = item.plus2;
int bow_brand = SPWPN_NORMAL;
if (projected == LRET_LAUNCHED)
bow_brand = get_weapon_brand(*you.weapon());
const int ammo_brand = get_ammo_brand( item );
if (projected == LRET_LAUNCHED)
{
const item_def &launcher = *you.weapon();
lnchHitBonus = launcher.plus;
lnchDamBonus = launcher.plus2;
const int item_base_dam = property( item, PWPN_DAMAGE );
const int lnch_base_dam = property( launcher, PWPN_DAMAGE );
const skill_type launcher_skill = range_skill( launcher );
baseHit = property( launcher, PWPN_HIT );
baseDam = lnch_base_dam + random2(1 + item_base_dam);
if (lnch_base_dam == 0)
baseDam = item_base_dam;
if (!baseDam && elemental_missile_beam(bow_brand, ammo_brand))
baseDam = 4;
if (wepClass == OBJ_MISSILES && wepType == MI_NEEDLE)
pbolt.ench_power = AUTOMATIC_HIT;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"Base hit == %d; Base damage == %d "
"(item %d + launcher %d)",
baseHit, baseDam,
item_base_dam, lnch_base_dam);
#endif
ammoDamBonus = ammoHitBonus;
if (!(get_equip_race(*you.weapon()) == 0))
{
if (get_equip_race(*you.weapon()) == get_equip_race(item))
{
baseHit++;
baseDam++;
if (get_equip_race(*you.weapon()) == ISFLAG_ELVEN
&& player_genus(GENPC_ELVEN))
{
baseHit++;
}
}
}
if (you.attribute[ATTR_HELD])
baseHit--;
shoot_skill = you.skills[launcher_skill];
effSkill = shoot_skill;
const int speed = launcher_final_speed(launcher, player_shield());
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "Final launcher speed: %d", speed);
#endif
you.time_taken = speed * you.time_taken / 100;
exDamBonus = lnchDamBonus + random2(1 + ammoDamBonus);
exDamBonus = (exDamBonus > 0 ? random2(exDamBonus + 1)
: -random2(-exDamBonus + 1));
exHitBonus = (lnchHitBonus > 0 ? random2(lnchHitBonus + 1)
: -random2(-lnchHitBonus + 1));
if (determines_ammo_brand(bow_brand, ammo_brand))
{
set_ident_flags(item, ISFLAG_KNOW_TYPE);
if (ammo_brand != SPMSL_NORMAL)
{
set_ident_flags(you.inv[throw_2], ISFLAG_KNOW_TYPE);
ammo_ided = true;
}
}
switch (launcher_skill)
{
case SK_SLINGS:
{
exercise(SK_SLINGS, 1 + random2avg(3, 2));
if (wepType == MI_SLING_BULLET)
baseHit += 4;
exHitBonus += (effSkill * 3) / 2;
int strbonus = (10 * (you.strength - 10)) / 9;
strbonus = (strbonus * (2 * baseDam + ammoDamBonus)) / 20;
strbonus = std::min(lnchDamBonus + 1, strbonus);
exDamBonus += strbonus;
dice_mult = dice_mult * (14 + random2(1 + effSkill)) / 14;
lnchDamBonus = std::min(0, lnchDamBonus);
break;
}
case SK_DARTS:
baseHit -= 2;
exercise(SK_DARTS, (coinflip()? 2 : 1));
exHitBonus += (effSkill * 3) / 2 + you.dex / 2;
lnchDamBonus = std::min(0, lnchDamBonus);
ammoDamBonus = std::min(0, ammoDamBonus);
break;
case SK_BOWS:
{
baseHit -= 3;
exercise(SK_BOWS, (coinflip()? 2 : 1));
exHitBonus += (effSkill * 2);
int strbonus = (10 * (you.strength - 10)) / 4;
strbonus = (strbonus * (2 * baseDam + ammoDamBonus)) / 20;
strbonus = std::min(lnchDamBonus + 1, strbonus);
exDamBonus += strbonus;
dice_mult = dice_mult * (17 + random2(1 + effSkill)) / 17;
lnchDamBonus = std::min(0, lnchDamBonus);
break;
}
case SK_CROSSBOWS:
exercise(SK_CROSSBOWS, (coinflip()? 2 : 1));
baseHit++;
exHitBonus += (3 * effSkill) / 2 + 6;
dice_mult = dice_mult * (22 + random2(1 + effSkill)) / 22;
if (lnchType == WPN_HAND_CROSSBOW)
{
exHitBonus -= 2;
dice_mult = dice_mult * 26 / 30;
}
break;
default:
break;
}
if (launcher_skill == SK_SLINGS || launcher_skill == SK_DARTS)
{
if (coinflip())
exercise(SK_THROWING, 1);
exHitBonus += you.skills[SK_THROWING] / 5;
}
if (bow_brand == SPWPN_VORPAL)
{
dice_mult = dice_mult * 130 / 100;
}
if (ammo_brand == SPMSL_STEEL)
dice_mult = dice_mult * 150 / 100;
if (item_ident(*you.weapon(), ISFLAG_KNOW_PLUSES))
{
if (!teleport
&& !item_ident(you.inv[throw_2], ISFLAG_KNOW_PLUSES)
&& x_chance_in_y(shoot_skill, 100))
{
set_ident_flags( item, ISFLAG_KNOW_PLUSES );
set_ident_flags( you.inv[throw_2], ISFLAG_KNOW_PLUSES );
ammo_ided = true;
identify_floor_missiles_matching(item, ISFLAG_KNOW_PLUSES);
mprf("You are firing %s.",
you.inv[throw_2].name(DESC_NOCAP_A).c_str());
}
}
else if (!teleport && x_chance_in_y(shoot_skill, 100))
{
item_def& weapon = *you.weapon();
set_ident_flags(weapon, ISFLAG_KNOW_PLUSES);
mprf("You are wielding %s.", weapon.name(DESC_NOCAP_A).c_str());
more();
you.wield_change = true;
}
}
if (projected == LRET_THROWN)
{
returning = returning && !teleport;
if (returning && !one_chance_in(1 + skill_bump(SK_THROWING)))
did_return = true;
baseHit = 0;
if (wepClass == OBJ_MISSILES)
ammoDamBonus = ammoHitBonus;
if (wepClass == OBJ_WEAPONS
|| (wepClass == OBJ_MISSILES
&& (wepType == MI_STONE || wepType == MI_LARGE_ROCK
|| wepType == MI_DART || wepType == MI_JAVELIN)))
{
if (get_equip_race(item) == ISFLAG_ELVEN
&& player_genus(GENPC_ELVEN))
{
baseHit++;
}
if (wepClass == OBJ_WEAPONS)
{
switch (wepType)
{
case WPN_DAGGER:
baseHit++;
break;
case WPN_SPEAR:
baseHit--;
break;
default:
baseHit -= 5;
break;
}
}
else if (wepClass == OBJ_MISSILES)
{
switch (wepType)
{
case MI_DART:
baseHit += 2;
break;
case MI_JAVELIN:
baseHit++;
break;
default:
break;
}
}
exHitBonus = you.skills[SK_THROWING] * 2;
baseDam = property(item, PWPN_DAMAGE);
if (get_equip_race(item) == ISFLAG_DWARVEN
&& player_genus(GENPC_DWARVEN)
|| get_equip_race(item) == ISFLAG_ORCISH
&& you.species == SP_HILL_ORC)
{
baseDam++;
}
exDamBonus =
(10 * (you.skills[SK_THROWING] / 2 + you.strength - 10)) / 12;
exDamBonus = (exDamBonus * (3 * baseDam + ammoDamBonus)) / 30;
}
if (wepClass == OBJ_MISSILES)
{
set_ident_flags(you.inv[throw_2], ISFLAG_KNOW_TYPE);
ammo_ided = true;
switch (wepType)
{
case MI_LARGE_ROCK:
if (you.can_throw_large_rocks())
baseHit = 1;
break;
case MI_DART:
exHitBonus = you.skills[SK_DARTS] * 2;
exHitBonus += (you.skills[SK_THROWING] * 2) / 3;
exDamBonus = you.skills[SK_DARTS] / 3;
exDamBonus += you.skills[SK_THROWING] / 5;
exercise(SK_DARTS, 1 + random2avg(3, 2));
break;
case MI_JAVELIN:
exHitBonus += skill_bump(SK_THROWING);
exDamBonus += you.skills[SK_THROWING] * 3 / 5;
exDamBonus = str_adjust_thrown_damage(exDamBonus);
exHitBonus = dex_adjust_thrown_tohit(exHitBonus);
exDamBonus = stat_adjust(exDamBonus, you.dex, 20, 150, 100);
exercise(SK_THROWING, 1 + coinflip());
break;
case MI_THROWING_NET:
baseDam = 0;
exDamBonus = 0;
ammoDamBonus = 0;
baseHit = 1;
exHitBonus += (skill_bump(SK_THROWING) * 7 / 2);
exHitBonus = dex_adjust_thrown_tohit(exHitBonus);
exercise(SK_THROWING, 1);
break;
}
}
if (wepClass == OBJ_MISSILES
&& (wepType == MI_DART || wepType == MI_STONE))
{
baseDam = div_rand_round(baseDam, 2);
}
if (coinflip())
exercise(SK_THROWING, 1);
if (!teleport
&& !item_ident(you.inv[throw_2], ISFLAG_KNOW_PLUSES)
&& x_chance_in_y(you.skills[SK_THROWING], 100))
{
set_ident_flags( item, ISFLAG_KNOW_PLUSES );
set_ident_flags( you.inv[throw_2], ISFLAG_KNOW_PLUSES );
identify_floor_missiles_matching(item, ISFLAG_KNOW_PLUSES);
ammo_ided = true;
mprf("You are throwing %s.",
you.inv[throw_2].name(DESC_NOCAP_A).c_str());
}
}
if (pbolt.flavour != BEAM_MISSILE)
dice_mult = (dice_mult * 150) / 100;
if (projected)
{
if (wepType != MI_LARGE_ROCK && wepType != MI_THROWING_NET)
{
exHitBonus += you.dex / 2;
if (projected != LRET_LAUNCHED || wepType != MI_NEEDLE)
{
slayDam = slaying_bonus(PWPN_DAMAGE);
slayDam = (slayDam < 0 ? -random2(1 - slayDam)
: random2(1 + slayDam));
}
exHitBonus += slaying_bonus(PWPN_HIT);
}
}
else {
if (one_chance_in(20))
exercise(SK_THROWING, 1);
exHitBonus = you.dex / 4;
if (wepClass == OBJ_MISSILES && wepType == MI_NEEDLE)
{
exHitBonus -= (30 - you.skills[SK_DARTS]) / 3;
baseHit -= (30 - you.skills[SK_DARTS]) / 3;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "Needle base hit = %d, exHitBonus = %d",
baseHit, exHitBonus);
#endif
}
}
if (exHitBonus >= 0)
pbolt.hit = baseHit + random2avg(exHitBonus + 1, 2);
else
pbolt.hit = baseHit - random2avg(0 - (exHitBonus - 1), 2);
if (exDamBonus >= 0)
pbolt.damage = dice_def(1, baseDam + random2(exDamBonus + 1));
else
pbolt.damage = dice_def(1, baseDam - random2(0 - (exDamBonus - 1)));
pbolt.damage.size = dice_mult * pbolt.damage.size / 100;
pbolt.damage.size += slayDam;
if (projected || wepClass == OBJ_WEAPONS)
{
pbolt.hit += ammoHitBonus + lnchHitBonus;
pbolt.damage.size += ammoDamBonus + lnchDamBonus;
}
if (acc_bonus != DEBUG_COOKIE)
pbolt.hit += acc_bonus;
scale_dice(pbolt.damage);
#if DEBUG_DIAGNOSTICS
mprf( MSGCH_DIAGNOSTICS,
"H:%d+%d;a%dl%d. D:%d+%d;a%dl%d -> %d,%dd%d",
baseHit, exHitBonus, ammoHitBonus, lnchHitBonus,
baseDam, exDamBonus, ammoDamBonus, lnchDamBonus,
pbolt.hit, pbolt.damage.num, pbolt.damage.size );
#endif
mprf( "%s %s%s %s.",
teleport ? "Magically, you" : "You",
projected ? "" : "awkwardly ",
projected == LRET_LAUNCHED ? "shoot" : "throw",
ammo_name.c_str() );
pbolt.is_beam = false;
pbolt.is_tracer = false;
if (wepClass == OBJ_MISSILES || wepClass == OBJ_WEAPONS)
item.flags |= ISFLAG_THROWN;
bool hit = false;
if (teleport)
{
pbolt.use_target_as_pos = true;
pbolt.affect_cell();
if (acc_bonus != DEBUG_COOKIE)
pbolt.drop_object();
}
else
{
if (Options.tutorial_left)
Options.tut_throw_counter++;
pbolt.drop_item = !did_return;
pbolt.fire();
if (did_return && thrown_object_destroyed(&item, pbolt.target, true))
did_return = false;
}
if (bow_brand == SPWPN_CHAOS || ammo_brand == SPMSL_CHAOS)
{
did_god_conduct(DID_CHAOS, 2 + random2(3),
bow_brand == SPWPN_CHAOS || ammo_brand_known);
}
if (ammo_brand == SPMSL_REAPING || bow_brand == SPWPN_REAPING)
{
did_god_conduct(DID_NECROMANCY, 2,
bow_brand == SPWPN_REAPING || ammo_brand_known);
}
if (did_return)
{
pbolt.setup_retrace();
viewwindow(true, false);
pbolt.fire();
msg::stream << item.name(DESC_CAP_THE) << " returns to your pack!"
<< std::endl;
if (!is_artefact(you.inv[throw_2]))
{
set_ident_flags(you.inv[throw_2],
ISFLAG_KNOW_TYPE | ISFLAG_KNOW_PROPERTIES);
}
}
else
{
if (returning && item_type_known(you.inv[throw_2]))
{
msg::stream << item.name(DESC_CAP_THE)
<< " fails to return to your pack!" << std::endl;
}
dec_inv_item_quantity(throw_2, 1);
if (unwielded)
canned_msg(MSG_EMPTY_HANDED);
}
if (projected == LRET_LAUNCHED && lnchType != WPN_BLOWGUN)
noisy(6, you.pos());
alert_nearby_monsters();
if (ammo_ided)
_merge_ammo_in_inventory(throw_2);
you.turn_is_over = true;
if (pbolt.special_explosion != NULL)
delete pbolt.special_explosion;
return (hit);
}
bool thrown_object_destroyed(item_def *item, const coord_def& where,
bool returning)
{
ASSERT(item != NULL);
int chance = 0;
std::string name = item->name(DESC_PLAIN, false, true, false);
if (name.find("explod") != std::string::npos)
return (true);
if (item->base_type == OBJ_MISSILES)
{
int brand = get_ammo_brand(*item);
switch (item->sub_type)
{
case MI_NEEDLE:
chance = (brand == SPMSL_CURARE ? 3 : 6);
break;
case MI_SLING_BULLET:
case MI_STONE: chance = 4; break;
case MI_DART: chance = 3; break;
case MI_ARROW: chance = 4; break;
case MI_BOLT: chance = 4; break;
case MI_JAVELIN: chance = 10; break;
case MI_THROWING_NET: break;
case MI_LARGE_ROCK:
default:
chance = 25;
break;
}
if (brand == SPMSL_STEEL)
chance *= 10;
}
bool destroyed = (chance == 0) ? false : (one_chance_in(chance)
&& one_chance_in(item->plus + 1));
bool hostile_grid = feat_destroys_items(grd(where));
if (returning && !destroyed)
hostile_grid = false;
if (hostile_grid)
{
if (player_can_hear(where))
mprf(MSGCH_SOUND, feat_item_destruction_message(grd(where)));
item_was_destroyed(*item, NON_MONSTER);
destroyed = true;
}
return destroyed;
}
void jewellery_wear_effects(item_def &item)
{
item_type_id_state_type ident = ID_TRIED_TYPE;
artefact_prop_type fake_rap = ARTP_NUM_PROPERTIES;
bool learn_pluses = false;
const bool artefact = is_artefact(item);
const bool known_pluses = item_ident(item, ISFLAG_KNOW_PLUSES);
const bool known_cursed = item_known_cursed(item);
const bool known_bad = (item_type_known(item)
&& item_value(item) <= 2);
switch (item.sub_type)
{
case RING_FIRE:
case RING_HUNGER:
case RING_ICE:
case RING_LIFE_PROTECTION:
case RING_POISON_RESISTANCE:
case RING_PROTECTION_FROM_COLD:
case RING_PROTECTION_FROM_FIRE:
case RING_PROTECTION_FROM_MAGIC:
case RING_SUSTAIN_ABILITIES:
case RING_SUSTENANCE:
case RING_SLAYING:
case RING_WIZARDRY:
case RING_REGENERATION:
case RING_TELEPORT_CONTROL:
break;
case RING_SEE_INVISIBLE:
if (item_type_known(item))
autotoggle_autopickup(false);
break;
case RING_PROTECTION:
you.redraw_armour_class = true;
if (item.plus != 0)
{
if (!artefact)
ident = ID_KNOWN_TYPE;
else if (!known_pluses)
{
mprf("You feel %s.", item.plus > 0 ?
"well-protected" : "more vulnerable");
}
learn_pluses = true;
}
break;
case RING_INVISIBILITY:
if (!you.duration[DUR_INVIS])
{
mpr("You become transparent for a moment.");
if (artefact)
fake_rap = ARTP_INVISIBLE;
else
ident = ID_KNOWN_TYPE;
}
break;
case RING_EVASION:
you.redraw_evasion = true;
if (item.plus != 0)
{
if (!artefact)
ident = ID_KNOWN_TYPE;
else if (!known_pluses)
mprf("You feel %s.", item.plus > 0? "nimbler" : "more awkward");
learn_pluses = true;
}
break;
case RING_STRENGTH:
if (item.plus)
{
modify_stat(STAT_STRENGTH, item.plus, false, item);
if (artefact)
fake_rap = ARTP_STRENGTH;
else
ident = ID_KNOWN_TYPE;
learn_pluses = true;
}
break;
case RING_DEXTERITY:
if (item.plus)
{
modify_stat(STAT_DEXTERITY, item.plus, false, item);
if (artefact)
fake_rap = ARTP_DEXTERITY;
else
ident = ID_KNOWN_TYPE;
learn_pluses = true;
}
break;
case RING_INTELLIGENCE:
if (item.plus)
{
modify_stat(STAT_INTELLIGENCE, item.plus, false, item);
if (artefact)
fake_rap = ARTP_INTELLIGENCE;
else
ident = ID_KNOWN_TYPE;
learn_pluses = true;
}
break;
case RING_MAGICAL_POWER:
mpr("You feel your mana capacity increase.");
calc_mp();
if (artefact)
fake_rap = ARTP_MAGICAL_POWER;
else
ident = ID_KNOWN_TYPE;
break;
case RING_LEVITATION:
if (!scan_artefacts(ARTP_LEVITATE))
{
if (player_is_airborne())
mpr("You feel vaguely more buoyant than before.");
else
mpr("You feel buoyant.");
if (artefact)
fake_rap = ARTP_LEVITATE;
else
ident = ID_KNOWN_TYPE;
}
break;
case RING_TELEPORTATION:
if (!scan_artefacts(ARTP_CAN_TELEPORT))
{
mpr("You feel slightly jumpy.");
if (artefact)
fake_rap = ARTP_CAUSE_TELEPORTATION;
else
ident = ID_KNOWN_TYPE;
}
break;
case AMU_RAGE:
if (!scan_artefacts(ARTP_BERSERK))
{
mpr("You feel a brief urge to hack something to bits.");
if (artefact)
fake_rap = ARTP_BERSERK;
else
ident = ID_KNOWN_TYPE;
}
break;
case AMU_THE_GOURMAND:
you.duration[DUR_GOURMAND] = 0;
break;
case AMU_CONTROLLED_FLIGHT:
if (you.duration[DUR_LEVITATION]
&& !extrinsic_amulet_effect(AMU_CONTROLLED_FLIGHT))
{
ident = ID_KNOWN_TYPE;
}
break;
case AMU_GUARDIAN_SPIRIT:
if (player_spirit_shield()<2)
{
set_mp(0, false);
mpr("You feel your power drawn to a protective spirit.");
ident = ID_KNOWN_TYPE;
}
break;
}
if (artefact)
{
use_artefact(item);
if (learn_pluses && (item.plus != 0 || item.plus2 != 0))
set_ident_flags(item, ISFLAG_KNOW_PLUSES);
if (fake_rap != ARTP_NUM_PROPERTIES)
artefact_wpn_learn_prop(item, fake_rap);
if (!item.props.exists("jewellery_tried")
|| !item.props["jewellery_tried"].get_bool())
{
item.props["jewellery_tried"].get_bool() = true;
}
}
else
{
set_ident_type(item, ident);
if (ident == ID_KNOWN_TYPE)
set_ident_flags(item, ISFLAG_EQ_JEWELLERY_MASK);
}
if (item_cursed(item))
{
mprf("Oops, that %s feels deathly cold.",
jewellery_is_amulet(item)? "amulet" : "ring");
learned_something_new(TUT_YOU_CURSED);
int amusement = 32;
if (!known_cursed && !known_bad)
{
amusement *= 2;
god_type god;
if (origin_is_god_gift(item, &god) && god == GOD_XOM)
amusement *= 2;
}
xom_is_stimulated(amusement);
}
set_ident_flags(item, ISFLAG_KNOW_CURSE);
mpr(item.name(DESC_INVENTORY_EQUIP).c_str());
}
static int _prompt_ring_to_remove(int new_ring)
{
const item_def *left = you.slot_item(EQ_LEFT_RING);
const item_def *right = you.slot_item(EQ_RIGHT_RING);
if (item_cursed(*left) && item_cursed(*right))
{
mprf("You're already wearing two cursed rings!");
return (-1);
}
mesclr();
mprf("Wearing %s.", you.inv[new_ring].name(DESC_NOCAP_A).c_str());
const char lslot = index_to_letter(left->link);
const char rslot = index_to_letter(right->link);
mprf(MSGCH_PROMPT,
"You're wearing two rings. Remove which one? (%c/%c/<</>/Esc)",
lslot, rslot);
mprf(" < or %s", left->name(DESC_INVENTORY).c_str());
mprf(" > or %s", right->name(DESC_INVENTORY).c_str());
mouse_control mc(MOUSE_MODE_MORE);
int c;
do
c = getch();
while (c != lslot && c != rslot && c != '<' && c != '>'
&& c != ESCAPE && c != ' ');
mesclr();
if (c == ESCAPE || c == ' ')
return (-1);
const int eqslot = (c == lslot || c == '<') ? EQ_LEFT_RING
: EQ_RIGHT_RING;
if (!check_warning_inscriptions(you.inv[you.equip[eqslot]], OPER_REMOVE))
return (-1);
return (you.equip[eqslot]);
}
bool safe_to_remove_or_wear(const item_def &item, bool remove,
bool quiet)
{
int prop_str = 0;
int prop_dex = 0;
int prop_int = 0;
if (item.base_type == OBJ_JEWELLERY
&& item_ident(item, ISFLAG_KNOW_PLUSES))
{
switch (item.sub_type)
{
case RING_STRENGTH:
if (item.plus != 0)
prop_str = item.plus;
break;
case RING_DEXTERITY:
if (item.plus != 0)
prop_dex = item.plus;
break;
case RING_INTELLIGENCE:
if (item.plus != 0)
prop_int = item.plus;
break;
default:
break;
}
}
if (is_artefact(item))
{
prop_str += artefact_known_wpn_property(item, ARTP_STRENGTH);
prop_int += artefact_known_wpn_property(item, ARTP_INTELLIGENCE);
prop_dex += artefact_known_wpn_property(item, ARTP_DEXTERITY);
if (!remove && artefact_known_wpn_property(item, ARTP_EYESIGHT))
{
autotoggle_autopickup(false);
}
}
if (remove)
{
if (prop_str >= you.strength || prop_int >= you.intel
|| prop_dex >= you.dex)
{
if (!quiet)
{
mprf(MSGCH_WARN, "%s this item would be fatal, so you refuse "
"to do that.",
(item.base_type == OBJ_WEAPONS ? "Unwielding"
: "Removing"));
}
return (false);
}
}
else {
if (-prop_str >= you.strength || -prop_int >= you.intel
|| -prop_dex >= you.dex)
{
if (!quiet)
{
mprf(MSGCH_WARN, "%s this item would be fatal, so you refuse "
"to do that.",
(item.base_type == OBJ_WEAPONS ? "Wielding"
: "Wearing"));
}
return (false);
}
}
return (true);
}
static bool _swap_rings(int ring_slot)
{
const item_def* lring = you.slot_item(EQ_LEFT_RING);
const item_def* rring = you.slot_item(EQ_RIGHT_RING);
if (item_cursed(*lring) && item_cursed(*rring))
{
mprf("You're already wearing two cursed rings!");
return (false);
}
int unwanted;
if (lring->sub_type == rring->sub_type
&& lring->plus == rring->plus
&& lring->plus2 == rring->plus2
&& !is_artefact(*lring) && !is_artefact(*rring))
{
if (item_cursed(*lring))
unwanted = you.equip[EQ_RIGHT_RING];
else
unwanted = you.equip[EQ_LEFT_RING];
}
else
{
unwanted = _prompt_ring_to_remove(ring_slot);
}
if (unwanted == -1)
{
canned_msg(MSG_OK);
return (false);
}
if (!remove_ring(unwanted, false))
return (false);
if (!safe_to_remove_or_wear(you.inv[ring_slot], false))
return (false);
start_delay(DELAY_JEWELLERY_ON, 1, ring_slot);
return (true);
}
bool puton_item(int item_slot)
{
item_def& item = you.inv[item_slot];
if (item_slot == you.equip[EQ_LEFT_RING]
|| item_slot == you.equip[EQ_RIGHT_RING]
|| item_slot == you.equip[EQ_AMULET])
{
if (Options.equip_unequip)
return (!remove_ring(item_slot));
else
{
mpr("You're already wearing that object!");
return (false);
}
}
if (item_slot == you.equip[EQ_WEAPON])
{
mpr("You are wielding that object.");
return (false);
}
if (item.base_type != OBJ_JEWELLERY)
{
mpr("You can only put on jewellery.");
return (false);
}
const bool lring = (you.slot_item(EQ_LEFT_RING) != NULL);
const bool rring = (you.slot_item(EQ_RIGHT_RING) != NULL);
const bool is_amulet = jewellery_is_amulet(item);
if (!is_amulet) {
const item_def* gloves = you.slot_item(EQ_GLOVES);
if (gloves && item_cursed(*gloves))
{
mpr("You can't take your gloves off to put on a ring!");
return (false);
}
if (lring && rring)
return _swap_rings(item_slot);
}
else if (item_def* amulet = you.slot_item(EQ_AMULET))
{
if (!check_warning_inscriptions(*amulet, OPER_REMOVE)
|| !remove_ring(you.equip[EQ_AMULET], true))
{
return (false);
}
if (!safe_to_remove_or_wear(item, false))
return (false);
start_delay(DELAY_JEWELLERY_ON, 1, item_slot);
return (true);
}
if (!safe_to_remove_or_wear(item, false))
return (false);
equipment_type hand_used;
if (is_amulet)
{
hand_used = EQ_AMULET;
}
else
{
hand_used = EQ_LEFT_RING;
if (lring && !rring)
hand_used = EQ_RIGHT_RING;
}
const unsigned int old_talents = your_talents(false).size();
you.equip[hand_used] = item_slot;
jewellery_wear_effects(item);
if (Options.tutorial_left && your_talents(false).size() > old_talents)
learned_something_new(TUT_NEW_ABILITY_ITEM);
you.time_taken /= 2;
you.turn_is_over = true;
return (true);
}
bool puton_ring(int slot)
{
if (player_in_bat_form())
{
mpr("You can't put on anything in your present form.");
return (false);
}
int item_slot;
if (inv_count() < 1)
{
canned_msg(MSG_NOTHING_CARRIED);
return (false);
}
if (you.duration[DUR_BERSERKER])
{
canned_msg(MSG_TOO_BERSERK);
return (false);
}
if (slot != -1)
item_slot = slot;
else
{
item_slot = prompt_invent_item( "Put on which piece of jewellery?",
MT_INVLIST, OBJ_JEWELLERY, true, true,
true, 0, -1, NULL, OPER_PUTON );
}
if (prompt_failed(item_slot))
return (false);
return puton_item(item_slot);
}
void jewellery_remove_effects(item_def &item, bool mesg)
{
const bool old_showuncursed = Options.show_uncursed;
Options.show_uncursed = false;
if (mesg)
mprf("You remove %s.", item.name(DESC_NOCAP_YOUR).c_str() );
Options.show_uncursed = old_showuncursed;
switch (item.sub_type)
{
case RING_FIRE:
case RING_HUNGER:
case RING_ICE:
case RING_LIFE_PROTECTION:
case RING_POISON_RESISTANCE:
case RING_PROTECTION_FROM_COLD:
case RING_PROTECTION_FROM_FIRE:
case RING_PROTECTION_FROM_MAGIC:
case RING_REGENERATION:
case RING_SEE_INVISIBLE:
case RING_SLAYING:
case RING_SUSTAIN_ABILITIES:
case RING_SUSTENANCE:
case RING_TELEPORTATION:
case RING_WIZARDRY:
case RING_TELEPORT_CONTROL:
break;
case RING_PROTECTION:
you.redraw_armour_class = true;
break;
case RING_EVASION:
you.redraw_evasion = true;
break;
case RING_STRENGTH:
modify_stat(STAT_STRENGTH, -item.plus, false, item, true);
break;
case RING_DEXTERITY:
modify_stat(STAT_DEXTERITY, -item.plus, false, item, true);
break;
case RING_INTELLIGENCE:
modify_stat(STAT_INTELLIGENCE, -item.plus, false, item, true);
break;
case RING_MAGICAL_POWER:
mpr("You feel your mana capacity decrease.");
break;
case AMU_THE_GOURMAND:
you.duration[DUR_GOURMAND] = 0;
break;
}
if (is_artefact(item))
unuse_artefact(item, &mesg);
calc_mp();
}
bool remove_ring(int slot, bool announce)
{
if (player_in_bat_form())
{
mpr("You can't wear or remove anything in your present form.");
return (false);
}
equipment_type hand_used = EQ_NONE;
int ring_wear_2;
if (you.equip[EQ_LEFT_RING] == -1 && you.equip[EQ_RIGHT_RING] == -1
&& you.equip[EQ_AMULET] == -1)
{
mpr("You aren't wearing any rings or amulets.");
return (false);
}
if (you.duration[DUR_BERSERKER])
{
canned_msg(MSG_TOO_BERSERK);
return (false);
}
if (you.equip[EQ_GLOVES] != -1
&& item_cursed( you.inv[you.equip[EQ_GLOVES]] )
&& you.equip[EQ_AMULET] == -1)
{
mpr("You can't take your gloves off to remove any rings!");
return (false);
}
if (you.equip[EQ_LEFT_RING] != -1 && you.equip[EQ_RIGHT_RING] == -1
&& you.equip[EQ_AMULET] == -1)
{
hand_used = EQ_LEFT_RING;
}
if (you.equip[EQ_LEFT_RING] == -1 && you.equip[EQ_RIGHT_RING] != -1
&& you.equip[EQ_AMULET] == -1)
{
hand_used = EQ_RIGHT_RING;
}
if (you.equip[EQ_LEFT_RING] == -1 && you.equip[EQ_RIGHT_RING] == -1
&& you.equip[EQ_AMULET] != -1)
{
hand_used = EQ_AMULET;
}
if (hand_used == EQ_NONE)
{
const int equipn =
(slot == -1)? prompt_invent_item("Remove which piece of jewellery?",
MT_INVLIST,
OBJ_JEWELLERY, true, true, true,
0, -1, NULL, OPER_REMOVE)
: slot;
if (prompt_failed(equipn))
return (false);
if (you.inv[equipn].base_type != OBJ_JEWELLERY)
{
mpr("That isn't a piece of jewellery.");
return (false);
}
if (you.equip[EQ_LEFT_RING] == equipn)
hand_used = EQ_LEFT_RING;
else if (you.equip[EQ_RIGHT_RING] == equipn)
hand_used = EQ_RIGHT_RING;
else if (you.equip[EQ_AMULET] == equipn)
hand_used = EQ_AMULET;
else
{
mpr("You aren't wearing that.");
return (false);
}
}
if (!check_warning_inscriptions(you.inv[you.equip[hand_used]],
OPER_REMOVE))
{
canned_msg(MSG_OK);
return (false);
}
if (you.equip[EQ_GLOVES] != -1
&& item_cursed( you.inv[you.equip[EQ_GLOVES]] )
&& (hand_used == EQ_LEFT_RING || hand_used == EQ_RIGHT_RING))
{
mpr("You can't take your gloves off to remove any rings!");
return (false);
}
if (you.equip[hand_used] == -1)
{
mpr("I don't think you really meant that.");
return (false);
}
if (item_cursed( you.inv[you.equip[hand_used]] ))
{
if (announce)
{
mprf("%s is stuck to you!",
you.inv[you.equip[hand_used]].name(DESC_CAP_YOUR).c_str());
}
else
mpr("It's stuck to you!");
set_ident_flags(you.inv[you.equip[hand_used]], ISFLAG_KNOW_CURSE);
return (false);
}
ring_wear_2 = you.equip[hand_used];
if (!safe_to_remove_or_wear(you.inv[ring_wear_2], true))
return (false);
you.equip[hand_used] = -1;
jewellery_remove_effects(you.inv[ring_wear_2]);
you.time_taken /= 2;
you.turn_is_over = true;
return (true);
}
int _wand_range(zap_type ztype)
{
return (8);
}
int _max_wand_range()
{
return (8);
}
static bool _dont_use_invis()
{
if (!you.backlit())
return (false);
if (you.haloed())
{
mpr("You can't turn invisible.");
return (true);
}
else if (get_contamination_level() > 0
&& !yesno("Invisibility will do you no good right now; "
"use anyways?"))
{
return (true);
}
return (false);
}
void zap_wand(int slot)
{
if (player_in_bat_form())
{
canned_msg(MSG_PRESENT_FORM);
return;
}
bolt beam;
dist zap_wand;
int item_slot;
targ_mode_type targ_mode = TARG_HOSTILE;
beam.obvious_effect = false;
if (inv_count() < 1)
{
canned_msg(MSG_NOTHING_CARRIED);
return;
}
if (you.duration[DUR_BERSERKER])
{
canned_msg(MSG_TOO_BERSERK);
return;
}
if (slot != -1)
item_slot = slot;
else
{
item_slot = prompt_invent_item("Zap which item?",
MT_INVLIST,
OBJ_WANDS,
true, true, true, 0, -1, NULL,
OPER_ZAP);
}
if (prompt_failed(item_slot))
return;
item_def& wand = you.inv[item_slot];
if (wand.base_type != OBJ_WANDS)
{
canned_msg(MSG_NOTHING_HAPPENS);
return;
}
if (you.equip[EQ_WEAPON] == item_slot)
you.wield_change = true;
const zap_type type_zapped = wand.zap();
bool has_charges = true;
if (wand.plus < 1)
{
if (wand.plus2 == ZAPCOUNT_EMPTY)
{
mpr("This wand has no charges.");
return;
}
has_charges = false;
}
const bool alreadyknown = item_type_known(wand);
const bool alreadytried = item_type_tried(wand);
bool invis_enemy = false;
const bool dangerous = player_in_a_dangerous_place(&invis_enemy);
if (!alreadyknown)
beam.effect_known = false;
else
{
switch (wand.sub_type)
{
case WAND_DIGGING:
case WAND_TELEPORTATION:
targ_mode = TARG_ANY;
break;
case WAND_HEALING:
if (you.religion == GOD_ELYVILON)
{
targ_mode = TARG_ANY;
break;
}
case WAND_HASTING:
case WAND_INVISIBILITY:
targ_mode = TARG_FRIEND;
break;
default:
targ_mode = TARG_HOSTILE;
break;
}
}
int tracer_range = (alreadyknown && wand.sub_type != WAND_RANDOM_EFFECTS) ?
_wand_range(type_zapped) : _max_wand_range();
message_current_target();
direction(zap_wand, DIR_NONE, targ_mode, tracer_range);
if (!zap_wand.isValid)
{
if (zap_wand.isCancel)
canned_msg(MSG_OK);
return;
}
if (alreadyknown && zap_wand.target == you.pos())
{
if (wand.sub_type == WAND_TELEPORTATION
&& scan_artefacts(ARTP_PREVENT_TELEPORTATION, false))
{
mpr("You cannot teleport right now.");
return;
}
else if (wand.sub_type == WAND_INVISIBILITY
&& _dont_use_invis())
{
return;
}
}
if (!has_charges)
{
canned_msg(MSG_NOTHING_HAPPENS);
wand.plus2 = ZAPCOUNT_EMPTY;
you.turn_is_over = true;
return;
}
if (you.confused())
zap_wand.target = you.pos() + coord_def(random2(13)-6, random2(13)-6);
if (wand.sub_type == WAND_RANDOM_EFFECTS)
beam.effect_known = false;
beam.source = you.pos();
beam.set_target(zap_wand);
bool aimed_at_self = (beam.target == you.pos());
if (!aimed_at_self)
{
beam.range = tracer_range;
if (!player_tracer(beam.effect_known ? type_zapped
: ZAP_DEBUGGING_RAY,
2 * (you.skills[SK_EVOCATIONS] - 1),
beam, beam.effect_known ? 0 : 17))
{
return;
}
}
const bool risky = dangerous && (beam.friend_info.count
|| beam.foe_info.count
|| invis_enemy
|| aimed_at_self);
if (risky && alreadyknown && wand.sub_type == WAND_RANDOM_EFFECTS)
{
xom_is_stimulated(255);
}
beam.range = _wand_range(type_zapped);
zapping( type_zapped, 30 + roll_dice(2, you.skills[SK_EVOCATIONS]), beam );
wand.plus--;
if (wand.plus2 == ZAPCOUNT_MAX_CHARGED || wand.plus2 == ZAPCOUNT_RECHARGED)
wand.plus2 = 0;
if (wand.plus2 >= 0)
wand.plus2++;
if (!alreadyknown && (beam.obvious_effect || type_zapped == ZAP_FIREBALL))
{
set_ident_type(wand, ID_KNOWN_TYPE);
if (wand.sub_type == WAND_RANDOM_EFFECTS)
mpr("You feel that this wand is rather unreliable.");
mpr(wand.name(DESC_INVENTORY_EQUIP).c_str());
}
else
set_ident_type(wand, ID_TRIED_TYPE);
if (item_type_known(wand)
&& (item_ident(wand, ISFLAG_KNOW_PLUSES)
|| you.skills[SK_EVOCATIONS] > 5 + random2(15)))
{
if (!item_ident(wand, ISFLAG_KNOW_PLUSES))
{
mpr("Your skill with magical items lets you calculate "
"the power of this device...");
}
mprf("This wand has %d charge%s left.",
wand.plus, wand.plus == 1 ? "" : "s");
set_ident_flags(wand, ISFLAG_KNOW_PLUSES);
}
exercise(SK_EVOCATIONS, 1);
alert_nearby_monsters();
if (!alreadyknown && !alreadytried && risky)
{
xom_is_stimulated(255);
}
you.turn_is_over = true;
}
void prompt_inscribe_item()
{
if (inv_count() < 1)
{
mpr("You don't have anything to inscribe.");
return;
}
int item_slot = prompt_invent_item("Inscribe which item?",
MT_INVLIST, OSEL_ANY);
if (prompt_failed(item_slot))
return;
inscribe_item(you.inv[item_slot], true);
}
void drink(int slot)
{
if (you.is_undead == US_UNDEAD)
{
mpr("You can't drink.");
return;
}
if (slot == -1)
{
const dungeon_feature_type feat = grd(you.pos());
if (feat >= DNGN_FOUNTAIN_BLUE && feat <= DNGN_FOUNTAIN_BLOOD)
if (_drink_fountain())
return;
}
if (inv_count() == 0)
{
canned_msg(MSG_NOTHING_CARRIED);
return;
}
if (you.duration[DUR_BERSERKER])
{
canned_msg(MSG_TOO_BERSERK);
return;
}
if (player_in_bat_form())
{
canned_msg(MSG_PRESENT_FORM);
return;
}
if (slot == -1)
{
slot = prompt_invent_item("Drink which item?",
MT_INVLIST, OBJ_POTIONS,
true, true, true, 0, -1, NULL,
OPER_QUAFF);
if (prompt_failed(slot))
return;
}
item_def& potion = you.inv[slot];
if (potion.base_type != OBJ_POTIONS)
{
mpr("You can't drink that!");
return;
}
const bool alreadyknown = item_type_known(potion);
if (alreadyknown && you.hunger_state == HS_ENGORGED
&& (is_blood_potion(potion) || potion.sub_type == POT_PORRIDGE))
{
mpr("You are much too full right now.");
return;
}
if (alreadyknown && potion.sub_type == POT_INVISIBILITY
&& _dont_use_invis())
{
return;
}
if (alreadyknown && potion.sub_type == POT_BERSERK_RAGE
&& !berserk_check_wielded_weapon())
{
return;
}
const bool dangerous = (player_in_a_dangerous_place()
&& you.experience_level > 1);
if (potion_effect(static_cast<potion_type>(potion.sub_type),
40, true, alreadyknown))
{
set_ident_flags(potion, ISFLAG_IDENT_MASK);
set_ident_type(potion, ID_KNOWN_TYPE);
}
else
{
set_ident_type(potion, ID_TRIED_TYPE);
}
if (!alreadyknown && dangerous)
{
xom_is_stimulated(255);
}
if (is_blood_potion(potion))
{
remove_oldest_blood_potion(potion);
}
dec_inv_item_quantity(slot, 1);
you.turn_is_over = true;
if (you.species != SP_VAMPIRE)
lessen_hunger(40, true);
}
bool _drink_fountain()
{
const dungeon_feature_type feat = grd(you.pos());
if (feat < DNGN_FOUNTAIN_BLUE || feat > DNGN_FOUNTAIN_BLOOD)
return (false);
if (you.flight_mode() == FL_LEVITATE)
{
mpr("You're floating high above the fountain.");
return (false);
}
if (you.duration[DUR_BERSERKER])
{
canned_msg(MSG_TOO_BERSERK);
return (true);
}
potion_type fountain_effect = POT_WATER;
if (feat == DNGN_FOUNTAIN_BLUE)
{
if (!yesno("Drink from the fountain?"))
return (false);
mpr("You drink the pure, clear water.");
}
else if (feat == DNGN_FOUNTAIN_BLOOD)
{
if (!yesno("Drink from the fountain of blood?"))
return (false);
mpr("You drink the blood.");
fountain_effect = POT_BLOOD;
}
else
{
if (!yesno("Drink from the sparkling fountain?"))
return (false);
mpr("You drink the sparkling water.");
fountain_effect = static_cast<potion_type>(
random_choose_weighted(467, POT_WATER,
48, POT_DECAY,
40, POT_MUTATION,
40, POT_HEALING,
40, POT_HEAL_WOUNDS,
40, POT_SPEED,
40, POT_MIGHT,
40, POT_AGILITY,
40, POT_BRILLIANCE,
32, POT_DEGENERATION,
27, POT_LEVITATION,
27, POT_POISON,
27, POT_SLOWING,
27, POT_PARALYSIS,
27, POT_CONFUSION,
27, POT_INVISIBILITY,
20, POT_MAGIC,
20, POT_RESTORE_ABILITIES,
20, POT_RESISTANCE,
20, POT_STRONG_POISON,
20, POT_BERSERK_RAGE,
4, POT_GAIN_STRENGTH,
4, POT_GAIN_INTELLIGENCE,
4, POT_GAIN_DEXTERITY,
0));
}
if (fountain_effect != POT_WATER && fountain_effect != POT_BLOOD)
xom_is_stimulated(64);
potion_effect(fountain_effect, 100, true, feat != DNGN_FOUNTAIN_SPARKLING);
bool gone_dry = false;
if (feat == DNGN_FOUNTAIN_BLUE)
{
if (one_chance_in(20))
gone_dry = true;
}
else if (feat == DNGN_FOUNTAIN_BLOOD)
{
if (one_chance_in(3))
gone_dry = true;
}
else {
if (one_chance_in(10))
gone_dry = true;
else if (random2(50) > 40)
{
grd(you.pos()) = DNGN_FOUNTAIN_BLUE;
set_terrain_changed(you.pos());
}
}
if (gone_dry)
{
mpr("The fountain dries up!");
grd(you.pos()) = static_cast<dungeon_feature_type>(feat
+ DNGN_DRY_FOUNTAIN_BLUE - DNGN_FOUNTAIN_BLUE);
set_terrain_changed(you.pos());
crawl_state.cancel_cmd_repeat();
}
you.turn_is_over = true;
return (true);
}
static bool _vorpalise_weapon()
{
if (!you.weapon())
return (false);
item_def& wpn = *you.weapon();
if (wpn.base_type != OBJ_WEAPONS || wpn.sub_type == WPN_BLOWGUN
|| is_artefact(wpn))
{
return (false);
}
you.wield_change = true;
if (get_weapon_brand(wpn) == SPWPN_NORMAL)
{
alert_nearby_monsters();
mprf("%s emits a brilliant flash of light!",
wpn.name(DESC_CAP_YOUR).c_str());
set_item_ego_type(wpn, OBJ_WEAPONS, SPWPN_VORPAL);
return (true);
}
if (you.duration[DUR_WEAPON_BRAND] == 0)
return (false);
const std::string itname = wpn.name(DESC_CAP_YOUR);
bool success = true;
bool msg = true;
switch (get_weapon_brand(wpn))
{
case SPWPN_VORPAL:
if (get_vorpal_type(wpn) != DVORP_CRUSHING)
mprf("%s's sharpness seems more permanent.", itname.c_str());
else
mprf("%s's heaviness feels very stable.", itname.c_str());
break;
case SPWPN_FLAMING:
mprf("%s is engulfed in an explosion of flames!", itname.c_str());
immolation(10, IMMOLATION_SPELL, you.pos(), true, &you);
break;
case SPWPN_FREEZING:
mprf("%s glows brilliantly blue for a moment.", itname.c_str());
cast_refrigeration(60);
break;
case SPWPN_DRAINING:
mprf("%s thirsts for the lives of mortals!", itname.c_str());
drain_exp();
break;
case SPWPN_VENOM:
mprf("%s seems more permanently poisoned.", itname.c_str());
cast_toxic_radiance();
break;
case SPWPN_PAIN:
mprf("%s shrieks out in agony!", itname.c_str());
torment_monsters(you.pos(), 0, TORMENT_GENERIC);
success = false;
did_god_conduct(DID_UNHOLY, 10,
get_ident_type(OBJ_SCROLLS, SCR_VORPALISE_WEAPON)
== ID_KNOWN_TYPE);
break;
case SPWPN_DISTORTION:
mprf("%s twongs alarmingly.", itname.c_str());
MiscastEffect(&you, NON_MONSTER, SPTYP_TRANSLOCATION, 9, 90,
"distortion affixation");
success = false;
break;
default:
success = false;
msg = false;
break;
}
if (success)
you.duration[DUR_WEAPON_BRAND] = 0;
return (msg);
}
bool enchant_weapon(enchant_stat_type which_stat, bool quiet, item_def &wpn)
{
bool to_hit = (which_stat == ENCHANT_TO_HIT);
if (!is_enchantable_weapon(wpn, true, to_hit))
{
if (!quiet)
canned_msg( MSG_NOTHING_HAPPENS );
return (false);
}
const bool is_cursed = item_cursed(wpn);
if (wpn.base_type == OBJ_MISSILES)
{
which_stat = ENCHANT_TO_HIT;
to_hit = true;
}
int enchant_level = (to_hit ? wpn.plus
: wpn.plus2);
if (!is_enchantable_weapon(wpn, false, to_hit)
|| enchant_level >= 4 && x_chance_in_y(enchant_level, MAX_WPN_ENCHANT))
{
if (is_cursed)
{
if (!quiet)
{
mprf("%s glows silver for a moment.",
wpn.name(DESC_CAP_YOUR).c_str());
}
do_uncurse_item(wpn);
return (true);
}
else
{
if (!quiet)
canned_msg(MSG_NOTHING_HAPPENS);
if (is_enchantable_weapon(wpn, false, to_hit))
xom_is_stimulated(32);
return (false);
}
}
std::string iname = wpn.name(DESC_CAP_YOUR);
if (wpn.base_type == OBJ_WEAPONS)
{
if (to_hit)
{
if (!quiet)
mprf("%s glows green for a moment.", iname.c_str());
wpn.plus++;
}
else
{
if (!quiet)
mprf("%s glows red for a moment.", iname.c_str());
wpn.plus2++;
}
}
else if (wpn.base_type == OBJ_MISSILES)
{
if (!quiet)
{
mprf("%s glow%s red for a moment.", iname.c_str(),
wpn.quantity > 1 ? "" : "s");
}
wpn.plus++;
}
if (is_cursed)
do_uncurse_item(wpn);
return (true);
}
static bool _handle_enchant_weapon(enchant_stat_type which_stat,
bool quiet, int item_slot)
{
if (item_slot == -1)
item_slot = you.equip[ EQ_WEAPON ];
if (item_slot == -1)
{
canned_msg(MSG_NOTHING_HAPPENS);
return (false);
}
item_def& wpn(you.inv[item_slot]);
bool result = enchant_weapon(which_stat, quiet, wpn);
you.wield_change = true;
return result;
}
bool enchant_armour(int &ac_change, bool quiet, item_def &arm)
{
ac_change = 0;
if (!is_enchantable_armour(arm, true))
{
if (!quiet)
canned_msg( MSG_NOTHING_HAPPENS );
return (false);
}
const bool is_cursed = item_cursed(arm);
if (armour_is_hide(arm, false))
{
if (!quiet)
{
mprf("%s glows purple and changes!",
arm.name(DESC_CAP_YOUR).c_str());
}
ac_change = property(arm, PARM_AC);
hide2armour(arm);
ac_change = property(arm, PARM_AC) - ac_change;
if (is_cursed)
do_uncurse_item(arm);
return (true);
}
if (!is_enchantable_armour(arm, false)
|| arm.plus > MAX_SEC_ENCHANT
&& x_chance_in_y(arm.plus, MAX_ARM_ENCHANT))
{
if (is_cursed)
{
if (!quiet)
{
mprf("%s glows silver for a moment.",
arm.name(DESC_CAP_YOUR).c_str());
}
do_uncurse_item(arm);
return (true);
}
else
{
if (!quiet)
canned_msg(MSG_NOTHING_HAPPENS);
if (is_enchantable_armour(arm, false))
xom_is_stimulated(32);
return (false);
}
}
if (!quiet)
{
mprf("%s glows green for a moment.",
arm.name(DESC_CAP_YOUR).c_str());
}
arm.plus++;
ac_change++;
if (is_cursed)
do_uncurse_item(arm);
return (true);
}
static bool _handle_enchant_armour(int item_slot)
{
do
{
if (item_slot == -1)
{
item_slot = prompt_invent_item("Enchant which item?", MT_INVLIST,
OSEL_ENCH_ARM, true, true, false);
}
if (prompt_failed(item_slot))
return (false);
item_def& arm(you.inv[item_slot]);
if (!is_enchantable_armour(arm, true, true))
{
mpr("Choose some type of armour to enchant, or Esc to abort.");
if (Options.auto_list)
more();
item_slot = -1;
continue;
}
int ac_change;
bool result = enchant_armour(ac_change, false, arm);
if (ac_change)
you.redraw_armour_class = true;
return (result);
}
while (true);
return (false);
}
static void handle_read_book(int item_slot)
{
item_def& book(you.inv[item_slot]);
if (book.sub_type == BOOK_DESTRUCTION)
{
if (silenced(you.pos()))
mpr("This book does not work if you cannot read it aloud!");
else
tome_of_power(item_slot);
return;
}
else if (book.sub_type == BOOK_MANUAL)
{
skill_manual(item_slot);
return;
}
while (true)
{
const int ltr = read_book( book, RBOOK_READ_SPELL );
if (ltr < 'a' || ltr > 'h') {
mesclr(true);
return;
}
const spell_type spell = which_spell_in_book(book,
letter_to_index(ltr));
if (spell == SPELL_NO_SPELL)
{
mesclr(true);
return;
}
describe_spell(spell, &book);
if (you.turn_is_over)
return;
}
}
static bool _scroll_modify_item(item_def scroll)
{
ASSERT(scroll.base_type == OBJ_SCROLLS);
int item_slot = scroll.slot;
item_slot = prompt_invent_item("Use on which item? (\\ to view known items)",
MT_INVLIST, OSEL_ANY, true, true, false, 0,
item_slot, NULL, OPER_ANY, true);
if (prompt_failed(item_slot))
return (false);
item_def &item = you.inv[item_slot];
switch (scroll.sub_type)
{
case SCR_IDENTIFY:
if (!fully_identified(item))
{
mpr("This is a scroll of identify!");
identify(-1, item_slot);
return (true);
}
break;
case SCR_RECHARGING:
if (item_is_rechargeable(item, false, true))
{
if (recharge_wand(item_slot))
return (true);
return (false);
}
break;
case SCR_ENCHANT_ARMOUR:
if (is_enchantable_armour(item, true))
{
if (_handle_enchant_armour(item_slot))
return (true);
return (false);
}
break;
default:
mprf("Buggy scroll %d can't modify item!", scroll.sub_type);
break;
}
canned_msg(MSG_NOTHING_HAPPENS);
return (false);
}
static void _vulnerability_scroll()
{
antimagic();
const enchant_type lost_enchantments[] = {
ENCH_SLOW,
ENCH_HASTE,
ENCH_MIGHT,
ENCH_FEAR,
ENCH_CONFUSION,
ENCH_INVIS,
ENCH_BACKLIGHT,
ENCH_CHARM,
ENCH_PARALYSIS,
ENCH_PETRIFYING,
ENCH_PETRIFIED
};
mon_enchant lowered_mr(ENCH_LOWERED_MR, 1, KC_YOU, 40);
for (radius_iterator ri(you.pos(), LOS_RADIUS); ri; ++ri)
{
if (monsters* mon = monster_at(*ri))
{
for (unsigned int i = 0; i < ARRAYSZ(lost_enchantments); ++i)
mon->del_ench(lost_enchantments[i], true, true);
if (!mons_immune_magic(mon))
mon->add_ench(lowered_mr);
if (!mons_wont_attack(mon))
behaviour_event(mon, ME_ANNOY, MHITYOU);
}
}
you.duration[DUR_LOWERED_MR] = 40;
mpr("Magic dampens around you!");
}
void read_scroll(int slot)
{
if (you.duration[DUR_BERSERKER])
{
canned_msg(MSG_TOO_BERSERK);
return;
}
if (player_in_bat_form())
{
canned_msg(MSG_PRESENT_FORM);
return;
}
if (inv_count() < 1)
{
canned_msg(MSG_NOTHING_CARRIED);
return;
}
int item_slot = (slot != -1) ? slot
: prompt_invent_item( "Read which item?",
MT_INVLIST,
OBJ_SCROLLS,
true, true, true, 0, -1,
NULL, OPER_READ );
if (prompt_failed(item_slot))
return;
item_def& scroll = you.inv[item_slot];
if (scroll.base_type != OBJ_BOOKS && scroll.base_type != OBJ_SCROLLS)
{
mpr("You can't read that!");
crawl_state.zero_turns_taken();
return;
}
if (scroll.base_type == OBJ_BOOKS)
{
handle_read_book( item_slot );
return;
}
if (silenced(you.pos()))
{
mpr("Magic scrolls do not work when you're silenced!");
crawl_state.zero_turns_taken();
return;
}
you.turn_is_over = true;
if (player_mutation_level(MUT_BLURRY_VISION)
&& x_chance_in_y(player_mutation_level(MUT_BLURRY_VISION), 5))
{
mpr((player_mutation_level(MUT_BLURRY_VISION) == 3 && one_chance_in(3))
? "This scroll appears to be blank."
: "The writing blurs in front of your eyes.");
return;
}
const scroll_type which_scroll = static_cast<scroll_type>(scroll.sub_type);
const bool alreadyknown = item_type_known(scroll);
if (alreadyknown
&& (which_scroll == SCR_BLINKING || which_scroll == SCR_TELEPORTATION)
&& scan_artefacts(ARTP_PREVENT_TELEPORTATION, false))
{
mpr("You cannot teleport right now.");
return;
}
if (which_scroll != SCR_PAPER
&& (which_scroll != SCR_IMMOLATION || you.confused()))
{
mpr("As you read the scroll, it crumbles to dust.");
}
const bool dangerous = player_in_a_dangerous_place();
if (which_scroll != SCR_PAPER)
{
if (you.confused())
{
random_uselessness(item_slot);
dec_inv_item_quantity(item_slot, 1);
return;
}
if (!you.skills[SK_SPELLCASTING])
exercise(SK_SPELLCASTING, (coinflip()? 2 : 1));
}
bool id_the_scroll = true; bool tried_on_item = false;
bool bad_effect = false; switch (which_scroll)
{
case SCR_PAPER:
mpr("This scroll appears to be blank.");
if (player_mutation_level(MUT_BLURRY_VISION) == 3)
id_the_scroll = false;
break;
case SCR_RANDOM_USELESSNESS:
random_uselessness(item_slot);
break;
case SCR_BLINKING:
blink(1000, false);
break;
case SCR_TELEPORTATION:
you_teleport();
break;
case SCR_REMOVE_CURSE:
if (!remove_curse(false))
id_the_scroll = false;
break;
case SCR_DETECT_CURSE:
if (!detect_curse(false))
id_the_scroll = false;
break;
case SCR_ACQUIREMENT:
mpr("This is a scroll of acquirement!");
more();
acquirement(OBJ_RANDOM, AQ_SCROLL);
break;
case SCR_FEAR:
{
int fear_influenced = 0;
mass_enchantment(ENCH_FEAR, 1000, MHITYOU, NULL, &fear_influenced);
id_the_scroll = fear_influenced;
break;
}
case SCR_NOISE:
noisy(25, you.pos(), "You hear a loud clanging noise!");
break;
case SCR_SUMMONING:
{
const int monster = create_monster(
mgen_data(MONS_ABOMINATION_SMALL, BEH_FRIENDLY,
0, 0, you.pos(), MHITYOU,
MG_FORCE_BEH));
if (monster != -1)
{
mpr("A horrible Thing appears!");
player_angers_monster(&menv[monster]);
}
else
{
canned_msg(MSG_NOTHING_HAPPENS);
id_the_scroll = false;
}
break;
}
case SCR_FOG:
mpr("The scroll dissolves into smoke.");
big_cloud(random_smoke_type(), KC_YOU, you.pos(), 50, 8 + random2(8));
break;
case SCR_MAGIC_MAPPING:
if (you.level_type == LEVEL_PANDEMONIUM)
{
if (!item_type_known(scroll))
mpr("You feel momentarily disoriented.");
else
mpr("Your Earth magic cannot map Pandemonium.");
}
else
magic_mapping(50, 90 + random2(11), false);
break;
case SCR_TORMENT:
torment(TORMENT_SCROLL, you.pos());
did_god_conduct(DID_UNHOLY, 10, item_type_known(scroll));
bad_effect = true;
break;
case SCR_IMMOLATION:
mpr("The scroll explodes in your hands!");
immolation(10, IMMOLATION_SCROLL, you.pos(), alreadyknown, &you);
bad_effect = true;
break;
case SCR_CURSE_WEAPON:
if (!you.weapon()
|| you.weapon()->base_type != OBJ_WEAPONS
|| item_cursed(*you.weapon()))
{
canned_msg(MSG_NOTHING_HAPPENS);
id_the_scroll = false;
}
else
{
do_curse_item( *you.weapon(), false );
learned_something_new(TUT_YOU_CURSED);
bad_effect = true;
}
break;
case SCR_ENCHANT_WEAPON_I:
id_the_scroll = _handle_enchant_weapon(ENCHANT_TO_HIT);
break;
case SCR_ENCHANT_WEAPON_II:
id_the_scroll = _handle_enchant_weapon(ENCHANT_TO_DAM);
break;
case SCR_ENCHANT_WEAPON_III:
if (you.weapon())
{
item_def& wpn = *you.weapon();
const bool is_cursed = item_cursed(wpn);
if (wpn.base_type != OBJ_WEAPONS && wpn.base_type != OBJ_MISSILES
|| !is_cursed
&& !is_enchantable_weapon(wpn, true, true)
&& !is_enchantable_weapon(wpn, true, false))
{
canned_msg(MSG_NOTHING_HAPPENS);
id_the_scroll = false;
break;
}
std::string iname = wpn.name(DESC_CAP_YOUR);
bool success = is_cursed;
if (_handle_enchant_weapon(ENCHANT_TO_HIT, true))
success = true;
if (is_enchantable_weapon(wpn, true, true) && coinflip()
&& _handle_enchant_weapon(ENCHANT_TO_HIT, true))
{
success = true;
}
if (wpn.base_type == OBJ_WEAPONS)
{
if (_handle_enchant_weapon(ENCHANT_TO_DAM, true))
success = true;
if (is_enchantable_weapon(wpn, true, false) && coinflip()
&& _handle_enchant_weapon(ENCHANT_TO_DAM, true))
{
success = true;
}
}
if (is_cursed)
do_uncurse_item(wpn);
if (success)
{
mprf("%s glow%s bright yellow for a while.", iname.c_str(),
wpn.quantity > 1 ? "" : "s");
}
else
{
canned_msg(MSG_NOTHING_HAPPENS);
id_the_scroll = false;
}
}
else
{
canned_msg(MSG_NOTHING_HAPPENS);
id_the_scroll = false;
}
break;
case SCR_VORPALISE_WEAPON:
id_the_scroll = _vorpalise_weapon();
if (!id_the_scroll)
canned_msg(MSG_NOTHING_HAPPENS);
break;
case SCR_IDENTIFY:
if (!item_type_known(scroll))
{
id_the_scroll = _scroll_modify_item(scroll);
if (!id_the_scroll)
tried_on_item = true;
}
else
identify(-1);
break;
case SCR_RECHARGING:
if (!item_type_known(scroll))
{
id_the_scroll = _scroll_modify_item(scroll);
if (!id_the_scroll)
tried_on_item = true;
}
else
recharge_wand(-1);
break;
case SCR_ENCHANT_ARMOUR:
if (!item_type_known(scroll))
{
id_the_scroll = _scroll_modify_item(scroll);
if (!id_the_scroll)
tried_on_item = true;
}
else
_handle_enchant_armour(-1);
break;
case SCR_CURSE_ARMOUR:
{
int count = 0;
int affected = EQ_WEAPON;
for (int i = EQ_CLOAK; i <= EQ_BODY_ARMOUR; i++)
{
if (you.equip[i] != -1 && !item_cursed(you.inv[you.equip[i]]))
{
count++;
if (one_chance_in(count))
affected = i;
}
}
if (affected == EQ_WEAPON)
{
canned_msg(MSG_NOTHING_HAPPENS);
id_the_scroll = false;
break;
}
do_curse_item( you.inv[you.equip[affected]], false );
learned_something_new(TUT_YOU_CURSED);
bad_effect = true;
break;
}
case SCR_HOLY_WORD:
{
int pow = 100;
if (is_good_god(you.religion))
{
pow += (you.religion == GOD_SHINING_ONE) ? you.piety :
you.piety / 2;
}
const bool success = holy_word(pow, HOLY_WORD_SCROLL, you.pos(),
!item_type_known(scroll), &you);
if (!success)
{
canned_msg(MSG_NOTHING_HAPPENS);
id_the_scroll = false;
}
if (item_type_known(scroll) || success)
did_god_conduct(DID_HOLY, 10, item_type_known(scroll));
break;
}
case SCR_SILENCE:
cast_silence(25);
break;
case SCR_VULNERABILITY:
_vulnerability_scroll();
break;
default:
mpr("Read a buggy scroll, please report this.");
break;
}
set_ident_type(scroll, id_the_scroll ? ID_KNOWN_TYPE :
tried_on_item ? ID_TRIED_ITEM_TYPE
: ID_TRIED_TYPE);
if (which_scroll != SCR_PAPER)
{
if (id_the_scroll)
set_ident_flags(scroll, ISFLAG_KNOW_TYPE);
dec_inv_item_quantity( item_slot, 1 );
}
if (!alreadyknown && dangerous)
{
xom_is_stimulated(bad_effect ? 128 : 64);
}
}
void examine_object(void)
{
int item_slot = prompt_invent_item( "Examine which item?",
MT_INVLIST, -1,
true, true, true, 0, -1, NULL,
OPER_EXAMINE );
if (prompt_failed(item_slot))
return;
describe_item( you.inv[item_slot], true );
redraw_screen();
mesclr(true);
}
void use_artefact(unsigned char item_wield_2, bool *show_msgs)
{
use_artefact( you.inv[ item_wield_2 ], show_msgs );
}
void use_artefact(item_def &item, bool *show_msgs, bool unmeld)
{
#define unknown_proprt(prop) (proprt[(prop)] && !known[(prop)])
ASSERT( is_artefact( item ) );
if (is_unrandom_artefact( item ))
{
const unrandart_entry *entry = get_unrand_entry(item.special);
if (entry->equip_func)
entry->equip_func(&item, show_msgs, unmeld);
if (entry->world_reacts_func)
{
equipment_type eq = get_item_slot(item.base_type, item.sub_type);
you.unrand_reacts |= (1 << eq);
}
}
const bool alreadyknown = item_type_known(item);
const bool dangerous = player_in_a_dangerous_place();
artefact_properties_t proprt;
artefact_known_props_t known;
artefact_wpn_properties( item, proprt, known );
if (proprt[ARTP_AC])
{
you.redraw_armour_class = true;
if (!known[ARTP_AC])
{
mprf("You feel %s.", proprt[ARTP_AC] > 0?
"well-protected" : "more vulnerable");
artefact_wpn_learn_prop(item, ARTP_AC);
}
}
if (proprt[ARTP_EVASION])
{
you.redraw_evasion = true;
if (!known[ARTP_EVASION])
{
mprf("You feel somewhat %s.", proprt[ARTP_EVASION] > 0?
"nimbler" : "more awkward");
artefact_wpn_learn_prop(item, ARTP_EVASION);
}
}
if (proprt[ARTP_MAGICAL_POWER])
{
you.redraw_magic_points = true;
if (!known[ARTP_MAGICAL_POWER])
{
mprf("You feel your mana capacity %s.",
proprt[ARTP_MAGICAL_POWER] > 0? "increase" : "decrease");
artefact_wpn_learn_prop(item, ARTP_MAGICAL_POWER);
}
}
modify_stat( STAT_STRENGTH, proprt[ARTP_STRENGTH], false, item );
modify_stat( STAT_INTELLIGENCE, proprt[ARTP_INTELLIGENCE], false, item );
modify_stat( STAT_DEXTERITY, proprt[ARTP_DEXTERITY], false, item );
const artefact_prop_type stat_props[3] =
{ARTP_STRENGTH, ARTP_INTELLIGENCE, ARTP_DEXTERITY};
for (int i = 0; i < 3; i++)
if (unknown_proprt(stat_props[i]))
artefact_wpn_learn_prop(item, stat_props[i]);
if (unknown_proprt(ARTP_LEVITATE)
&& !items_give_ability(item.link, ARTP_LEVITATE))
{
if (player_is_airborne())
mpr("You feel vaguely more buoyant than before.");
else
mpr("You feel buoyant.");
artefact_wpn_learn_prop(item, ARTP_LEVITATE);
}
if (unknown_proprt(ARTP_INVISIBLE) && !you.duration[DUR_INVIS])
{
mpr("You become transparent for a moment.");
artefact_wpn_learn_prop(item, ARTP_INVISIBLE);
}
if (unknown_proprt(ARTP_CAN_TELEPORT)
&& !items_give_ability(item.link, ARTP_CAN_TELEPORT))
{
mpr("You feel slightly jumpy.");
artefact_wpn_learn_prop(item, ARTP_CAN_TELEPORT);
}
if (unknown_proprt(ARTP_BERSERK)
&& !items_give_ability(item.link, ARTP_BERSERK))
{
mpr("You feel a brief urge to hack something to bits.");
artefact_wpn_learn_prop(item, ARTP_BERSERK);
}
if (!unmeld && !item_cursed(item) && proprt[ARTP_CURSED] > 0
&& one_chance_in(proprt[ARTP_CURSED]))
{
do_curse_item( item, false );
artefact_wpn_learn_prop(item, ARTP_CURSED);
}
if (proprt[ARTP_NOISES])
you.attribute[ATTR_NOISES] = 1;
if (proprt[ARTP_SPIRIT_SHIELD])
{
set_mp(0, false);
mpr("You feel the spirits watch over you.");
artefact_wpn_learn_prop(item, ARTP_SPIRIT_SHIELD);
}
if (!alreadyknown && Options.autoinscribe_artefacts)
add_autoinscription(item, artefact_auto_inscription(item));
if (!alreadyknown && dangerous)
{
xom_is_stimulated(128);
}
#undef unknown_proprt
}
bool wearing_slot(int inv_slot)
{
for (int i = EQ_CLOAK; i <= EQ_AMULET; ++i)
if (inv_slot == you.equip[i])
return (true);
return (false);
}
#ifdef USE_TILE
void tile_item_use_floor(int idx)
{
if (mitm[idx].base_type == OBJ_CORPSES
&& mitm[idx].sub_type != CORPSE_SKELETON
&& !food_is_rotten(mitm[idx]))
{
butchery(idx);
}
}
void tile_item_pickup(int idx)
{
pickup_single_item(idx, mitm[idx].quantity);
}
void tile_item_drop(int idx)
{
drop_item(idx, you.inv[idx].quantity);
}
void tile_item_eat_floor(int idx)
{
if (mitm[idx].base_type == OBJ_CORPSES
&& you.species == SP_VAMPIRE
|| mitm[idx].base_type == OBJ_FOOD
&& you.is_undead != US_UNDEAD && you.species != SP_VAMPIRE)
{
if (can_ingest(mitm[idx].base_type, mitm[idx].sub_type, false))
eat_floor_item(idx);
}
}
void tile_item_use_secondary(int idx)
{
const item_def item = you.inv[idx];
if (item.base_type == OBJ_WEAPONS && is_throwable(&you, item))
{
if (check_warning_inscriptions(item, OPER_FIRE))
fire_thing(idx); }
else if (you.equip[EQ_WEAPON] == idx)
{
wield_weapon(true, PROMPT_GOT_SPECIAL); }
else if (_valid_weapon_swap(item))
{
wield_weapon(true, idx); }
}
void tile_item_use(int idx)
{
const item_def item = you.inv[idx];
bool equipped = false;
bool equipped_weapon = false;
for (unsigned int i = 0; i < NUM_EQUIP; i++)
{
if (you.equip[i] == idx)
{
equipped = true;
if (i == EQ_WEAPON)
equipped_weapon = true;
break;
}
}
if (you.equip[EQ_WEAPON] == idx
&& (item.base_type == OBJ_ARMOUR
|| item.base_type == OBJ_JEWELLERY))
{
wield_weapon(true, PROMPT_GOT_SPECIAL);
return;
}
const int type = item.base_type;
switch (type)
{
case OBJ_WEAPONS:
case OBJ_STAVES:
case OBJ_MISCELLANY:
case OBJ_WANDS:
if (!equipped
&& (type == OBJ_WEAPONS || type == OBJ_STAVES || is_deck(item)
|| type == OBJ_MISCELLANY
&& item.sub_type == MISC_LANTERN_OF_SHADOWS))
{
wield_weapon(true, idx);
return;
}
if (item_is_evokable(item))
{
evoke_item(idx);
return;
}
if (equipped)
wield_weapon(true, PROMPT_GOT_SPECIAL); return;
case OBJ_MISSILES:
if (check_warning_inscriptions(item, OPER_FIRE))
fire_thing(idx);
return;
case OBJ_ARMOUR:
if (player_in_bat_form())
{
mpr("You can't wear or remove anything in your present form.");
return;
}
if (equipped && !equipped_weapon)
{
if (check_warning_inscriptions(item, OPER_TAKEOFF))
takeoff_armour(idx);
}
else if (check_warning_inscriptions(item, OPER_WEAR))
wear_armour(idx);
return;
case OBJ_CORPSES:
if (you.species != SP_VAMPIRE
|| item.sub_type == CORPSE_SKELETON
|| food_is_rotten(item))
{
break;
}
case OBJ_FOOD:
if (check_warning_inscriptions(item, OPER_EAT))
eat_food(idx);
return;
case OBJ_BOOKS:
if (item.sub_type == BOOK_MANUAL
|| item.sub_type == BOOK_DESTRUCTION)
{
if (check_warning_inscriptions(item, OPER_READ))
handle_read_book(idx);
} else if (check_warning_inscriptions(item, OPER_MEMORISE))
learn_spell(); return;
case OBJ_SCROLLS:
if (check_warning_inscriptions(item, OPER_READ))
read_scroll(idx);
return;
case OBJ_JEWELLERY:
if (equipped && !equipped_weapon)
{
if (check_warning_inscriptions(item, OPER_REMOVE))
remove_ring(idx);
}
else if (check_warning_inscriptions(item, OPER_PUTON))
puton_ring(idx);
return;
case OBJ_POTIONS:
if (check_warning_inscriptions(item, OPER_QUAFF))
drink(idx);
return;
default:
return;
}
}
#endif