/*
* File: itemprop.h
* Summary: Misc functions.
* Written by: Brent Ross
*/
#include "AppHdr.h"
#include "itemname.h"
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "externs.h"
#include "artefact.h"
#include "decks.h"
#include "food.h"
#include "invent.h"
#include "items.h"
#include "itemprop.h"
#include "macro.h"
#include "mon-util.h"
#include "mon-stuff.h"
#include "notes.h"
#include "player.h"
#include "quiver.h"
#include "random.h"
#include "shopping.h"
#include "stuff.h"
#include "xom.h"
// XXX: Name strings in most of the following are currently unused!
struct armour_def
{
armour_type id;
const char *name;
int ac;
int ev;
int mass;
bool light;
equipment_type slot;
size_type fit_min;
size_type fit_max;
};
// Note: the Little-Giant range is used to make armours which are very
// flexible and adjustable and can be worn by any player character...
// providing they also pass the shape test, of course.
static int Armour_index[NUM_ARMOURS];
static armour_def Armour_prop[NUM_ARMOURS] =
{
{ ARM_ANIMAL_SKIN, "animal skin", 2, 0, 100,
true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_ROBE, "robe", 2, 0, 60,
true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_BIG },
{ ARM_LEATHER_ARMOUR, "leather armour", 3, -1, 150,
true, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM },
{ ARM_RING_MAIL, "ring mail", 4, -2, 250,
false, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM },
{ ARM_SCALE_MAIL, "scale mail", 5, -3, 350,
false, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM },
{ ARM_CHAIN_MAIL, "chain mail", 6, -4, 400,
false, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM },
{ ARM_BANDED_MAIL, "banded mail", 7, -5, 500,
false, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM },
{ ARM_SPLINT_MAIL, "splint mail", 8, -5, 550,
false, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM },
{ ARM_PLATE_MAIL, "plate mail", 10, -6, 650,
false, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM },
{ ARM_CRYSTAL_PLATE_MAIL, "crystal plate mail", 14, -8, 1200,
false, EQ_BODY_ARMOUR, SIZE_SMALL, SIZE_MEDIUM },
{ ARM_TROLL_HIDE, "troll hide", 2, -1, 220,
true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_TROLL_LEATHER_ARMOUR, "troll leather armour", 4, -1, 220,
true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_STEAM_DRAGON_HIDE, "steam dragon hide", 2, 0, 120,
true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_STEAM_DRAGON_ARMOUR, "steam dragon armour", 4, 0, 120,
true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_MOTTLED_DRAGON_HIDE, "mottled dragon hide", 3, -1, 150,
true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_MOTTLED_DRAGON_ARMOUR,"mottled dragon armour", 5, -1, 150,
true, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_SWAMP_DRAGON_HIDE, "swamp dragon hide", 3, -2, 200,
false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_SWAMP_DRAGON_ARMOUR, "swamp dragon armour", 7, -2, 200,
false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_DRAGON_HIDE, "dragon hide", 3, -3, 350,
false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_DRAGON_ARMOUR, "dragon armour", 8, -3, 350,
false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_ICE_DRAGON_HIDE, "ice dragon hide", 4, -3, 350,
false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_ICE_DRAGON_ARMOUR, "ice dragon armour", 9, -3, 350,
false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_STORM_DRAGON_HIDE, "storm dragon hide", 4, -4, 600,
false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_STORM_DRAGON_ARMOUR, "storm dragon armour", 10, -5, 600,
false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_GOLD_DRAGON_HIDE, "gold dragon hide", 4, -5, 1100,
false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_GOLD_DRAGON_ARMOUR, "gold dragon armour", 11, -9, 1100,
false, EQ_BODY_ARMOUR, SIZE_LITTLE, SIZE_GIANT },
{ ARM_CLOAK, "cloak", 1, 0, 40,
true, EQ_CLOAK, SIZE_LITTLE, SIZE_BIG },
{ ARM_GLOVES, "gloves", 1, 0, 20,
true, EQ_GLOVES, SIZE_SMALL, SIZE_MEDIUM },
{ ARM_HELMET, "helmet", 1, 0, 80,
false, EQ_HELMET, SIZE_SMALL, SIZE_MEDIUM },
{ ARM_CAP, "cap", 0, 0, 40,
true, EQ_HELMET, SIZE_LITTLE, SIZE_LARGE },
{ ARM_WIZARD_HAT, "wizard hat", 0, 0, 40,
true, EQ_HELMET, SIZE_LITTLE, SIZE_LARGE },
// Note that barding size is compared against torso so it currently
// needs to fit medium, but that doesn't matter as much as race
// and shapeshift status.
{ ARM_BOOTS, "boots", 1, 0, 30,
true, EQ_BOOTS, SIZE_SMALL, SIZE_MEDIUM },
// Changed max. barding size to large to allow for the appropriate
// monster types (monsters don't differentiate between torso and general).
{ ARM_CENTAUR_BARDING, "centaur barding", 4, -2, 100,
true, EQ_BOOTS, SIZE_MEDIUM, SIZE_LARGE },
{ ARM_NAGA_BARDING, "naga barding", 4, -2, 100,
true, EQ_BOOTS, SIZE_MEDIUM, SIZE_LARGE },
// Note: shields use ac-value as sh-value, EV pen is used for heavy_shield.
{ ARM_BUCKLER, "buckler", 5, 0, 90,
true, EQ_SHIELD, SIZE_LITTLE, SIZE_MEDIUM },
{ ARM_SHIELD, "shield", 8, -1, 150,
false, EQ_SHIELD, SIZE_SMALL, SIZE_BIG },
{ ARM_LARGE_SHIELD, "large shield", 13, -2, 230,
false, EQ_SHIELD, SIZE_MEDIUM, SIZE_GIANT },
};
struct weapon_def
{
int id;
const char *name;
int dam;
int hit;
int speed;
int mass;
int str_weight;
skill_type skill;
hands_reqd_type hands;
size_type fit_size; // actual size is one size smaller
missile_type ammo; // MI_NONE for non-launchers
bool throwable;
int dam_type;
int acquire_weight;
};
static int Weapon_index[NUM_WEAPONS];
static weapon_def Weapon_prop[NUM_WEAPONS] =
{
// Maces & Flails
{ WPN_CLUB, "club", 5, 3, 13, 50, 7,
SK_MACES_FLAILS, HANDS_ONE, SIZE_SMALL, MI_NONE, true,
DAMV_CRUSHING, 0 },
{ WPN_WHIP, "whip", 6, 2, 11, 30, 2,
SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLASHING, 0 },
{ WPN_HAMMER, "hammer", 7, 3, 13, 90, 7,
SK_MACES_FLAILS, HANDS_ONE, SIZE_SMALL, MI_NONE, false,
DAMV_CRUSHING, 0 },
{ WPN_MACE, "mace", 8, 3, 14, 120, 8,
SK_MACES_FLAILS, HANDS_ONE, SIZE_SMALL, MI_NONE, false,
DAMV_CRUSHING, 10 },
{ WPN_FLAIL, "flail", 9, 2, 15, 130, 8,
SK_MACES_FLAILS, HANDS_ONE, SIZE_SMALL, MI_NONE, false,
DAMV_CRUSHING, 10 },
{ WPN_ANKUS, "ankus", 9, 2, 14, 120, 8,
SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_PIERCING | DAM_BLUDGEON, 10 },
{ WPN_MORNINGSTAR, "morningstar", 10, -1, 15, 140, 8,
SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_PIERCING | DAM_BLUDGEON, 10 },
{ WPN_DEMON_WHIP, "demon whip", 12, 1, 11, 30, 2,
SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLASHING, 2 },
{ WPN_HOLY_SCOURGE, "holy scourge", 13, 0, 11, 30, 2,
SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLASHING, 2 },
{ WPN_SPIKED_FLAIL, "spiked flail", 12, -2, 16, 190, 8,
SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_PIERCING | DAM_BLUDGEON, 10 },
{ WPN_DIRE_FLAIL, "dire flail", 13, -3, 14, 240, 9,
SK_MACES_FLAILS, HANDS_DOUBLE, SIZE_LARGE, MI_NONE, false,
DAMV_PIERCING | DAM_BLUDGEON, 10 },
{ WPN_EVENINGSTAR, "eveningstar", 14, -1, 15, 180, 8,
SK_MACES_FLAILS, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_PIERCING | DAM_BLUDGEON, 2 },
{ WPN_GREAT_MACE, "great mace", 17, -4, 17, 270, 9,
SK_MACES_FLAILS, HANDS_TWO, SIZE_LARGE, MI_NONE, false,
DAMV_CRUSHING, 10 },
{ WPN_GIANT_CLUB, "giant club", 20, -6, 17, 330, 10,
SK_MACES_FLAILS, HANDS_TWO, SIZE_BIG, MI_NONE, false,
DAMV_CRUSHING, 10 },
{ WPN_GIANT_SPIKED_CLUB, "giant spiked club", 22, -7, 18, 350, 10,
SK_MACES_FLAILS, HANDS_TWO, SIZE_BIG, MI_NONE, false,
DAMV_PIERCING | DAM_BLUDGEON, 10 },
// Short Blades
{ WPN_KNIFE, "knife", 3, 5, 10, 10, 1,
SK_SHORT_BLADES, HANDS_ONE, SIZE_LITTLE, MI_NONE, false,
DAMV_STABBING | DAM_SLICE, 0 },
{ WPN_DAGGER, "dagger", 4, 6, 10, 20, 1,
SK_SHORT_BLADES, HANDS_ONE, SIZE_LITTLE, MI_NONE, true,
DAMV_STABBING | DAM_SLICE, 10 },
{ WPN_QUICK_BLADE, "quick blade", 5, 6, 7, 50, 0,
SK_SHORT_BLADES, HANDS_ONE, SIZE_LITTLE, MI_NONE, false,
DAMV_STABBING | DAM_SLICE, 2 },
{ WPN_SHORT_SWORD, "short sword", 6, 4, 11, 80, 2,
SK_SHORT_BLADES, HANDS_ONE, SIZE_SMALL, MI_NONE, false,
DAMV_SLICING | DAM_PIERCE, 10 },
{ WPN_SABRE, "sabre", 7, 4, 12, 90, 2,
SK_SHORT_BLADES, HANDS_ONE, SIZE_SMALL, MI_NONE, false,
DAMV_SLICING | DAM_PIERCE, 10 },
// Long Blades
{ WPN_FALCHION, "falchion", 8, 2, 13, 170, 4,
SK_LONG_BLADES, HANDS_ONE, SIZE_SMALL, MI_NONE, false,
DAMV_SLICING, 10 }, // or perhaps DAMV_CHOPPING is more apt?
{ WPN_BLESSED_FALCHION, "blessed falchion", 10, 2, 11, 170, 4,
SK_LONG_BLADES, HANDS_ONE, SIZE_SMALL, MI_NONE, false,
DAMV_SLICING, 10 }, // or perhaps DAMV_CHOPPING is more apt?
{ WPN_LONG_SWORD, "long sword", 10, 1, 14, 160, 3,
SK_LONG_BLADES, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLICING, 10 },
{ WPN_BLESSED_LONG_SWORD, "blessed long sword", 12, 0, 12, 160, 3,
SK_LONG_BLADES, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLICING, 10 },
{ WPN_SCIMITAR, "scimitar", 11, -1, 14, 170, 3,
SK_LONG_BLADES, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLICING, 10 },
{ WPN_BLESSED_SCIMITAR, "blessed scimitar", 13, -1, 13, 170, 3,
SK_LONG_BLADES, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLICING, 10 },
{ WPN_KATANA, "katana", 13, 2, 13, 160, 3,
SK_LONG_BLADES, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLICING, 2 },
{ WPN_BLESSED_KATANA, "blessed katana", 14, 1, 13, 160, 3,
SK_LONG_BLADES, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLICING, 2 },
{ WPN_DEMON_BLADE, "demon blade", 13, -1, 15, 200, 4,
SK_LONG_BLADES, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLICING, 2 },
{ WPN_HOLY_EUDEMON_BLADE, "holy eudemon blade", 14, -2, 14, 200, 4,
SK_LONG_BLADES, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLICING, 0 },
{ WPN_DOUBLE_SWORD, "double sword", 15, -2, 16, 220, 5,
SK_LONG_BLADES, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLICING, 2 },
{ WPN_BLESSED_DOUBLE_SWORD, "blessed double sword", 15, -2, 15, 220, 5,
SK_LONG_BLADES, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false,
DAMV_SLICING, 2 },
{ WPN_GREAT_SWORD, "great sword", 16, -3, 17, 250, 6,
SK_LONG_BLADES, HANDS_TWO, SIZE_LARGE, MI_NONE, false,
DAMV_SLICING, 10 },
{ WPN_BLESSED_GREAT_SWORD, "blessed great sword", 17, -3, 17, 250, 6,
SK_LONG_BLADES, HANDS_TWO, SIZE_LARGE, MI_NONE, false,
DAMV_SLICING, 10 },
{ WPN_TRIPLE_SWORD, "triple sword", 19, -4, 19, 260, 6,
SK_LONG_BLADES, HANDS_TWO, SIZE_LARGE, MI_NONE, false,
DAMV_SLICING, 2 },
{ WPN_BLESSED_TRIPLE_SWORD, "blessed triple sword", 19, -4, 18, 260, 6,
SK_LONG_BLADES, HANDS_TWO, SIZE_LARGE, MI_NONE, false,
DAMV_SLICING, 2 },
// Axes
{ WPN_HAND_AXE, "hand axe", 7, 3, 13, 80, 6,
SK_AXES, HANDS_ONE, SIZE_SMALL, MI_NONE, true,
DAMV_CHOPPING, 10 },
{ WPN_WAR_AXE, "war axe", 11, 0, 16, 180, 7,
SK_AXES, HANDS_ONE, SIZE_MEDIUM, MI_NONE, false,
DAMV_CHOPPING, 10 },
{ WPN_BROAD_AXE, "broad axe", 14, -2, 17, 230, 8,
SK_AXES, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false,
DAMV_CHOPPING, 10 },
{ WPN_BATTLEAXE, "battleaxe", 17, -4, 18, 250, 8,
SK_AXES, HANDS_TWO, SIZE_LARGE, MI_NONE, false,
DAMV_CHOPPING, 10 },
{ WPN_EXECUTIONERS_AXE, "executioner\'s axe", 20, -6, 20, 280, 9,
SK_AXES, HANDS_TWO, SIZE_LARGE, MI_NONE, false,
DAMV_CHOPPING, 2 },
// Polearms
{ WPN_SPEAR, "spear", 6, 3, 12, 50, 3,
SK_POLEARMS, HANDS_HALF, SIZE_SMALL, MI_NONE, true,
DAMV_PIERCING, 10 },
{ WPN_TRIDENT, "trident", 9, 2, 14, 160, 4,
SK_POLEARMS, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false,
DAMV_PIERCING, 10 },
{ WPN_DEMON_TRIDENT, "demon trident", 13, 0, 14, 160, 4,
SK_POLEARMS, HANDS_HALF, SIZE_MEDIUM, MI_NONE, false,
DAMV_PIERCING, 2 },
{ WPN_HALBERD, "halberd", 13, -3, 16, 200, 5,
SK_POLEARMS, HANDS_TWO, SIZE_LARGE, MI_NONE, false,
DAMV_CHOPPING | DAM_PIERCE, 10 },
{ WPN_SCYTHE, "scythe", 14, -4, 20, 220, 7,
SK_POLEARMS, HANDS_TWO, SIZE_LARGE, MI_NONE, false,
DAMV_SLICING, 10 },
{ WPN_GLAIVE, "glaive", 15, -3, 18, 200, 6,
SK_POLEARMS, HANDS_TWO, SIZE_LARGE, MI_NONE, false,
DAMV_CHOPPING, 10 },
{ WPN_BARDICHE, "bardiche", 18, -6, 20, 200, 8,
SK_POLEARMS, HANDS_TWO, SIZE_LARGE, MI_NONE, false,
DAMV_CHOPPING, 2 },
// Staves
{ WPN_QUARTERSTAFF, "quarterstaff", 7, 6, 12, 180, 7,
SK_STAVES, HANDS_DOUBLE, SIZE_LARGE, MI_NONE, false,
DAMV_CRUSHING, 10 },
{ WPN_LAJATANG, "lajatang", 14, -3, 14, 200, 3,
SK_STAVES, HANDS_DOUBLE, SIZE_LARGE, MI_NONE, false,
DAMV_SLICING, 2 },
// Range weapons
// Notes:
// - HANDS_HALF means a reloading time penalty if using shield
// - damage field is used for bonus strength damage (string tension)
// - slings get a bonus from dex, not str (as tension is meaningless)
// - str weight is used for speed and applying dex to skill
{ WPN_BLOWGUN, "blowgun", 0, 2, 10, 20, 0,
SK_THROWING, HANDS_HALF, SIZE_LITTLE, MI_NEEDLE, false,
DAMV_NON_MELEE, 0 },
{ WPN_SLING, "sling", 0, 2, 11, 20, 1,
SK_SLINGS, HANDS_ONE, SIZE_LITTLE, MI_STONE, false,
DAMV_NON_MELEE, 10 },
{ WPN_CROSSBOW, "crossbow", 5, 4, 15, 150, 8,
SK_CROSSBOWS, HANDS_TWO, SIZE_MEDIUM, MI_BOLT, false,
DAMV_NON_MELEE, 10 },
{ WPN_BOW, "bow", 3, 1, 11, 90, 2,
SK_BOWS, HANDS_TWO, SIZE_MEDIUM, MI_ARROW, false,
DAMV_NON_MELEE, 10 },
{ WPN_LONGBOW, "longbow", 6, 0, 12, 120, 3,
SK_BOWS, HANDS_TWO, SIZE_LARGE, MI_ARROW, false,
DAMV_NON_MELEE, 10 },
};
struct missile_def
{
int id;
const char *name;
int dam;
int mass;
bool throwable;
};
static int Missile_index[NUM_MISSILES];
static missile_def Missile_prop[NUM_MISSILES] =
{
{ MI_NEEDLE, "needle", 0, 1, false },
{ MI_STONE, "stone", 4, 6, true },
{ MI_DART, "dart", 5, 3, true },
{ MI_ARROW, "arrow", 7, 5, false },
{ MI_BOLT, "bolt", 9, 5, false },
{ MI_LARGE_ROCK, "large rock", 20, 400, true },
{ MI_SLING_BULLET, "sling bullet", 6, 4, false },
{ MI_JAVELIN, "javelin", 10, 80, true },
{ MI_THROWING_NET, "throwing net", 0, 30, true },
};
struct food_def
{
int id;
const char *name;
int value;
int carn_mod;
int herb_mod;
int mass;
int turns;
};
// NOTE: Any food with special random messages or side effects
// currently only takes one turn to eat (except ghouls and chunks)...
// If this changes then those items will have to have special code
// (like ghoul chunks) to guarantee that the special thing is only
// done once. See the ghoul eating code over in food.cc.
static int Food_index[NUM_FOODS];
static food_def Food_prop[NUM_FOODS] =
{
{ FOOD_MEAT_RATION, "meat ration", 5000, 500, -1500, 80, 4 },
{ FOOD_SAUSAGE, "sausage", 1500, 150, -400, 40, 1 },
{ FOOD_CHUNK, "chunk", 1000, 100, -500, 100, 3 },
{ FOOD_BEEF_JERKY, "beef jerky", 800, 100, -250, 20, 1 },
{ FOOD_BREAD_RATION, "bread ration", 4400, -1500, 750, 80, 4 },
{ FOOD_SNOZZCUMBER, "snozzcumber", 1500, -500, 500, 50, 1 },
{ FOOD_ORANGE, "orange", 1000, -350, 400, 20, 1 },
{ FOOD_BANANA, "banana", 1000, -350, 400, 20, 1 },
{ FOOD_LEMON, "lemon", 1000, -350, 400, 20, 1 },
{ FOOD_PEAR, "pear", 700, -250, 300, 20, 1 },
{ FOOD_APPLE, "apple", 700, -250, 300, 20, 1 },
{ FOOD_APRICOT, "apricot", 700, -250, 300, 15, 1 },
{ FOOD_CHOKO, "choko", 600, -200, 250, 30, 1 },
{ FOOD_RAMBUTAN, "rambutan", 600, -200, 250, 10, 1 },
{ FOOD_LYCHEE, "lychee", 600, -200, 250, 10, 1 },
{ FOOD_STRAWBERRY, "strawberry", 200, -80, 100, 5, 1 },
{ FOOD_GRAPE, "grape", 100, -40, 50, 2, 1 },
{ FOOD_SULTANA, "sultana", 70, -30, 30, 1, 1 },
{ FOOD_ROYAL_JELLY, "royal jelly", 4000, 0, 0, 55, 1 },
{ FOOD_HONEYCOMB, "honeycomb", 2000, 0, 0, 40, 1 },
{ FOOD_PIZZA, "pizza", 1500, 0, 0, 40, 1 },
{ FOOD_CHEESE, "cheese", 1200, 0, 0, 40, 1 },
};
// Must call this functions early on so that the above tables can
// be accessed correctly.
void init_properties()
{
// Compare with enum comments, to catch changes.
COMPILE_CHECK(NUM_ARMOURS == 37, c1);
COMPILE_CHECK(NUM_WEAPONS == 55, c2);
COMPILE_CHECK(NUM_MISSILES == 9, c3);
COMPILE_CHECK(NUM_FOODS == 22, c4);
int i;
for (i = 0; i < NUM_ARMOURS; i++)
Armour_index[ Armour_prop[i].id ] = i;
for (i = 0; i < NUM_WEAPONS; i++)
Weapon_index[ Weapon_prop[i].id ] = i;
for (i = 0; i < NUM_MISSILES; i++)
Missile_index[ Missile_prop[i].id ] = i;
for (i = 0; i < NUM_FOODS; i++)
Food_index[ Food_prop[i].id ] = i;
}
// Some convenient functions to hide the bit operations and create
// an interface layer between the code and the data in case this
// gets changed again. - bwr
//
// Item cursed status functions:
//
bool item_known_cursed( const item_def &item )
{
return ((item.flags & ISFLAG_KNOW_CURSE) && (item.flags & ISFLAG_CURSED));
}
bool item_known_uncursed( const item_def &item )
{
return ((item.flags & ISFLAG_KNOW_CURSE) && !(item.flags & ISFLAG_CURSED));
}
void do_curse_item( item_def &item, bool quiet )
{
// Already cursed?
if (item.flags & ISFLAG_CURSED)
return;
// Holy wrath weapons cannot be cursed.
if (item.base_type == OBJ_WEAPONS
&& get_weapon_brand(item) == SPWPN_HOLY_WRATH)
{
if (!quiet)
{
mprf("Your %s glows black briefly, but repels the curse.",
item.name(DESC_PLAIN).c_str());
}
return;
}
if (!quiet)
{
mprf("Your %s glows black for a moment.",
item.name(DESC_PLAIN).c_str());
// If we get the message, we know the item is cursed now.
item.flags |= ISFLAG_KNOW_CURSE;
}
item.flags |= ISFLAG_CURSED;
// Xom is amused by the player's items being cursed, especially if
// they're worn/equipped.
if (in_inventory(item))
{
int amusement = 64;
if (item_is_equipped(item))
{
amusement *= 2;
// Cursed cloaks prevent you from removing body armour,
// gloves from switching rings.
if (item.base_type == OBJ_ARMOUR
&& (get_armour_slot(item) == EQ_CLOAK
|| get_armour_slot(item) == EQ_GLOVES))
{
amusement *= 2;
}
else if (you.equip[EQ_WEAPON] == item.link)
{
// Redraw the weapon.
you.wield_change = true;
}
}
xom_is_stimulated(amusement);
}
}
void do_uncurse_item( item_def &item )
{
if (in_inventory(item) && you.equip[EQ_WEAPON] == item.link)
{
// Redraw the weapon.
you.wield_change = true;
}
item.flags &= (~ISFLAG_CURSED);
}
// Is item stationary (cannot be picked up)?
void set_item_stationary( item_def &item )
{
if (item.base_type == OBJ_MISSILES && item.sub_type == MI_THROWING_NET)
item.plus2 = 1;
}
void remove_item_stationary( item_def &item )
{
if (item.base_type == OBJ_MISSILES && item.sub_type == MI_THROWING_NET)
item.plus2 = 0;
}
bool item_is_stationary( const item_def &item )
{
return (item.base_type == OBJ_MISSILES
&& item.sub_type == MI_THROWING_NET
&& item.plus2);
}
//
// Item identification status:
//
bool item_ident( const item_def &item, unsigned long flags )
{
return ((item.flags & flags) == flags);
}
void set_ident_flags( item_def &item, unsigned long flags )
{
preserve_quiver_slots p;
if ((item.flags & flags) != flags)
{
item.flags |= flags;
request_autoinscribe();
if (in_inventory(item))
shopping_list.cull_identical_items(item);
}
if (notes_are_active() && !(item.flags & ISFLAG_NOTED_ID)
&& get_ident_type(item) != ID_KNOWN_TYPE
&& fully_identified(item) && is_interesting_item(item))
{
// Make a note of it.
take_note(Note(NOTE_ID_ITEM, 0, 0, item.name(DESC_NOCAP_A).c_str(),
origin_desc(item).c_str()));
// Sometimes (e.g. shops) you can ID an item before you get it;
// don't note twice in those cases.
item.flags |= (ISFLAG_NOTED_ID | ISFLAG_NOTED_GET);
}
if (item.flags & ISFLAG_KNOW_TYPE && !is_artefact(item))
{
if (item.base_type == OBJ_WEAPONS)
you.seen_weapon[item.sub_type] |= 1 << item.special;
if (item.base_type == OBJ_ARMOUR)
you.seen_armour[item.sub_type] |= 1 << item.special;
}
}
void unset_ident_flags( item_def &item, unsigned long flags )
{
preserve_quiver_slots p;
item.flags &= (~flags);
}
// Returns the mask of interesting identify bits for this item
// (e.g., scrolls don't have know-cursedness).
unsigned long full_ident_mask( const item_def& item )
{
unsigned long flagset = ISFLAG_IDENT_MASK;
switch (item.base_type)
{
case OBJ_FOOD:
case OBJ_CORPSES:
flagset = 0;
break;
case OBJ_MISCELLANY:
if (item.sub_type == MISC_RUNE_OF_ZOT)
flagset = 0;
else
flagset = ISFLAG_KNOW_TYPE;
break;
case OBJ_BOOKS:
case OBJ_ORBS:
case OBJ_SCROLLS:
case OBJ_POTIONS:
case OBJ_STAVES:
flagset = ISFLAG_KNOW_TYPE;
break;
case OBJ_WANDS:
flagset = (ISFLAG_KNOW_TYPE | ISFLAG_KNOW_PLUSES);
break;
case OBJ_JEWELLERY:
flagset = (ISFLAG_KNOW_CURSE | ISFLAG_KNOW_TYPE);
if (ring_has_pluses(item))
flagset |= ISFLAG_KNOW_PLUSES;
break;
case OBJ_MISSILES:
flagset = ISFLAG_KNOW_PLUSES | ISFLAG_KNOW_TYPE;
if (get_ammo_brand(item) == SPMSL_NORMAL)
flagset &= ~ISFLAG_KNOW_TYPE;
break;
case OBJ_WEAPONS:
case OBJ_ARMOUR:
// All flags necessary for full identification.
default:
break;
}
if (item_type_known(item))
flagset &= (~ISFLAG_KNOW_TYPE);
if (is_artefact(item))
flagset |= ISFLAG_KNOW_PROPERTIES;
return flagset;
}
bool fully_identified( const item_def& item )
{
return item_ident(item, full_ident_mask(item));
}
//
// Equipment race and description:
//
unsigned long get_equip_race( const item_def &item )
{
return (item.flags & ISFLAG_RACIAL_MASK);
}
unsigned long get_equip_desc( const item_def &item )
{
return (item.flags & ISFLAG_COSMETIC_MASK);
}
void set_equip_race( item_def &item, unsigned long flags )
{
ASSERT( (flags & ~ISFLAG_RACIAL_MASK) == 0 );
// first check for base-sub pairs that can't ever have racial types
switch (item.base_type)
{
case OBJ_WEAPONS:
if (item.sub_type > WPN_MAX_RACIAL)
return;
break;
case OBJ_ARMOUR:
if (item.sub_type > ARM_MAX_RACIAL)
return;
break;
case OBJ_MISSILES:
if (item.sub_type > MI_MAX_RACIAL)
return;
break;
default:
return;
}
// check that item is appropriate for racial type
switch (flags)
{
case ISFLAG_ELVEN:
switch (item.base_type)
{
case OBJ_WEAPONS:
if ((weapon_skill(item) == SK_MACES_FLAILS
&& item.sub_type != WPN_WHIP)
|| (weapon_skill(item) == SK_LONG_BLADES
&& item.sub_type != WPN_FALCHION
&& item.sub_type != WPN_LONG_SWORD
&& item.sub_type != WPN_SCIMITAR)
|| weapon_skill(item) == SK_AXES
|| (weapon_skill(item) == SK_POLEARMS
&& item.sub_type != WPN_SPEAR
&& item.sub_type != WPN_TRIDENT)
|| item.sub_type == WPN_CROSSBOW)
{
return;
}
break;
case OBJ_ARMOUR:
if (item.sub_type == ARM_SPLINT_MAIL
|| item.sub_type == ARM_BANDED_MAIL
|| item.sub_type == ARM_PLATE_MAIL
|| is_hard_helmet(item))
{
return;
}
break;
case OBJ_MISSILES:
if (item.sub_type == MI_BOLT)
return;
break;
default:
break;
}
break;
case ISFLAG_DWARVEN:
switch (item.base_type)
{
case OBJ_WEAPONS:
if (item.sub_type == WPN_WHIP
|| item.sub_type == WPN_CLUB
|| item.sub_type == WPN_QUICK_BLADE
|| (weapon_skill(item) == SK_LONG_BLADES
&& item.sub_type != WPN_FALCHION
&& item.sub_type != WPN_LONG_SWORD)
|| weapon_skill(item) == SK_POLEARMS
|| item.sub_type == WPN_BLOWGUN
|| item.sub_type == WPN_BOW
|| item.sub_type == WPN_LONGBOW)
{
return;
}
break;
case OBJ_ARMOUR:
if (item.sub_type == ARM_ROBE
|| item.sub_type == ARM_LEATHER_ARMOUR
|| get_armour_slot(item) == EQ_HELMET && !is_hard_helmet(item))
{
return;
}
break;
case OBJ_MISSILES:
if (item.sub_type == MI_NEEDLE
|| item.sub_type == MI_ARROW
|| item.sub_type == MI_JAVELIN)
{
return;
}
break;
default:
break;
}
break;
case ISFLAG_ORCISH:
switch (item.base_type)
{
case OBJ_WEAPONS:
if (item.sub_type == WPN_QUICK_BLADE
|| item.sub_type == WPN_LONGBOW)
return;
break;
case OBJ_ARMOUR:
if (get_armour_slot(item) == EQ_HELMET && !is_hard_helmet(item))
return;
break;
default:
break;
}
default:
break;
}
item.flags &= ~ISFLAG_RACIAL_MASK; // delete previous
item.flags |= flags;
}
void set_equip_desc( item_def &item, unsigned long flags )
{
ASSERT( (flags & ~ISFLAG_COSMETIC_MASK) == 0 );
item.flags &= ~ISFLAG_COSMETIC_MASK; // delete previous
item.flags |= flags;
}
//
// These functions handle the description and subtypes for helmets/caps.
//
short get_helmet_desc( const item_def &item )
{
ASSERT( is_helmet(item) );
return item.plus2;
}
void set_helmet_desc( item_def &item, helmet_desc_type type )
{
ASSERT( is_helmet(item) );
if (!is_hard_helmet(item) && type > THELM_DESC_MAX_SOFT)
type = THELM_DESC_PLAIN;
item.plus2 = type;
}
bool is_helmet(const item_def& item)
{
return (item.base_type == OBJ_ARMOUR && get_armour_slot(item) == EQ_HELMET);
}
bool is_hard_helmet(const item_def &item)
{
return (item.base_type == OBJ_ARMOUR && item.sub_type == ARM_HELMET);
}
void set_helmet_random_desc( item_def &item )
{
ASSERT( is_helmet(item) );
if (is_hard_helmet(item))
item.plus2 = random2(THELM_NUM_DESCS);
else
item.plus2 = random2(THELM_DESC_MAX_SOFT + 1);
}
short get_gloves_desc( const item_def &item )
{
ASSERT( item.base_type == OBJ_ARMOUR && item.sub_type == ARM_GLOVES );
return item.plus2;
}
void set_gloves_random_desc( item_def &item )
{
ASSERT( item.base_type == OBJ_ARMOUR && item.sub_type == ARM_GLOVES );
item.plus2 = coinflip() ? TGLOV_DESC_GLOVES : TGLOV_DESC_GAUNTLETS;
if (get_armour_ego_type(item) == SPARM_ARCHERY)
item.plus2 = TGLOV_DESC_BRACERS;
}
//
// Ego item functions:
//
bool set_item_ego_type( item_def &item, int item_type, int ego_type )
{
if (item.base_type == item_type && !is_artefact(item))
{
item.special = ego_type;
return (true);
}
return (false);
}
int get_weapon_brand( const item_def &item )
{
// Weapon ego types are "brands", so we do the randart lookup here.
// Staves "brands" handled specially
if (item.base_type != OBJ_WEAPONS)
return (SPWPN_NORMAL);
if (is_artefact( item ))
return (artefact_wpn_property( item, ARTP_BRAND ));
return (item.special);
}
int get_ammo_brand( const item_def &item )
{
// No artefact arrows yet. -- bwr
if (item.base_type != OBJ_MISSILES || is_artefact( item ))
return (SPMSL_NORMAL);
return (item.special);
}
special_armour_type get_armour_ego_type( const item_def &item )
{
// Armour ego types are "brands", so we do the randart lookup here.
if (item.base_type != OBJ_ARMOUR)
return (SPARM_NORMAL);
if (is_artefact( item ))
{
return (static_cast<special_armour_type>(
artefact_wpn_property( item, ARTP_BRAND )));
}
return (static_cast<special_armour_type>(item.special));
}
// Armour information and checking functions.
bool hide2armour( item_def &item )
{
if (item.base_type != OBJ_ARMOUR)
return (false);
switch (item.sub_type)
{
default:
return (false);
case ARM_DRAGON_HIDE:
item.sub_type = ARM_DRAGON_ARMOUR;
break;
case ARM_TROLL_HIDE:
item.sub_type = ARM_TROLL_LEATHER_ARMOUR;
break;
case ARM_ICE_DRAGON_HIDE:
item.sub_type = ARM_ICE_DRAGON_ARMOUR;
break;
case ARM_MOTTLED_DRAGON_HIDE:
item.sub_type = ARM_MOTTLED_DRAGON_ARMOUR;
break;
case ARM_STORM_DRAGON_HIDE:
item.sub_type = ARM_STORM_DRAGON_ARMOUR;
break;
case ARM_GOLD_DRAGON_HIDE:
item.sub_type = ARM_GOLD_DRAGON_ARMOUR;
break;
case ARM_SWAMP_DRAGON_HIDE:
item.sub_type = ARM_SWAMP_DRAGON_ARMOUR;
break;
case ARM_STEAM_DRAGON_HIDE:
item.sub_type = ARM_STEAM_DRAGON_ARMOUR;
break;
}
return (true);
}
// Return the enchantment limit of a piece of armour.
int armour_max_enchant( const item_def &item )
{
ASSERT( item.base_type == OBJ_ARMOUR );
const int eq_slot = get_armour_slot( item );
int max_plus = MAX_SEC_ENCHANT;
if (eq_slot == EQ_BODY_ARMOUR || item.sub_type == ARM_CENTAUR_BARDING
|| item.sub_type == ARM_NAGA_BARDING)
{
max_plus = MAX_ARM_ENCHANT;
}
if (eq_slot == EQ_SHIELD)
{
max_plus = 3;
}
return (max_plus);
}
// Doesn't include animal skin (only skins we can make and enchant).
bool armour_is_hide( const item_def &item, bool inc_made )
{
ASSERT( item.base_type == OBJ_ARMOUR );
switch (item.sub_type)
{
case ARM_TROLL_LEATHER_ARMOUR:
case ARM_DRAGON_ARMOUR:
case ARM_ICE_DRAGON_ARMOUR:
case ARM_STEAM_DRAGON_ARMOUR:
case ARM_MOTTLED_DRAGON_ARMOUR:
case ARM_STORM_DRAGON_ARMOUR:
case ARM_GOLD_DRAGON_ARMOUR:
case ARM_SWAMP_DRAGON_ARMOUR:
return (inc_made);
case ARM_TROLL_HIDE:
case ARM_DRAGON_HIDE:
case ARM_ICE_DRAGON_HIDE:
case ARM_STEAM_DRAGON_HIDE:
case ARM_MOTTLED_DRAGON_HIDE:
case ARM_STORM_DRAGON_HIDE:
case ARM_GOLD_DRAGON_HIDE:
case ARM_SWAMP_DRAGON_HIDE:
return (true);
default:
break;
}
return (false);
}
equipment_type get_armour_slot( const item_def &item )
{
ASSERT( item.base_type == OBJ_ARMOUR );
return (Armour_prop[ Armour_index[item.sub_type] ].slot);
}
equipment_type get_armour_slot( armour_type arm )
{
return (Armour_prop[ Armour_index[arm] ].slot);
}
bool jewellery_is_amulet( const item_def &item )
{
ASSERT( item.base_type == OBJ_JEWELLERY );
return (item.sub_type >= AMU_RAGE);
}
bool jewellery_is_amulet( int sub_type )
{
return (sub_type >= AMU_RAGE);
}
// Returns the basic light status of an armour, ignoring things like the
// elven bonus... you probably want is_light_armour() most times.
bool base_armour_is_light( const item_def &item )
{
ASSERT( item.base_type == OBJ_ARMOUR );
return (Armour_prop[ Armour_index[item.sub_type] ].light);
}
// Returns number of sizes off (0 if fitting).
int fit_armour_size( const item_def &item, size_type size )
{
ASSERT( item.base_type == OBJ_ARMOUR );
const size_type min = Armour_prop[ Armour_index[item.sub_type] ].fit_min;
const size_type max = Armour_prop[ Armour_index[item.sub_type] ].fit_max;
if (size < min)
return (min - size); // -'ve means levels too small
else if (size > max)
return (max - size); // +'ve means levels too large
return (0);
}
// Returns true if armour fits size (shape needs additional verification).
bool check_armour_size( const item_def &item, size_type size )
{
ASSERT( item.base_type == OBJ_ARMOUR );
return (fit_armour_size( item, size ) == 0);
}
// Returns whether a wand or rod can be charged, or a weapon of electrocution
// enchanted.
// If hide_charged is true, wands known to be full will return false.
// (This distinction is necessary because even full wands/rods give a message.)
bool item_is_rechargeable(const item_def &it, bool hide_charged, bool weapons)
{
// These are obvious...
if (it.base_type == OBJ_WANDS)
{
if (!hide_charged)
return (true);
// Don't offer wands already maximally charged.
if (it.plus2 == ZAPCOUNT_MAX_CHARGED
|| item_ident(it, ISFLAG_KNOW_PLUSES)
&& it.plus >= 3 * wand_charge_value(it.sub_type))
{
return (false);
}
return (true);
}
else if (item_is_rod(it))
{
if (!hide_charged)
return (true);
if (item_ident(it, ISFLAG_KNOW_PLUSES))
{
return (it.plus2 < MAX_ROD_CHARGE * ROD_CHARGE_MULT
|| it.plus < it.plus2
|| !it.props.exists("rod_enchantment")
|| short(it.props["rod_enchantment"]) < MAX_WPN_ENCHANT);
}
return (true);
}
else if (it.base_type == OBJ_WEAPONS)
{
// Might be electrocution.
if (weapons && !item_type_known(it))
return (true);
// Weapons of electrocution can get +1 to-dam this way.
if (!is_artefact(it)
&& get_weapon_brand(it) == SPWPN_ELECTROCUTION
&& item_type_known(it)
&& (!item_ident(it, ISFLAG_KNOW_PLUSES)
|| it.plus2 < MAX_WPN_ENCHANT))
{
return (true);
}
}
return (false);
}
// Max. charges are 3 times this value.
int wand_charge_value(int type)
{
switch (type)
{
case WAND_INVISIBILITY:
case WAND_FIREBALL:
case WAND_TELEPORTATION:
case WAND_HEALING:
case WAND_HASTING:
return 3;
case WAND_LIGHTNING:
case WAND_DRAINING:
return 4;
case WAND_FIRE:
case WAND_COLD:
return 5;
default:
return 8;
}
}
bool is_enchantable_weapon(const item_def &wpn, bool uncurse, bool first)
{
if (wpn.base_type != OBJ_WEAPONS && wpn.base_type != OBJ_MISSILES)
return (false);
// Artefacts or highly enchanted weapons cannot be enchanted,
// only uncursed.
if (wpn.base_type == OBJ_WEAPONS)
{
if (is_artefact(wpn)
|| first && wpn.plus >= MAX_WPN_ENCHANT
|| !first && wpn.plus2 >= MAX_WPN_ENCHANT)
{
return (uncurse && wpn.cursed());
}
}
// Highly enchanted missiles, which have only one stat, cannot be
// enchanted or uncursed, since missiles cannot be artefacts or
// cursed.
else if (wpn.plus >= MAX_WPN_ENCHANT)
return (false);
return (true);
}
// Returns whether a piece of armour can be enchanted further.
// If unknown is true, unidentified armour will return true.
bool is_enchantable_armour(const item_def &arm, bool uncurse, bool unknown)
{
if (arm.base_type != OBJ_ARMOUR)
return (false);
// Melded armour cannot be enchanted.
if (!you_tran_can_wear(arm) && item_is_equipped(arm))
return (false);
// If we don't know the plusses, assume enchanting is possible.
if (unknown && !is_known_artefact(arm)
&& !item_ident(arm, ISFLAG_KNOW_PLUSES ))
{
return (true);
}
// Artefacts or highly enchanted armour cannot be enchanted, only
// uncursed.
if (is_artefact(arm) || arm.plus >= armour_max_enchant(arm))
return (uncurse && arm.cursed());
return (true);
}
//
// Weapon information and checking functions:
//
// Checks how rare a weapon is. Many of these have special routines for
// placement, especially those with a rarity of zero. Chance is out of 10.
int weapon_rarity( int w_type )
{
switch (w_type)
{
case WPN_CLUB:
case WPN_DAGGER:
return (10);
case WPN_HAND_AXE:
case WPN_MACE:
case WPN_QUARTERSTAFF:
return (9);
case WPN_BOW:
case WPN_FLAIL:
case WPN_HAMMER:
case WPN_SABRE:
case WPN_SHORT_SWORD:
case WPN_SLING:
case WPN_SPEAR:
return (8);
case WPN_FALCHION:
case WPN_LONG_SWORD:
case WPN_MORNINGSTAR:
case WPN_WAR_AXE:
return (7);
case WPN_BATTLEAXE:
case WPN_CROSSBOW:
case WPN_GREAT_SWORD:
case WPN_SCIMITAR:
case WPN_TRIDENT:
return (6);
case WPN_GLAIVE:
case WPN_HALBERD:
case WPN_BLOWGUN:
return (5);
case WPN_BROAD_AXE:
case WPN_SPIKED_FLAIL:
case WPN_WHIP:
return (4);
case WPN_GREAT_MACE:
return (3);
case WPN_ANKUS:
case WPN_DIRE_FLAIL:
case WPN_SCYTHE:
case WPN_LONGBOW:
case WPN_LAJATANG:
return (2);
case WPN_GIANT_CLUB:
case WPN_GIANT_SPIKED_CLUB:
case WPN_BARDICHE:
return (1);
case WPN_DOUBLE_SWORD:
case WPN_EVENINGSTAR:
case WPN_EXECUTIONERS_AXE:
case WPN_KATANA:
case WPN_KNIFE:
case WPN_QUICK_BLADE:
case WPN_TRIPLE_SWORD:
case WPN_DEMON_WHIP:
case WPN_DEMON_BLADE:
case WPN_DEMON_TRIDENT:
case WPN_BLESSED_FALCHION:
case WPN_BLESSED_LONG_SWORD:
case WPN_BLESSED_SCIMITAR:
case WPN_BLESSED_KATANA:
case WPN_HOLY_EUDEMON_BLADE:
case WPN_BLESSED_DOUBLE_SWORD:
case WPN_BLESSED_GREAT_SWORD:
case WPN_BLESSED_TRIPLE_SWORD:
case WPN_HOLY_SCOURGE:
// Zero value weapons must be placed specially -- see make_item() {dlb}
return (0);
default:
break;
}
return (0);
}
int get_vorpal_type(const item_def &item)
{
int ret = DVORP_NONE;
if (item.base_type == OBJ_WEAPONS)
ret = (Weapon_prop[Weapon_index[item.sub_type]].dam_type & DAMV_MASK);
return (ret);
}
int get_damage_type(const item_def &item)
{
int ret = DAM_BASH;
if (item_is_rod(item))
ret = DAM_BLUDGEON;
if (item.base_type == OBJ_WEAPONS)
ret = (Weapon_prop[Weapon_index[item.sub_type]].dam_type & DAM_MASK);
return (ret);
}
bool does_damage_type(const item_def &item, int dam_type)
{
return (get_damage_type(item) & dam_type);
}
int single_damage_type(const item_def &item)
{
int ret = get_damage_type(item);
if (ret > 0)
{
int count = 0;
for (int i = 1; i <= DAM_MAX_TYPE; i <<= 1)
{
if (!does_damage_type(item, i))
continue;
if (one_chance_in(++count))
ret = i;
}
}
return (ret);
}
hands_reqd_type hands_reqd(object_class_type base_type, int sub_type,
size_type size)
{
item_def item;
item.base_type = base_type;
item.sub_type = sub_type;
return hands_reqd(item, size);
}
// Give hands required to wield weapon for a torso of "size".
hands_reqd_type hands_reqd( const item_def &item, size_type size )
{
int ret = HANDS_ONE;
int fit;
bool doub = false;
switch (item.base_type)
{
case OBJ_STAVES:
case OBJ_WEAPONS:
// Merging staff with magical staves for consistency... doing
// as a special case because we want to be very flexible with
// these useful objects (we want spriggans and ogres to be
// able to use them).
if (item.base_type == OBJ_STAVES || weapon_skill(item) == SK_STAVES)
{
if (size < SIZE_SMALL)
ret = HANDS_TWO;
else if (size > SIZE_LARGE)
ret = HANDS_ONE;
else
ret = HANDS_HALF;
break;
}
ret = Weapon_prop[ Weapon_index[item.sub_type] ].hands;
// Size is the level where we can use one hand for one end.
if (ret == HANDS_DOUBLE)
{
doub = true;
ret = HANDS_TWO; // HANDS_HALF once double-ended is implemented.
}
// Adjust handedness for size only for non-whip melee weapons.
if (!is_range_weapon(item)
&& item.sub_type != WPN_WHIP
&& item.sub_type != WPN_DEMON_WHIP
&& item.sub_type != WPN_HOLY_SCOURGE)
{
fit = cmp_weapon_size(item, size);
// Adjust handedness for non-medium races:
// (XX values don't matter, see fit_weapon_wieldable_size)
//
// Spriggan Kobold Human Ogre Big Giant
// Little 0 0 0 XX XX XX
// Small +1 0 0 -2 XX XX
// Medium XX +1 0 -1 -2 XX
// Large XX XX 0 0 -1 -2
// Big XX XX XX 0 0 -1
// Giant XX XX XX XX 0 0
// Note the stretching of double weapons for larger characters
// by one level since they tend to be larger weapons.
if (size < SIZE_MEDIUM && fit > 0)
ret += fit;
else if (size > SIZE_MEDIUM && fit < 0)
ret += (fit + doub);
}
break;
case OBJ_CORPSES: // unwieldy
ret = HANDS_TWO;
break;
case OBJ_ARMOUR: // Bardings and body armours are unwieldy.
if (item.sub_type == ARM_NAGA_BARDING
|| item.sub_type == ARM_CENTAUR_BARDING
|| get_armour_slot( item ) == EQ_BODY_ARMOUR)
{
ret = HANDS_TWO;
}
break;
default:
break;
}
if (ret > HANDS_TWO)
ret = HANDS_TWO;
else if (ret < HANDS_ONE)
ret = HANDS_ONE;
return (static_cast< hands_reqd_type >( ret ));
}
bool is_demonic(const item_def &item)
{
if (item.base_type == OBJ_WEAPONS)
{
switch (item.sub_type)
{
case WPN_DEMON_BLADE:
case WPN_DEMON_WHIP:
case WPN_DEMON_TRIDENT:
return (true);
default:
break;
}
}
return (false);
}
bool is_blessed(const item_def &item)
{
if (item.base_type == OBJ_WEAPONS)
{
switch (item.sub_type)
{
case WPN_BLESSED_FALCHION:
case WPN_BLESSED_LONG_SWORD:
case WPN_BLESSED_SCIMITAR:
case WPN_BLESSED_KATANA:
case WPN_HOLY_EUDEMON_BLADE:
case WPN_BLESSED_DOUBLE_SWORD:
case WPN_BLESSED_GREAT_SWORD:
case WPN_BLESSED_TRIPLE_SWORD:
case WPN_HOLY_SCOURGE:
return (true);
default:
break;
}
}
return (false);
}
bool is_blessed_convertible(const item_def &item)
{
return (!is_artefact(item)
&& (item.base_type == OBJ_WEAPONS
&& (is_demonic(item)
|| item.sub_type == WPN_HOLY_SCOURGE
|| weapon_skill(item) == SK_LONG_BLADES)));
}
bool convert2good(item_def &item, bool allow_blessed)
{
if (item.base_type != OBJ_WEAPONS)
return (false);
switch (item.sub_type)
{
default:
return (false);
case WPN_FALCHION:
if (!allow_blessed)
return (false);
item.sub_type = WPN_BLESSED_FALCHION;
break;
case WPN_LONG_SWORD:
if (!allow_blessed)
return (false);
item.sub_type = WPN_BLESSED_LONG_SWORD;
break;
case WPN_SCIMITAR:
if (!allow_blessed)
return (false);
item.sub_type = WPN_BLESSED_SCIMITAR;
break;
case WPN_DEMON_BLADE:
if (!allow_blessed)
item.sub_type = WPN_SCIMITAR;
else
item.sub_type = WPN_HOLY_EUDEMON_BLADE;
break;
case WPN_KATANA:
if (!allow_blessed)
return (false);
item.sub_type = WPN_BLESSED_KATANA;
break;
case WPN_DOUBLE_SWORD:
if (!allow_blessed)
return (false);
item.sub_type = WPN_BLESSED_DOUBLE_SWORD;
break;
case WPN_GREAT_SWORD:
if (!allow_blessed)
return (false);
item.sub_type = WPN_BLESSED_GREAT_SWORD;
break;
case WPN_TRIPLE_SWORD:
if (!allow_blessed)
return (false);
item.sub_type = WPN_BLESSED_TRIPLE_SWORD;
break;
case WPN_DEMON_WHIP:
if (!allow_blessed)
item.sub_type = WPN_WHIP;
else
item.sub_type = WPN_HOLY_SCOURGE;
break;
case WPN_DEMON_TRIDENT:
item.sub_type = WPN_TRIDENT;
break;
}
if (is_blessed(item))
item.flags &= ~ISFLAG_RACIAL_MASK;
return (true);
}
bool convert2bad(item_def &item)
{
if (item.base_type != OBJ_WEAPONS)
return (false);
switch (item.sub_type)
{
default:
return (false);
case WPN_BLESSED_FALCHION:
item.sub_type = WPN_FALCHION;
break;
case WPN_BLESSED_LONG_SWORD:
item.sub_type = WPN_LONG_SWORD;
break;
case WPN_BLESSED_SCIMITAR:
item.sub_type = WPN_SCIMITAR;
break;
case WPN_HOLY_EUDEMON_BLADE:
item.sub_type = WPN_DEMON_BLADE;
break;
case WPN_BLESSED_KATANA:
item.sub_type = WPN_KATANA;
break;
case WPN_BLESSED_DOUBLE_SWORD:
item.sub_type = WPN_DOUBLE_SWORD;
break;
case WPN_BLESSED_GREAT_SWORD:
item.sub_type = WPN_GREAT_SWORD;
break;
case WPN_BLESSED_TRIPLE_SWORD:
item.sub_type = WPN_TRIPLE_SWORD;
break;
case WPN_HOLY_SCOURGE:
item.sub_type = WPN_DEMON_WHIP;
break;
}
return (true);
}
int weapon_str_weight( const item_def &wpn )
{
ASSERT (wpn.base_type == OBJ_WEAPONS || wpn.base_type == OBJ_STAVES);
if (wpn.base_type == OBJ_STAVES)
return (Weapon_prop[ Weapon_index[WPN_QUARTERSTAFF] ].str_weight);
return (Weapon_prop[ Weapon_index[wpn.sub_type] ].str_weight);
}
int weapon_dex_weight( const item_def &wpn )
{
return (10 - weapon_str_weight( wpn ));
}
// Returns melee skill of item.
skill_type weapon_skill( const item_def &item )
{
if (item.base_type == OBJ_WEAPONS && !is_range_weapon( item ))
return (Weapon_prop[ Weapon_index[item.sub_type] ].skill);
else if (item_is_rod( item ))
return (SK_MACES_FLAILS); // Rods are short and stubby
else if (item.base_type == OBJ_STAVES)
return (SK_STAVES);
// This is used to mark that only fighting applies.
return (SK_FIGHTING);
}
// Front function for the above when we don't have a physical item to check.
skill_type weapon_skill( object_class_type wclass, int wtype )
{
item_def wpn;
wpn.base_type = wclass;
wpn.sub_type = wtype;
return (weapon_skill( wpn ));
}
// Returns range skill of the item.
skill_type range_skill( const item_def &item )
{
if (item.base_type == OBJ_WEAPONS && is_range_weapon( item ))
return (Weapon_prop[ Weapon_index[item.sub_type] ].skill);
else if (item.base_type == OBJ_MISSILES)
{
switch (item.sub_type)
{
case MI_DART: return (SK_THROWING);
case MI_JAVELIN: return (SK_POLEARMS);
default: break;
}
}
return (SK_THROWING);
}
// Front function for the above when we don't have a physical item to check.
skill_type range_skill( object_class_type wclass, int wtype )
{
item_def wpn;
wpn.base_type = wclass;
wpn.sub_type = wtype;
return (range_skill( wpn ));
}
// Calculate the bonus to melee EV for using "wpn", with "skill" and "dex"
// to protect a body of size "body".
int weapon_ev_bonus( const item_def &wpn, int skill, size_type body, int dex,
bool hide_hidden )
{
ASSERT( wpn.base_type == OBJ_WEAPONS || wpn.base_type == OBJ_STAVES );
int ret = 0;
// Note: ret currently measured in halves (see skill factor).
if (wpn.sub_type == WPN_WHIP || wpn.sub_type == WPN_DEMON_WHIP)
ret = 3 + (dex / 5);
else if (weapon_skill( wpn ) == SK_POLEARMS)
ret = 2 + (dex / 5);
// Weapons of reaching are naturally a bit longer/flexier.
if (!hide_hidden || item_type_known( wpn ))
{
if (get_weapon_brand( wpn ) == SPWPN_REACHING)
ret += 1;
}
// Only consider additional modifications if we have a positive base:
if (ret > 0)
{
// Size factors:
// - large characters can't cover their flanks as well
// - note that not all weapons are available to small characters
if (body > SIZE_LARGE)
ret -= (4 * (body - SIZE_LARGE) - 2);
else if (body < SIZE_MEDIUM)
ret += 1;
// apply skill (and dividing by 2)
ret = (ret * (skill + 10)) / 20;
// Make sure things can't get too insane.
if (ret > 8)
ret = 8 + (ret - 8) / 2;
}
// Note: this is always a bonus.
return ((ret > 0) ? ret : 0);
}
static size_type weapon_size( const item_def &item )
{
ASSERT (item.base_type == OBJ_WEAPONS || item.base_type == OBJ_STAVES);
if (item.base_type == OBJ_STAVES)
return (Weapon_prop[ Weapon_index[WPN_QUARTERSTAFF] ].fit_size);
return (Weapon_prop[ Weapon_index[item.sub_type] ].fit_size);
}
// Returns number of sizes off.
int cmp_weapon_size( const item_def &item, size_type size )
{
ASSERT( item.base_type == OBJ_WEAPONS || item.base_type == OBJ_STAVES );
return (weapon_size( item ) - size);
}
// Returns number of sizes away from being a usable weapon.
int fit_weapon_wieldable_size( const item_def &item, size_type size )
{
ASSERT( item.base_type == OBJ_WEAPONS || item.base_type == OBJ_STAVES );
const int fit = cmp_weapon_size( item, size );
return ((fit < -2) ? fit + 2 :
(fit > 1) ? fit - 1 : 0);
}
// Returns number of sizes away from being throwable... the window
// is currently [size - 5, size - 1].
int fit_item_throwable_size( const item_def &item, size_type size )
{
int ret = item_size( item ) - size;
return ((ret >= 0) ? ret + 1 :
(ret > -6) ? 0
: ret + 5);
}
// Returns true if weapon is usable as a weapon.
bool check_weapon_wieldable_size( const item_def &item, size_type size )
{
ASSERT( item.base_type == OBJ_WEAPONS || item.base_type == OBJ_STAVES );
// Staves are currently wieldable for everyone just to be nice.
if (item.base_type == OBJ_STAVES || weapon_skill(item) == SK_STAVES)
return (true);
int fit = fit_weapon_wieldable_size( item, size );
// Adjust fit for size.
if (size < SIZE_SMALL && fit > 0)
fit--;
else if (size > SIZE_LARGE && fit < 0)
fit++;
return (fit == 0);
}
//
// Launcher and ammo functions:
//
missile_type fires_ammo_type(const item_def &item)
{
if (item.base_type != OBJ_WEAPONS)
return (MI_NONE);
return (Weapon_prop[Weapon_index[item.sub_type]].ammo);
}
missile_type fires_ammo_type(weapon_type wtype)
{
item_def wpn;
wpn.base_type = OBJ_WEAPONS;
wpn.sub_type = wtype;
return (fires_ammo_type(wpn));
}
bool is_range_weapon(const item_def &item)
{
return (fires_ammo_type(item) != MI_NONE);
}
bool is_range_weapon_type(weapon_type wtype)
{
item_def wpn;
wpn.base_type = OBJ_WEAPONS;
wpn.sub_type = wtype;
return (is_range_weapon(wpn));
}
const char *ammo_name(missile_type ammo)
{
return (ammo < 0 || ammo >= NUM_MISSILES ? "eggplant"
: Missile_prop[ Missile_index[ammo] ].name);
}
const char *ammo_name(const item_def &bow)
{
ASSERT(is_range_weapon(bow));
return ammo_name(fires_ammo_type(bow));
}
// Returns true if item has an associated launcher.
bool has_launcher(const item_def &ammo)
{
ASSERT(ammo.base_type == OBJ_MISSILES);
return (ammo.sub_type != MI_DART
&& ammo.sub_type != MI_LARGE_ROCK
&& ammo.sub_type != MI_JAVELIN
&& ammo.sub_type != MI_THROWING_NET);
}
// Returns true if item can be reasonably thrown without a launcher.
bool is_throwable(const actor *actor, const item_def &wpn, bool force)
{
size_type bodysize = actor->body_size();
if (wpn.base_type == OBJ_WEAPONS)
return (Weapon_prop[Weapon_index[wpn.sub_type]].throwable);
else if (wpn.base_type == OBJ_MISSILES)
{
if (!force)
{
if ((bodysize < SIZE_LARGE
|| !actor->can_throw_large_rocks())
&& wpn.sub_type == MI_LARGE_ROCK)
{
return (false);
}
if (bodysize < SIZE_MEDIUM
&& (wpn.sub_type == MI_JAVELIN
|| wpn.sub_type == MI_THROWING_NET))
{
return (false);
}
}
return (Missile_prop[Missile_index[wpn.sub_type]].throwable);
}
return (false);
}
// Decide if something is launched or thrown.
launch_retval is_launched(const actor *actor, const item_def *launcher,
const item_def &missile)
{
if (missile.base_type == OBJ_MISSILES
&& launcher
&& missile.launched_by(*launcher))
{
return (LRET_LAUNCHED);
}
return (is_throwable(actor, missile) ? LRET_THROWN : LRET_FUMBLED);
}
//
// Staff/rod functions:
//
bool item_is_rod( const item_def &item )
{
return (item.base_type == OBJ_STAVES && item.sub_type >= STAFF_FIRST_ROD);
}
bool item_is_staff( const item_def &item )
{
return (item.base_type == OBJ_STAVES && !item_is_rod(item));
}
//
// Ring functions:
// Returns number of pluses on jewellery (always none for amulets yet).
int ring_has_pluses( const item_def &item )
{
ASSERT (item.base_type == OBJ_JEWELLERY);
// not known -> no pluses
if (!item_type_known(item))
return (0);
switch (item.sub_type)
{
case RING_SLAYING:
return (2);
case RING_PROTECTION:
case RING_EVASION:
case RING_STRENGTH:
case RING_INTELLIGENCE:
case RING_DEXTERITY:
return (1);
default:
break;
}
return (0);
}
// Returns true if having two rings of the same type on at the same
// has more effect than just having one on.
bool ring_has_stackable_effect( const item_def &item )
{
ASSERT (item.base_type == OBJ_JEWELLERY);
ASSERT (!jewellery_is_amulet(item));
if (!item_type_known(item))
return (false);
if (ring_has_pluses(item))
return (true);
switch (item.sub_type)
{
case RING_PROTECTION_FROM_FIRE:
case RING_PROTECTION_FROM_COLD:
case RING_LIFE_PROTECTION:
case RING_SUSTENANCE:
case RING_WIZARDRY:
case RING_FIRE:
case RING_ICE:
return (true);
default:
break;
}
return (false);
}
//
// Food functions:
//
bool food_is_meat(const item_def &item)
{
ASSERT(item.is_valid() && item.base_type == OBJ_FOOD);
return (Food_prop[Food_index[item.sub_type]].carn_mod > 0);
}
bool food_is_veg( const item_def &item )
{
ASSERT(item.is_valid() && item.base_type == OBJ_FOOD);
return (Food_prop[Food_index[item.sub_type]].herb_mod > 0);
}
bool is_blood_potion(const item_def &item)
{
if (item.base_type != OBJ_POTIONS)
return (false);
return (item.sub_type == POT_BLOOD
|| item.sub_type == POT_BLOOD_COAGULATED);
}
// Returns food value for one turn of eating.
int food_value(const item_def &item)
{
ASSERT(item.is_valid() && item.base_type == OBJ_FOOD);
const int herb = player_mutation_level(MUT_HERBIVOROUS);
// XXX: This needs to be better merged with the mutation system.
const int carn = player_mutation_level(MUT_CARNIVOROUS);
const food_def &food = Food_prop[Food_index[item.sub_type]];
int ret = food.value;
ret += (carn * food.carn_mod);
ret += (herb * food.herb_mod);
return ((ret > 0) ? div_rand_round(ret, food.turns) : 0);
}
int food_turns(const item_def &item)
{
ASSERT(item.is_valid() && item.base_type == OBJ_FOOD);
return (Food_prop[Food_index[item.sub_type]].turns);
}
bool can_cut_meat(const item_def &item)
{
return (does_damage_type(item, DAM_SLICE));
}
bool is_fruit(const item_def & item)
{
if (item.base_type != OBJ_FOOD)
return (false);
switch (item.sub_type)
{
case FOOD_APPLE:
case FOOD_APRICOT:
case FOOD_BANANA:
case FOOD_CHOKO:
case FOOD_GRAPE:
case FOOD_LEMON:
case FOOD_LYCHEE:
case FOOD_ORANGE:
case FOOD_PEAR:
case FOOD_RAMBUTAN:
case FOOD_STRAWBERRY:
case FOOD_SULTANA:
return (true);
};
return (false);
}
bool food_is_rotten(const item_def &item)
{
return (item.special < 100) && (item.base_type == OBJ_CORPSES
&& item.sub_type == CORPSE_BODY
|| item.base_type == OBJ_FOOD
&& item.sub_type == FOOD_CHUNK);
}
int corpse_freshness(const item_def &item)
{
ASSERT(item.base_type == OBJ_CORPSES);
ASSERT(item.special <= FRESHEST_CORPSE);
return (item.special);
}
//
// Generic item functions:
//
int property(const item_def &item, int prop_type)
{
weapon_type weapon_sub;
switch (item.base_type)
{
case OBJ_ARMOUR:
if (prop_type == PARM_AC)
return (Armour_prop[ Armour_index[item.sub_type] ].ac);
else if (prop_type == PARM_EVASION)
return (Armour_prop[ Armour_index[item.sub_type] ].ev);
break;
case OBJ_WEAPONS:
if (prop_type == PWPN_DAMAGE)
return (Weapon_prop[ Weapon_index[item.sub_type] ].dam);
else if (prop_type == PWPN_HIT)
return (Weapon_prop[ Weapon_index[item.sub_type] ].hit);
else if (prop_type == PWPN_SPEED)
return (Weapon_prop[ Weapon_index[item.sub_type] ].speed);
else if (prop_type == PWPN_ACQ_WEIGHT)
return (Weapon_prop[ Weapon_index[item.sub_type] ].acquire_weight);
break;
case OBJ_MISSILES:
if (prop_type == PWPN_DAMAGE)
return (Missile_prop[ Missile_index[item.sub_type] ].dam);
break;
case OBJ_STAVES:
weapon_sub = item_is_rod(item) ? WPN_CLUB : WPN_QUARTERSTAFF;
if (prop_type == PWPN_DAMAGE)
return (Weapon_prop[ Weapon_index[weapon_sub] ].dam);
else if (prop_type == PWPN_HIT)
return (Weapon_prop[ Weapon_index[weapon_sub] ].hit);
else if (prop_type == PWPN_SPEED)
return (Weapon_prop[ Weapon_index[weapon_sub] ].speed);
break;
default:
break;
}
return (0);
}
// Returns true if item is evokable.
bool gives_ability(const item_def &item)
{
if (!item_type_known(item))
return (false);
switch (item.base_type)
{
case OBJ_WEAPONS:
break;
case OBJ_JEWELLERY:
if (!jewellery_is_amulet(item))
{
if (item.sub_type == RING_TELEPORTATION
|| item.sub_type == RING_LEVITATION
|| item.sub_type == RING_INVISIBILITY)
{
return (true);
}
}
else
{
if (item.sub_type == AMU_RAGE)
return (true);
}
break;
case OBJ_ARMOUR:
{
const equipment_type eq = get_armour_slot(item);
if (eq == EQ_NONE)
return (false);
const special_armour_type ego = get_armour_ego_type(item);
if (ego == SPARM_DARKNESS || ego == SPARM_LEVITATION)
return (true);
break;
}
default:
return (false);
}
if (!is_artefact(item))
return (false);
// Check for evokable randart properties.
for (int rap = ARTP_INVISIBLE; rap <= ARTP_BERSERK; rap++)
if (artefact_wpn_property( item, static_cast<artefact_prop_type>(rap) ))
return (true);
return (false);
}
// Returns true if the item confers an intrinsic that is shown on the % screen.
bool gives_resistance(const item_def &item)
{
if (!item_type_known(item))
return (false);
switch (item.base_type)
{
case OBJ_WEAPONS:
break;
case OBJ_JEWELLERY:
if (!jewellery_is_amulet(item))
{
if (item.sub_type >= RING_PROTECTION_FROM_FIRE
&& item.sub_type <= RING_PROTECTION_FROM_COLD
|| item.sub_type == RING_SEE_INVISIBLE
|| item.sub_type >= RING_LIFE_PROTECTION
&& item.sub_type <= RING_TELEPORT_CONTROL
|| item.sub_type == RING_SUSTAIN_ABILITIES)
{
return (true);
}
}
else
{
if (item.sub_type != AMU_RAGE && item.sub_type != AMU_INACCURACY)
return (true);
}
break;
case OBJ_ARMOUR:
{
const equipment_type eq = get_armour_slot(item);
if (eq == EQ_NONE)
return (false);
const int ego = get_armour_ego_type(item);
if (ego >= SPARM_FIRE_RESISTANCE && ego <= SPARM_SEE_INVISIBLE
|| ego == SPARM_RESISTANCE || ego == SPARM_POSITIVE_ENERGY)
{
return (true);
}
}
case OBJ_STAVES:
if (item.sub_type >= STAFF_FIRE && item.sub_type <= STAFF_POISON
|| item.sub_type == STAFF_AIR)
{
return (true);
}
return (false);
default:
return (false);
}
if (!is_artefact(item))
return (false);
// Check for randart resistances.
for (int rap = ARTP_FIRE; rap <= ARTP_BERSERK; rap++)
{
if (rap == ARTP_MAGIC || rap >= ARTP_INVISIBLE)
continue;
if (artefact_wpn_property( item, static_cast<artefact_prop_type>(rap) ))
return (true);
}
return (false);
}
int item_mass(const item_def &item)
{
int unit_mass = 0;
switch (item.base_type)
{
case OBJ_WEAPONS:
unit_mass = Weapon_prop[ Weapon_index[item.sub_type] ].mass;
break;
case OBJ_ARMOUR:
unit_mass = Armour_prop[ Armour_index[item.sub_type] ].mass;
if (get_equip_race( item ) == ISFLAG_ELVEN)
{
const int reduc = (unit_mass >= 25) ? unit_mass / 5 : 5;
// Truncate to the nearest 5 and reduce the item mass:
unit_mass -= ((reduc / 5) * 5);
unit_mass = std::max(unit_mass, 5);
}
break;
case OBJ_MISSILES:
{
unit_mass = Missile_prop[ Missile_index[item.sub_type] ].mass;
int brand = get_ammo_brand(item);
if (brand == SPMSL_SILVER)
unit_mass *= 2;
else if (brand == SPMSL_STEEL)
unit_mass *= 3;
break;
}
case OBJ_FOOD:
unit_mass = Food_prop[ Food_index[item.sub_type] ].mass;
break;
case OBJ_WANDS:
unit_mass = 100;
break;
case OBJ_UNKNOWN_I:
unit_mass = 200; // labeled "books"
break;
case OBJ_SCROLLS:
unit_mass = 20;
break;
case OBJ_JEWELLERY:
unit_mass = 10;
break;
case OBJ_POTIONS:
unit_mass = 40;
break;
case OBJ_UNKNOWN_II:
unit_mass = 5; // labeled "gems"
break;
case OBJ_BOOKS:
unit_mass = 70;
break;
case OBJ_STAVES:
unit_mass = 130;
break;
case OBJ_ORBS:
unit_mass = 300;
break;
case OBJ_MISCELLANY:
if ( is_deck(item) )
{
unit_mass = 50;
break;
}
switch (item.sub_type)
{
case MISC_BOTTLED_EFREET:
case MISC_CRYSTAL_BALL_OF_SEEING:
case MISC_CRYSTAL_BALL_OF_ENERGY:
case MISC_CRYSTAL_BALL_OF_FIXATION:
unit_mass = 150;
break;
default:
unit_mass = 100;
break;
}
break;
case OBJ_CORPSES:
unit_mass = mons_weight( item.plus );
if (item.sub_type == CORPSE_SKELETON)
unit_mass /= 10;
break;
default:
case OBJ_GOLD:
unit_mass = 0;
break;
}
return ((unit_mass > 0) ? unit_mass : 0);
}
// Note that this function, and item sizes in general aren't quite on the
// same scale as PCs and monsters.
size_type item_size(const item_def &item)
{
int size = SIZE_TINY;
switch (item.base_type)
{
case OBJ_WEAPONS:
case OBJ_STAVES:
size = Weapon_prop[ Weapon_index[item.sub_type] ].fit_size - 1;
break;
case OBJ_ARMOUR:
size = SIZE_MEDIUM;
switch (item.sub_type)
{
case ARM_GLOVES:
case ARM_HELMET:
case ARM_CAP:
case ARM_WIZARD_HAT:
case ARM_BOOTS:
case ARM_BUCKLER:
// tiny armour
size = SIZE_TINY;
break;
case ARM_SHIELD:
size = SIZE_LITTLE;
break;
case ARM_LARGE_SHIELD:
size = SIZE_SMALL;
break;
default: // Body armours and bardings.
size = SIZE_MEDIUM;
break;
}
break;
case OBJ_MISSILES:
if (item.sub_type == MI_LARGE_ROCK)
size = SIZE_SMALL;
break;
case OBJ_MISCELLANY:
break;
case OBJ_CORPSES:
// FIXME: This should depend on the original monster's size!
size = SIZE_SMALL;
break;
default: // sundry tiny items
break;
}
if (size < SIZE_TINY)
size = SIZE_TINY;
else if (size > SIZE_HUGE)
size = SIZE_HUGE;
return (static_cast<size_type>(size));
}
equipment_type get_item_slot(const item_def& item)
{
return get_item_slot(item.base_type, item.sub_type);
}
equipment_type get_item_slot(object_class_type type, int sub_type)
{
switch(type)
{
case OBJ_WEAPONS:
case OBJ_STAVES:
case OBJ_MISCELLANY:
return (EQ_WEAPON);
case OBJ_ARMOUR:
return get_armour_slot(static_cast<armour_type>(sub_type));
case OBJ_JEWELLERY:
return (jewellery_is_amulet(sub_type) ? EQ_AMULET : EQ_RINGS);
default:
break;
}
return (EQ_NONE);
}
bool is_shield(const item_def &item)
{
return (item.base_type == OBJ_ARMOUR
&& get_armour_slot(item) == EQ_SHIELD);
}
// Returns true if the given item cannot be wielded with the given shield.
// The currently equipped shield is used if no shield is passed in.
bool is_shield_incompatible(const item_def &weapon, const item_def *shield)
{
// If there's no shield, there's no problem.
if (!shield && !(shield = you.shield()))
return (false);
hands_reqd_type hand = hands_reqd(weapon, you.body_size());
return (hand == HANDS_TWO
&& !item_is_rod(weapon)
&& !is_range_weapon(weapon));
}
bool shield_reflects(const item_def &shield)
{
ASSERT(is_shield(shield));
return (get_armour_ego_type(shield) == SPARM_REFLECTION);
}
void ident_reflector(item_def *item)
{
if (!is_artefact(*item))
set_ident_flags(*item, ISFLAG_KNOW_TYPE);
}
std::string item_base_name(const item_def &item)
{
switch (item.base_type)
{
case OBJ_WEAPONS:
return Weapon_prop[Weapon_index[item.sub_type]].name;
case OBJ_MISSILES:
return Missile_prop[Missile_index[item.sub_type]].name;
case OBJ_ARMOUR:
return Armour_prop[Armour_index[item.sub_type]].name;
case OBJ_JEWELLERY:
return (jewellery_is_amulet(item) ? "amulet" : "ring");
default:
return "";
}
}
const char* weapon_base_name(unsigned char subtype)
{
return Weapon_prop[Weapon_index[subtype]].name;
}
void seen_item(const item_def &item)
{
if (!is_artefact(item))
{
// Known brands will be set in set_item_flags().
if (item.base_type == OBJ_WEAPONS)
you.seen_weapon[item.sub_type] |= 1 << SP_UNKNOWN_BRAND;
if (item.base_type == OBJ_ARMOUR)
you.seen_armour[item.sub_type] |= 1 << SP_UNKNOWN_BRAND;
}
}