throwing weapons (hand axes, spears, clubs, daggers). Give orcs and orc warriors a chance of being generated with throwing weapons (axes or spears).
Throwing weapons are stacked in monster inventory, but unstack when dropped. We may want to consider fully stackable throwing weapons at some point.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1717 c06c8d41-db1a-0410-9941-cceddc491573
QS3ZRS3E6KL3YJHPKYEWCWJYRBJSXD5OOYF6Y25HZVECGPJRDB5QC
AKDVUYRVYFPCV23KOSWGC3VQ52UK5AH2KZONXTPL57I66V4CRFFAC
7IAREA6ETDRIFRARG5MEAZMXQV44FQZT3VIWJG6AAZRIEFNHKNQAC
OYTCBRC7LE44EUVRZVYTOOVKQWJ6P6YE3FXTOGUTNKEMLNWPHKSQC
FA2V3G4NYTWJWUT7EWH75L3YOUX6YVOZ5LNRSFLO2XF273JKSUTAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
E5DMZFW6WCFAKTKKOQPYTQXZ2CGLWMVH64LRXDUI2UIG4VYUHIVQC
CIPVRZGLOZHCERK6YPOBV3P2E4IAB4H6D5EHLRQE2O5E4P4VCBUAC
7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC
33ZMPQC6OXTESW7SRW765GRNJUEJRSYONRVZVIEUDAUEJ2PPMB4AC
R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC
Y56C5OMUQ5XF2G6DKDV4R5MED44UOIUPTBBQVWQBUHYIXYA5MOZAC
DTO3EUKWHZ5RJNGNCFYXSOVTIPVXPP637F2W7WFGYKJ7JK7VNKNQC
5KJCHLIUFKRPMIVWUAYT6EOF7SW4PTQF6Y5OPEFWXGLE7DUGYLZAC
GDHH6O4KVTDWSENR573WKVFCRM2L4AVOBRSVPI6F5A2UR7U7SPXAC
34C4U6EQWERY75GZJKUCM5KVGU2OUICETS5LGZF6RMKMZT4R5SQAC
KFULGQQOHWUTXOM3BXCCYPGGVGGY4Z6265XUFRCBPNLTZAEHJZSQC
JVSCP4FTW2G57C6YD5HZOZXTODGZH7TR75JQGFJBEPX3LCZH236QC
HH3HFWVXABJ4IRMN22PPJCREMULZSN6DA7VYKOGECGMNUQTZ5QNQC
ZBTD5R4OOQ5D4W6ZRK2XEGLI3AWI37Z2G5X2EQ6IYPXDYKCKWMFAC
X2FMEN4E345XD26Z2X7JMJ7VGHOGCGIELMHQRE2ITLVNQACP3NOQC
// Returns a rough estimate of damage from throwing the wielded weapon.
int mons_thrown_weapon_damage(const item_def *weap)
{
if (!weap || get_weapon_brand(*weap) != SPWPN_RETURNING)
return (0);
return std::max(0, (property(*weap, PWPN_DAMAGE) + weap->plus2 / 2));
}
// Returns a rough estimate of damage from firing/throwing missile.
int mons_missile_damage(const item_def *launch,
const item_def *missile)
{
if (!missile || (!launch && !is_throwable(*missile)))
return (0);
const int missile_damage = property(*missile, PWPN_DAMAGE) / 2 + 1;
const int launch_damage = launch? property(*launch, PWPN_DAMAGE) : 0;
return std::max(0, launch_damage + missile_damage);
}
int mons_weapon_damage_rating(const item_def &launcher)
{
return (property(launcher, PWPN_DAMAGE) + launcher.plus2);
}
// Given the monster's current weapon and alt weapon (either or both of
// which may be NULL), works out whether using missiles or throwing the
// main weapon (with returning brand) is better. If using missiles that
// need a launcher, sets *launcher to the launcher.
//
// If the monster has no ranged weapon attack, returns NON_ITEM.
//
int mons_pick_best_missile(monsters *mons, item_def **launcher,
bool ignore_melee)
{
*launcher = NULL;
item_def *melee = NULL, *launch = NULL;
for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i)
{
if (item_def *item = mons->mslot_item(static_cast<mon_inv_type>(i)))
{
if (is_range_weapon(*item))
launch = item;
else if (!ignore_melee)
melee = item;
}
}
const item_def *missiles = mons->missiles();
if (launch && missiles && !missiles->launched_by(*launch))
launch = NULL;
if (monster->has_ench(ENCH_CONFUSION)
|| monster->behaviour == BEH_SLEEP
|| monster->has_ench(ENCH_SUBMERGED))
if (monster->incapacitated()
|| monster->asleep()
|| monster->submerged())
int mon_item;
const int mon_wpn = monster->inv[MSLOT_WEAPON];
bool returning = false;
// weapons of returning can be thrown
if ( mon_wpn != NON_ITEM &&
get_weapon_brand(mitm[mon_wpn]) == SPWPN_RETURNING )
{
mon_item = mon_wpn;
returning = true;
}
else
{
mon_item = monster->inv[MSLOT_MISSILE];
}
if (mon_item == NON_ITEM || !is_valid_item( mitm[mon_item] ))
if (one_chance_in(5))
// new (GDL) - don't throw idiotic stuff. It's a waste of time.
int wepClass = mitm[mon_item].base_type;
int wepType = mitm[mon_item].sub_type;
int weapon = monster->inv[MSLOT_WEAPON];
int lnchClass = (weapon != NON_ITEM) ? mitm[weapon].base_type : -1;
int lnchType = (weapon != NON_ITEM) ? mitm[weapon].sub_type : 0;
bool thrown = false;
bool launched = false;
throw_type( lnchClass, lnchType, wepClass, wepType, launched, thrown );
if (!launched && !thrown && !returning)
if (mon_item == NON_ITEM || !is_valid_item(mitm[mon_item]))
static bool monster_wants_weapon(const monsters *monster, const item_def &weap)
{
if (is_fixed_artefact( weap ))
return (false);
if (is_random_artefact( weap ))
return (false);
// wimpy monsters (Kob, gob) shouldn't pick up halberds etc
// of course, this also block knives {dlb}:
if ((mons_species(monster->type) == MONS_KOBOLD
|| mons_species(monster->type) == MONS_GOBLIN)
&& property( weap, PWPN_HIT ) <= 0)
{
return (false);
}
// Nobody picks up giant clubs:
if (weap.sub_type == WPN_GIANT_CLUB
|| weap.sub_type == WPN_GIANT_SPIKED_CLUB)
{
return (false);
}
const int brand = get_weapon_brand(weap);
const int holiness = monster->holiness();
if (brand == SPWPN_DISRUPTION && holiness == MH_UNDEAD)
return (false);
if (brand == SPWPN_HOLY_WRATH
&& (holiness == MH_DEMONIC || holiness == MH_UNDEAD))
{
return (false);
}
return (true);
}
case OBJ_WEAPONS:
if (monster->inv[MSLOT_WEAPON] != NON_ITEM)
return (false);
if (!monster_wants_weapon(monster, mitm[item]))
return (false);
monster->inv[MSLOT_WEAPON] = item;
if (get_weapon_brand(mitm[monster->inv[MSLOT_WEAPON]]) == SPWPN_PROTECTION)
{
monster->ac += 3;
}
if (monsterNearby)
{
mprf("%s picks up %s.", str_monam(*monster, DESC_CAP_THE).c_str(),
mitm[monster->inv[MSLOT_WEAPON]].name(DESC_NOCAP_A).c_str());
}
break;
case OBJ_MISSILES:
// don't pick up if we're in combat, and there isn't much there
if (mitm[item].quantity < 5 || monster->behaviour != BEH_WANDER)
return (false);
if (monster->inv[MSLOT_MISSILE] != NON_ITEM
&& mitm[monster->inv[MSLOT_MISSILE]].sub_type == mitm[item].sub_type
&& mitm[monster->inv[MSLOT_MISSILE]].plus == mitm[item].plus
&& mitm[monster->inv[MSLOT_MISSILE]].special == mitm[item].special)
{
if (monsterNearby)
{
mprf("%s picks up %s.",
str_monam(*monster, DESC_CAP_THE).c_str(),
mitm[item].name(DESC_NOCAP_A).c_str());
}
inc_mitm_item_quantity( monster->inv[MSLOT_MISSILE],
mitm[item].quantity );
dec_mitm_item_quantity( item, mitm[item].quantity );
item_def &topickup = mitm[item];
item = topickup.link;
if (monster->pickup_item(topickup, monsterNearby))
}
// nobody bothers to pick up rocks if they don't already have some:
if (mitm[item].sub_type == MI_LARGE_ROCK)
return (false);
// monsters with powerful melee attacks don't bother
if (mons_damage(monster->type, 0) > 5)
return (false);
monster->inv[MSLOT_MISSILE] = item;
if (monsterNearby)
{
mprf("%s picks up %s.", str_monam(*monster, DESC_CAP_THE).c_str(),
mitm[item].name(DESC_NOCAP_A).c_str());
}
break;
case OBJ_WANDS:
if (monster->inv[MSLOT_WAND] != NON_ITEM)
return (false);
monster->inv[MSLOT_WAND] = item;
if (monsterNearby)
{
mprf("%s picks up %s.", str_monam(*monster, DESC_CAP_THE).c_str(),
mitm[item].name(DESC_NOCAP_A).c_str());
}
break;
case OBJ_SCROLLS:
if (monster->inv[MSLOT_SCROLL] != NON_ITEM)
return (false);
monster->inv[MSLOT_SCROLL] = item;
if (monsterNearby)
{
mprf("%s picks up %s.", str_monam(*monster, DESC_CAP_THE).c_str(),
mitm[item].name(DESC_NOCAP_A).c_str());
}
break;
case OBJ_POTIONS:
if (monster->inv[MSLOT_POTION] != NON_ITEM)
return (false);
monster->inv[MSLOT_POTION] = item;
if (monsterNearby)
{
mprf("%s picks up %s.", str_monam(*monster, DESC_CAP_THE).c_str(),
mitm[item].name(DESC_NOCAP_A).c_str());
}
break;
case OBJ_CORPSES:
if (monster->type != MONS_NECROPHAGE && monster->type != MONS_GHOUL)
return (false);
monster->hit_points += 1 + random2(mons_weight(mitm[item].plus))/100;
// limited growth factor here -- should 77 really be the cap? {dlb}:
if (monster->hit_points > 100)
monster->hit_points = 100;
if (monster->hit_points > monster->max_hit_points)
monster->max_hit_points = monster->hit_points;
if (monsterNearby)
{
mprf("%s eats %s.", str_monam(*monster, DESC_CAP_THE).c_str(),
mitm[item].name(DESC_NOCAP_THE).c_str());
}
destroy_item( item );
return (true);
case OBJ_GOLD: //mv - monsters now pick up gold (19 May 2001)
if (monsterNearby)
{
mprf("%s picks up some gold.",
str_monam(*monster, DESC_CAP_THE).c_str());
}
if (monster->inv[MSLOT_GOLD] != NON_ITEM)
{
// transfer gold to monster's object, destroy ground object
inc_mitm_item_quantity( monster->inv[MSLOT_GOLD],
mitm[item].quantity );
destroy_item( item );
return (true);
}
else
{
monster->inv[MSLOT_GOLD] = item;
}
break;
default:
return (false);
// Item has been picked-up, move to monster inventory.
mitm[item].x = 0;
mitm[item].y = 0;
// Monster's only take the top item of stacks, so relink the
// top item, and unlink the item.
igrd[monster->x][monster->y] = mitm[item].link;
mitm[item].link = NON_ITEM;
if (monster->speed_increment > 25)
monster->speed_increment -= monster->speed;
return (true);
return (false);
}
item_def *monsters::missiles()
{
return (inv[MSLOT_MISSILE] != NON_ITEM? &mitm[inv[MSLOT_MISSILE]] : NULL);
}
int monsters::missile_count()
{
if (const item_def *missile = missiles())
return (missile->quantity);
return (0);
}
item_def *monsters::launcher()
{
item_def *weap = mslot_item(MSLOT_WEAPON);
if (weap && is_range_weapon(*weap))
return (weap);
weap = mslot_item(MSLOT_ALT_WEAPON);
return (weap && is_range_weapon(*weap)? weap : NULL);
static int equip_slot_to_mslot(equipment_type eq)
bool monsters::can_throw_rocks() const
{
return (type == MONS_STONE_GIANT || type == MONS_CYCLOPS);
}
bool monsters::can_use_missile(const item_def &item) const
{
// Pretty simplistic at the moment. We allow monsters to pick up
// missiles without the corresponding launcher, assuming that sufficient
// wandering may get them to stumble upon the launcher.
if (item.base_type == OBJ_WEAPONS)
return (is_throwable(item));
if (item.base_type != OBJ_MISSILES)
return (false);
if (item.sub_type == MI_LARGE_ROCK && !can_throw_rocks())
return (false);
return (true);
}
void monsters::swap_slots(mon_inv_type a, mon_inv_type b)
{
const int swap = inv[a];
inv[a] = inv[b];
inv[b] = swap;
}
void monsters::equip_weapon(const item_def &item, int near)
{
const int brand = get_weapon_brand(item);
if (brand == SPWPN_PROTECTION)
ac += 5;
if (brand != SPWPN_NORMAL && need_message(near))
{
switch (brand)
{
case SPWPN_FLAMING:
mpr("It bursts into flame!");
break;
case SPWPN_FREEZING:
mpr("It glows with a cold blue light!");
break;
case SPWPN_HOLY_WRATH:
mpr("It softly glows with a divine radiance!");
break;
case SPWPN_ELECTROCUTION:
mpr("You hear the crackle of electricity.");
break;
case SPWPN_VENOM:
mpr("It begins to drip with poison!");
break;
case SPWPN_DRAINING:
mpr("You sense an unholy aura.");
break;
case SPWPN_FLAME:
mpr("It glows red for a moment.");
break;
case SPWPN_FROST:
mpr("It is covered in frost.");
break;
case SPWPN_DISRUPTION:
mpr("You sense a holy aura.");
break;
case SPWPN_RETURNING:
mpr("It wiggles slightly.");
break;
}
}
}
void monsters::equip(const 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.
break;
}
default:
break;
}
}
void monsters::unequip(const item_def &item, int slot, int near)
{
// XXX: Handle armour removal when armour swapping is implemented.
switch (item.base_type)
{
case OBJ_WEAPONS:
if (get_weapon_brand(item) == SPWPN_PROTECTION)
ac -= 5;
break;
default:
break;
}
}
void monsters::lose_pickup_energy()
{
if (speed_increment > 25 && speed < speed_increment)
speed_increment -= speed;
}
void monsters::pickup_message(const item_def &item, int near)
{
if (need_message(near))
mprf("%s picks up %s.",
name(DESC_CAP_THE).c_str(),
item.base_type == OBJ_GOLD? "some gold"
: item.name(DESC_NOCAP_A).c_str());
}
bool monsters::pickup(item_def &item, int slot, int near, bool force_merge)
{
if (inv[slot] != NON_ITEM)
{
if (items_stack(item, mitm[inv[slot]], force_merge))
{
pickup_message(item, near);
inc_mitm_item_quantity( inv[slot], item.quantity );
destroy_item(item.index());
equip(item, slot, near);
lose_pickup_energy();
return (true);
}
return (false);
}
const int index = item.index();
unlink_item(index);
inv[slot] = index;
pickup_message(item, near);
equip(item, slot, near);
lose_pickup_energy();
return (true);
}
bool monsters::drop_item(int eslot, int near)
{
if (eslot < 0 || eslot >= NUM_MONSTER_SLOTS)
return (false);
int index = inv[eslot];
if (index == NON_ITEM)
return (true);
// Cannot drop cursed weapon or armour.
if ((eslot == MSLOT_WEAPON || eslot == MSLOT_ARMOUR)
&& mitm[index].cursed())
return (false);
const std::string iname = mitm[index].name(DESC_NOCAP_A);
move_item_to_grid(&index, x, y);
if (index == inv[eslot])
return (false);
if (need_message(near))
mprf("%s drops %s.", name(DESC_CAP_THE).c_str(), iname.c_str());
inv[eslot] = NON_ITEM;
return (true);
}
bool monsters::pickup_launcher(item_def &launch, int near)
{
const int mdam_rating = mons_weapon_damage_rating(launch);
const missile_type mt = fires_ammo_type(launch);
int eslot = -1;
for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i)
{
if (const item_def *elaunch = mslot_item(static_cast<mon_inv_type>(i)))
{
if (is_range_weapon(*elaunch))
continue;
return (fires_ammo_type(*elaunch) == mt
&& mons_weapon_damage_rating(*elaunch) < mdam_rating
&& drop_item(i, near) && pickup(launch, i, near));
}
else
eslot = i;
}
return (eslot == -1? false : pickup(launch, eslot, near));
}
bool monsters::pickup_melee_weapon(item_def &item, int near)
{
if (mons_wields_two_weapons(this))
{
// If we have either weapon slot free, pick up the weapon.
if (inv[MSLOT_WEAPON] == NON_ITEM)
return pickup(item, MSLOT_WEAPON, near);
else if (inv[MSLOT_ALT_WEAPON] == NON_ITEM)
return pickup(item, MSLOT_ALT_WEAPON, near);
}
const int mdam_rating = mons_weapon_damage_rating(item);
int eslot = -1;
bool has_melee = false;
for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i)
{
if (const item_def *weap = mslot_item(static_cast<mon_inv_type>(i)))
{
if (is_range_weapon(*weap))
continue;
has_melee = true;
if (mons_weapon_damage_rating(*weap) < mdam_rating)
return (drop_item(i, near) && pickup(item, i, near));
}
else
eslot = i;
}
if (eslot == MSLOT_ALT_WEAPON && inv[MSLOT_WEAPON] == NON_ITEM)
eslot = MSLOT_WEAPON;
return (eslot == -1 || has_melee? false : pickup(item, eslot, near));
}
// Arbitrary damage adjustment for quantity of missiles. So sue me.
static int q_adj_damage(int damage, int qty)
{
return (damage * std::min(qty, 8));
}
bool monsters::pickup_throwable_weapon(item_def &item, int near)
if (mslot_item(MSLOT_MISSILE) && pickup(item, MSLOT_MISSILE, near, true))
return (true);
item_def *launch = NULL;
const int exist_missile = mons_pick_best_missile(this, &launch, true);
if (exist_missile == NON_ITEM
|| (q_adj_damage(mons_missile_damage(launch, &mitm[exist_missile]),
mitm[exist_missile].quantity)
<
q_adj_damage(mons_thrown_weapon_damage(&item), item.quantity)))
{
if (inv[MSLOT_MISSILE] != NON_ITEM && !drop_item(MSLOT_MISSILE, near))
return (false);
return pickup(item, MSLOT_MISSILE, near);
}
return (false);
}
bool monsters::wants_weapon(const item_def &weap) const
{
if (is_fixed_artefact( weap ))
return (false);
// wimpy monsters (Kob, gob) shouldn't pick up halberds etc
// of course, this also block knives {dlb}:
if ((::mons_species(type) == MONS_KOBOLD
|| ::mons_species(type) == MONS_GOBLIN)
&& property( weap, PWPN_HIT ) <= 0)
{
return (false);
}
// Nobody picks up giant clubs:
if (weap.sub_type == WPN_GIANT_CLUB
|| weap.sub_type == WPN_GIANT_SPIKED_CLUB)
{
return (false);
}
const int brand = get_weapon_brand(weap);
const int holy = holiness();
if (brand == SPWPN_DISRUPTION && holy == MH_UNDEAD)
return (false);
if (brand == SPWPN_HOLY_WRATH
&& (holy == MH_DEMONIC || holy == MH_UNDEAD))
{
return (false);
}
return (true);
}
bool monsters::pickup_weapon(item_def &item, int near, bool force)
{
if (!force && !wants_weapon(item))
return (false);
// Weapon pickup involves:
// - If we have no weapons, always pick this up.
// - If this is a melee weapon and we already have a melee weapon, pick
// it up if it is superior to the one we're carrying (and drop the
// one we have).
// - If it is a ranged weapon, and we already have a ranged weapon,
// pick it up if it is better than the one we have.
// - If it is a throwable weapon, and we're carrying no missiles (or our
// missiles are the same type), pick it up.
if (is_range_weapon(item))
return (pickup_launcher(item, near));
if (pickup_melee_weapon(item, near))
return (true);
return (can_use_missile(item) && pickup_throwable_weapon(item, near));
}
bool monsters::pickup_missile(item_def &item, int near)
{
// XXX: Missile pickup could get a lot smarter if we allow monsters to
// drop their existing missiles and pick up new stuff, but that's too
// much work for now.
const item_def *miss = missiles();
if (miss && items_stack(*miss, item))
return (pickup(item, MSLOT_MISSILE, near));
if (!can_use_missile(item))
return (false);
return pickup(item, MSLOT_MISSILE, near);
}
bool monsters::pickup_wand(item_def &item, int near)
{
return pickup(item, MSLOT_WEAPON, near);
}
bool monsters::pickup_scroll(item_def &item, int near)
{
return pickup(item, MSLOT_SCROLL, near);
}
bool monsters::pickup_potion(item_def &item, int near)
{
return pickup(item, MSLOT_POTION, near);
}
bool monsters::pickup_gold(item_def &item, int near)
{
return pickup(item, MSLOT_GOLD, near);
}
bool monsters::eat_corpse(item_def &carrion, int near)
{
if (!mons_eats_corpses(this))
return (false);
hit_points += 1 + random2(mons_weight(carrion.plus)) / 100;
// limited growth factor here -- should 77 really be the cap? {dlb}:
if (hit_points > 100)
hit_points = 100;
if (hit_points > max_hit_points)
max_hit_points = hit_points;
if (need_message(near))
mprf("%s eats %s.", name(DESC_CAP_THE).c_str(),
carrion.name(DESC_NOCAP_THE).c_str());
destroy_item( carrion.index() );
return (true);
}
bool monsters::pickup_item(item_def &item, int near, bool force)
{
// Never pick up stuff when we're in battle.
if (behaviour != BEH_WANDER && !force)
return (false);
// Jellies are not handled here.
switch (item.base_type)
{
case OBJ_WEAPONS:
return pickup_weapon(item, near, force);
case OBJ_ARMOUR:
return pickup(item, MSLOT_ARMOUR, near);
case OBJ_MISSILES:
return pickup_missile(item, near);
case OBJ_WANDS:
return pickup_wand(item, near);
case OBJ_SCROLLS:
return pickup_scroll(item, near);
case OBJ_CORPSES:
return eat_corpse(item, near);
case OBJ_MISCELLANY:
return pickup(item, MSLOT_MISCELLANY, near);
case OBJ_GOLD:
return pickup_gold(item, near);
default:
return (false);
}
}
bool monsters::need_message(int &near) const
{
return near != -1? near : (near = visible());
}
void monsters::swap_weapons(int near)
{
const item_def *weap = mslot_item(MSLOT_WEAPON);
const item_def *alt = mslot_item(MSLOT_ALT_WEAPON);
if (weap)
unequip(*weap, MSLOT_WEAPON, !alt && need_message(near));
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);
// Monsters can swap weapons really fast. :-)
if ((weap || alt) && speed_increment >= 2)
speed_increment -= 2;
}
void monsters::wield_melee_weapon(int near)
{
const item_def *weap = mslot_item(MSLOT_WEAPON);
if (!weap || (!weap->cursed() && is_range_weapon(*weap)))
{
const item_def *alt = mslot_item(MSLOT_ALT_WEAPON);
if (alt && (!weap || !is_range_weapon(*alt)))
swap_weapons(near);
}
}
static mon_inv_type equip_slot_to_mslot(equipment_type eq)
{
int mslot = equip_slot_to_mslot(eq);
int mindex = mslot == -1? NON_ITEM : inv[mslot];
return mslot_item(equip_slot_to_mslot(eq));
}
item_def *monsters::mslot_item(mon_inv_type mslot)
{
const int mindex = mslot == NUM_MONSTER_SLOTS? NON_ITEM : inv[mslot];
switch (mthing.base_type)
{
case OBJ_WEAPONS:
{
const int slot = mon->inv[MSLOT_WEAPON] == NON_ITEM? 0 : 1;
mon->inv[slot] = thing;
break;
}
case OBJ_MISSILES:
mon->inv[MSLOT_MISSILE] = thing;
break;
case OBJ_SCROLLS:
mon->inv[MSLOT_SCROLL] = thing;
break;
case OBJ_GOLD:
mon->inv[MSLOT_GOLD] = thing;
break;
case OBJ_POTIONS:
mon->inv[MSLOT_POTION] = thing;
break;
case OBJ_MISCELLANY:
mon->inv[MSLOT_MISCELLANY] = thing;
break;
case OBJ_WANDS:
mon->inv[MSLOT_WAND] = thing;
break;
case OBJ_ARMOUR:
{
mon->inv[MSLOT_ARMOUR] = thing;
mon->ac += property( mthing, PARM_AC );
const int armour_plus = mthing.plus;
ASSERT(abs(armour_plus) < 20);
if (abs(armour_plus) < 20)
mon->ac += armour_plus;
mon->ev += property( mthing, PARM_EVASION ) / 2;
if (mon->ev < 1)
mon->ev = 1; // This *shouldn't* happen.
break;
}
default:
break;
}
const int speed_inc = mon->speed_increment;
if (!(pickupfn? (mon->*pickupfn)(mthing, false)
: mon->pickup_item(mthing, false, true)))
{
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_WARN, "Destroying %s because %s doesn't want it!",
mthing.name(DESC_PLAIN).c_str(), mon->name(DESC_PLAIN).c_str());
#endif
destroy_item(thing);
return ;
}
mon->speed_increment = speed_inc;
else
{
// Give some monsters throwing weapons.
int weap_type = WPN_UNKNOWN;
int qty = 0;
switch (mon->type)
{
case MONS_ORC_WARRIOR:
if (one_chance_in(
you.where_are_you == BRANCH_ORCISH_MINES? 9 : 20))
{
weap_type =
random_choose(WPN_HAND_AXE, WPN_SPEAR, -1);
qty = random_range(4, 8);
}
break;
case MONS_ORC:
if (one_chance_in(20))
{
weap_type =
random_choose(WPN_HAND_AXE, WPN_SPEAR, -1);
qty = random_range(2, 5);
}
break;
}
if (weap_type == WPN_UNKNOWN)
return ;
const int thing_created =
items( 0, OBJ_WEAPONS, weap_type, true, level, item_race );
if (thing_created != NON_ITEM)
{
mitm[thing_created].quantity = qty;
mitm[thing_created].flags = 0;
give_monster_item(mon, thing_created, false,
&monsters::pickup_throwable_weapon);
}
}
}
}
// Non-stackable item that's been fudge-stacked (monster throwing weapons).
// Explode the stack when dropped.
else if (mitm[*obj].quantity > 1)
{
while (mitm[*obj].quantity > 1)
{
// If we can't copy the items out, we lose the surplus.
if (copy_item_to_grid(mitm[*obj], x, y, 1, false))
--mitm[*obj].quantity;
else
mitm[*obj].quantity = 1;
bool item_def::launched_by(const item_def &launcher) const
{
if (base_type != OBJ_MISSILES)
return (false);
return (sub_type == fires_ammo_type(launcher));
}
int item_def::index() const
{
return (this - mitm.buffer());
}
void wield_melee_weapon(int near = -1);
void swap_weapons(int near = -1);
bool pickup_item(item_def &item, int near = -1, bool force = false);
void pickup_message(const item_def &item, int near);
bool pickup_wand(item_def &item, int near);
bool pickup_scroll(item_def &item, int near);
bool pickup_potion(item_def &item, int near);
bool pickup_gold(item_def &item, int near);
bool pickup_launcher(item_def &launcher, int near);
bool pickup_melee_weapon(item_def &item, int near);
bool pickup_throwable_weapon(item_def &item, int near);
bool pickup_weapon(item_def &item, int near, bool force);
bool pickup_missile(item_def &item, int near);
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);
bool can_use_missile(const item_def &item) const;
void swap_slots(mon_inv_type a, mon_inv_type b);
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);
bool drop_item(int eslot, int near);
bool wants_weapon(const item_def &item) const;
bool can_throw_rocks() const;
void lose_pickup_energy();
MSLOT_WEAPON,
MSLOT_MISSILE, // although it is a second weapon for MONS_TWO_HEADED_OGRE - how to reconcile cleanly? {dlb}
MSLOT_WEAPON, // Primary weapon (melee)
MSLOT_ALT_WEAPON, // Alternate weapon, ranged or second melee weapon
// for monsters that can use two weapons.
MSLOT_MISSILE,