From d5f99b33e02371f88fa0028b496f48539d9ec8a1 Mon Sep 17 00:00:00 2001 From: j-p-e-g Date: Mon, 10 Sep 2007 18:21:32 +0000 Subject: Identification overhaul part II. Okay, not really. ;) Applying Matthew's latest patch (1789869): Identify items you see monsters use. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2068 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/acr.cc | 27 +++++++ crawl-ref/source/command.cc | 1 + crawl-ref/source/debug.cc | 187 +++++++++++++++++++++++++++++++++++++++++++ crawl-ref/source/debug.h | 1 + crawl-ref/source/direct.cc | 12 ++- crawl-ref/source/enum.h | 6 +- crawl-ref/source/externs.h | 10 ++- crawl-ref/source/fight.cc | 51 ++++++++++-- crawl-ref/source/fight.h | 2 +- crawl-ref/source/itemname.cc | 25 ++++-- crawl-ref/source/mon-util.cc | 167 ++++++++++++++++++++++++++++++-------- crawl-ref/source/monstuff.cc | 95 +++++++++++++++++++--- crawl-ref/source/monstuff.h | 3 + crawl-ref/source/view.cc | 24 ++++++ 14 files changed, 547 insertions(+), 64 deletions(-) (limited to 'crawl-ref') diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc index 89f55868d9..98a3697da4 100644 --- a/crawl-ref/source/acr.cc +++ b/crawl-ref/source/acr.cc @@ -669,6 +669,33 @@ static void handle_wizard_command( void ) } } you.wield_change = true; + + // Forget things that nearby monsters are carrying, as well + // (for use with the "give monster an item" wizard targetting + // command). + for (i = 0; i < MAX_MONSTERS; i++) + { + monsters* mon = &menv[i]; + + if (!invalid_monster(mon) && mons_near(mon)) + { + for (j = 0; j < NUM_MONSTER_SLOTS; j++) + { + if (mon->inv[j] == NON_ITEM) + continue; + + item_def &item = mitm[mon->inv[j]]; + + if (!is_valid_item(item)) + continue; + + set_ident_type( item.base_type, item.sub_type, + ID_UNKNOWN_TYPE ); + + unset_ident_flags( item, ISFLAG_IDENT_MASK ); + } + } + } break; case 'X': diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc index 167f93c0c3..8cdc6b6ed1 100644 --- a/crawl-ref/source/command.cc +++ b/crawl-ref/source/command.cc @@ -541,6 +541,7 @@ static const char *targeting_help_1 = "Wizard targeting comands:\n" "F: make target friendly\n" "s: force target to shout or speak\n" + "g: give item to monster\n" #endif ; diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc index 5edb16a701..541e6d2974 100644 --- a/crawl-ref/source/debug.cc +++ b/crawl-ref/source/debug.cc @@ -2603,6 +2603,193 @@ void debug_make_monster_shout(monsters* mon) } #endif +#ifdef WIZARD +void wizard_give_monster_item(monsters *mon) +{ + mon_itemuse_type item_use = mons_itemuse( mon->type ); + if (item_use < MONUSE_STARTING_EQUIPMENT) + { + mpr("That type of monster can't use any items."); + return; + } + + int player_slot = prompt_invent_item( "Give which item to monster?", + MT_DROP, -1 ); + + if (player_slot == PROMPT_ABORT) + return; + + for (int i = 0; i < NUM_EQUIP; i++) + if (you.equip[i] == player_slot) + { + mpr("Can't give equipped items to a monster."); + return; + } + + item_def &item = you.inv[player_slot]; + mon_inv_type mon_slot; + + switch(item.base_type) + { + case OBJ_WEAPONS: + // Let wizard specify which slot to put weapon into via + // inscriptions. + if (item.inscription.find("first") != std::string::npos + || item.inscription.find("primary") != std::string::npos) + { + mpr("Putting weapon into primary slot by inscription"); + mon_slot = MSLOT_WEAPON; + break; + } + else if (item.inscription.find("second") != std::string::npos + || item.inscription.find("alt") != std::string::npos) + { + mpr("Putting weapon into alt slot by inscription"); + mon_slot = MSLOT_ALT_WEAPON; + break; + } + + // For monsters which can wield two weapons, prefer whichever + // slot is empty (if there is an empty slot). + if (mons_wields_two_weapons(mon)) + { + if (mon->inv[MSLOT_WEAPON] == NON_ITEM) + { + mpr("Dual wielding monster, putting into empty primary slot"); + mon_slot = MSLOT_WEAPON; + break; + } + else if (mon->inv[MSLOT_ALT_WEAPON] == NON_ITEM) + { + mpr("Dual wielding monster, putting into empty alt slot"); + mon_slot = MSLOT_ALT_WEAPON; + break; + } + } + + // Try to replace a ranged weapon with a ranged weapon and + // a non-ranged weapon with a non-ranged weapon + if (mon->inv[MSLOT_WEAPON] != NON_ITEM + && (is_range_weapon(mitm[mon->inv[MSLOT_WEAPON]]) + == is_range_weapon(item))) + { + mpr("Replacing primary slot with similar weapon"); + mon_slot = MSLOT_WEAPON; + break; + } + if (mon->inv[MSLOT_ALT_WEAPON] != NON_ITEM + && (is_range_weapon(mitm[mon->inv[MSLOT_ALT_WEAPON]]) + == is_range_weapon(item))) + { + mpr("Replacing alt slot with similar weapon"); + mon_slot = MSLOT_ALT_WEAPON; + break; + } + + // Prefer the empty slot (if any) + if (mon->inv[MSLOT_WEAPON] == NON_ITEM) + { + mpr("Putting weapon into empty primary slot"); + mon_slot = MSLOT_WEAPON; + break; + } + else if (mon->inv[MSLOT_ALT_WEAPON] == NON_ITEM) + { + mpr("Putting weapon into empty alt slot"); + mon_slot = MSLOT_ALT_WEAPON; + break; + } + + // Default to primary weapon slot + mpr("Defaulting to primary slot"); + mon_slot = MSLOT_WEAPON; + break; + + case OBJ_ARMOUR: + mon_slot = MSLOT_ARMOUR; + break; + case OBJ_MISSILES: + mon_slot = MSLOT_MISSILE; + break; + case OBJ_WANDS: + mon_slot = MSLOT_WAND; + break; + case OBJ_SCROLLS: + mon_slot = MSLOT_SCROLL; + break; + case OBJ_POTIONS: + mon_slot = MSLOT_POTION; + break; + case OBJ_MISCELLANY: + mon_slot = MSLOT_MISCELLANY; + break; + default: + mpr("You can't give that type of item to a monster."); + return; + } + + // Shouldn't be be using MONUSE_MAGIC_ITEMS? + if (item_use == MONUSE_STARTING_EQUIPMENT + && !mons_is_unique( mon->type )) + { + switch(mon_slot) + { + case MSLOT_WEAPON: + case MSLOT_ALT_WEAPON: + case MSLOT_ARMOUR: + case MSLOT_MISSILE: + break; + + default: + mpr("That type of monster can only use weapons and armour."); + return; + } + } + + int index = get_item_slot(10); + + if (index == NON_ITEM) + { + mpr("Too many items on level, bailing."); + return; + } + + // Move monster's old item to player's inventory as last step + int old_eq = NON_ITEM; + if (mon->inv[mon_slot] != NON_ITEM) + { + old_eq = mon->inv[mon_slot]; + + // Alternative weapons don't get (un)wielded unless the monster + // can wield two weapons. + if (mon_slot != MSLOT_ALT_WEAPON || mons_wields_two_weapons(mon)) + mon->unequip(*(mon->mslot_item(mon_slot)), mon_slot, 1, true); + } + + item_def &new_item = mitm[index]; + new_item = item; + new_item.link = NON_ITEM; + new_item.x = 0; + new_item.y = 0; + + mon->inv[mon_slot] = index; + + // Alternative weapons don't get (un)wielded unless the monster + // can wield two weapons. + if (mon_slot != MSLOT_ALT_WEAPON || mons_wields_two_weapons(mon)) + mon->equip(new_item, mon_slot, 1); + + // Item is gone from player's inventory + dec_inv_item_quantity(player_slot, item.quantity); + + // Monster's old item moves to player's inventory. + if (old_eq != NON_ITEM) + { + mpr("Fetching monster's old item."); + move_item_to_player(old_eq, mitm[old_eq].quantity); + } +} +#endif #ifdef DEBUG_DIAGNOSTICS diff --git a/crawl-ref/source/debug.h b/crawl-ref/source/debug.h index 6dd314f592..5e8b80ee76 100644 --- a/crawl-ref/source/debug.h +++ b/crawl-ref/source/debug.h @@ -160,6 +160,7 @@ void debug_dismiss_all_monsters(); class monsters; void debug_make_monster_shout(monsters* mon); +void wizard_give_monster_item(monsters* mon); #ifdef DEBUG_DIAGNOSTICS void generate_map_stats(); diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc index 9c347adaeb..88da50188a 100644 --- a/crawl-ref/source/direct.cc +++ b/crawl-ref/source/direct.cc @@ -600,8 +600,17 @@ void direction(dist& moves, targeting_type restricts, debug_make_monster_shout(&menv[mid]); break; -#endif + case CMD_TARGET_WIZARD_GIVE_ITEM: + if (!you.wizard || !in_bounds(moves.tx, moves.ty)) + break; + mid = mgrd[moves.tx][moves.ty]; + if (mid == NON_MONSTER) // can put in terrain description here + break; + + wizard_give_monster_item(&menv[mid]); + break; +#endif case CMD_TARGET_DESCRIBE: full_describe_square(moves.target()); @@ -1915,6 +1924,7 @@ command_type targeting_behaviour::get_command(int key) #ifdef WIZARD case 'F': return CMD_TARGET_WIZARD_MAKE_FRIENDLY; case 's': return CMD_TARGET_WIZARD_MAKE_SHOUT; + case 'g': return CMD_TARGET_WIZARD_GIVE_ITEM; #endif case 'v': return CMD_TARGET_DESCRIBE; case '?': return CMD_TARGET_HELP; diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 2fd4585600..1718d6cc9d 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -758,6 +758,7 @@ enum command_type CMD_TARGET_DESCRIBE, CMD_TARGET_WIZARD_MAKE_FRIENDLY, CMD_TARGET_WIZARD_MAKE_SHOUT, + CMD_TARGET_WIZARD_GIVE_ITEM, CMD_TARGET_HELP, // Disable/enable -more- prompts. @@ -1605,8 +1606,9 @@ enum item_type_id_type enum item_type_id_state_type // used for values in id[4][50] { ID_UNKNOWN_TYPE = 0, - ID_KNOWN_TYPE, - ID_TRIED_TYPE + ID_MON_TRIED_TYPE, + ID_TRIED_TYPE, + ID_KNOWN_TYPE }; enum jewellery_type diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index f326a6a251..b93b613304 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -1111,8 +1111,9 @@ public: bool pickup_misc(item_def &item, int near); bool pickup_missile(item_def &item, int near, bool force); bool eat_corpse(item_def &carrion, int near); - void equip(const item_def &item, int slot, int near = -1); - void unequip(const item_def &item, int slot, int near = -1); + void equip(item_def &item, int slot, int near = -1); + bool unequip(item_def &item, int slot, int near = -1, + bool force = false); bool can_use_missile(const item_def &item) const; @@ -1192,7 +1193,10 @@ private: bool need_message(int &near) const; bool pickup(item_def &item, int slot, int near, bool force_merge = false); - void equip_weapon(const item_def &item, int near); + void equip_weapon(item_def &item, int near); + void equip_armour(item_def &item, int near); + void unequip_weapon(item_def &item, int near); + void unequip_armour(item_def &item, int near); bool decay_enchantment(const mon_enchant &me, bool decay_degree = true); diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index e32961c628..9619b92e38 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -286,7 +286,7 @@ melee_attack::melee_attack(actor *attk, actor *defn, : attacker(attk), defender(defn), atk(NULL), def(NULL), cancel_attack(false), - did_hit(false), perceived_attack(false), + did_hit(false), perceived_attack(false), obvious_effect(false), needs_message(false), attacker_visible(false), defender_visible(false), attacker_invisible(false), defender_invisible(false), unarmed_ok(allow_unarmed), @@ -1615,6 +1615,8 @@ bool melee_attack::distortion_affects_defender() if (one_chance_in(3)) { emit_nodmg_hit_message(); + if (defender_visible) + obvious_effect = true; defender->blink(); return (false); } @@ -1626,6 +1628,8 @@ bool melee_attack::distortion_affects_defender() if (!one_chance_in(3)) { emit_nodmg_hit_message(); + if (defender_visible) + obvious_effect = true; defender->teleport(coinflip(), one_chance_in(5)); return (false); } @@ -1633,6 +1637,19 @@ bool melee_attack::distortion_affects_defender() if (you.level_type != LEVEL_ABYSS && coinflip()) { emit_nodmg_hit_message(); + + if (defender->atype() == ACT_PLAYER && attacker_visible + && weapon != NULL && !is_artefact(*weapon)) + { + // If the player is being sent to the Abyss by being attacked + // with a distortion weapon, then we have to ID it before + // the player goes to Abyss, while the weapon object is + // still in memory. + set_ident_flags(*weapon, ISFLAG_KNOW_TYPE); + } + else if (defender_visible) + obvious_effect = true; + defender->banish( atk? atk->name(DESC_PLAIN, true) : attacker->name(DESC_PLAIN) ); return (true); @@ -1643,10 +1660,13 @@ bool melee_attack::distortion_affects_defender() bool melee_attack::apply_damage_brand() { + bool ret = false; + // Monster resistance to the brand. int res = 0; - + special_damage = 0; + obvious_effect = false; switch (damage_brand) { case SPWPN_FLAMING: @@ -1718,12 +1738,24 @@ bool melee_attack::apply_damage_brand() case SPWPN_STAFF_OF_OLGREB: if (!one_chance_in(4)) { + int old_poison = 0; + + if (defender->atype() == ACT_PLAYER) + old_poison = you.duration[DUR_POISONING]; + // Poison monster message needs to arrive after hit message. emit_nodmg_hit_message(); // Weapons of venom do two levels of poisoning to the player, // but only one level to monsters. defender->poison( attacker, 2 ); + + if (defender->atype() == ACT_PLAYER + && old_poison < you.duration[DUR_POISONING]) + { + obvious_effect = true; + } + } break; @@ -1747,6 +1779,8 @@ bool melee_attack::apply_damage_brand() break; } + obvious_effect = true; + // vampire bat form if (you.species == SP_VAMPIRE && attacker->atype() == ACT_PLAYER && you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT) @@ -1853,8 +1887,7 @@ bool melee_attack::apply_damage_brand() break; case SPWPN_DISTORTION: - if (distortion_affects_defender()) - return (true); + ret = distortion_affects_defender(); break; case SPWPN_CONFUSE: @@ -1885,7 +1918,15 @@ bool melee_attack::apply_damage_brand() } } - return (false); + obvious_effect = obvious_effect || (special_damage_message != ""); + + if (obvious_effect && attacker_visible && weapon != NULL + && !is_artefact(*weapon)) + { + set_ident_flags(*weapon, ISFLAG_KNOW_TYPE); + } + + return (ret); } // Returns true if a head got lopped off. diff --git a/crawl-ref/source/fight.h b/crawl-ref/source/fight.h index 900e90c156..58ecd98160 100644 --- a/crawl-ref/source/fight.h +++ b/crawl-ref/source/fight.h @@ -65,7 +65,7 @@ public: monsters *atk, *def; bool cancel_attack; - bool did_hit, perceived_attack; + bool did_hit, perceived_attack, obvious_effect; // If all or part of the action is visible to the player, we need a message. bool needs_message; diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc index 1c13c750ae..ee02552c5d 100644 --- a/crawl-ref/source/itemname.cc +++ b/crawl-ref/source/itemname.cc @@ -213,16 +213,28 @@ std::string item_def::name(description_level_type descrip, } } - const bool tried = (!ident && item_type_tried(*this)); + const bool tried = (!ident && item_type_tried(*this)); + std::string tried_str = ""; + + if (tried) + { + item_type_id_state_type id_type = + get_ident_type(this->base_type, this->sub_type); + if (id_type == ID_MON_TRIED_TYPE) + tried_str = "tried by monster"; + else + tried_str = "tried"; + } + if ( with_inscription && !(this->inscription.empty()) ) { buff << " {"; if ( tried ) - buff << "tried, "; + buff << tried_str << ", "; buff << this->inscription << "}"; } else if ( tried ) - buff << " {tried}"; + buff << " {" << tried_str << "}"; return buff.str(); } @@ -1559,7 +1571,8 @@ bool item_type_tried( const item_def& item ) const item_type_id_type idt = objtype_to_idtype(item.base_type); if (idt != NUM_IDTYPE && item.sub_type < 50) - return ( type_ids[idt][item.sub_type] == ID_TRIED_TYPE ); + return ( type_ids[idt][item.sub_type] == ID_TRIED_TYPE + || type_ids[idt][item.sub_type] == ID_MON_TRIED_TYPE); else return false; } @@ -1574,8 +1587,8 @@ void set_ident_type( object_class_type basetype, int subtype, { // Don't allow overwriting of known type with tried unless forced. if (!force - && setting == ID_TRIED_TYPE - && get_ident_type( basetype, subtype ) == ID_KNOWN_TYPE) + && (setting == ID_MON_TRIED_TYPE || setting == ID_TRIED_TYPE) + && setting <= get_ident_type( basetype, subtype )) { return; } diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index ff7ef0d590..58b4c5bb2b 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -2459,14 +2459,19 @@ void monsters::swap_slots(mon_inv_type a, mon_inv_type b) inv[b] = swap; } -void monsters::equip_weapon(const item_def &item, int near) +void monsters::equip_weapon(item_def &item, int near) { + if (need_message(near)) + mprf("%s wields %s.", name(DESC_CAP_THE).c_str(), + item.name(DESC_NOCAP_A).c_str()); + const int brand = get_weapon_brand(item); if (brand == SPWPN_PROTECTION) ac += 5; if (brand != SPWPN_NORMAL && need_message(near)) { + bool message_given = true; switch (brand) { case SPWPN_FLAMING: @@ -2499,52 +2504,137 @@ void monsters::equip_weapon(const item_def &item, int near) case SPWPN_RETURNING: mpr("It wiggles slightly."); break; + case SPWPN_DISTORTION: + mpr("Its appearance distorts for a moment."); + break; + default: + message_given = false; } + if (message_given) + set_ident_flags(item, ISFLAG_KNOW_TYPE); } } -void monsters::equip(const item_def &item, int slot, int near) +void monsters::equip_armour(item_def &item, int near) +{ + if (need_message(near)) + mprf("%s wears %s.", name(DESC_CAP_THE).c_str(), + item.name(DESC_NOCAP_A).c_str()); + + ac += property( item, PARM_AC ); + + const int armour_plus = item.plus; + ASSERT(abs(armour_plus) < 20); + if (abs(armour_plus) < 20) + ac += armour_plus; + ev += property( item, PARM_EVASION ) / 2; + + if (ev < 1) + ev = 1; // This *shouldn't* happen. +} + +void monsters::equip(item_def &item, int slot, int near) { switch (item.base_type) { case OBJ_WEAPONS: - if (need_message(near)) - mprf("%s wields %s.", name(DESC_CAP_THE).c_str(), - item.name(DESC_NOCAP_A).c_str()); equip_weapon(item, near); break; case OBJ_ARMOUR: - { - ac += property( item, PARM_AC ); - - const int armour_plus = item.plus; - ASSERT(abs(armour_plus) < 20); - if (abs(armour_plus) < 20) - ac += armour_plus; - ev += property( item, PARM_EVASION ) / 2; - - if (ev < 1) - ev = 1; // This *shouldn't* happen. + equip_armour(item, near); break; - } default: break; } } -void monsters::unequip(const item_def &item, int slot, int near) +void monsters::unequip_weapon(item_def &item, int near) { - // XXX: Handle armour removal when armour swapping is implemented. + if (need_message(near)) + mprf("%s unwields %s.", name(DESC_CAP_THE).c_str(), + item.name(DESC_NOCAP_A).c_str()); + + const int brand = get_weapon_brand(item); + if (brand == SPWPN_PROTECTION) + ac -= 5; + + if (brand != SPWPN_NORMAL && need_message(near)) + { + bool message_given = true; + switch (brand) + { + case SPWPN_FLAMING: + mpr("It stops flaming."); + break; + + case SPWPN_HOLY_WRATH: + mpr("It stops glowing."); + break; + + case SPWPN_ELECTROCUTION: + mpr("It stops crackling."); + break; + + case SPWPN_VENOM: + mpr("It stops dripping with poison."); + break; + + case SPWPN_DISTORTION: + mpr("Its appearance distorts for a moment."); + break; + + default: + message_given = false; + } + if (message_given) + set_ident_flags(item, ISFLAG_KNOW_TYPE); + } +} + +void monsters::unequip_armour(item_def &item, int near) +{ + if (need_message(near)) + mprf("%s takes off %s.", name(DESC_CAP_THE).c_str(), + item.name(DESC_NOCAP_A).c_str()); + + ac -= property( item, PARM_AC ); + + const int armour_plus = item.plus; + ASSERT(abs(armour_plus) < 20); + if (abs(armour_plus) < 20) + ac -= armour_plus; + ev -= property( item, PARM_EVASION ) / 2; + + if (ev < 1) + ev = 1; // This *shouldn't* happen. +} + +bool monsters::unequip(item_def &item, int slot, int near, bool force) +{ + if (item.cursed() && !force) + return false; + + if (!force && mons_near(this) && player_monster_visible(this)) + set_ident_flags(item, ISFLAG_KNOW_CURSE); + switch (item.base_type) { case OBJ_WEAPONS: - if (get_weapon_brand(item) == SPWPN_PROTECTION) - ac -= 5; + unequip_weapon(item, near); + break; + + // Armour swapping not implemented yet, but armour can be + // removed if wizard forces monster to wear a new piece of + // armour with the "give item" wizard command. + case OBJ_ARMOUR: + unequip_armour(item, near); break; default: break; } + + return true; } void monsters::lose_pickup_energy() @@ -2597,16 +2687,28 @@ bool monsters::drop_item(int eslot, int near) if (index == NON_ITEM) return (true); - // Cannot drop cursed weapon or armour. - if ((eslot == MSLOT_WEAPON || eslot == MSLOT_ARMOUR) - && mitm[index].cursed()) - return (false); + // Unequip equipped items before dropping them; unequip() prevents + // cursed items from being removed. + bool was_unequipped = false; + if (eslot == MSLOT_WEAPON || eslot == MSLOT_ARMOUR + || (eslot == MSLOT_ALT_WEAPON && mons_wields_two_weapons(this) )) + { + if (!unequip(mitm[index], eslot, near)) + return (false); + was_unequipped = true; + } const std::string iname = mitm[index].name(DESC_NOCAP_A); move_item_to_grid(&index, x, y); if (index == inv[eslot]) + { + // Re-equip item if we somehow failed to drop it. + if (was_unequipped) + equip(mitm[index], eslot, near); + return (false); + } if (need_message(near)) mprf("%s drops %s.", name(DESC_CAP_THE).c_str(), iname.c_str()); @@ -2869,21 +2971,16 @@ bool monsters::need_message(int &near) const void monsters::swap_weapons(int near) { - const item_def *weap = mslot_item(MSLOT_WEAPON); - const item_def *alt = mslot_item(MSLOT_ALT_WEAPON); + item_def *weap = mslot_item(MSLOT_WEAPON); + item_def *alt = mslot_item(MSLOT_ALT_WEAPON); if (weap) - unequip(*weap, MSLOT_WEAPON, !alt && need_message(near)); + if(!unequip(*weap, MSLOT_WEAPON, near)) + // Item was cursed + return; swap_slots(MSLOT_WEAPON, MSLOT_ALT_WEAPON); - if (need_message(near)) - { - if (!alt && weap) - mprf("%s unwields %s.", name(DESC_CAP_THE).c_str(), - weap->name(DESC_NOCAP_A).c_str()); - } - if (alt) equip(*alt, MSLOT_WEAPON, near); diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index 120c4be4da..0e51695aa6 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -2587,7 +2587,10 @@ static bool handle_potion(monsters *monster, bolt & beem) return (false); else { - bool imbibed = false; + bool imbibed = false; + item_type_id_state_type ident = ID_UNKNOWN_TYPE; + bool was_visible = + mons_near(monster) && player_monster_visible(monster); const int potion_type = mitm[monster->inv[MSLOT_POTION]].sub_type; switch (potion_type) @@ -2602,7 +2605,10 @@ static bool handle_potion(monsters *monster, bolt & beem) simple_monster_message(monster, " drinks a potion."); if (heal_monster(monster, 5 + random2(7), false)) + { simple_monster_message(monster, " is healed!"); + ident = ID_MON_TRIED_TYPE; + } if (mitm[monster->inv[MSLOT_POTION]].sub_type == POT_HEAL_WOUNDS) @@ -2614,8 +2620,10 @@ static bool handle_potion(monsters *monster, bolt & beem) { monster->del_ench(ENCH_POISON); monster->del_ench(ENCH_SICK); - monster->del_ench(ENCH_CONFUSION); - monster->del_ench(ENCH_ROT); + if (monster->del_ench(ENCH_CONFUSION)) + ident = ID_KNOWN_TYPE; + if (monster->del_ench(ENCH_ROT)) + ident = ID_KNOWN_TYPE; } imbibed = true; @@ -2646,6 +2654,7 @@ static bool handle_potion(monsters *monster, bolt & beem) imbibed = true; } + ident = ID_KNOWN_TYPE; break; } @@ -2653,6 +2662,9 @@ static bool handle_potion(monsters *monster, bolt & beem) { if (dec_mitm_item_quantity( monster->inv[MSLOT_POTION], 1 )) monster->inv[MSLOT_POTION] = NON_ITEM; + + if (ident != ID_UNKNOWN_TYPE && was_visible) + set_ident_type(OBJ_POTIONS, potion_type, ident); } return (imbibed); @@ -2704,6 +2716,13 @@ static bool handle_reaching(monsters *monster) } } + // Player saw the item reach + if (ret && !is_artefact(mitm[wpn]) && mons_near(monster) + && player_monster_visible(monster)) + { + set_ident_flags(mitm[wpn], ISFLAG_KNOW_TYPE); + } + return ret; } // end handle_reaching() @@ -2730,10 +2749,14 @@ static bool handle_scroll(monsters *monster) return (false); else { - bool read = false; + bool read = false; + item_type_id_state_type ident = ID_UNKNOWN_TYPE; + bool was_visible = + mons_near(monster) && player_monster_visible(monster); // notice how few cases are actually accounted for here {dlb}: - switch (mitm[monster->inv[MSLOT_SCROLL]].sub_type) + const int scroll_type = mitm[monster->inv[MSLOT_SCROLL]].sub_type; + switch (scroll_type) { case SCR_TELEPORTATION: if (!monster->has_ench(ENCH_TP)) @@ -2742,7 +2765,8 @@ static bool handle_scroll(monsters *monster) { simple_monster_message(monster, " reads a scroll."); monster_teleport(monster, false); - read = true; + read = true; + ident = ID_KNOWN_TYPE; } } break; @@ -2755,7 +2779,8 @@ static bool handle_scroll(monsters *monster) simple_monster_message(monster, " reads a scroll."); simple_monster_message(monster, " blinks!"); monster_blink(monster); - read = true; + read = true; + ident = ID_KNOWN_TYPE; } } break; @@ -2767,7 +2792,8 @@ static bool handle_scroll(monsters *monster) create_monster( MONS_ABOMINATION_SMALL, 2, SAME_ATTITUDE(monster), monster->x, monster->y, monster->foe, 250 ); - read = true; + read = true; + ident = ID_KNOWN_TYPE; } break; } @@ -2776,6 +2802,9 @@ static bool handle_scroll(monsters *monster) { if (dec_mitm_item_quantity( monster->inv[MSLOT_SCROLL], 1 )) monster->inv[MSLOT_SCROLL] = NON_ITEM; + + if (ident != ID_UNKNOWN_TYPE && was_visible) + set_ident_type(OBJ_SCROLLS, scroll_type, ident); } return read; @@ -2806,8 +2835,10 @@ static bool handle_wand(monsters *monster, bolt &beem) } else if (coinflip()) { - bool niceWand = false; - bool zap = false; + bool niceWand = false; + bool zap = false; + bool was_visible = + mons_near(monster) && player_monster_visible(monster); // map wand type to monster spell type int mzap = map_wand_to_mspell(mitm[monster->inv[MSLOT_WAND]].sub_type); @@ -2845,7 +2876,8 @@ static bool handle_wand(monsters *monster, bolt &beem) beem.aux_source = item.name(DESC_PLAIN); - switch (mitm[monster->inv[MSLOT_WAND]].sub_type) + const int wand_type = mitm[monster->inv[MSLOT_WAND]].sub_type; + switch (wand_type) { // these have been deemed "too tricky" at this time {dlb}: case WAND_POLYMORPH_OTHER: @@ -2931,6 +2963,14 @@ static bool handle_wand(monsters *monster, bolt &beem) beem.is_tracer = false; fire_beam( beem ); + if (was_visible) + { + if (niceWand || beem.name != "0" || beem.obvious_effect) + set_ident_type(OBJ_WANDS, wand_type, ID_KNOWN_TYPE); + else + set_ident_type(OBJ_WANDS, wand_type, ID_MON_TRIED_TYPE); + } + return (true); } } @@ -3516,6 +3556,39 @@ int mons_pick_best_missile(monsters *mons, item_def **launcher, } } +bool mons_eq_obvious_ego(const item_def &item, const struct monsters *mon) +{ + if (is_artefact(item)) + return false; + + // Only do weapons for now + if (item.base_type != OBJ_WEAPONS) + return false; + + int ego = item.special; + + switch(ego) + { + + case SPWPN_FLAMING: + case SPWPN_FREEZING: + case SPWPN_HOLY_WRATH: + case SPWPN_VENOM: + // Electric branded weapons are constantly crackling + case SPWPN_ELECTROCUTION: + // Unlike SPWPN_FLAME, SPWPN_FROST has a persistant visual effect. + case SPWPN_FROST: + return true; + + // SPWPN_FLAME just "glows red for a moment" when first equipped, + // so it isn't obvious after that. + case SPWPN_FLAME: + return false; + } + + return false; +} + //--------------------------------------------------------------- // // handle_throw diff --git a/crawl-ref/source/monstuff.h b/crawl-ref/source/monstuff.h index 152c44787f..9612fed7af 100644 --- a/crawl-ref/source/monstuff.h +++ b/crawl-ref/source/monstuff.h @@ -173,4 +173,7 @@ int mons_missile_damage(const item_def *launch, const item_def *missile); int mons_thrown_weapon_damage(const item_def *weap); +bool mons_eq_obvious_ego(const item_def &item, + const struct monsters *mon = NULL); + #endif diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index 896eb12874..ad6f9641e3 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -1009,6 +1009,29 @@ void monster_grid(bool do_updates) } } +static void learn_visible_mon_eq_egos(monsters *mon) +{ + for (int i = 0; i < NUM_MONSTER_SLOTS; i++) + { + mon_inv_type slot = static_cast(i); + int index = mon->inv[slot]; + + if (index == NON_ITEM) + continue; + + // Alternate weapons aren't wielded (and thus have no + // visible differences) unless the monster can wield two + // weapons at once. + if (slot == MSLOT_ALT_WEAPON && !mons_wields_two_weapons(mon)) + continue; + + item_def &item = mitm[index]; + + if (mons_eq_obvious_ego(item, mon)) + set_ident_flags(item, ISFLAG_KNOW_TYPE); + } +} + void fire_monster_alerts() { for (int s = 0; s < MAX_MONSTERS; s++) @@ -1028,6 +1051,7 @@ void fire_monster_alerts() interrupt_activity( AI_SEE_MONSTER, monster ); } seen_monster( monster ); + learn_visible_mon_eq_egos(monster); // Monster was viewed this turn monster->flags |= MF_WAS_IN_VIEW; -- cgit v1.2.3-54-g00ecf