diff options
author | dshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573> | 2006-11-22 08:41:20 +0000 |
---|---|---|
committer | dshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573> | 2006-11-22 08:41:20 +0000 |
commit | 1d0f57cbceb778139ca215cc4fcfd1584951f6dd (patch) | |
tree | cafd60c944c51fcce778aa5d6912bc548c518339 /crawl-ref/source/item_use.cc | |
parent | 6f5e187a9e5cd348296dba2fd89d2e206e775a01 (diff) | |
download | crawl-ref-1d0f57cbceb778139ca215cc4fcfd1584951f6dd.tar.gz crawl-ref-1d0f57cbceb778139ca215cc4fcfd1584951f6dd.zip |
Merged stone_soup r15:451 into trunk.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@452 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/item_use.cc')
-rw-r--r-- | crawl-ref/source/item_use.cc | 1626 |
1 files changed, 984 insertions, 642 deletions
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc index 7ea1db4900..f683822891 100644 --- a/crawl-ref/source/item_use.cc +++ b/crawl-ref/source/item_use.cc @@ -3,6 +3,8 @@ * Summary: Functions for making use of inventory items. * Written by: Linley Henzell * + * Modified for Crawl Reference by $Author$ on $Date$ + * * Change History (most recent first): * * <8> 28July2000 GDL Revised player throwing @@ -45,6 +47,7 @@ #include "it_use3.h" #include "items.h" #include "itemname.h" +#include "itemprop.h" #include "misc.h" #include "monplace.h" #include "monstuff.h" @@ -64,66 +67,103 @@ #include "stuff.h" #include "transfor.h" #include "view.h" -#include "wpn-misc.h" bool drink_fountain(void); -static void throw_it(struct bolt &pbolt, int throw_2); -void use_randart(unsigned char item_wield_2); -static bool enchant_weapon( int which_stat, bool quiet = false ); static bool enchant_armour( void ); // Rather messy - we've gathered all the can't-wield logic from wield_weapon() // here. -bool can_wield(const item_def& weapon) +bool can_wield(const item_def *weapon, bool say_reason) { - if (you.berserker) return false; +#define SAY(x) if (say_reason) { x; } else + + if (you.berserker) + { + SAY(canned_msg(MSG_TOO_BERSERK)); + return false; + } if (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE && !can_equip( EQ_WEAPON )) + { + SAY(mpr("You can't wield anything in your present form.")); return false; + } if (you.equip[EQ_WEAPON] != -1 && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_WEAPONS && item_cursed( you.inv[you.equip[EQ_WEAPON]] )) + { + SAY(mpr("You can't unwield your weapon to draw a new one!")); return false; + } - if (weapon.base_type != OBJ_WEAPONS && weapon.base_type == OBJ_STAVES - && you.equip[EQ_SHIELD] != -1) - return false; + // If we don't have an actual weapon to check, return now. + 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 (is_shield_incompatible(*weapon)) + { + SAY(mpr("You can't wield that with a shield.")); + return (false); + } + + // We don't have to check explicitly for staves - all staves are wieldable + // by everyone. + if (weapon->base_type != OBJ_WEAPONS) + return (true); if ((you.species < SP_OGRE || you.species > SP_OGRE_MAGE) - && mass_item( weapon ) >= 500) + && item_mass( *weapon ) >= 300) + { + SAY(mpr("That's too large and heavy for you to wield.")); return false; + } if ((you.species == SP_HALFLING || you.species == SP_GNOME || you.species == SP_KOBOLD || you.species == SP_SPRIGGAN) - && (weapon.sub_type == WPN_GREAT_SWORD - || weapon.sub_type == WPN_TRIPLE_SWORD - || weapon.sub_type == WPN_GREAT_MACE - || weapon.sub_type == WPN_GREAT_FLAIL - || weapon.sub_type == WPN_BATTLEAXE - || weapon.sub_type == WPN_EXECUTIONERS_AXE - || weapon.sub_type == WPN_HALBERD - || weapon.sub_type == WPN_GLAIVE - || weapon.sub_type == WPN_GIANT_CLUB - || weapon.sub_type == WPN_GIANT_SPIKED_CLUB - || weapon.sub_type == WPN_SCYTHE)) + && (weapon->sub_type == WPN_GREAT_SWORD + || weapon->sub_type == WPN_TRIPLE_SWORD + || weapon->sub_type == WPN_GREAT_MACE + || weapon->sub_type == WPN_DIRE_FLAIL + || weapon->sub_type == WPN_BATTLEAXE + || weapon->sub_type == WPN_EXECUTIONERS_AXE + || weapon->sub_type == WPN_LOCHABER_AXE + || weapon->sub_type == WPN_HALBERD + || weapon->sub_type == WPN_GLAIVE + || weapon->sub_type == WPN_GIANT_CLUB + || weapon->sub_type == WPN_GIANT_SPIKED_CLUB + || weapon->sub_type == WPN_LONGBOW + || weapon->sub_type == WPN_SCYTHE)) + { + SAY(mpr("That's too large for you to wield.")); return false; + } - if (hands_reqd_for_weapon( weapon.base_type, - weapon.sub_type ) == HANDS_TWO_HANDED - && you.equip[EQ_SHIELD] != -1) - return false; - - int weap_brand = get_weapon_brand( weapon ); - + int weap_brand = get_weapon_brand( *weapon ); if ((you.is_undead || you.species == SP_DEMONSPAWN) - && (!is_fixed_artefact( weapon ) + && (!is_fixed_artefact( *weapon ) && (weap_brand == SPWPN_HOLY_WRATH - || weap_brand == SPWPN_DISRUPTION))) + || weap_brand == SPWPN_DISRUPTION + || (weapon->base_type == OBJ_WEAPONS + && weapon->sub_type == WPN_BLESSED_BLADE)))) + { + SAY(mpr("This weapon will not allow you to wield it.")); return false; + } // We can wield this weapon. Phew! return true; + +#undef SAY } bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) @@ -137,28 +177,9 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) return (false); } - if (you.berserker) - { - canned_msg(MSG_TOO_BERSERK); - return (false); - } - - if (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE) - { - if (!can_equip( EQ_WEAPON )) - { - mpr("You can't wield anything in your present form."); - return (false); - } - } - - if (you.equip[EQ_WEAPON] != -1 - && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_WEAPONS - && item_cursed( you.inv[you.equip[EQ_WEAPON]] )) - { - mpr("You can't unwield your weapon to draw a new one!"); + // Any general reasons why we can't wield a new object? + if (!can_wield(NULL, true)) return (false); - } if (you.sure_blade) { @@ -185,8 +206,10 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) if (!auto_wield || !is_valid_item(you.inv[item_slot]) || force_unwield) { if (!auto_wield) - item_slot = prompt_invent_item( "Wield which item (- for none)?", - OSEL_WIELD, true, true, true, '-' ); + item_slot = prompt_invent_item( + "Wield which item (- for none)?", + MT_INVSELECT, OSEL_WIELD, + true, true, true, '-', NULL, OPER_WIELD); else item_slot = PROMPT_GOT_SPECIAL; @@ -199,8 +222,8 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) { if (you.equip[EQ_WEAPON] != -1) { - unwield_item(you.equip[EQ_WEAPON]); - you.turn_is_over = 1; + unwield_item(you.equip[EQ_WEAPON], show_weff_messages); + you.turn_is_over = true; you.equip[EQ_WEAPON] = -1; canned_msg( MSG_EMPTY_HANDED ); @@ -221,83 +244,14 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) return (true); } - for (int i = EQ_CLOAK; i <= EQ_AMULET; i++) - { - if (item_slot == you.equip[i]) - { - mpr("You are wearing that object!"); - return (false); - } - } - - if (you.inv[item_slot].base_type != OBJ_WEAPONS) - { - if (you.inv[item_slot].base_type == OBJ_STAVES - && you.equip[EQ_SHIELD] != -1) - { - mpr("You can't wield that with a shield."); - return (false); - } - - if (you.equip[EQ_WEAPON] != -1) - unwield_item(you.equip[EQ_WEAPON]); - - you.equip[EQ_WEAPON] = item_slot; - } - else - { - if ((you.species < SP_OGRE || you.species > SP_OGRE_MAGE) - && mass_item( you.inv[item_slot] ) >= 500) - { - mpr("That's too large and heavy for you to wield."); - return (false); - } - - if ((you.species == SP_HALFLING || you.species == SP_GNOME - || you.species == SP_KOBOLD || you.species == SP_SPRIGGAN) - - && (you.inv[item_slot].sub_type == WPN_GREAT_SWORD - || you.inv[item_slot].sub_type == WPN_TRIPLE_SWORD - || you.inv[item_slot].sub_type == WPN_GREAT_MACE - || you.inv[item_slot].sub_type == WPN_GREAT_FLAIL - || you.inv[item_slot].sub_type == WPN_BATTLEAXE - || you.inv[item_slot].sub_type == WPN_EXECUTIONERS_AXE - || you.inv[item_slot].sub_type == WPN_HALBERD - || you.inv[item_slot].sub_type == WPN_GLAIVE - || you.inv[item_slot].sub_type == WPN_GIANT_CLUB - || you.inv[item_slot].sub_type == WPN_GIANT_SPIKED_CLUB - || you.inv[item_slot].sub_type == WPN_SCYTHE)) - { - mpr("That's too large for you to wield."); - return (false); - - } - - if (hands_reqd_for_weapon( you.inv[item_slot].base_type, - you.inv[item_slot].sub_type ) == HANDS_TWO_HANDED - && you.equip[EQ_SHIELD] != -1) - { - mpr("You can't wield that with a shield."); - return (false); - } - - int weap_brand = get_weapon_brand( you.inv[item_slot] ); - - if ((you.is_undead || you.species == SP_DEMONSPAWN) - && (!is_fixed_artefact( you.inv[item_slot] ) - && (weap_brand == SPWPN_HOLY_WRATH - || weap_brand == SPWPN_DISRUPTION))) - { - mpr("This weapon will not allow you to wield it."); - you.turn_is_over = 1; - return (false); - } + if (!can_wield(&you.inv[item_slot], true)) + return (false); - if (you.equip[EQ_WEAPON] != -1) - unwield_item(you.equip[EQ_WEAPON]); + // Go ahead and wield the weapon. + if (you.equip[EQ_WEAPON] != -1) + unwield_item(you.equip[EQ_WEAPON], show_weff_messages); - you.equip[EQ_WEAPON] = item_slot; - } + you.equip[EQ_WEAPON] = item_slot; // any oddness on wielding taken care of here wield_effects(item_slot, show_weff_messages); @@ -313,13 +267,77 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) you.time_taken /= 10; you.wield_change = true; - you.turn_is_over = 1; + 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); + + // Any way to avoid the double entendre? :-) + 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; + + // Don't warn the player unless the slowdown is real. + 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); + } +} + +// Warn if your shield is greatly impacting the effectiveness of your weapon? +void warn_shield_penalties() +{ + if (!player_shield()) + return; + + // Warnings are limited to rods and bows at the moment. + 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); +} + // provide a function for handling initial wielding of 'special' -// weapons, or those whose function is annoying to reproduce in +// weapons, or those whose function is annoying to reproduce in // other places *cough* auto-butchering *cough* {gdl} void wield_effects(int item_wield_2, bool showMsgs) @@ -356,7 +374,7 @@ void wield_effects(int item_wield_2, bool showMsgs) if (you.inv[item_wield_2].base_type == OBJ_WEAPONS) { - if (is_demonic(you.inv[item_wield_2].sub_type) + if (is_demonic(you.inv[item_wield_2]) && (you.religion == GOD_ZIN || you.religion == GOD_SHINING_ONE || you.religion == GOD_ELYVILON)) { @@ -554,6 +572,9 @@ void wield_effects(int item_wield_2, bool showMsgs) if (item_cursed( you.inv[item_wield_2] )) mpr("It sticks to your hand!"); } + + if (showMsgs) + warn_shield_penalties(); } // end wield_weapon() //--------------------------------------------------------------- @@ -564,7 +585,7 @@ void wield_effects(int item_wield_2, bool showMsgs) // something legit. // //--------------------------------------------------------------- -bool armour_prompt( const std::string & mesg, int *index ) +bool armour_prompt( const std::string & mesg, int *index, operation_types oper) { ASSERT(index != NULL); @@ -577,7 +598,9 @@ bool armour_prompt( const std::string & mesg, int *index ) canned_msg(MSG_TOO_BERSERK); else { - slot = prompt_invent_item( mesg.c_str(), OBJ_ARMOUR ); + slot = prompt_invent_item( mesg.c_str(), MT_INVSELECT, OBJ_ARMOUR, + true, true, true, 0, NULL, + oper ); if (slot != PROMPT_ABORT) { @@ -609,43 +632,28 @@ void wear_armour(void) { int armour_wear_2; - if (!armour_prompt("Wear which item?", &armour_wear_2)) + if (!armour_prompt("Wear which item?", &armour_wear_2, OPER_WEAR)) return; do_wear_armour( armour_wear_2, false ); } -int armour_equip_slot(const item_def &item) +static int armour_equip_delay(const item_def &item) { - int wh_equip = EQ_BODY_ARMOUR; + int delay = property( item, PARM_AC ); - switch (item.sub_type) - { - case ARM_BUCKLER: - case ARM_LARGE_SHIELD: - case ARM_SHIELD: - wh_equip = EQ_SHIELD; - break; - case ARM_CLOAK: - wh_equip = EQ_CLOAK; - break; - case ARM_HELMET: - wh_equip = EQ_HELMET; - break; - case ARM_GLOVES: - wh_equip = EQ_GLOVES; - break; - case ARM_BOOTS: - wh_equip = EQ_BOOTS; - break; - } - return (wh_equip); + // Shields are comparatively easy to wear. + if (is_shield( item )) + delay = delay / 2 + 1; + + if (delay < 1) + delay = 1; + + return (delay); } bool do_wear_armour( int item, bool quiet ) { - char wh_equip = 0; - if (!is_valid_item( you.inv[item] )) { if (!quiet) @@ -654,7 +662,8 @@ bool do_wear_armour( int item, bool quiet ) return (false); } - if (you.inv[item].base_type != OBJ_ARMOUR) + const int base_type = you.inv[item].base_type; + if (base_type != OBJ_ARMOUR) { if (!quiet) mpr("You can't wear that."); @@ -662,6 +671,10 @@ bool do_wear_armour( int item, bool quiet ) return (false); } + const int sub_type = you.inv[item].sub_type; + const item_def &invitem = you.inv[item]; + const equipment_type slot = get_armour_slot(invitem); + if (item == you.equip[EQ_WEAPON]) { if (!quiet) @@ -684,12 +697,10 @@ bool do_wear_armour( int item, bool quiet ) // if you're wielding something, if (you.equip[EQ_WEAPON] != -1 // attempting to wear a shield, - && (you.inv[item].sub_type == ARM_SHIELD - || you.inv[item].sub_type == ARM_BUCKLER - || you.inv[item].sub_type == ARM_LARGE_SHIELD) - // weapon is two-handed - && hands_reqd_for_weapon(you.inv[you.equip[EQ_WEAPON]].base_type, - you.inv[you.equip[EQ_WEAPON]].sub_type) == HANDS_TWO_HANDED) + && is_shield(you.inv[item]) + && is_shield_incompatible( + you.inv[you.equip[EQ_WEAPON]], + &you.inv[item])) { if (!quiet) mpr("You'd need three hands to do that!"); @@ -697,21 +708,26 @@ bool do_wear_armour( int item, bool quiet ) return (false); } - if (you.inv[item].sub_type == ARM_BOOTS) - { - if (you.species != SP_NAGA && you.inv[item].plus2 == TBOOT_NAGA_BARDING) - { - if (!quiet) - mpr("You can't wear that!"); + bool can_wear = true; + if (sub_type == ARM_NAGA_BARDING) + can_wear = (you.species == SP_NAGA); - return (false); - } + if (sub_type == ARM_CENTAUR_BARDING) + can_wear = (you.species == SP_CENTAUR); + + if (!can_wear) + { + if (!quiet) + mpr("You can't wear that!"); + return (false); + } - if (you.species != SP_CENTAUR && you.inv[item].plus2 == TBOOT_CENTAUR_BARDING) + if (you.inv[item].sub_type == ARM_BOOTS) + { + if (you.species == SP_NAGA || you.species == SP_CENTAUR) { if (!quiet) - mpr("You can't wear that!"); - + mpr("You can't wear that!"); return (false); } @@ -724,28 +740,24 @@ bool do_wear_armour( int item, bool quiet ) } } - wh_equip = armour_equip_slot(you.inv[item]); - - if (you.species == SP_NAGA && you.inv[item].sub_type == ARM_BOOTS - && you.inv[item].plus2 == TBOOT_NAGA_BARDING + if (you.species == SP_NAGA && sub_type == ARM_NAGA_BARDING && !player_is_shapechanged()) { // it fits } else if (you.species == SP_CENTAUR - && you.inv[item].sub_type == ARM_BOOTS - && you.inv[item].plus2 == TBOOT_CENTAUR_BARDING + && sub_type == ARM_CENTAUR_BARDING && !player_is_shapechanged()) { // it fits } - else if (you.inv[item].sub_type == ARM_HELMET - && (cmp_helmet_type( you.inv[item], THELM_CAP ) - || cmp_helmet_type( you.inv[item], THELM_WIZARD_HAT ))) + else if (sub_type == ARM_HELMET + && (get_helmet_type(invitem) == THELM_CAP + || get_helmet_type(invitem) == THELM_WIZARD_HAT)) { // caps & wiz hats always fit, unless your head's too big (ogres &c) } - else if (!can_equip( wh_equip )) + else if (!can_equip( slot )) { if (!quiet) mpr("You can't wear that in your present form."); @@ -755,8 +767,8 @@ bool do_wear_armour( int item, bool quiet ) // Cannot swim in heavy armour if (player_is_swimming() - && wh_equip == EQ_BODY_ARMOUR - && !is_light_armour( you.inv[item] )) + && slot == EQ_BODY_ARMOUR + && !is_light_armour( invitem )) { if (!quiet) mpr("You can't swim in that!"); @@ -768,14 +780,14 @@ bool do_wear_armour( int item, bool quiet ) if ((you.species >= SP_OGRE && you.species <= SP_OGRE_MAGE) || player_genus(GENPC_DRACONIAN)) { - if ((you.inv[item].sub_type >= ARM_LEATHER_ARMOUR - && you.inv[item].sub_type <= ARM_PLATE_MAIL) - || (you.inv[item].sub_type >= ARM_GLOVES - && you.inv[item].sub_type <= ARM_BUCKLER) - || you.inv[item].sub_type == ARM_CRYSTAL_PLATE_MAIL - || (you.inv[item].sub_type == ARM_HELMET - && (cmp_helmet_type( you.inv[item], THELM_HELM ) - || cmp_helmet_type( you.inv[item], THELM_HELMET )))) + if ((sub_type >= ARM_LEATHER_ARMOUR + && sub_type <= ARM_PLATE_MAIL) + || (sub_type >= ARM_GLOVES + && sub_type <= ARM_BUCKLER) + || sub_type == ARM_CRYSTAL_PLATE_MAIL + || (sub_type == ARM_HELMET + && (get_helmet_type(invitem) == THELM_HELM + || get_helmet_type(invitem) == THELM_HELMET))) { if (!quiet) mpr("This armour doesn't fit on your body."); @@ -787,16 +799,16 @@ bool do_wear_armour( int item, bool quiet ) // Tiny races if (you.species == SP_SPRIGGAN) { - if ((you.inv[item].sub_type >= ARM_LEATHER_ARMOUR - && you.inv[item].sub_type <= ARM_PLATE_MAIL) - || you.inv[item].sub_type == ARM_GLOVES - || you.inv[item].sub_type == ARM_BOOTS - || you.inv[item].sub_type == ARM_SHIELD - || you.inv[item].sub_type == ARM_LARGE_SHIELD - || you.inv[item].sub_type == ARM_CRYSTAL_PLATE_MAIL - || (you.inv[item].sub_type == ARM_HELMET - && (cmp_helmet_type( you.inv[item], THELM_HELM ) - || cmp_helmet_type( you.inv[item], THELM_HELMET )))) + 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 + || (sub_type == ARM_HELMET + && (get_helmet_type(invitem) == THELM_HELM + || get_helmet_type(invitem) == THELM_HELMET))) { if (!quiet) mpr("This armour doesn't fit on your body."); @@ -808,11 +820,10 @@ bool do_wear_armour( int item, bool quiet ) bool removedCloak = false; int cloak = -1; - if ((you.inv[item].sub_type < ARM_SHIELD - || you.inv[item].sub_type > ARM_LARGE_SHIELD) - && (you.equip[EQ_CLOAK] != -1 && !cloak_is_being_removed())) + if (slot == EQ_BODY_ARMOUR + && you.equip[EQ_CLOAK] != -1 && !cloak_is_being_removed()) { - if (item_uncursed( you.inv[you.equip[EQ_CLOAK]] )) + if (!item_cursed( you.inv[you.equip[EQ_CLOAK]] )) { cloak = you.equip[ EQ_CLOAK ]; if (!takeoff_armour(you.equip[EQ_CLOAK])) @@ -829,54 +840,45 @@ bool do_wear_armour( int item, bool quiet ) } } - if (you.inv[item].sub_type == ARM_CLOAK && you.equip[EQ_CLOAK] != -1) + if (slot == EQ_CLOAK && you.equip[EQ_CLOAK] != -1) { if (!takeoff_armour(you.equip[EQ_CLOAK])) return (false); } - if (you.inv[item].sub_type == ARM_HELMET && you.equip[EQ_HELMET] != -1) + if (slot == EQ_HELMET && you.equip[EQ_HELMET] != -1) { if (!takeoff_armour(you.equip[EQ_HELMET])) return (false); } - if (you.inv[item].sub_type == ARM_GLOVES && you.equip[EQ_GLOVES] != -1) + if (slot == EQ_GLOVES && you.equip[EQ_GLOVES] != -1) { if (!takeoff_armour(you.equip[EQ_GLOVES])) return (false); } - if (you.inv[item].sub_type == ARM_BOOTS && you.equip[EQ_BOOTS] != -1) + if (slot == EQ_BOOTS && you.equip[EQ_BOOTS] != -1) { if (!takeoff_armour(you.equip[EQ_BOOTS])) return (false); } - if ((you.inv[item].sub_type == ARM_SHIELD - || you.inv[item].sub_type == ARM_LARGE_SHIELD - || you.inv[item].sub_type == ARM_BUCKLER) - && you.equip[EQ_SHIELD] != -1) + if (slot == EQ_SHIELD && you.equip[EQ_SHIELD] != -1) { if (!takeoff_armour(you.equip[EQ_SHIELD])) return (false); } - if ((you.inv[item].sub_type < ARM_SHIELD - || you.inv[item].sub_type > ARM_LARGE_SHIELD) - && you.equip[EQ_BODY_ARMOUR] != -1) + if (slot == EQ_BODY_ARMOUR && you.equip[EQ_BODY_ARMOUR] != -1) { if (!takeoff_armour(you.equip[EQ_BODY_ARMOUR])) return (false); } - you.turn_is_over = 1; - - int delay = property( you.inv[item], PARM_AC ); - - if (delay < 1) - delay = 1; + you.turn_is_over = true; + int delay = armour_equip_delay( you.inv[item] ); if (delay) start_delay( DELAY_ARMOUR_ON, delay, item ); @@ -910,13 +912,13 @@ bool takeoff_armour(int item) bool removedCloak = false; int cloak = -1; + const equipment_type slot = get_armour_slot(you.inv[item]); - if (you.inv[item].sub_type < ARM_SHIELD - || you.inv[item].sub_type > ARM_LARGE_SHIELD) + if (slot == EQ_BODY_ARMOUR) { if (you.equip[EQ_CLOAK] != -1 && !cloak_is_being_removed()) { - if (item_uncursed( you.inv[you.equip[EQ_CLOAK]] )) + if (!item_cursed( you.inv[you.equip[EQ_CLOAK]] )) { cloak = you.equip[ EQ_CLOAK ]; if (!takeoff_armour(you.equip[EQ_CLOAK])) @@ -941,11 +943,9 @@ bool takeoff_armour(int item) } else { - switch (you.inv[item].sub_type) + switch (slot) { - case ARM_BUCKLER: - case ARM_LARGE_SHIELD: - case ARM_SHIELD: + case EQ_SHIELD: if (item != you.equip[EQ_SHIELD]) { mpr("You aren't wearing that!"); @@ -953,7 +953,7 @@ bool takeoff_armour(int item) } break; - case ARM_CLOAK: + case EQ_CLOAK: if (item != you.equip[EQ_CLOAK]) { mpr("You aren't wearing that!"); @@ -961,7 +961,7 @@ bool takeoff_armour(int item) } break; - case ARM_HELMET: + case EQ_HELMET: if (item != you.equip[EQ_HELMET]) { mpr("You aren't wearing that!"); @@ -969,8 +969,7 @@ bool takeoff_armour(int item) } break; - - case ARM_GLOVES: + case EQ_GLOVES: if (item != you.equip[EQ_GLOVES]) { mpr("You aren't wearing that!"); @@ -978,23 +977,22 @@ bool takeoff_armour(int item) } break; - case ARM_BOOTS: + case EQ_BOOTS: if (item != you.equip[EQ_BOOTS]) { mpr("You aren't wearing that!"); return false; } break; + + default: + break; } } - you.turn_is_over = 1; - - int delay = property( you.inv[item], PARM_AC ); - - if (delay < 1) - delay = 1; + you.turn_is_over = true; + int delay = armour_equip_delay( you.inv[item] ); start_delay( DELAY_ARMOUR_OFF, delay, item ); if (removedCloak) @@ -1019,7 +1017,9 @@ void throw_anything(void) return; } - throw_slot = prompt_invent_item( "Throw which item?", OBJ_MISSILES ); + throw_slot = prompt_invent_item( "Throw which item?", MT_INVSELECT, + OBJ_MISSILES, true, true, true, 0, NULL, + OPER_THROW ); if (throw_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -1108,9 +1108,9 @@ int get_fire_item_index( void ) // check if we have ammo for a wielded launcher: if (weapon != -1 && you.inv[ weapon ].base_type == OBJ_WEAPONS - && launches_things( you.inv[ weapon ].sub_type )) + && is_range_weapon( you.inv[ weapon ] )) { - int type_wanted = launched_by( you.inv[ weapon ].sub_type ); + int type_wanted = fires_ammo_type( you.inv[ weapon ] ); item = try_finding_missile( type_wanted ); } break; @@ -1179,9 +1179,103 @@ void shoot_thing(void) throw_it( beam, item ); } // end shoot_thing() +// Returns delay multiplier numerator (denominator should be 100) for the +// launcher with the currently equipped shield. +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, player_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; + } + + // Adjust for shields skill. + if (speed_adjust > 100) + speed_adjust -= ((speed_adjust - 100) * 5 / 10) + * you.skills[SK_SHIELDS] / 27; + + return (speed_adjust); +} + +// Returns the attack cost of using the launcher, taking skill and shields +// into consideration. NOTE: You must pass in the shield; if you send in +// NULL, this function assumes no shield is in use. +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; + + // Reduce runaway bow overpoweredness. + if (launcher_skill == SK_BOWS) + speed_min = 60; + + if (shield) + { + const int speed_adjust = launcher_shield_slowdown(launcher, shield); + + // Shields also reduce the speed cap. + 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 nerf as per 4.1. Even with the nerf, bows of speed are the + // best bows, bar none. + speed = 2 * speed / 3; + } + + return (speed); +} + +// Determines if the end result of the combined launcher + ammo brands a +// fire/frost beam. +bool elemental_missile_beam(int launcher_brand, int ammo_brand) +{ + int element = (launcher_brand == SPWPN_FROST) + + (ammo_brand == SPMSL_ICE) + - (launcher_brand == SPWPN_FLAME) + - (ammo_brand == SPMSL_FLAME); + return (element); +} + // throw_it - currently handles player throwing only. Monster // throwing is handled in mstuff2:mons_throw() -static void throw_it(struct bolt &pbolt, int throw_2) +// Note: If dummy_target is non-NULL, throw_it fakes a bolt and calls +// affect() on the monster's square. +// +// Return value is only relevant if dummy_target is non-NULL, and returns +// true if dummy_target is hit. +bool throw_it(struct bolt &pbolt, int throw_2, monsters *dummy_target) { struct dist thr; char shoot_skill = 0; @@ -1194,31 +1288,50 @@ static void throw_it(struct bolt &pbolt, int throw_2) int lnchHitBonus = 0, lnchDamBonus = 0; // special add from launcher int exHitBonus = 0, exDamBonus = 0; // 'extra' bonus from skill/dex/str int effSkill = 0; // effective launcher skill + int dice_mult = 100; bool launched = false; // item is launched bool thrown = false; // item is sensible thrown item + int slayDam = 0; - // Making a copy of the item: changed only for venom launchers - item_def item = you.inv[throw_2]; - item.quantity = 1; - item.slot = index_to_letter(item.link); - origin_set_unknown(item); - - char str_pass[ ITEMNAME_SIZE ]; - - mpr( STD_DIRECTION_PROMPT, MSGCH_PROMPT ); - - message_current_target(); - - direction( thr, DIR_NONE, TARG_ENEMY ); + if (dummy_target) + { + thr.isValid = true; + thr.isCancel = false; + thr.tx = dummy_target->x; + thr.ty = dummy_target->y; + } + else + { + mpr( STD_DIRECTION_PROMPT, MSGCH_PROMPT ); + message_current_target(); + direction( thr, DIR_NONE, TARG_ENEMY ); + } if (!thr.isValid) { if (thr.isCancel) canned_msg(MSG_OK); - return; + return (false); + } + + // Must unwield before fire_beam() makes a copy in order to remove things + // like temporary branding. -- bwr + if (throw_2 == you.equip[EQ_WEAPON] && you.inv[throw_2].quantity == 1) + { + unwield_item( throw_2 ); + you.equip[EQ_WEAPON] = -1; + canned_msg( MSG_EMPTY_HANDED ); } + // Making a copy of the item: changed only for venom launchers + item_def item = you.inv[throw_2]; + item.quantity = 1; + item.slot = index_to_letter(item.link); + origin_set_unknown(item); + + char str_pass[ ITEMNAME_SIZE ]; + if (you.conf) { thr.isTarget = true; @@ -1226,10 +1339,9 @@ static void throw_it(struct bolt &pbolt, int throw_2) thr.ty = you.y_pos + random2(13) - 6; } - // even though direction is allowed, we're throwing so we + // even though direction is allowed, we're throwing so we // want to use tx, ty to make the missile fly to map edge. - pbolt.target_x = thr.tx; - pbolt.target_y = thr.ty; + pbolt.set_target(thr); pbolt.flavour = BEAM_MISSILE; // pbolt.range is set below @@ -1257,10 +1369,10 @@ static void throw_it(struct bolt &pbolt, int throw_2) pbolt.colour = item.colour; item_name( item, DESC_PLAIN, str_pass ); - strcpy( pbolt.beam_name, str_pass ); + pbolt.name = str_pass; pbolt.thrower = KILL_YOU_MISSILE; - pbolt.aux_source = NULL; + pbolt.aux_source.clear(); // get the ammo/weapon type. Convenience. wepClass = item.base_type; @@ -1281,11 +1393,11 @@ static void throw_it(struct bolt &pbolt, int throw_2) } // baseHit and damage for generic objects - baseHit = you.strength - mass_item(item) / 10; + baseHit = you.strength - item_mass(item) / 10; if (baseHit > 0) baseHit = 0; - baseDam = mass_item(item) / 100; + baseDam = item_mass(item) / 100; // special: might be throwing generic weapon; // use base wep. damage, w/ penalty @@ -1313,21 +1425,49 @@ static void throw_it(struct bolt &pbolt, int throw_2) // CALCULATIONS FOR LAUNCHED WEAPONS if (launched) { - const int bow_brand = get_weapon_brand( you.inv[you.equip[EQ_WEAPON]] ); + const item_def &launcher = you.inv[you.equip[EQ_WEAPON]]; + const int bow_brand = get_weapon_brand( launcher ); const int ammo_brand = get_ammo_brand( item ); bool poisoned = (ammo_brand == SPMSL_POISONED || ammo_brand == SPMSL_POISONED_II); + const int rc_skill = you.skills[SK_RANGED_COMBAT]; - // this is deliberately confusing: the 'hit' value for - // ammo is the _damage_ when used with a launcher. Geez. - baseHit = 0; - baseDam = property( item, PWPN_HIT ); + 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); + + // Slings are terribly weakened otherwise + if (lnch_base_dam == 0) + baseDam = item_base_dam; + + // If we've a zero base damage + an elemental brand, up the damage + // slightly so the brand has something to work with. This should + // only apply to needles. + if (!baseDam && elemental_missile_beam(bow_brand, ammo_brand)) + baseDam = 4; + + // [dshaligram] This is a horrible hack - we force beam.cc to consider + // this beam "needle-like". + 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 // fix ammo damage bonus, since missiles only use inv_plus ammoDamBonus = ammoHitBonus; // check for matches; dwarven,elven,orcish - if (!cmp_equip_race( you.inv[you.equip[EQ_WEAPON]], 0 )) + if (!get_equip_race(you.inv[you.equip[EQ_WEAPON]]) == 0) { if (get_equip_race( you.inv[you.equip[EQ_WEAPON]] ) == get_equip_race( item )) @@ -1336,7 +1476,8 @@ static void throw_it(struct bolt &pbolt, int throw_2) baseDam += 1; // elves with elven bows - if (cmp_equip_race(you.inv[you.equip[EQ_WEAPON]], ISFLAG_ELVEN) + if (get_equip_race(you.inv[you.equip[EQ_WEAPON]]) + == ISFLAG_ELVEN && player_genus(GENPC_ELVEN)) { baseHit += 1; @@ -1344,86 +1485,78 @@ static void throw_it(struct bolt &pbolt, int throw_2) } } - if (you.inv[you.equip[EQ_WEAPON]].sub_type == WPN_CROSSBOW) - { - // extra time taken, as a percentage. range from 30 -> 12 - int extraTime = 30 - ((you.skills[SK_CROSSBOWS] * 2) / 3); - - you.time_taken = (100 + extraTime) * you.time_taken; - you.time_taken /= 100; - } - - if (bow_brand == SPWPN_SPEED) - { - you.time_taken *= 5; - you.time_taken /= 10; - } - - // for all launched weapons, maximum effective specific skill + // for all launched weapons, maximum effective specific skill // is twice throwing skill. This models the fact that no matter - // how 'good' you are with a bow, if you know nothing about + // how 'good' you are with a bow, if you know nothing about // trajectories you're going to be a damn poor bowman. Ditto // for crossbows and slings. - switch (lnchType) - { - case WPN_SLING: - shoot_skill = you.skills[SK_SLINGS]; - break; - case WPN_BOW: - shoot_skill = you.skills[SK_BOWS]; - break; - case WPN_BLOWGUN: - shoot_skill = you.skills[SK_DARTS]; - break; - case WPN_CROSSBOW: - case WPN_HAND_CROSSBOW: - shoot_skill = you.skills[SK_CROSSBOWS]; - break; - default: - shoot_skill = 0; - break; - } - effSkill = you.skills[SK_THROWING] * 2 + 1; - effSkill = (shoot_skill > effSkill) ? effSkill : shoot_skill; + // [dshaligram] Throwing now two parts launcher skill, one part + // ranged combat. Removed the old model which is... silly. + + shoot_skill = you.skills[launcher_skill]; + effSkill = (shoot_skill * 2 + rc_skill) / 3; + + 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; + + // effSkill = you.skills[SK_RANGED_COMBAT] * 2 + 1; + // effSkill = (shoot_skill > effSkill) ? effSkill : shoot_skill; + + // [dshaligram] Improving missile weapons: + // - Remove the strength/enchantment cap where you need to be strong + // to exploit a launcher bonus. + // - Add on launcher and missile pluses to extra damage. + + // [dshaligram] This can get large... + exDamBonus = lnchDamBonus + random2(1 + ammoDamBonus); + exDamBonus = exDamBonus > 0? random2(exDamBonus + 1) + : -random2(-exDamBonus + 1); + exHitBonus = lnchHitBonus > 0? random2(lnchHitBonus + 1) + : -random2(-lnchHitBonus + 1); // removed 2 random2(2)s from each of the learning curves, but // left slings because they're hard enough to develop without // a good source of shot in the dungeon. - switch (lnchType) + switch (launcher_skill) + { + case SK_SLINGS: { - case WPN_SLING: // Slings are really easy to learn because they're not // really all that good, and its harder to get ammo anyways. exercise(SK_SLINGS, 1 + random2avg(3, 2)); baseHit += 0; - exHitBonus = (effSkill * 3) / 2; + exHitBonus += (effSkill * 3) / 2; // strength is good if you're using a nice sling. - exDamBonus = (10 * (you.strength - 10)) / 9; - exDamBonus = (exDamBonus * (2 * baseDam + ammoDamBonus)) / 20; + int strbonus = (10 * (you.strength - 10)) / 9; + strbonus = (strbonus * (2 * baseDam + ammoDamBonus)) / 20; // cap - if (exDamBonus > lnchDamBonus + 1) - exDamBonus = lnchDamBonus + 1; + if (strbonus > lnchDamBonus + 1) + strbonus = lnchDamBonus + 1; + exDamBonus += strbonus; // add skill for slings.. helps to find those vulnerable spots - exDamBonus += effSkill / 2; + dice_mult = dice_mult * (14 + random2(1 + effSkill)) / 14; // now kill the launcher damage bonus if (lnchDamBonus > 0) lnchDamBonus = 0; break; - + } // blowguns take a _very_ steady hand; a lot of the bonus // comes from dexterity. (Dex bonus here as well as below) - case WPN_BLOWGUN: - exercise(SK_DARTS, (coinflip()? 2 : 1)); + case SK_DARTS: baseHit -= 2; - exHitBonus = (effSkill * 3) / 2 + you.dex / 2; + exercise(SK_DARTS, (coinflip()? 2 : 1)); + exHitBonus += (effSkill * 3) / 2 + you.dex / 2; // no extra damage for blowguns - exDamBonus = 0; + // exDamBonus = 0; // now kill the launcher damage and ammo bonuses if (lnchDamBonus > 0) @@ -1432,52 +1565,70 @@ static void throw_it(struct bolt &pbolt, int throw_2) ammoDamBonus = 0; break; - - case WPN_BOW: + case SK_BOWS: + { + baseHit -= 3; exercise(SK_BOWS, (coinflip()? 2 : 1)); - baseHit -= 4; - exHitBonus = (effSkill * 2); + exHitBonus += (effSkill * 2); // strength is good if you're using a nice bow - exDamBonus = (10 * (you.strength - 10)) / 4; - exDamBonus = (exDamBonus * (2 * baseDam + ammoDamBonus)) / 20; + int strbonus = (10 * (you.strength - 10)) / 4; + strbonus = (strbonus * (2 * baseDam + ammoDamBonus)) / 20; - // cap - if (exDamBonus > (lnchDamBonus + 1) * 3) - exDamBonus = (lnchDamBonus + 1) * 3; + // cap; reduced this cap, because we don't want to allow + // the extremely-strong to quadruple the enchantment bonus. + if (strbonus > lnchDamBonus + 1) + strbonus = lnchDamBonus + 1; + + exDamBonus += strbonus; // add in skill for bows.. help you to find those vulnerable spots. - exDamBonus += effSkill; + // exDamBonus += effSkill; + + dice_mult = dice_mult * (17 + random2(1 + effSkill)) / 17; // now kill the launcher damage bonus if (lnchDamBonus > 0) lnchDamBonus = 0; break; - + } // Crossbows are easy for unskilled people. - case WPN_CROSSBOW: + case SK_CROSSBOWS: exercise(SK_CROSSBOWS, (coinflip()? 2 : 1)); - baseHit += 2; - exHitBonus = (3 * effSkill) / 2 + 6; - exDamBonus = effSkill / 2 + 4; + baseHit++; + exHitBonus += (3 * effSkill) / 2 + 6; + // exDamBonus += effSkill * 2 / 3 + 4; + + dice_mult = dice_mult * (22 + random2(1 + effSkill)) / 22; + + if (lnchType == WPN_HAND_CROSSBOW) + { + exHitBonus -= 2; + dice_mult = dice_mult * 26 / 30; + } break; - case WPN_HAND_CROSSBOW: - exercise(SK_CROSSBOWS, (coinflip()? 2 : 1)); - baseHit += 1; - exHitBonus = (3 * effSkill) / 2 + 4; - exDamBonus = effSkill / 2 + 2; + default: break; } // all launched weapons have a slight chance of improving // throwing skill if (coinflip()) - exercise(SK_THROWING, 1); + exercise(SK_RANGED_COMBAT, 1); + + // all launched weapons get a minor tohit boost from throwing skill. + exHitBonus += you.skills[SK_RANGED_COMBAT] / 5; - // all launched weapons get a tohit boost from throwing skill. - exHitBonus += (3 * you.skills[SK_THROWING]) / 4; + if (bow_brand == SPWPN_VORPAL) + { + // Vorpal brand adds 30% damage bonus. Increased from 25% + // because at 25%, vorpal brand is completely inferior to + // speed. At 30% it's marginally better than speed when + // fighting monsters with very heavy armour. + dice_mult = dice_mult * 130 / 100; + } // special cases for flame, frost, poison, etc. // check for venom brand (usually only available for blowguns) @@ -1486,25 +1637,27 @@ static void throw_it(struct bolt &pbolt, int throw_2) // poison brand the ammo set_item_ego_type( item, OBJ_MISSILES, SPMSL_POISONED ); item_name( item, DESC_PLAIN, str_pass ); - strcpy( pbolt.beam_name, str_pass ); + pbolt.name = str_pass; } // Note that bow_brand is known since the bow is equiped. if ((bow_brand == SPWPN_FLAME || ammo_brand == SPMSL_FLAME) && ammo_brand != SPMSL_ICE && bow_brand != SPWPN_FROST) { - baseDam += 1 + random2(5); + // [dshaligram] Branded arrows are much stronger. + dice_mult = dice_mult * 150 / 100; + pbolt.flavour = BEAM_FIRE; - strcpy(pbolt.beam_name, "bolt of "); + pbolt.name = "bolt of "; if (poisoned) - strcat(pbolt.beam_name, "poison "); + pbolt.name += "poison "; - strcat(pbolt.beam_name, "flame"); + pbolt.name += "flame"; pbolt.colour = RED; pbolt.type = SYM_BOLT; pbolt.thrower = KILL_YOU_MISSILE; - pbolt.aux_source = NULL; + pbolt.aux_source.clear(); // ammo known if we can't attribute it to the bow if (bow_brand != SPWPN_FLAME) @@ -1517,18 +1670,20 @@ static void throw_it(struct bolt &pbolt, int throw_2) if ((bow_brand == SPWPN_FROST || ammo_brand == SPMSL_ICE) && ammo_brand != SPMSL_FLAME && bow_brand != SPWPN_FLAME) { - baseDam += 1 + random2(5); + // [dshaligram] Branded arrows are much stronger. + dice_mult = dice_mult * 150 / 100; + pbolt.flavour = BEAM_COLD; - strcpy(pbolt.beam_name, "bolt of "); + pbolt.name = "bolt of "; if (poisoned) - strcat(pbolt.beam_name, "poison "); + pbolt.name += "poison "; - strcat(pbolt.beam_name, "frost"); + pbolt.name += "frost"; pbolt.colour = WHITE; pbolt.type = SYM_BOLT; pbolt.thrower = KILL_YOU_MISSILE; - pbolt.aux_source = NULL; + pbolt.aux_source.clear(); // ammo known if we can't attribute it to the bow if (bow_brand != SPWPN_FROST) @@ -1554,8 +1709,19 @@ static void throw_it(struct bolt &pbolt, int throw_2) * and vice versa */ // ID check - if (item_not_ident( you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_PLUSES ) - && random2(100) < shoot_skill) + if (item_ident(you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_PLUSES)) + { + if ( !item_ident(you.inv[throw_2], ISFLAG_KNOW_PLUSES) && + random2(100) < rc_skill ) + { + set_ident_flags( item, ISFLAG_KNOW_PLUSES ); + set_ident_flags( you.inv[throw_2], ISFLAG_KNOW_PLUSES ); + in_name( throw_2, DESC_NOCAP_A, str_pass); + snprintf(info, INFO_SIZE, "You are firing %s.", str_pass); + mpr(info); + } + } + else if (random2(100) < shoot_skill) { set_ident_flags(you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_PLUSES); @@ -1568,6 +1734,7 @@ static void throw_it(struct bolt &pbolt, int throw_2) more(); you.wield_change = true; } + } // CALCULATIONS FOR THROWN WEAPONS @@ -1584,7 +1751,8 @@ static void throw_it(struct bolt &pbolt, int throw_2) || (wepClass == OBJ_MISSILES && wepType == MI_STONE)) { // elves with elven weapons - if (cmp_equip_race(item, ISFLAG_ELVEN) && player_genus(GENPC_ELVEN)) + if (get_equip_race(item) == ISFLAG_ELVEN + && player_genus(GENPC_ELVEN)) baseHit += 1; // give an appropriate 'tohit' - @@ -1608,14 +1776,14 @@ static void throw_it(struct bolt &pbolt, int throw_2) } } - exHitBonus = you.skills[SK_THROWING] * 2; + exHitBonus = you.skills[SK_RANGED_COMBAT] * 2; baseDam = property( item, PWPN_DAMAGE ); exDamBonus = - (10 * (you.skills[SK_THROWING] / 2 + you.strength - 10)) / 12; + (10 * (you.skills[SK_RANGED_COMBAT] / 2 + you.strength - 10)) / 12; // now, exDamBonus is a multiplier. The full multiplier - // is applied to base damage, but only a third is applied + // is applied to base damage, but only a third is applied // to the magical modifier. exDamBonus = (exDamBonus * (3 * baseDam + ammoDamBonus)) / 30; } @@ -1627,16 +1795,35 @@ static void throw_it(struct bolt &pbolt, int throw_2) baseDam = property( item, PWPN_DAMAGE ); exHitBonus = you.skills[SK_DARTS] * 2; - exHitBonus += (you.skills[SK_THROWING] * 2) / 3; - exDamBonus = you.skills[SK_DARTS] / 4; + exHitBonus += (you.skills[SK_RANGED_COMBAT] * 2) / 3; + exDamBonus = you.skills[SK_DARTS] / 3; + exDamBonus += you.skills[SK_RANGED_COMBAT] / 5; // exercise skills exercise(SK_DARTS, 1 + random2avg(3, 2)); } + // [dshaligram] The defined base damage applies only when used + // for launchers. Hand-thrown stones and darts do only half + // base damage. Yet another evil 4.0ism. + if (wepClass == OBJ_MISSILES + && (wepType == MI_DART || wepType == MI_STONE)) + baseDam = div_rand_round(baseDam, 2); + // exercise skill if (coinflip()) - exercise(SK_THROWING, 1); + exercise(SK_RANGED_COMBAT, 1); + + // ID check + if ( !item_ident(you.inv[throw_2], ISFLAG_KNOW_PLUSES) && + random2(100) < you.skills[SK_RANGED_COMBAT] ) + { + set_ident_flags( item, ISFLAG_KNOW_PLUSES ); + set_ident_flags( you.inv[throw_2], ISFLAG_KNOW_PLUSES ); + in_name( throw_2, DESC_NOCAP_A, str_pass); + snprintf(info, INFO_SIZE, "You are throwing %s.", str_pass); + mpr(info); + } } // range, dexterity bonus, possible skill increase for silly throwing @@ -1659,7 +1846,11 @@ static void throw_it(struct bolt &pbolt, int throw_2) // slaying bonuses if (!(launched && wepType == MI_NEEDLE)) - exDamBonus += slaying_bonus(PWPN_DAMAGE); + { + slayDam = slaying_bonus(PWPN_DAMAGE); + slayDam = slayDam < 0? -random2(1 - slayDam) + : random2(1 + slayDam); + } exHitBonus += slaying_bonus(PWPN_HIT); } @@ -1667,7 +1858,7 @@ static void throw_it(struct bolt &pbolt, int throw_2) else { // range based on mass & strength, between 1 and 9 - pbolt.range = you.strength - mass_item(item) / 10 + 3; + pbolt.range = you.strength - item_mass(item) / 10 + 3; if (pbolt.range < 1) pbolt.range = 1; @@ -1678,9 +1869,22 @@ static void throw_it(struct bolt &pbolt, int throw_2) pbolt.rangeMax = pbolt.range; if (one_chance_in(20)) - exercise(SK_THROWING, 1); + exercise(SK_RANGED_COMBAT, 1); exHitBonus = you.dex / 4; + + if (wepClass == OBJ_MISSILES && wepType == MI_NEEDLE) + { + // Throwing needles is now seriously frowned upon; it's difficult + // to grip a fiddly little needle, and not penalising it cheapens + // blowguns. + 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 + } } // FINALIZE tohit and damage @@ -1694,6 +1898,11 @@ static void throw_it(struct bolt &pbolt, int throw_2) else pbolt.damage = dice_def( 1, baseDam - random2(0 - (exDamBonus - 1)) ); + pbolt.damage.size = dice_mult * pbolt.damage.size / 100; + pbolt.damage.size += slayDam; + + scale_dice( pbolt.damage ); + // only add bonuses if we're throwing something sensible if (thrown || launched || wepClass == OBJ_WEAPONS) { @@ -1702,49 +1911,43 @@ static void throw_it(struct bolt &pbolt, int throw_2) } #if DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, - "H:%d+%d;a%dl%d. D:%d+%d;a%dl%d -> %d,%dd%d", + 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 ); - - mpr( info, MSGCH_DIAGNOSTICS ); #endif - // Must unwield before fire_beam() makes a copy in order to remove things - // like temporary branding. -- bwr - if (throw_2 == you.equip[EQ_WEAPON] && you.inv[throw_2].quantity == 1) - { - unwield_item( throw_2 ); - you.equip[EQ_WEAPON] = -1; - canned_msg( MSG_EMPTY_HANDED ); - } - // create message if (launched) strcpy(info, "You shoot "); else strcpy(info, "You throw "); - item_name( item, DESC_NOCAP_A, str_pass ); + item_name( item, DESC_NOCAP_A, str_pass ); strcat(info, str_pass); strcat(info, "."); mpr(info); // ensure we're firing a 'missile'-type beam - pbolt.isBeam = false; - pbolt.isTracer = false; + pbolt.is_beam = false; + pbolt.is_tracer = false; // mark this item as thrown if it's a missile, so that we'll pick it up // when we walk over it. if (wepClass == OBJ_MISSILES || wepClass == OBJ_WEAPONS) item.flags |= ISFLAG_THROWN; + bool hit = false; // using copy, since the launched item might be differect (venom blowgun) - fire_beam( pbolt, &item ); - - dec_inv_item_quantity( throw_2, 1 ); + if (dummy_target) + hit = (affect( pbolt, dummy_target->x, dummy_target->y ) != 0); + else + { + fire_beam( pbolt, &item ); + dec_inv_item_quantity( throw_2, 1 ); + } // throwing and blowguns are silent if (launched && lnchType != WPN_BLOWGUN) @@ -1753,108 +1956,16 @@ static void throw_it(struct bolt &pbolt, int throw_2) // but any monster nearby can see that something has been thrown: alert_nearby_monsters(); - you.turn_is_over = 1; + you.turn_is_over = true; + + return (hit); } // end throw_it() -bool puton_item(int item_slot, bool prompt_finger) +void jewellery_wear_effects(item_def &item) { - if (item_slot == you.equip[EQ_LEFT_RING] - || item_slot == you.equip[EQ_RIGHT_RING] - || item_slot == you.equip[EQ_AMULET]) - { - mpr("You've already put that on!"); - return (true); - } - - if (item_slot == you.equip[EQ_WEAPON]) - { - mpr("You are wielding that object."); - return (false); - } - - if (you.inv[item_slot].base_type != OBJ_JEWELLERY) - { - //jmf: let's not take our inferiority complex out on players, eh? :-p - //mpr("You're sadly mistaken if you consider that jewellery.") - mpr("You can only put on jewellery."); - return (false); - } - - bool is_amulet = (you.inv[item_slot].sub_type >= AMU_RAGE); - - if (!is_amulet) // ie it's a ring - { - if (you.equip[EQ_GLOVES] != -1 - && item_cursed( you.inv[you.equip[EQ_GLOVES]] )) - { - mpr("You can't take your gloves off to put on a ring!"); - return (false); - } - - if (you.inv[item_slot].base_type == OBJ_JEWELLERY - && you.equip[EQ_LEFT_RING] != -1 - && you.equip[EQ_RIGHT_RING] != -1) - { - // and you are trying to wear body you.equip. - mpr("You've already put a ring on each hand."); - return (false); - } - } - else if (you.equip[EQ_AMULET] != -1) - { - strcpy(info, "You are already wearing an amulet."); - - if (one_chance_in(20)) - { - strcat(info, " And I must say it looks quite fetching."); - } - - mpr(info); - return (false); - } - - int hand_used = 0; - - if (you.equip[EQ_LEFT_RING] != -1) - hand_used = 1; - - if (you.equip[EQ_RIGHT_RING] != -1) - hand_used = 0; - - if (is_amulet) - hand_used = 2; - else if (you.equip[EQ_LEFT_RING] == -1 && you.equip[EQ_RIGHT_RING] == -1) - { - if (prompt_finger) - { - mpr("Put on which hand (l or r)?", MSGCH_PROMPT); - - int keyin = get_ch(); - - if (keyin == 'l') - hand_used = 0; - else if (keyin == 'r') - hand_used = 1; - else if (keyin == ESCAPE) - return (false); - else - { - mpr("You don't have such a hand!"); - return (false); - } - } - else - { - // First ring goes on left hand if we're choosing automatically. - hand_used = 0; - } - } - - you.equip[ EQ_LEFT_RING + hand_used ] = item_slot; - int ident = ID_TRIED_TYPE; - switch (you.inv[item_slot].sub_type) + switch (item.sub_type) { case RING_FIRE: case RING_HUNGER: @@ -1871,11 +1982,12 @@ bool puton_item(int item_slot, bool prompt_finger) case RING_TELEPORTATION: case RING_WIZARDRY: case RING_REGENERATION: + case RING_TELEPORT_CONTROL: break; case RING_PROTECTION: you.redraw_armour_class = 1; - if (you.inv[item_slot].plus != 0) + if (item.plus != 0) ident = ID_KNOWN_TYPE; break; @@ -1889,25 +2001,25 @@ bool puton_item(int item_slot, bool prompt_finger) case RING_EVASION: you.redraw_evasion = 1; - if (you.inv[item_slot].plus != 0) + if (item.plus != 0) ident = ID_KNOWN_TYPE; break; case RING_STRENGTH: - modify_stat(STAT_STRENGTH, you.inv[item_slot].plus, true); - if (you.inv[item_slot].plus != 0) + modify_stat(STAT_STRENGTH, item.plus, true); + if (item.plus != 0) ident = ID_KNOWN_TYPE; break; case RING_DEXTERITY: - modify_stat(STAT_DEXTERITY, you.inv[item_slot].plus, true); - if (you.inv[item_slot].plus != 0) + modify_stat(STAT_DEXTERITY, item.plus, true); + if (item.plus != 0) ident = ID_KNOWN_TYPE; break; case RING_INTELLIGENCE: - modify_stat(STAT_INTELLIGENCE, you.inv[item_slot].plus, true); - if (you.inv[item_slot].plus != 0) + modify_stat(STAT_INTELLIGENCE, item.plus, true); + if (item.plus != 0) ident = ID_KNOWN_TYPE; break; @@ -1921,46 +2033,181 @@ bool puton_item(int item_slot, bool prompt_finger) ident = ID_KNOWN_TYPE; break; - case RING_TELEPORT_CONTROL: - // XXX: is this safe or should we make it a function -- bwr - you.attribute[ATTR_CONTROL_TELEPORT]++; - break; - case AMU_RAGE: mpr("You feel a brief urge to hack something to bits."); ident = ID_KNOWN_TYPE; break; - } - you.turn_is_over = 1; + case AMU_THE_GOURMAND: + you.duration[DUR_GOURMAND] = 0; + break; + } // Artefacts have completely different appearance than base types // so we don't allow them to make the base types known - if (is_random_artefact( you.inv[item_slot] )) - use_randart(item_slot); + if (is_random_artefact( item )) + use_randart(item); else + set_ident_type( item.base_type, item.sub_type, 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"); + + // cursed or not, we know that since we've put the ring on + set_ident_flags( item, ISFLAG_KNOW_CURSE ); + + mpr( item_name( item, DESC_INVENTORY_EQUIP ) ); +} + +static int prompt_ring_to_remove(int new_ring) +{ + const item_def &left = you.inv[ you.equip[EQ_LEFT_RING] ]; + const item_def &right = you.inv[ you.equip[EQ_RIGHT_RING] ]; + + if (item_cursed(left) && item_cursed(right)) { - set_ident_type( you.inv[item_slot].base_type, - you.inv[item_slot].sub_type, ident ); + mprf("You're already wearing two cursed rings!"); + return (-1); } - if (ident == ID_KNOWN_TYPE) - set_ident_flags( you.inv[item_slot], ISFLAG_EQ_JEWELLERY_MASK ); + mesclr(); + mprf("Wearing %s.", in_name(new_ring, DESC_NOCAP_A)); + + char lslot = index_to_letter(left.link), + rslot = index_to_letter(right.link); + + mprf(MSGCH_PROMPT, + "You're wearing two rings. Remove which one? (%c/%c/Esc)", + lslot, rslot); + mprf(" %s", item_name( left, DESC_INVENTORY )); + mprf(" %s", item_name( right, DESC_INVENTORY )); + + int c; + do + c = getch(); + while (c != lslot && c != rslot && c != ESCAPE && c != ' '); + + mesclr(); + + if (c == ESCAPE || c == ' ') + return (-1); - if (item_cursed( you.inv[item_slot] )) + int eqslot = c == lslot? EQ_LEFT_RING : EQ_RIGHT_RING; + return (you.equip[eqslot]); +} + +// Assumptions: +// you.inv[ring_slot] is a valid ring. +// EQ_LEFT_RING and EQ_RIGHT_RING are both occupied, and ring_slot is not +// one of the worn rings. +// +// Does not do amulets. +static bool swap_rings(int ring_slot) +{ + // Ask the player which existing ring is persona non grata. + int unwanted = prompt_ring_to_remove(ring_slot); + if (unwanted == -1) + return (false); + + if (!remove_ring(unwanted, false)) + return (false); + + start_delay(DELAY_JEWELLERY_ON, 1, ring_slot); + + return (true); +} + +bool puton_item(int item_slot, bool prompt_finger) +{ + if (item_slot == you.equip[EQ_LEFT_RING] + || item_slot == you.equip[EQ_RIGHT_RING] + || item_slot == you.equip[EQ_AMULET]) { - snprintf( info, INFO_SIZE, - "Oops, that %s feels deathly cold.", (is_amulet) ? "amulet" - : "ring" ); - mpr(info); + mpr("You've already put that on!"); + return (true); } - // cursed or not, we know that since we've put the ring on - set_ident_flags( you.inv[item_slot], ISFLAG_KNOW_CURSE ); + if (item_slot == you.equip[EQ_WEAPON]) + { + mpr("You are wielding that object."); + return (false); + } - char str_pass[ ITEMNAME_SIZE ]; - in_name( item_slot, DESC_INVENTORY_EQUIP, str_pass ); - mpr( str_pass ); + if (you.inv[item_slot].base_type != OBJ_JEWELLERY) + { + mpr("You can only put on jewellery."); + return (false); + } + + bool is_amulet = jewellery_is_amulet( you.inv[item_slot] ); + + if (!is_amulet) // ie it's a ring + { + if (you.equip[EQ_GLOVES] != -1 + && item_cursed( you.inv[you.equip[EQ_GLOVES]] )) + { + mpr("You can't take your gloves off to put on a ring!"); + return (false); + } + + if (you.equip[EQ_LEFT_RING] != -1 + && you.equip[EQ_RIGHT_RING] != -1) + return swap_rings(item_slot); + } + else if (you.equip[EQ_AMULET] != -1) + { + if (!remove_ring( you.equip[EQ_AMULET], true )) + return (false); + + start_delay(DELAY_JEWELLERY_ON, 1, item_slot); + + // Assume it's going to succeed. + return (true); + } + + // First ring goes on left hand if we're choosing automatically. + int hand_used = 0; + + if (you.equip[EQ_LEFT_RING] != -1) + hand_used = 1; + + if (you.equip[EQ_RIGHT_RING] != -1) + hand_used = 0; + + if (is_amulet) + hand_used = 2; + else if (prompt_finger + && you.equip[EQ_LEFT_RING] == -1 + && you.equip[EQ_RIGHT_RING] == -1) + { + mpr("Put on which hand (l or r)?", MSGCH_PROMPT); + + int keyin = get_ch(); + + if (keyin == 'l') + hand_used = 0; + else if (keyin == 'r') + hand_used = 1; + else if (keyin == ESCAPE) + return (false); + else + { + mpr("You don't have such a hand!"); + return (false); + } + } + + you.equip[ EQ_LEFT_RING + hand_used ] = item_slot; + + jewellery_wear_effects( you.inv[item_slot] ); + + // Putting on jewellery is as fast as wielding weapons. + you.time_taken = you.time_taken * 5 / 10; + you.turn_is_over = true; return (true); } @@ -1985,7 +2232,7 @@ bool puton_ring(int slot, bool prompt_finger) item_slot = slot; else item_slot = prompt_invent_item( "Put on which piece of jewellery?", - OBJ_JEWELLERY ); + MT_INVSELECT, OBJ_JEWELLERY ); if (item_slot == PROMPT_ABORT) { @@ -1996,11 +2243,91 @@ bool puton_ring(int slot, bool prompt_finger) return puton_item(item_slot, prompt_finger); } // end puton_ring() -bool remove_ring(int slot) +void jewellery_remove_effects(item_def &item) +{ + // The ring/amulet must already be removed from you.equip at this point. + + // Turn off show_uncursed before getting the item name, because this item + // was just removed, and the player knows it's uncursed. + bool old_showuncursed = Options.show_uncursed; + Options.show_uncursed = false; + + mprf("You remove %s.", item_name(item, DESC_NOCAP_YOUR)); + + 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 = 1; + break; + + case RING_EVASION: + you.redraw_evasion = 1; + break; + + case RING_STRENGTH: + modify_stat(STAT_STRENGTH, -item.plus, true); + break; + + case RING_DEXTERITY: + modify_stat(STAT_DEXTERITY, -item.plus, true); + break; + + case RING_INTELLIGENCE: + modify_stat(STAT_INTELLIGENCE, -item.plus, true); + break; + + case RING_INVISIBILITY: + // removing this ring effectively cancels all invisibility {dlb} + if (you.invis) + you.invis = 1; + break; + + case RING_LEVITATION: + // removing this ring effectively cancels all levitation {dlb} + if (you.levitation) + you.levitation = 1; + break; + + case RING_MAGICAL_POWER: + // dec_max_mp(9); + break; + + case AMU_THE_GOURMAND: + you.duration[DUR_GOURMAND] = 0; + break; + } + + if (is_random_artefact(item)) + unuse_randart(item); + + // must occur after ring is removed -- bwr + calc_mp(); +} + +bool remove_ring(int slot, bool announce) { int hand_used = 10; int ring_wear_2; - char str_pass[ ITEMNAME_SIZE ]; if (you.equip[EQ_LEFT_RING] == -1 && you.equip[EQ_RIGHT_RING] == -1 && you.equip[EQ_AMULET] == -1) @@ -2045,7 +2372,9 @@ bool remove_ring(int slot) { int equipn = slot == -1? prompt_invent_item( "Remove which piece of jewellery?", - OBJ_JEWELLERY ) + MT_INVSELECT, + OBJ_JEWELLERY, true, true, true, + 0, NULL, OPER_REMOVE) : slot; if (equipn == PROMPT_ABORT) @@ -2081,99 +2410,33 @@ bool remove_ring(int slot) return (false); } - if (you.equip[hand_used + 7] == -1) + if (you.equip[hand_used + EQ_LEFT_RING] == -1) { mpr("I don't think you really meant that."); return (false); } - if (item_cursed( you.inv[you.equip[hand_used + 7]] )) + if (item_cursed( you.inv[you.equip[hand_used + EQ_LEFT_RING]] )) { - mpr("It's stuck to you!"); + if (announce) + mprf("%s is stuck to you!", + in_name(you.equip[hand_used + EQ_LEFT_RING], + DESC_CAP_YOUR)); + else + mpr("It's stuck to you!"); - set_ident_flags( you.inv[you.equip[hand_used + 7]], ISFLAG_KNOW_CURSE ); + set_ident_flags( you.inv[you.equip[hand_used + EQ_LEFT_RING]], + ISFLAG_KNOW_CURSE ); return (false); } - strcpy(info, "You remove "); - in_name(you.equip[hand_used + 7], DESC_NOCAP_YOUR, str_pass); + ring_wear_2 = you.equip[hand_used + EQ_LEFT_RING]; + you.equip[hand_used + EQ_LEFT_RING] = -1; - strcat(info, str_pass); - strcat(info, "."); - mpr(info); + jewellery_remove_effects(you.inv[ring_wear_2]); - // I'll still use ring_wear_2 here. - ring_wear_2 = you.equip[hand_used + 7]; - - switch (you.inv[ring_wear_2].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: - break; - - case RING_PROTECTION: - you.redraw_armour_class = 1; - break; - - case RING_EVASION: - you.redraw_evasion = 1; - break; - - case RING_STRENGTH: - modify_stat(STAT_STRENGTH, -you.inv[ring_wear_2].plus, true); - break; - - case RING_DEXTERITY: - modify_stat(STAT_DEXTERITY, -you.inv[ring_wear_2].plus, true); - break; - - case RING_INTELLIGENCE: - modify_stat(STAT_INTELLIGENCE, -you.inv[ring_wear_2].plus, true); - break; - - case RING_INVISIBILITY: - // removing this ring effectively cancels all invisibility {dlb} - if (you.invis) - you.invis = 1; - break; - - case RING_LEVITATION: - // removing this ring effectively cancels all levitation {dlb} - if (you.levitation) - you.levitation = 1; - break; - - case RING_MAGICAL_POWER: - // dec_max_mp(9); - break; - - case RING_TELEPORT_CONTROL: - you.attribute[ATTR_CONTROL_TELEPORT]--; - break; - } - - if (is_random_artefact( you.inv[ring_wear_2] )) - unuse_randart(ring_wear_2); - - you.equip[hand_used + 7] = -1; - - // must occur after ring is removed -- bwr - calc_mp(); - - you.turn_is_over = 1; + you.time_taken = you.time_taken * 5 / 10; + you.turn_is_over = true; return (true); } // end remove_ring() @@ -2189,7 +2452,7 @@ void zap_wand(void) // system will default to cycling through all monsters. -- bwr int targ_mode = TARG_ANY; - beam.obviousEffect = false; + beam.obvious_effect = false; if (inv_count() < 1) { @@ -2203,21 +2466,46 @@ void zap_wand(void) return; } - item_slot = prompt_invent_item( "Zap which item?", OBJ_WANDS ); + item_slot = prompt_invent_item( "Zap which item?", + MT_INVSELECT, + OBJ_WANDS, + true, true, true, 0, NULL, + OPER_ZAP ); if (item_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); return; } - if (you.inv[item_slot].base_type != OBJ_WANDS - || you.inv[item_slot].plus < 1) + if (you.inv[item_slot].base_type != OBJ_WANDS) { canned_msg(MSG_NOTHING_HAPPENS); - you.turn_is_over = 1; + you.turn_is_over = true; return; } + // if you happen to be wielding the wand, its display might change + if (you.equip[EQ_WEAPON] == item_slot) + you.wield_change = true; + + if ( you.inv[item_slot].plus < 1 ) { + // it's an empty wand, inscribe it that way + canned_msg(MSG_NOTHING_HAPPENS); + you.turn_is_over = true; + if ( !item_ident(you.inv[item_slot], ISFLAG_KNOW_PLUSES) ) { + + if ( you.inv[item_slot].inscription.find("empty") == + std::string::npos ) { + + if ( !you.inv[item_slot].inscription.empty() ) + you.inv[item_slot].inscription += ' '; + you.inv[item_slot].inscription += "[empty]"; + + } + } + return; + } + if (item_ident( you.inv[item_slot], ISFLAG_KNOW_TYPE )) { if (you.inv[item_slot].sub_type == WAND_HASTING @@ -2275,12 +2563,11 @@ void zap_wand(void) beam.source_x = you.x_pos; beam.source_y = you.y_pos; - beam.target_x = zap_wand.tx; - beam.target_y = zap_wand.ty; + beam.set_target(zap_wand); zapping( type_zapped, 30 + roll_dice(2, you.skills[SK_EVOCATIONS]), beam ); - if (beam.obviousEffect == 1 || you.inv[item_slot].sub_type == WAND_FIREBALL) + if (beam.obvious_effect == 1 || you.inv[item_slot].sub_type == WAND_FIREBALL) { if (get_ident_type( you.inv[item_slot].base_type, you.inv[item_slot].sub_type ) != ID_KNOWN_TYPE) @@ -2309,7 +2596,7 @@ void zap_wand(void) && (item_ident( you.inv[item_slot], ISFLAG_KNOW_PLUSES ) || you.skills[SK_EVOCATIONS] > 5 + random2(15))) { - if (item_not_ident( you.inv[item_slot], ISFLAG_KNOW_PLUSES )) + if (!item_ident( you.inv[item_slot], ISFLAG_KNOW_PLUSES )) { mpr("Your skill with magical items lets you calculate the power of this device..."); } @@ -2325,9 +2612,34 @@ void zap_wand(void) exercise( SK_EVOCATIONS, 1 ); alert_nearby_monsters(); - you.turn_is_over = 1; + you.turn_is_over = true; } // end zap_wand() +/*** HP CHANGE ***/ +void inscribe_item() +{ + int item_slot; + char buf[79]; + if (inv_count() < 1) + { + mpr("You don't have anything to inscribe."); + return; + } + item_slot = prompt_invent_item( + "Inscribe which item? ", + MT_INVSELECT, + OSEL_ANY ); + if (item_slot == PROMPT_ABORT) + { + canned_msg( MSG_OK ); + return; + } + mpr( "Inscribe with what? ", MSGCH_PROMPT ); + get_input_line( buf, sizeof(buf) ); + you.inv[item_slot].inscription = std::string(buf); + you.wield_change = true; +} + void drink(void) { int item_slot; @@ -2357,7 +2669,10 @@ void drink(void) return; } - item_slot = prompt_invent_item( "Drink which item?", OBJ_POTIONS ); + item_slot = prompt_invent_item( "Drink which item?", + MT_INVSELECT, OBJ_POTIONS, + true, true, true, 0, NULL, + OPER_QUAFF ); if (item_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -2384,7 +2699,7 @@ void drink(void) } dec_inv_item_quantity( item_slot, 1 ); - you.turn_is_over = 1; + you.turn_is_over = true; lessen_hunger(40, true); } // end drink() @@ -2474,7 +2789,7 @@ bool drink_fountain(void) grd[you.x_pos][you.y_pos] = DNGN_DRY_FOUNTAIN_II; } - you.turn_is_over = 1; + you.turn_is_over = true; return true; } // end drink_fountain() @@ -2491,8 +2806,7 @@ static bool affix_weapon_enchantment( void ) switch (get_weapon_brand( you.inv[wpn] )) { case SPWPN_VORPAL: - if (damage_type( you.inv[wpn].base_type, - you.inv[wpn].sub_type ) != DVORP_CRUSHING) + if (get_vorpal_type( you.inv[wpn] ) != DVORP_CRUSHING) { strcat(info, "'s sharpness seems more permanent."); } @@ -2512,12 +2826,13 @@ static bool affix_weapon_enchantment( void ) beam.flavour = 2; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "fiery explosion"); + beam.name = "fiery explosion"; beam.colour = RED; beam.thrower = KILL_YOU; beam.aux_source = "a fiery explosion"; beam.ex_size = 2; - beam.isTracer = false; + beam.is_tracer = false; + beam.is_explosion = true; explosion(beam); break; @@ -2541,11 +2856,15 @@ static bool affix_weapon_enchantment( void ) break; case SPWPN_DISTORTION: + // [dshaligram] Attempting to fix a distortion brand gets you a free + // distortion effect, and no permabranding. Sorry, them's the breaks. strcat(info, " twongs alarmingly."); mpr(info); // from unwield_item - miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, "a distortion effect" ); + miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, + "a distortion effect" ); + success = false; break; default: @@ -2559,7 +2878,7 @@ static bool affix_weapon_enchantment( void ) return (success); } -static bool enchant_weapon( int which_stat, bool quiet ) +bool enchant_weapon( int which_stat, bool quiet ) { const int wpn = you.equip[ EQ_WEAPON ]; bool affected = true; @@ -2693,7 +3012,7 @@ static bool enchant_armour( void ) you.redraw_armour_class = 1; - hide2armour( &(you.inv[nthing].sub_type) ); + hide2armour(you.inv[nthing]); return (true); } @@ -2824,7 +3143,7 @@ void read_scroll(void) char str_pass[ ITEMNAME_SIZE ]; // added: scroll effects are never tracers. - beam.isTracer = false; + beam.is_tracer = false; if (you.berserker) { @@ -2838,7 +3157,10 @@ void read_scroll(void) return; } - int item_slot = prompt_invent_item( "Read which item?", OBJ_SCROLLS ); + int item_slot = prompt_invent_item( + "Read which item?", + MT_INVSELECT, + OBJ_SCROLLS ); if (item_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -2866,7 +3188,7 @@ void read_scroll(void) } // ok - now we FINALLY get to read a scroll !!! {dlb} - you.turn_is_over = 1; + you.turn_is_over = true; // imperfect vision prevents players from reading actual content {dlb}: if (you.mutation[MUT_BLURRY_VISION] @@ -2909,6 +3231,8 @@ void read_scroll(void) case SCR_PAPER: // remember paper scrolls handled as special case above, too: mpr("This scroll appears to be blank."); + if (you.mutation[MUT_BLURRY_VISION] == 3) + id_the_scroll = false; break; case SCR_RANDOM_USELESSNESS: @@ -2977,17 +3301,19 @@ void read_scroll(void) break; case SCR_TORMENT: - torment( you.x_pos, you.y_pos ); + torment( TORMENT_SCROLL, you.x_pos, you.y_pos ); // is only naughty if you know you're doing it if (get_ident_type( OBJ_SCROLLS, SCR_TORMENT ) == ID_KNOWN_TYPE) { - naughty(NAUGHTY_UNHOLY, 10); + did_god_conduct(DID_UNHOLY, 10); } break; case SCR_IMMOLATION: mpr("The scroll explodes in your hands!"); + // we do this here to prevent it from blowing itself up + dec_inv_item_quantity( item_slot, 1 ); beam.type = SYM_BURST; beam.damage = dice_def( 3, 10 ); @@ -2995,17 +3321,23 @@ void read_scroll(void) beam.flavour = BEAM_FIRE; beam.target_x = you.x_pos; beam.target_y = you.y_pos; - strcpy(beam.beam_name, "fiery explosion"); + beam.name = "fiery explosion"; beam.colour = RED; // your explosion, (not someone else's explosion) beam.thrower = KILL_YOU; beam.aux_source = "reading a scroll of immolation"; beam.ex_size = 2; + beam.is_explosion = true; explosion(beam); break; case SCR_IDENTIFY: + if ( get_ident_type( OBJ_SCROLLS, scroll_type ) != ID_KNOWN_TYPE ) { + mpr("This is a scroll of identify!"); + more(); + } + set_ident_flags( you.inv[item_slot], ISFLAG_IDENT_MASK ); // important {dlb} @@ -3083,7 +3415,7 @@ void read_scroll(void) || (you.inv[ nthing ].base_type == OBJ_WEAPONS && (is_fixed_artefact( you.inv[ nthing ] ) || is_random_artefact( you.inv[ nthing ] ) - || launches_things( you.inv[ nthing ].sub_type )))) + || you.inv[nthing].sub_type == WPN_BLOWGUN))) { canned_msg(MSG_NOTHING_HAPPENS); break; @@ -3136,7 +3468,7 @@ void read_scroll(void) affected = EQ_WEAPON; for (i = EQ_CLOAK; i <= EQ_BODY_ARMOUR; i++) { - if (you.equip[i] != -1 && item_uncursed( you.inv[you.equip[i]] )) + if (you.equip[i] != -1 && !item_cursed( you.inv[you.equip[i]] )) { count++; if (one_chance_in( count )) @@ -3162,7 +3494,8 @@ void read_scroll(void) } // end switch // finally, destroy and identify the scroll - if (scroll_type != SCR_PAPER) + // scrolls of immolation were already destroyed earlier + if (scroll_type != SCR_PAPER && scroll_type != SCR_IMMOLATION) { dec_inv_item_quantity( item_slot, 1 ); } @@ -3171,9 +3504,12 @@ void read_scroll(void) (id_the_scroll) ? ID_KNOWN_TYPE : ID_TRIED_TYPE ); } // end read_scroll() -void original_name(void) +void examine_object(void) { - int item_slot = prompt_invent_item( "Examine which item?", -1 ); + int item_slot = prompt_invent_item( "Examine which item?", + MT_INVSELECT, -1, + true, true, true, 0, NULL, + OPER_EXAMINE ); if (item_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -3182,14 +3518,20 @@ void original_name(void) describe_item( you.inv[item_slot] ); redraw_screen(); + mesclr(true); } // end original_name() void use_randart(unsigned char item_wield_2) { - ASSERT( is_random_artefact( you.inv[ item_wield_2 ] ) ); + use_randart( you.inv[ item_wield_2 ] ); +} + +void use_randart(const item_def &item) +{ + ASSERT( is_random_artefact( item ) ); FixedVector< char, RA_PROPERTIES > proprt; - randart_wpn_properties( you.inv[item_wield_2], proprt ); + randart_wpn_properties( item, proprt ); if (proprt[RAP_AC]) you.redraw_armour_class = 1; |