summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/artefact.cc
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source/artefact.cc')
-rw-r--r--crawl-ref/source/artefact.cc2016
1 files changed, 2016 insertions, 0 deletions
diff --git a/crawl-ref/source/artefact.cc b/crawl-ref/source/artefact.cc
new file mode 100644
index 0000000000..e026b73baf
--- /dev/null
+++ b/crawl-ref/source/artefact.cc
@@ -0,0 +1,2016 @@
+/*
+ * File: artefact.cc
+ * Summary: Random and unrandom artefact functions.
+ * Written by: Linley Henzell
+ *
+ * Modified for Crawl Reference by $Author$ on $Date$
+ */
+
+#include "AppHdr.h"
+REVISION("$Rev$");
+
+#include "artefact.h"
+
+#include <cstdlib>
+#include <climits>
+#include <string.h>
+#include <stdio.h>
+#include <algorithm>
+
+#include "externs.h"
+
+#include "database.h"
+#include "describe.h"
+#include "itemname.h"
+#include "itemprop.h"
+#include "items.h"
+#include "place.h"
+#include "player.h"
+#include "religion.h"
+#include "spl-book.h"
+#include "stuff.h"
+
+#define KNOWN_PROPS_KEY "artefact_known_props"
+#define ARTEFACT_PROPS_KEY "artefact_props"
+#define ARTEFACT_NAME_KEY "artefact_name"
+#define ARTEFACT_APPEAR_KEY "artefact_appearance"
+
+// The initial generation of a randart is very simple - it occurs in
+// dungeon.cc and consists of giving it a few random things - plus &
+// plus2 mainly.
+
+static bool _god_fits_artefact(const god_type which_god, const item_def &item,
+ bool name_check_only = false)
+{
+ if (which_god == GOD_NO_GOD)
+ return (false);
+
+ // First check the item's base_type and sub_type, then check the
+ // item's brand and other randart properties.
+ bool type_bad = false;
+ switch (which_god)
+ {
+ case GOD_ELYVILON:
+ // Peaceful healer god: no weapons, no berserking.
+ if (item.base_type == OBJ_WEAPONS)
+ type_bad = true;
+
+ if (item.base_type == OBJ_JEWELLERY && item.sub_type == AMU_RAGE)
+ type_bad = true;
+ break;
+
+ case GOD_OKAWARU:
+ // Precision fighter god: no inaccuracy.
+ if (item.base_type == OBJ_JEWELLERY && item.sub_type == AMU_INACCURACY)
+ type_bad = true;
+ break;
+
+ case GOD_ZIN:
+ // Lawful god: no increased hunger.
+ if (item.base_type == OBJ_JEWELLERY && item.sub_type == RING_HUNGER)
+ type_bad = true;
+ break;
+
+ case GOD_SIF_MUNA:
+ case GOD_KIKUBAAQUDGHA:
+ case GOD_VEHUMET:
+ // The magic gods: no weapons, no preventing spellcasting.
+ if (item.base_type == OBJ_WEAPONS)
+ type_bad = true;
+ break;
+
+ case GOD_TROG:
+ // Anti-magic god: no spell use, no enhancing magic.
+ if (item.base_type == OBJ_BOOKS)
+ type_bad = true;
+
+ if (item.base_type == OBJ_JEWELLERY && (item.sub_type == RING_WIZARDRY
+ || item.sub_type == RING_FIRE || item.sub_type == RING_ICE
+ || item.sub_type == RING_MAGICAL_POWER))
+ {
+ type_bad = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (type_bad && !name_check_only)
+ {
+ ASSERT(!"God attempting to gift invalid type of item.");
+ mprf(MSGCH_ERROR, "%s attempting to gift invalid type of item.",
+ god_name(which_god).c_str());
+ // Prevent infinite loop in make_item_randart().
+ return (true);
+ }
+
+ if (type_bad)
+ return (false);
+
+ const int brand = get_weapon_brand(item);
+
+ if (is_evil_god(which_god) && brand == SPWPN_HOLY_WRATH)
+ return (false);
+ else if (is_good_god(which_god)
+ && (brand == SPWPN_DRAINING || brand == SPWPN_PAIN
+ || brand == SPWPN_VAMPIRICISM
+ || artefact_wpn_property(item, ARTP_CURSED) != 0))
+ {
+ return (false);
+ }
+
+ switch (which_god)
+ {
+ case GOD_BEOGH:
+ // Orc god: no orc slaying.
+ if (brand == SPWPN_ORC_SLAYING)
+ return (false);
+ break;
+
+ case GOD_ELYVILON:
+ // Peaceful healer god: no berserking.
+ if (artefact_wpn_property(item, ARTP_ANGRY)
+ || artefact_wpn_property(item, ARTP_BERSERK))
+ {
+ return (false);
+ }
+ break;
+
+ case GOD_ZIN:
+ // Lawful god: no mutagenics.
+ if (artefact_wpn_property(item, ARTP_MUTAGENIC))
+ return (false);
+ break;
+
+ case GOD_SHINING_ONE:
+ // Crusader god: holiness, honourable combat.
+ if (item.base_type == OBJ_WEAPONS && brand != SPWPN_HOLY_WRATH)
+ return (false);
+
+ if (artefact_wpn_property(item, ARTP_INVISIBLE)
+ || artefact_wpn_property(item, ARTP_STEALTH) > 0)
+ {
+ return (false);
+ }
+ break;
+
+ case GOD_LUGONU:
+ // Abyss god: corruption.
+ if (item.base_type == OBJ_WEAPONS && brand != SPWPN_DISTORTION)
+ return (false);
+ break;
+
+ case GOD_SIF_MUNA:
+ case GOD_KIKUBAAQUDGHA:
+ case GOD_VEHUMET:
+ // The magic gods: no preventing spellcasting.
+ if (artefact_wpn_property(item, ARTP_PREVENT_SPELLCASTING))
+ return (false);
+ break;
+
+ case GOD_TROG:
+ // Anti-magic god: no spell use, no enhancing magic.
+ if (brand == SPWPN_PAIN) // Pain involves necromantic spell use.
+ return (false);
+
+ if (artefact_wpn_property(item, ARTP_MAGICAL_POWER))
+ return (false);
+
+ default:
+ break;
+ }
+
+ return (true);
+}
+
+static std::string _replace_name_parts(const std::string name_in,
+ const item_def& item)
+{
+ std::string name = name_in;
+
+ god_type god_gift;
+ (void) origin_is_god_gift(item, &god_gift);
+
+ // Don't allow "player's Death" type names for god gifts (except
+ // for those from Xom).
+ if (name.find("@player_death@", 0) != std::string::npos
+ || name.find("@player_doom@", 0) != std::string::npos)
+ {
+ if (god_gift == GOD_NO_GOD || god_gift == GOD_XOM)
+ {
+ name = replace_all(name, "@player_death@",
+ "@player_name@"
+ + getRandNameString("killer_name"));
+ name = replace_all(name, "@player_doom@",
+ "@player_name@'s "
+ + getRandNameString("death_or_doom"));
+ }
+ else
+ {
+ // Simply overwrite the name with one of type "God's Favour".
+ name = "of ";
+ name += god_name(god_gift, false);
+ name += "'s ";
+ name += getRandNameString("divine_esteem");
+ }
+ }
+ name = replace_all(name, "@player_name@", you.your_name);
+
+ name = replace_all(name, "@player_species@",
+ species_name(static_cast<species_type>(you.species), 1));
+
+ if (name.find("@branch_name@", 0) != std::string::npos)
+ {
+ std::string place;
+ if (one_chance_in(5))
+ {
+ switch (random2(8))
+ {
+ case 0:
+ case 1:
+ default:
+ place = "the Abyss";
+ break;
+ case 2:
+ case 3:
+ place = "Pandemonium";
+ break;
+ case 4:
+ case 5:
+ place = "the Realm of Zot";
+ break;
+ case 6:
+ place = "the Labyrinth";
+ break;
+ case 7:
+ place = "the Portal Chambers";
+ break;
+ }
+ }
+ else
+ {
+ const branch_type branch =
+ static_cast<branch_type>(random2(BRANCH_TARTARUS));
+ place = place_name( get_packed_place(branch, 1, LEVEL_DUNGEON),
+ true, false );
+ }
+ if (!place.empty())
+ name = replace_all(name, "@branch_name@", place);
+ }
+
+ // Occasionally use long name for Xom (see religion.cc).
+ name = replace_all(name, "@xom_name@", god_name(GOD_XOM, coinflip()));
+
+ if (name.find("@god_name@", 0) != std::string::npos)
+ {
+ god_type which_god;
+
+ // God gifts will always get the gifting god's name.
+ if (god_gift != GOD_NO_GOD)
+ which_god = god_gift;
+ else
+ {
+ do
+ which_god = static_cast<god_type>(random2(NUM_GODS - 1) + 1);
+ while (!_god_fits_artefact(which_god, item, true));
+ }
+
+ name = replace_all(name, "@god_name@", god_name(which_god, false));
+ }
+
+ // copied from monster speech handling (mon-util.cc):
+ // The proper possessive for a word ending in an "s" is to
+ // put an apostrophe after the "s": "Chris" -> "Chris'",
+ // not "Chris" -> "Chris's". Stupid English language...
+ name = replace_all(name, "s's", "s'");
+
+ return name;
+}
+
+// Remember: disallow unrandart creation in Abyss/Pan.
+
+static unrandart_entry unranddata[] = {
+#include "art-data.h"
+};
+
+static FixedVector < bool, NO_UNRANDARTS > unrandart_exist;
+
+static unrandart_entry *_seekunrandart( const item_def &item );
+
+bool is_known_artefact( const item_def &item )
+{
+ if (!item_type_known(item))
+ return (false);
+
+ return (is_artefact(item));
+}
+
+bool is_artefact( const item_def &item )
+{
+ return (item.flags & ISFLAG_ARTEFACT_MASK);
+}
+
+// returns true is item is a pure randart
+bool is_random_artefact( const item_def &item )
+{
+ return (item.flags & ISFLAG_RANDART);
+}
+
+// returns true if item in an unrandart
+bool is_unrandom_artefact( const item_def &item )
+{
+ return (item.flags & ISFLAG_UNRANDART);
+}
+
+bool is_special_unrandom_artefact( const item_def &item )
+{
+ return (item.flags & ISFLAG_UNRANDART
+ && get_unrand_specialness(item.special) == UNRANDPSEC_SPECIAL);
+}
+
+unique_item_status_type get_unique_item_status(const item_def& item)
+{
+ if (item.flags & ISFLAG_UNRANDART)
+ return get_unique_item_status(item.special);
+
+ return (UNIQ_NOT_EXISTS);
+}
+
+unique_item_status_type get_unique_item_status( int art )
+{
+ ASSERT(art > UNRAND_START && art < UNRAND_LAST);
+ return (you.unique_items[art - UNRAND_START]);
+}
+
+void set_unique_item_status(const item_def& item,
+ unique_item_status_type status )
+{
+ if (item.flags & ISFLAG_UNRANDART)
+ set_unique_item_status(item.special, status);
+}
+
+void set_unique_item_status( int art, unique_item_status_type status )
+{
+ ASSERT(art > UNRAND_START && art < UNRAND_LAST);
+ you.unique_items[art - UNRAND_START] = status;
+}
+
+static long _calc_seed( const item_def &item )
+{
+ return (item.special & RANDART_SEED_MASK);
+}
+
+void artefact_desc_properties( const item_def &item,
+ artefact_properties_t &proprt,
+ artefact_known_props_t &known,
+ bool force_fake_props)
+{
+ // Randart books have no randart properties.
+ if (item.base_type == OBJ_BOOKS)
+ return;
+
+ artefact_wpn_properties( item, proprt, known);
+
+ if (!force_fake_props && item_ident( item, ISFLAG_KNOW_PROPERTIES ))
+ return;
+
+ // Only jewellery need fake randart properties.
+ if (item.base_type != OBJ_JEWELLERY)
+ return;
+
+ artefact_prop_type fake_rap = ARTP_NUM_PROPERTIES;
+ int fake_plus = 1;
+
+ // The base jewellery type is one whose property is revealed by
+ // wearing it, but whose property isn't revealed by having
+ // ISFLAG_KNOW_PLUSES set. For a randart with a base type of, for
+ // example, a ring of strength, wearing it sets
+ // ISFLAG_KNOW_PLUSES, which reveals the ring's strength plus.
+ switch (item.sub_type)
+ {
+ case RING_INVISIBILITY:
+ fake_rap = ARTP_INVISIBLE;
+ break;
+
+ case RING_TELEPORTATION:
+ fake_rap = ARTP_CAUSE_TELEPORTATION;
+ break;
+
+ case RING_MAGICAL_POWER:
+ fake_rap = ARTP_MAGICAL_POWER;
+ fake_plus = 9;
+ break;
+
+ case RING_LEVITATION:
+ fake_rap = ARTP_LEVITATE;
+ break;
+
+ case AMU_RAGE:
+ fake_rap = ARTP_BERSERK;
+ break;
+ }
+
+ if (fake_rap != ARTP_NUM_PROPERTIES)
+ {
+ proprt[fake_rap] += fake_plus;
+
+ if (item_ident( item, ISFLAG_KNOW_PROPERTIES )
+ || item_ident( item, ISFLAG_KNOW_TYPE ))
+ {
+ known[fake_rap] = true;
+ }
+
+ return;
+ }
+
+ if (!force_fake_props)
+ return;
+
+ // For auto-inscribing randart jewellery, force_fake_props folds as
+ // much info about the base type as possible into the randarts
+ // property struct.
+
+ artefact_prop_type fake_rap2 = ARTP_NUM_PROPERTIES;
+ int fake_plus2 = 1;
+
+ switch (item.sub_type)
+ {
+ case RING_PROTECTION:
+ fake_rap = ARTP_AC;
+ fake_plus = item.plus;
+ break;
+
+ case RING_PROTECTION_FROM_FIRE:
+ fake_rap = ARTP_FIRE;
+ break;
+
+ case RING_POISON_RESISTANCE:
+ fake_rap = ARTP_POISON;
+ break;
+
+ case RING_PROTECTION_FROM_COLD:
+ fake_rap = ARTP_COLD;
+ break;
+
+ case RING_SLAYING:
+ fake_rap = ARTP_ACCURACY;
+ fake_plus = item.plus;
+ fake_rap2 = ARTP_DAMAGE;
+ fake_plus2 = item.plus2;
+ break;
+
+ case RING_SEE_INVISIBLE:
+ fake_rap = ARTP_EYESIGHT;
+ break;
+
+ case RING_HUNGER:
+ fake_rap = ARTP_METABOLISM;
+ break;
+
+ case RING_EVASION:
+ fake_rap = ARTP_EVASION;
+ fake_plus = item.plus;
+ break;
+
+ case RING_STRENGTH:
+ fake_rap = ARTP_STRENGTH;
+ fake_plus = item.plus;
+ break;
+
+ case RING_INTELLIGENCE:
+ fake_rap = ARTP_INTELLIGENCE;
+ fake_plus = item.plus;
+ break;
+
+ case RING_DEXTERITY:
+ fake_rap = ARTP_DEXTERITY;
+ fake_plus = item.plus;
+ break;
+
+ case RING_LIFE_PROTECTION:
+ fake_rap = ARTP_NEGATIVE_ENERGY;
+ break;
+
+ case RING_PROTECTION_FROM_MAGIC:
+ fake_rap = ARTP_MAGIC;
+ break;
+
+ case RING_FIRE:
+ fake_rap = ARTP_FIRE;
+ fake_rap2 = ARTP_COLD;
+ fake_plus2 = -1;
+ break;
+
+ case RING_ICE:
+ fake_rap = ARTP_COLD;
+ fake_rap2 = ARTP_FIRE;
+ fake_plus2 = -1;
+ break;
+
+ case AMU_INACCURACY:
+ fake_rap = ARTP_ACCURACY;
+ fake_plus = -5;
+ break;
+ }
+
+ if (fake_rap != ARTP_NUM_PROPERTIES && fake_plus != 0)
+ proprt[fake_rap] += fake_plus;
+
+ if (fake_rap2 != ARTP_NUM_PROPERTIES && fake_plus2 != 0)
+ proprt[fake_rap2] += fake_plus2;
+
+ if (item_ident( item, ISFLAG_KNOW_PROPERTIES )
+ || item_ident( item, ISFLAG_KNOW_TYPE ))
+ {
+ if (fake_rap != ARTP_NUM_PROPERTIES && proprt[fake_rap] != 0)
+ known[fake_rap] = true;
+
+ if (fake_rap2 != ARTP_NUM_PROPERTIES && proprt[fake_rap2] != 0)
+ known[fake_rap2] = true;
+ }
+}
+
+inline static void _randart_propset( artefact_properties_t &p,
+ artefact_prop_type pt,
+ int value,
+ bool neg )
+{
+ // This shouldn't be called with 0, else no property gets added after all.
+ ASSERT(value != 0);
+ p[pt] = (neg? -value : value);
+}
+
+static int _randart_add_one_property( const item_def &item,
+ artefact_properties_t &proprt )
+{
+ // This function assumes that no properties have been added to this
+ // randart yet.
+
+ const object_class_type cl = item.base_type;
+ const int ty = item.sub_type;
+
+ // 0 - ac, 1 - ev, 2 - str, 3 - int, 4 - dex
+ int prop;
+ int skip = -1;
+
+ // Determine if we need to skip any of the above.
+ if (cl == OBJ_ARMOUR || cl == OBJ_JEWELLERY && ty == RING_PROTECTION)
+ skip = 0;
+ else if (cl == OBJ_JEWELLERY && ty == RING_EVASION)
+ skip = 1;
+ else if (cl == OBJ_JEWELLERY && ty == RING_STRENGTH)
+ skip = 2;
+ else if (cl == OBJ_JEWELLERY && ty == RING_INTELLIGENCE)
+ skip = 3;
+ else if (cl == OBJ_JEWELLERY && ty == RING_DEXTERITY)
+ skip = 4;
+
+ // Pick a random enchantment, taking into account the skipped index.
+ if (skip >= 0)
+ {
+ prop = random2(4);
+ if (prop >= skip)
+ prop++;
+ }
+ else
+ {
+ prop = random2(5);
+ }
+
+ const bool negench = one_chance_in(4);
+
+ switch (prop)
+ {
+ default:
+ case 0:
+ _randart_propset(proprt, ARTP_AC,
+ 1 + random2(3) + random2(3) + random2(3),
+ negench);
+ break;
+ case 1:
+ _randart_propset(proprt, ARTP_EVASION,
+ 1 + random2(3) + random2(3) + random2(3),
+ negench);
+ break;
+ case 2:
+ _randart_propset(proprt, ARTP_STRENGTH,
+ 1 + random2(3) + random2(3),
+ negench);
+ break;
+ case 3:
+ _randart_propset(proprt, ARTP_INTELLIGENCE,
+ 1 + random2(3) + random2(3),
+ negench);
+ break;
+ case 4:
+ _randart_propset(proprt, ARTP_DEXTERITY,
+ 1 + random2(3) + random2(3),
+ negench);
+ break;
+ }
+
+ return (negench ? -1 : 1);
+}
+
+void static _get_randart_properties(const item_def &item,
+ artefact_properties_t &proprt)
+{
+ const object_class_type aclass = item.base_type;
+ const int atype = item.sub_type;
+ int power_level = 0;
+
+ const long seed = _calc_seed( item );
+ rng_save_excursion exc;
+ seed_rng( seed );
+
+ if (aclass == OBJ_ARMOUR)
+ power_level = item.plus / 2 + 2;
+ else if (aclass == OBJ_JEWELLERY)
+ power_level = 1 + random2(3) + random2(2);
+ else // OBJ_WEAPON
+ power_level = item.plus / 3 + item.plus2 / 3;
+
+ if (power_level < 0)
+ power_level = 0;
+
+ proprt.init(0);
+
+ if (aclass == OBJ_WEAPONS) // only weapons get brands, of course
+ {
+ proprt[ARTP_BRAND] = SPWPN_FLAMING + random2(15); // brand
+
+ if (one_chance_in(6))
+ proprt[ARTP_BRAND] = SPWPN_FLAMING + random2(2);
+
+ if (one_chance_in(6))
+ proprt[ARTP_BRAND] = SPWPN_ORC_SLAYING + random2(5);
+
+ if (proprt[ARTP_BRAND] == SPWPN_DRAGON_SLAYING
+ && weapon_skill(item) != SK_POLEARMS)
+ {
+ proprt[ARTP_BRAND] = SPWPN_NORMAL;
+ }
+
+ if (proprt[ARTP_BRAND] == SPWPN_VENOM
+ && get_vorpal_type(item) == DVORP_CRUSHING)
+ {
+ proprt[ARTP_BRAND] = SPWPN_NORMAL;
+ }
+
+ if (one_chance_in(6))
+ proprt[ARTP_BRAND] = SPWPN_VORPAL;
+
+ if (proprt[ARTP_BRAND] == SPWPN_FLAME
+ || proprt[ARTP_BRAND] == SPWPN_FROST)
+ {
+ proprt[ARTP_BRAND] = SPWPN_NORMAL; // missile wpns
+ }
+
+ if (proprt[ARTP_BRAND] == SPWPN_PROTECTION)
+ proprt[ARTP_BRAND] = SPWPN_NORMAL; // no protection
+
+ // if this happens, things might get broken - bwr
+ if (proprt[ARTP_BRAND] == SPWPN_SPEED && atype == WPN_QUICK_BLADE)
+ proprt[ARTP_BRAND] = SPWPN_NORMAL;
+
+ if (is_range_weapon(item))
+ {
+ proprt[ARTP_BRAND] = SPWPN_NORMAL;
+
+ if (one_chance_in(3))
+ {
+ int tmp = random2(20);
+
+ proprt[ARTP_BRAND] = (tmp >= 18) ? SPWPN_SPEED :
+ (tmp >= 14) ? SPWPN_PROTECTION :
+ (tmp >= 10) ? SPWPN_VENOM
+ : SPWPN_VORPAL + random2(3);
+
+ if (atype == WPN_BLOWGUN
+ && proprt[ARTP_BRAND] != SPWPN_SPEED
+ && proprt[ARTP_BRAND] != SPWPN_PROTECTION)
+ {
+ proprt[ARTP_BRAND] = SPWPN_NORMAL;
+ }
+
+ if (atype == WPN_SLING
+ && proprt[ARTP_BRAND] == SPWPN_VENOM)
+ {
+ proprt[ARTP_BRAND] = SPWPN_NORMAL;
+ }
+ }
+ }
+
+ if (is_demonic(item))
+ {
+ switch (random2(9))
+ {
+ case 0:
+ proprt[ARTP_BRAND] = SPWPN_DRAINING;
+ break;
+ case 1:
+ proprt[ARTP_BRAND] = SPWPN_FLAMING;
+ break;
+ case 2:
+ proprt[ARTP_BRAND] = SPWPN_FREEZING;
+ break;
+ case 3:
+ proprt[ARTP_BRAND] = SPWPN_ELECTROCUTION;
+ break;
+ case 4:
+ proprt[ARTP_BRAND] = SPWPN_VAMPIRICISM;
+ break;
+ case 5:
+ proprt[ARTP_BRAND] = SPWPN_PAIN;
+ break;
+ case 6:
+ proprt[ARTP_BRAND] = SPWPN_VENOM;
+ break;
+ default:
+ power_level -= 2;
+ }
+ power_level += 2;
+ }
+ else if (one_chance_in(3))
+ proprt[ARTP_BRAND] = SPWPN_NORMAL;
+ else
+ power_level++;
+ }
+
+ if (!one_chance_in(5))
+ {
+ // AC mod - not for armours or rings of protection
+ if (one_chance_in(4 + power_level)
+ && aclass != OBJ_ARMOUR
+ && (aclass != OBJ_JEWELLERY || atype != RING_PROTECTION))
+ {
+ proprt[ARTP_AC] = 1 + random2(3) + random2(3) + random2(3);
+ power_level++;
+ if (one_chance_in(4))
+ {
+ proprt[ARTP_AC] -= 1 + random2(3) + random2(3) + random2(3);
+ power_level--;
+ }
+ }
+
+ // ev mod - not for rings of evasion
+ if (one_chance_in(4 + power_level)
+ && (aclass != OBJ_JEWELLERY || atype != RING_EVASION))
+ {
+ proprt[ARTP_EVASION] = 1 + random2(3) + random2(3) + random2(3);
+ power_level++;
+ if (one_chance_in(4))
+ {
+ proprt[ARTP_EVASION] -= 1 + random2(3) + random2(3)
+ + random2(3);
+ power_level--;
+ }
+ }
+
+ // str mod - not for rings of strength
+ if (one_chance_in(4 + power_level)
+ && (aclass != OBJ_JEWELLERY || atype != RING_STRENGTH))
+ {
+ proprt[ARTP_STRENGTH] = 1 + random2(3) + random2(2);
+ power_level++;
+ if (one_chance_in(4))
+ {
+ proprt[ARTP_STRENGTH] -= 1 + random2(3) + random2(3)
+ + random2(3);
+ power_level--;
+ }
+ }
+
+ // int mod - not for rings of intelligence
+ if (one_chance_in(4 + power_level)
+ && (aclass != OBJ_JEWELLERY || atype != RING_INTELLIGENCE))
+ {
+ proprt[ARTP_INTELLIGENCE] = 1 + random2(3) + random2(2);
+ power_level++;
+ if (one_chance_in(4))
+ {
+ proprt[ARTP_INTELLIGENCE] -= 1 + random2(3) + random2(3)
+ + random2(3);
+ power_level--;
+ }
+ }
+
+ // dex mod - not for rings of dexterity
+ if (one_chance_in(4 + power_level)
+ && (aclass != OBJ_JEWELLERY || atype != RING_DEXTERITY))
+ {
+ proprt[ARTP_DEXTERITY] = 1 + random2(3) + random2(2);
+ power_level++;
+ if (one_chance_in(4))
+ {
+ proprt[ARTP_DEXTERITY] -= 1 + random2(3) + random2(3)
+ + random2(3);
+ power_level--;
+ }
+ }
+ }
+
+ if (random2(15) >= power_level && aclass != OBJ_WEAPONS
+ && (aclass != OBJ_JEWELLERY || atype != RING_SLAYING))
+ {
+ // Weapons and rings of slaying can't get these.
+ if (one_chance_in(4 + power_level)) // to-hit
+ {
+ proprt[ARTP_ACCURACY] = 1 + random2(3) + random2(2);
+ power_level++;
+ if (one_chance_in(4))
+ {
+ proprt[ARTP_ACCURACY] -= 1 + random2(3) + random2(3)
+ + random2(3);
+ power_level--;
+ }
+ }
+
+ if (one_chance_in(4 + power_level)) // to-dam
+ {
+ proprt[ARTP_DAMAGE] = 1 + random2(3) + random2(2);
+ power_level++;
+ if (one_chance_in(4))
+ {
+ proprt[ARTP_DAMAGE] -= 1 + random2(3) + random2(3) + random2(3);
+ power_level--;
+ }
+ }
+ }
+
+ // This used to be: bool done_powers = (random2(12 < power_level));
+ // ... which can't be right. random2(boolean) == 0, always.
+ // So it's probably more along the lines of... (jpeg)
+// bool done_powers = (random2(12) < power_level);
+
+ // Try to improve items that still have a low power level.
+ bool done_powers = x_chance_in_y(power_level, 12);
+
+ // res_fire
+ if (!done_powers
+ && one_chance_in(4 + power_level)
+ && (aclass != OBJ_JEWELLERY
+ || (atype != RING_PROTECTION_FROM_FIRE
+ && atype != RING_FIRE
+ && atype != RING_ICE))
+ && (aclass != OBJ_ARMOUR
+ || (atype != ARM_DRAGON_ARMOUR
+ && atype != ARM_ICE_DRAGON_ARMOUR
+ && atype != ARM_GOLD_DRAGON_ARMOUR)))
+ {
+ proprt[ARTP_FIRE] = 1;
+ if (one_chance_in(5))
+ proprt[ARTP_FIRE]++;
+ power_level++;
+ }
+
+ // res_cold
+ if (!done_powers
+ && one_chance_in(4 + power_level)
+ && (aclass != OBJ_JEWELLERY
+ || atype != RING_PROTECTION_FROM_COLD
+ && atype != RING_FIRE
+ && atype != RING_ICE)
+ && (aclass != OBJ_ARMOUR
+ || atype != ARM_DRAGON_ARMOUR
+ && atype != ARM_ICE_DRAGON_ARMOUR
+ && atype != ARM_GOLD_DRAGON_ARMOUR))
+ {
+ proprt[ARTP_COLD] = 1;
+ if (one_chance_in(5))
+ proprt[ARTP_COLD]++;
+ power_level++;
+ }
+
+ if (x_chance_in_y(power_level, 12) || power_level > 7)
+ done_powers = true;
+
+ // res_elec
+ if (!done_powers
+ && one_chance_in(4 + power_level)
+ && (aclass != OBJ_ARMOUR || atype != ARM_STORM_DRAGON_ARMOUR))
+ {
+ proprt[ARTP_ELECTRICITY] = 1;
+ power_level++;
+ }
+
+ // res_poison
+ if (!done_powers
+ && one_chance_in(5 + power_level)
+ && (aclass != OBJ_JEWELLERY || atype != RING_POISON_RESISTANCE)
+ && (aclass != OBJ_ARMOUR
+ || atype != ARM_GOLD_DRAGON_ARMOUR
+ && atype != ARM_SWAMP_DRAGON_ARMOUR
+ && atype != ARM_NAGA_BARDING))
+ {
+ proprt[ARTP_POISON] = 1;
+ power_level++;
+ }
+
+ // prot_life - no necromantic brands on weapons allowed
+ if (!done_powers
+ && one_chance_in(4 + power_level)
+ && (aclass != OBJ_JEWELLERY || atype != RING_LIFE_PROTECTION)
+ && proprt[ARTP_BRAND] != SPWPN_DRAINING
+ && proprt[ARTP_BRAND] != SPWPN_VAMPIRICISM
+ && proprt[ARTP_BRAND] != SPWPN_PAIN)
+ {
+ proprt[ARTP_NEGATIVE_ENERGY] = 1;
+ power_level++;
+ }
+
+ // res magic
+ if (!done_powers
+ && one_chance_in(4 + power_level)
+ && (aclass != OBJ_JEWELLERY || atype != RING_PROTECTION_FROM_MAGIC))
+ {
+ proprt[ARTP_MAGIC] = 35 + random2(65);
+ power_level++;
+ }
+
+ // see_invis
+ if (!done_powers
+ && one_chance_in(4 + power_level)
+ && (aclass != OBJ_JEWELLERY || atype != RING_INVISIBILITY)
+ && (aclass != OBJ_ARMOUR || atype != ARM_NAGA_BARDING))
+ {
+ proprt[ARTP_EYESIGHT] = 1;
+ power_level++;
+ }
+
+ if (x_chance_in_y(power_level, 12) || power_level > 10)
+ done_powers = true;
+
+ // turn invis
+ if (!done_powers
+ && one_chance_in(10)
+ && (aclass != OBJ_JEWELLERY || atype != RING_INVISIBILITY))
+ {
+ proprt[ARTP_INVISIBLE] = 1;
+ power_level++;
+ }
+
+ // levitate
+ if (!done_powers
+ && one_chance_in(10)
+ && (aclass != OBJ_JEWELLERY || atype != RING_LEVITATION))
+ {
+ proprt[ARTP_LEVITATE] = 1;
+ power_level++;
+ }
+
+ // blink
+ if (!done_powers && one_chance_in(10))
+ {
+ proprt[ARTP_BLINK] = 1;
+ power_level++;
+ }
+
+ // teleport
+ if (!done_powers
+ && one_chance_in(10)
+ && (aclass != OBJ_JEWELLERY || atype != RING_TELEPORTATION))
+ {
+ proprt[ARTP_CAN_TELEPORT] = 1;
+ power_level++;
+ }
+
+ // go berserk
+ if (!done_powers
+ && one_chance_in(10)
+ && (aclass != OBJ_WEAPONS || !is_range_weapon(item))
+ && (aclass != OBJ_JEWELLERY || atype != AMU_RAGE))
+ {
+ proprt[ARTP_BERSERK] = 1;
+ power_level++;
+ }
+
+ // sense surroundings
+ if (!done_powers && one_chance_in(10))
+ {
+ proprt[ARTP_MAPPING] = 1;
+ power_level++;
+ }
+
+ // Armours get fewer powers, and are also less likely to be
+ // cursed than weapons
+ if (aclass == OBJ_ARMOUR)
+ power_level -= 4;
+
+ if (power_level >= 2 && x_chance_in_y(power_level, 17))
+ {
+ switch (random2(9))
+ {
+ case 0: // makes noise
+ if (aclass != OBJ_WEAPONS)
+ break;
+ proprt[ARTP_NOISES] = 1 + random2(4);
+ break;
+ case 1: // no magic
+ proprt[ARTP_PREVENT_SPELLCASTING] = 1;
+ break;
+ case 2: // random teleport
+ if (aclass != OBJ_WEAPONS)
+ break;
+ proprt[ARTP_CAUSE_TELEPORTATION] = 5 + random2(15);
+ break;
+ case 3: // no teleport - doesn't affect some instantaneous
+ // teleports
+ if (aclass == OBJ_JEWELLERY && atype == RING_TELEPORTATION)
+ break; // already is a ring of tport
+ if (aclass == OBJ_JEWELLERY && atype == RING_TELEPORT_CONTROL)
+ break; // already is a ring of tport ctrl
+ proprt[ARTP_BLINK] = 0;
+ proprt[ARTP_CAN_TELEPORT] = 0;
+ proprt[ARTP_PREVENT_TELEPORTATION] = 1;
+ break;
+ case 4: // berserk on attack
+ if (aclass != OBJ_WEAPONS || is_range_weapon(item))
+ break;
+ proprt[ARTP_ANGRY] = 1 + random2(8);
+ break;
+ case 5: // susceptible to fire
+ if (aclass == OBJ_JEWELLERY
+ && (atype == RING_PROTECTION_FROM_FIRE || atype == RING_FIRE
+ || atype == RING_ICE))
+ {
+ break; // already does this or something
+ }
+ if (aclass == OBJ_ARMOUR
+ && (atype == ARM_DRAGON_ARMOUR || atype == ARM_ICE_DRAGON_ARMOUR
+ || atype == ARM_GOLD_DRAGON_ARMOUR))
+ {
+ break;
+ }
+ proprt[ARTP_FIRE] = -1;
+ break;
+ case 6: // susceptible to cold
+ if (aclass == OBJ_JEWELLERY
+ && (atype == RING_PROTECTION_FROM_COLD || atype == RING_FIRE
+ || atype == RING_ICE))
+ {
+ break; // already does this or something
+ }
+ if (aclass == OBJ_ARMOUR
+ && (atype == ARM_DRAGON_ARMOUR || atype == ARM_ICE_DRAGON_ARMOUR
+ || atype == ARM_GOLD_DRAGON_ARMOUR))
+ {
+ break;
+ }
+ proprt[ARTP_COLD] = -1;
+ break;
+ case 7: // speed metabolism
+ if (aclass == OBJ_JEWELLERY && atype == RING_HUNGER)
+ break; // already is a ring of hunger
+ if (aclass == OBJ_JEWELLERY && atype == RING_SUSTENANCE)
+ break; // already is a ring of sustenance
+ proprt[ARTP_METABOLISM] = 1 + random2(3);
+ break;
+ case 8:
+ // emits mutagenic radiation - increases
+ // magic_contamination. property is chance (1 in ...) of
+ // increasing magic_contamination
+ proprt[ARTP_MUTAGENIC] = 2 + random2(4);
+ break;
+ }
+ }
+
+ if (one_chance_in(10)
+ && (aclass != OBJ_ARMOUR
+ || atype != ARM_CLOAK
+ || get_equip_race(item) != ISFLAG_ELVEN)
+ && (aclass != OBJ_ARMOUR
+ || atype != ARM_BOOTS
+ || get_equip_race(item) != ISFLAG_ELVEN)
+ && get_armour_ego_type(item) != SPARM_STEALTH)
+ {
+ power_level++;
+ proprt[ARTP_STEALTH] = 10 + random2(70);
+
+ if (one_chance_in(4))
+ {
+ proprt[ARTP_STEALTH] = -proprt[ARTP_STEALTH] - random2(20);
+ power_level--;
+ }
+ }
+
+ if (artefact_wpn_num_props(proprt) == 0)
+ power_level += _randart_add_one_property(item, proprt);
+
+ if ((power_level < 2 && one_chance_in(5)) || one_chance_in(30))
+ {
+ if (one_chance_in(4))
+ proprt[ARTP_CURSED] = 1 + random2(5);
+ else
+ proprt[ARTP_CURSED] = -1;
+ }
+}
+
+static bool _redo_book(item_def &book)
+{
+ int num_spells = 0;
+ int num_unknown = 0;
+
+ for (int i = 0; i < SPELLBOOK_SIZE; i++)
+ {
+ spell_type spell = which_spell_in_book(book, i);
+
+ if (spell == SPELL_NO_SPELL)
+ continue;
+
+ num_spells++;
+ if (!you.seen_spell[spell])
+ num_unknown++;
+ }
+
+ if (num_spells <= 5 && num_unknown == 0)
+ return (true);
+ else if (num_spells > 5 && num_unknown <= 1)
+ return (true);
+
+ return (false);
+}
+
+static bool _init_artefact_book(item_def &book)
+{
+ ASSERT(book.sub_type == BOOK_RANDART_LEVEL
+ || book.sub_type == BOOK_RANDART_THEME);
+ ASSERT(book.plus != 0);
+
+ god_type god;
+ bool redo = (!origin_is_god_gift(book, &god) || god != GOD_XOM);
+
+ // Plus and plus2 contain paramaters to make_book_foo_randart()
+ // which might get changed after the book has been made into a
+ // randart, so reset them on each iteration of the loop.
+ int plus = book.plus;
+ int plus2 = book.plus2;
+ bool book_good;
+ for (int i = 0; i < 4; i++)
+ {
+ book.plus = plus;
+ book.plus2 = plus2;
+
+ if (book.sub_type == BOOK_RANDART_LEVEL)
+ book_good = make_book_level_randart(book);
+ else
+ book_good = make_book_theme_randart(book);
+
+ if (!book_good)
+ continue;
+
+ if (redo && _redo_book(book))
+ continue;
+
+ break;
+ }
+
+ return (book_good);
+}
+
+static bool _init_artefact_properties(item_def &item)
+{
+ ASSERT( is_artefact( item ) );
+ CrawlHashTable &props = item.props;
+ if (!props.exists( ARTEFACT_PROPS_KEY ))
+ props[ARTEFACT_PROPS_KEY].new_vector(SV_SHORT).resize(ART_PROPERTIES);
+
+ CrawlVector &rap = props[ARTEFACT_PROPS_KEY];
+ rap.set_max_size(ART_PROPERTIES);
+
+ for (vec_size i = 0; i < ART_PROPERTIES; i++)
+ rap[i] = (short) 0;
+
+ if (is_unrandom_artefact( item ))
+ {
+ const unrandart_entry *unrand = _seekunrandart( item );
+
+ for (int i = 0; i < ART_PROPERTIES; i++)
+ rap[i] = (short) unrand->prpty[i];
+
+ return (true);
+ }
+
+ if (item.base_type == OBJ_BOOKS)
+ return _init_artefact_book(item);
+
+ artefact_properties_t prop;
+ _get_randart_properties(item, prop);
+
+ for (int i = 0; i < ART_PROPERTIES; i++)
+ {
+ if (i == ARTP_CURSED && prop[i] < 0)
+ {
+ do_curse_item(item);
+ continue;
+ }
+ rap[i] = (short) prop[i];
+ }
+
+ return (true);
+}
+
+void artefact_wpn_properties( const item_def &item,
+ artefact_properties_t &proprt,
+ artefact_known_props_t &known)
+{
+ ASSERT( is_artefact( item ) );
+ ASSERT( item.props.exists( KNOWN_PROPS_KEY ) );
+
+ const CrawlStoreValue &_val = item.props[KNOWN_PROPS_KEY];
+ ASSERT( _val.get_type() == SV_VEC );
+ const CrawlVector &known_vec = _val.get_vector();
+ ASSERT( known_vec.get_type() == SV_BOOL );
+ ASSERT( known_vec.size() == ART_PROPERTIES);
+ ASSERT( known_vec.get_max_size() == ART_PROPERTIES);
+
+ if (item_ident( item, ISFLAG_KNOW_PROPERTIES ))
+ {
+ for (vec_size i = 0; i < ART_PROPERTIES; i++)
+ known[i] = (bool) true;
+ }
+ else
+ {
+ for (vec_size i = 0; i < ART_PROPERTIES; i++)
+ known[i] = known_vec[i];
+ }
+
+ if (item.props.exists( ARTEFACT_PROPS_KEY ))
+ {
+ const CrawlVector &rap_vec = item.props[ARTEFACT_PROPS_KEY].get_vector();
+ ASSERT( rap_vec.get_type() == SV_SHORT );
+ ASSERT( rap_vec.size() == ART_PROPERTIES);
+ ASSERT( rap_vec.get_max_size() == ART_PROPERTIES);
+
+ for (vec_size i = 0; i < ART_PROPERTIES; i++)
+ proprt[i] = rap_vec[i].get_short();
+ }
+ else if (is_unrandom_artefact( item ))
+ {
+ const unrandart_entry *unrand = _seekunrandart( item );
+
+ for (int i = 0; i < ART_PROPERTIES; i++)
+ proprt[i] = (short) unrand->prpty[i];
+ }
+ else
+ {
+ _get_randart_properties(item, proprt);
+ }
+}
+
+
+void artefact_wpn_properties( const item_def &item,
+ artefact_properties_t &proprt )
+{
+ artefact_known_props_t known;
+
+ artefact_wpn_properties(item, proprt, known);
+}
+
+int artefact_wpn_property( const item_def &item, artefact_prop_type prop,
+ bool &_known )
+{
+ artefact_properties_t proprt;
+ artefact_known_props_t known;
+
+ artefact_wpn_properties( item, proprt, known );
+
+ _known = known[prop];
+
+ return ( proprt[prop] );
+}
+
+int artefact_wpn_property( const item_def &item, artefact_prop_type prop )
+{
+ bool known;
+
+ return artefact_wpn_property( item, prop, known );
+}
+
+int artefact_known_wpn_property( const item_def &item,
+ artefact_prop_type prop )
+{
+ artefact_properties_t proprt;
+ artefact_known_props_t known;
+
+ artefact_wpn_properties( item, proprt, known );
+
+ if (known[prop])
+ return ( proprt[prop] );
+ else
+ return (0);
+}
+
+int artefact_wpn_num_props( const item_def &item )
+{
+ artefact_properties_t proprt;
+ artefact_wpn_properties( item, proprt );
+
+ return artefact_wpn_num_props( proprt );
+}
+
+int artefact_wpn_num_props( const artefact_properties_t &proprt )
+{
+ int num = 0;
+
+ for (int i = 0; i < ARTP_NUM_PROPERTIES; i++)
+ if (proprt[i] != 0)
+ num++;
+
+ return num;
+}
+
+void artefact_wpn_learn_prop( item_def &item, artefact_prop_type prop )
+{
+ ASSERT( is_artefact( item ) );
+ ASSERT( item.props.exists( KNOWN_PROPS_KEY ) );
+ CrawlStoreValue &_val = item.props[KNOWN_PROPS_KEY];
+ ASSERT( _val.get_type() == SV_VEC );
+ CrawlVector &known_vec = _val.get_vector();
+ ASSERT( known_vec.get_type() == SV_BOOL );
+ ASSERT( known_vec.size() == ART_PROPERTIES);
+ ASSERT( known_vec.get_max_size() == ART_PROPERTIES);
+
+ if (item_ident( item, ISFLAG_KNOW_PROPERTIES ))
+ return;
+
+ known_vec[prop] = (bool) true;
+ if (Options.autoinscribe_artefacts)
+ add_autoinscription( item, artefact_auto_inscription(item));
+}
+
+bool artefact_wpn_known_prop( const item_def &item, artefact_prop_type prop )
+{
+ bool known;
+ artefact_wpn_property( item, prop, known );
+ return known;
+}
+
+static std::string _get_artefact_type(const item_def &item,
+ bool appear = false)
+{
+ switch (item.base_type)
+ {
+ case OBJ_BOOKS:
+ return "book";
+ case OBJ_WEAPONS:
+ return "weapon";
+ case OBJ_ARMOUR:
+ return "armour";
+ case OBJ_JEWELLERY:
+ // Only in appearance distinguish between amulets and rings.
+ if (!appear)
+ return "jewellery";
+
+ if (jewellery_is_amulet(item))
+ return "amulet";
+ else
+ return "ring";
+ default:
+ return "artefact";
+ }
+}
+
+static bool _pick_db_name( const item_def &item )
+{
+ switch (item.base_type)
+ {
+ case OBJ_WEAPONS:
+ case OBJ_ARMOUR:
+ return coinflip();
+ case OBJ_JEWELLERY:
+ return one_chance_in(5);
+ default:
+ return (false);
+ }
+}
+
+std::string artefact_name(const item_def &item, bool appearance)
+{
+ ASSERT(is_artefact(item));
+
+ ASSERT(item.base_type == OBJ_WEAPONS
+ || item.base_type == OBJ_ARMOUR
+ || item.base_type == OBJ_JEWELLERY
+ || item.base_type == OBJ_BOOKS);
+
+ if (is_unrandom_artefact( item ))
+ {
+ const unrandart_entry *unrand = _seekunrandart( item );
+ return (item_type_known(item) ? unrand->name : unrand->unid_name);
+ }
+
+ const long seed = _calc_seed( item );
+
+ std::string lookup;
+ std::string result;
+
+ // Use prefix of gifting god, if applicable.
+ bool god_gift = false;
+ int item_orig = 0;
+ if (!appearance)
+ {
+ // Divine gifts don't look special, so this is only necessary
+ // for actually naming an item.
+ item_orig = item.orig_monnum;
+ if (item_orig < 0)
+ item_orig = -item_orig;
+ else
+ item_orig = 0;
+
+ if (item_orig > GOD_NO_GOD && item_orig < NUM_GODS)
+ {
+ lookup += god_name(static_cast<god_type>(item_orig), false) + " ";
+ god_gift = true;
+ }
+ }
+
+ // get base type
+ lookup += _get_artefact_type(item, appearance);
+
+ rng_save_excursion rng_state;
+ seed_rng( seed );
+
+ if (appearance)
+ {
+ std::string appear = getRandNameString(lookup, " appearance");
+ if (appear.empty())
+ {
+ appear = getRandNameString("general appearance");
+ if (appear.empty()) // still nothing found?
+ appear = "non-descript";
+ }
+
+ result += appear;
+ result += " ";
+ result += item_base_name(item);
+ return (result);
+ }
+
+ if (_pick_db_name(item))
+ {
+ result += item_base_name(item) + " ";
+
+ int tries = 100;
+ std::string name;
+ do
+ {
+ name = getRandNameString(lookup);
+
+ if (name.empty() && god_gift)
+ {
+ // If nothing found, try god name alone.
+ name = getRandNameString(
+ god_name(static_cast<god_type>(item_orig), false));
+
+ if (name.empty())
+ {
+ // If still nothing found, try base type alone.
+ name = getRandNameString(
+ _get_artefact_type(item).c_str());
+ }
+ }
+
+ name = _replace_name_parts(name, item);
+ }
+ while (--tries > 0 && name.length() > 25);
+
+ if (name.empty()) // still nothing found?
+ result += "of Bugginess";
+ else
+ result += name;
+ }
+ else
+ {
+ // construct a unique name
+ const std::string st_p = make_name(random_int(), false);
+ result += item_base_name(item);
+
+ if (one_chance_in(3))
+ {
+ result += " of ";
+ result += st_p;
+ }
+ else
+ {
+ result += " \"";
+ result += st_p;
+ result += "\"";
+ }
+ }
+
+ return result;
+}
+
+std::string get_artefact_name( const item_def &item )
+{
+ ASSERT( is_artefact( item ) );
+
+ if (item_type_known(item))
+ {
+ // print artefact's real name
+ if (item.props.exists(ARTEFACT_NAME_KEY))
+ return item.props[ARTEFACT_NAME_KEY].get_string();
+ return artefact_name(item, false);
+ }
+ // print artefact appearance
+ if (item.props.exists(ARTEFACT_APPEAR_KEY))
+ return item.props[ARTEFACT_APPEAR_KEY].get_string();
+ return artefact_name(item, false);
+}
+
+void set_artefact_name( item_def &item, const std::string &name )
+{
+ ASSERT( is_artefact( item ));
+ ASSERT( !name.empty() );
+ item.props[ARTEFACT_NAME_KEY].get_string() = name;
+}
+
+void set_artefact_appearance( item_def &item, const std::string &appear )
+{
+ ASSERT( is_artefact( item ));
+ ASSERT( !appear.empty() );
+ item.props[ARTEFACT_APPEAR_KEY].get_string() = appear;
+}
+
+int find_unrandart_index(const item_def& artefact)
+{
+ return (artefact.special);
+}
+
+unrandart_entry* get_unrand_entry(int unrand_index)
+{
+ unrand_index -= UNRAND_START;
+
+ if (unrand_index <= -1 || unrand_index >= NO_UNRANDARTS)
+ return &unranddata[0]; // dummy unrandart
+ else
+ return &unranddata[unrand_index];
+}
+
+static unrandart_entry *_seekunrandart( const item_def &item )
+{
+ return get_unrand_entry(item.special);
+}
+
+int find_okay_unrandart(unsigned char aclass, unsigned char atype,
+ unrand_special_type specialness, bool in_abyss)
+{
+ int ret = -1;
+
+ // Pick randomly among not-yet-existing unrandarts with the proper
+ // base_type and sub_type.
+ for (int i = 0, count = 0; i < NO_UNRANDARTS; i++)
+ {
+ const int index = i + UNRAND_START;
+ const unrandart_entry* entry = &unranddata[i];
+
+ // Skip dummy entries.
+ if (entry->base_type == OBJ_UNASSIGNED)
+ continue;
+
+ const unique_item_status_type status =
+ get_unique_item_status(index);
+
+ if (in_abyss && status != UNIQ_LOST_IN_ABYSS
+ || !in_abyss && status != UNIQ_NOT_EXISTS)
+ {
+ continue;
+ }
+
+ // Never randomly generated until lost in the abyss.
+ if ((!in_abyss || status != UNIQ_LOST_IN_ABYSS)
+ && index >= SPWPN_START_NOGEN_FIXEDARTS
+ && index <= SPWPN_END_FIXEDARTS)
+ {
+ continue;
+ }
+
+ if (entry->base_type != aclass
+ || (atype != OBJ_RANDOM && entry->sub_type != atype))
+ {
+ continue;
+ }
+
+ if (specialness != UNRANDSPEC_EITHER &&
+ specialness != get_unrand_specialness(index))
+ {
+ continue;
+ }
+
+ count++;
+
+ if (one_chance_in(count))
+ ret = index;
+ }
+
+ return (ret);
+}
+
+unrand_special_type get_unrand_specialness(int unrand_index)
+{
+ if (unrand_index >= UNRAND_SINGING_SWORD
+ && unrand_index <= UNRAND_ASMODEUS)
+ {
+ return (UNRANDPSEC_SPECIAL);
+ }
+ return (UNRANDSPEC_NORMAL);
+}
+
+unrand_special_type get_unrand_specialness(const item_def &item)
+{
+ return get_unrand_specialness(item.special);
+}
+
+int get_unrandart_num( const char *name )
+{
+ std::string quoted = "\"";
+ quoted += name;
+ quoted += "\"";
+
+ for (unsigned int i = 0; i < ARRAYSZ(unranddata); ++i)
+ {
+ std::string art = unranddata[i].name;
+ art = replace_all(art, " ", "_");
+ art = replace_all(art, "'", "");
+ lowercase(art);
+ if (art == name || art.find(quoted) != std::string::npos)
+ return (UNRAND_START + i);
+ }
+ return SPWPN_NORMAL;
+}
+
+static bool _randart_is_redundant( const item_def &item,
+ artefact_properties_t &proprt )
+{
+ if (item.base_type != OBJ_JEWELLERY)
+ return (false);
+
+ artefact_prop_type provides = ARTP_NUM_PROPERTIES;
+ artefact_prop_type provides2 = ARTP_NUM_PROPERTIES;
+
+ switch (item.sub_type)
+ {
+ case RING_PROTECTION:
+ provides = ARTP_AC;
+ break;
+
+ case RING_FIRE:
+ case RING_PROTECTION_FROM_FIRE:
+ provides = ARTP_FIRE;
+ break;
+
+ case RING_POISON_RESISTANCE:
+ provides = ARTP_POISON;
+ break;
+
+ case RING_ICE:
+ case RING_PROTECTION_FROM_COLD:
+ provides = ARTP_COLD;
+ break;
+
+ case RING_STRENGTH:
+ provides = ARTP_STRENGTH;
+ break;
+
+ case RING_SLAYING:
+ provides = ARTP_DAMAGE;
+ provides2 = ARTP_ACCURACY;
+ break;
+
+ case RING_SEE_INVISIBLE:
+ provides = ARTP_EYESIGHT;
+ break;
+
+ case RING_INVISIBILITY:
+ provides = ARTP_INVISIBLE;
+ break;
+
+ case RING_HUNGER:
+ provides = ARTP_METABOLISM;
+ break;
+
+ case RING_TELEPORTATION:
+ provides = ARTP_CAN_TELEPORT;
+ provides2 = ARTP_CAUSE_TELEPORTATION;
+ break;
+
+ case RING_EVASION:
+ provides = ARTP_EVASION;
+ break;
+
+ case RING_DEXTERITY:
+ provides = ARTP_DEXTERITY;
+ break;
+
+ case RING_INTELLIGENCE:
+ provides = ARTP_INTELLIGENCE;
+ break;
+
+ case RING_MAGICAL_POWER:
+ provides = ARTP_MAGICAL_POWER;
+ break;
+
+ case RING_LEVITATION:
+ provides = ARTP_LEVITATE;
+ break;
+
+ case RING_LIFE_PROTECTION:
+ provides = ARTP_AC;
+ break;
+
+ case RING_PROTECTION_FROM_MAGIC:
+ provides = ARTP_MAGIC;
+ break;
+
+ case AMU_RAGE:
+ provides = ARTP_BERSERK;
+ break;
+
+ case AMU_INACCURACY:
+ provides = ARTP_ACCURACY;
+ break;
+ }
+
+ if (provides == ARTP_NUM_PROPERTIES)
+ return (false);
+
+ if (proprt[provides] != 0)
+ return (true);
+
+ if (provides2 == ARTP_NUM_PROPERTIES)
+ return (false);
+
+ if (proprt[provides2] != 0)
+ return (true);
+
+ return (false);
+}
+
+static bool _randart_is_conflicting( const item_def &item,
+ artefact_properties_t &proprt )
+{
+ if (item.base_type != OBJ_JEWELLERY)
+ return (false);
+
+ artefact_prop_type conflicts = ARTP_NUM_PROPERTIES;
+
+ switch (item.sub_type)
+ {
+ case RING_SUSTENANCE:
+ conflicts = ARTP_METABOLISM;
+ break;
+
+ case RING_FIRE:
+ case RING_ICE:
+ case RING_WIZARDRY:
+ case RING_MAGICAL_POWER:
+ conflicts = ARTP_PREVENT_SPELLCASTING;
+ break;
+
+ case RING_TELEPORTATION:
+ case RING_TELEPORT_CONTROL:
+ conflicts = ARTP_PREVENT_TELEPORTATION;
+ break;
+
+ case AMU_RESIST_MUTATION:
+ conflicts = ARTP_MUTAGENIC;
+ break;
+
+ case AMU_RAGE:
+ conflicts = ARTP_STEALTH;
+ break;
+ }
+
+ if (conflicts == ARTP_NUM_PROPERTIES)
+ return (false);
+
+ if (proprt[conflicts] != 0)
+ return (true);
+
+ return (false);
+}
+
+bool randart_is_bad( const item_def &item, artefact_properties_t &proprt )
+{
+ if (item.base_type == OBJ_BOOKS)
+ return (false);
+
+ if (artefact_wpn_num_props( proprt ) == 0)
+ return (true);
+
+ return (_randart_is_redundant( item, proprt )
+ || _randart_is_conflicting( item, proprt ));
+}
+
+bool randart_is_bad( const item_def &item )
+{
+ artefact_properties_t proprt;
+ artefact_wpn_properties( item, proprt );
+
+ return randart_is_bad( item, proprt);
+}
+
+bool make_item_blessed_blade( item_def &item )
+{
+ if (item.base_type != OBJ_WEAPONS)
+ return (false);
+
+ // already is an artefact
+ if (is_artefact(item))
+ return (false);
+
+ // mark as a random artefact
+ item.flags |= ISFLAG_RANDART;
+
+ ASSERT(!item.props.exists( KNOWN_PROPS_KEY ));
+ item.props[KNOWN_PROPS_KEY].new_vector(SV_BOOL).resize(ART_PROPERTIES);
+ CrawlVector &known = item.props[KNOWN_PROPS_KEY];
+ known.set_max_size(ART_PROPERTIES);
+ for (vec_size i = 0; i < ART_PROPERTIES; i++)
+ known[i] = (bool) false;
+
+ ASSERT(!item.props.exists( ARTEFACT_PROPS_KEY ));
+ item.props[ARTEFACT_PROPS_KEY].new_vector(SV_SHORT).resize(ART_PROPERTIES);
+ CrawlVector &rap = item.props[ARTEFACT_PROPS_KEY];
+ rap.set_max_size(ART_PROPERTIES);
+ for (vec_size i = 0; i < ART_PROPERTIES; i++)
+ rap[i] = (short) 0;
+
+ // blessed blade of the Shining One
+ rap[ARTP_BRAND] = (short) SPWPN_HOLY_WRATH;
+
+ // set artefact name
+ ASSERT(!item.props.exists( ARTEFACT_NAME_KEY ));
+ item.props[ARTEFACT_NAME_KEY].get_string() = "Blessed Blade";
+
+ // set artefact appearance
+ ASSERT(!item.props.exists( ARTEFACT_APPEAR_KEY ));
+ item.props[ARTEFACT_APPEAR_KEY].get_string() = "brightly glowing blade";
+
+ // in case this is ever needed anywhere
+ item.special = (random_int() & RANDART_SEED_MASK);
+
+ return (true);
+}
+
+bool make_item_randart( item_def &item )
+{
+ if (item.base_type != OBJ_WEAPONS
+ && item.base_type != OBJ_ARMOUR
+ && item.base_type != OBJ_JEWELLERY
+ && item.base_type != OBJ_BOOKS)
+ {
+ return (false);
+ }
+
+ if (item.base_type == OBJ_BOOKS)
+ {
+ if (item.sub_type != BOOK_RANDART_LEVEL
+ && item.sub_type != BOOK_RANDART_THEME)
+ {
+ return (false);
+ }
+ }
+
+ // This item already is a randart.
+ if (item.flags & ISFLAG_RANDART)
+ return (true);
+
+ // Not a truly random artefact.
+ if (item.flags & ISFLAG_UNRANDART)
+ return (false);
+
+ if (item_is_mundane(item))
+ return (false);
+
+ ASSERT(!item.props.exists( KNOWN_PROPS_KEY ));
+ ASSERT(!item.props.exists( ARTEFACT_NAME_KEY ));
+ ASSERT(!item.props.exists( ARTEFACT_APPEAR_KEY ));
+ item.props[KNOWN_PROPS_KEY].new_vector(SV_BOOL).resize(ART_PROPERTIES);
+ CrawlVector &known = item.props[KNOWN_PROPS_KEY];
+ known.set_max_size(ART_PROPERTIES);
+ for (vec_size i = 0; i < ART_PROPERTIES; i++)
+ known[i] = (bool) false;
+
+ item.flags |= ISFLAG_RANDART;
+
+ god_type god_gift;
+ (void) origin_is_god_gift(item, &god_gift);
+
+ do
+ {
+ item.special = (random_int() & RANDART_SEED_MASK);
+ // Now that we found something, initialize the props array.
+ if (!_init_artefact_properties(item))
+ {
+ // Something went wrong that no amount of changing
+ // item.special will fix.
+ item.special = 0;
+ item.props.erase(ARTEFACT_PROPS_KEY);
+ item.props.erase(KNOWN_PROPS_KEY);
+ item.flags &= ~ISFLAG_RANDART;
+ return (false);
+ }
+ }
+ while (randart_is_bad(item)
+ || god_gift != GOD_NO_GOD && !_god_fits_artefact(god_gift, item));
+
+ // get true artefact name
+ if (item.props.exists( ARTEFACT_NAME_KEY ))
+ ASSERT(item.props[ARTEFACT_NAME_KEY].get_type() == SV_STR);
+ else
+ item.props[ARTEFACT_NAME_KEY].get_string() = artefact_name(item, false);
+
+ // get artefact appearance
+ if (item.props.exists( ARTEFACT_APPEAR_KEY ))
+ ASSERT(item.props[ARTEFACT_APPEAR_KEY].get_type() == SV_STR);
+ else
+ item.props[ARTEFACT_APPEAR_KEY].get_string() = artefact_name(item, true);
+
+ return (true);
+}
+
+bool make_item_unrandart( item_def &item, int unrand_index )
+{
+ ASSERT(unrand_index > UNRAND_START);
+ ASSERT(unrand_index < (UNRAND_START + NO_UNRANDARTS));
+
+ if (!item.props.exists( KNOWN_PROPS_KEY ))
+ {
+ item.props[KNOWN_PROPS_KEY].new_vector(SV_BOOL).resize(ART_PROPERTIES);
+ CrawlVector &known = item.props[KNOWN_PROPS_KEY];
+ known.set_max_size(ART_PROPERTIES);
+ for (vec_size i = 0; i < ART_PROPERTIES; i++)
+ known[i] = (bool) false;
+ }
+
+ const unrandart_entry *unrand = &unranddata[unrand_index - UNRAND_START];
+ item.base_type = unrand->base_type;
+ item.sub_type = unrand->sub_type;
+ item.plus = unrand->plus;
+ item.plus2 = unrand->plus2;
+ item.colour = unrand->colour;
+
+ item.flags |= ISFLAG_UNRANDART;
+ _init_artefact_properties(item);
+
+ item.special = unrand_index;
+ if (unrand->prpty[ARTP_BRAND] != 0)
+ do_curse_item( item );
+
+ // get true artefact name
+ ASSERT(!item.props.exists( ARTEFACT_NAME_KEY ));
+ item.props[ARTEFACT_NAME_KEY].get_string() = unrand->name;
+
+ // get artefact appearance
+ ASSERT(!item.props.exists( ARTEFACT_APPEAR_KEY ));
+ item.props[ARTEFACT_APPEAR_KEY].get_string() = unrand->unid_name;
+
+ set_unique_item_status(unrand_index, UNIQ_EXISTS);
+
+ return (true);
+}
+
+const char *unrandart_descrip( int which_descrip, const item_def &item )
+{
+ // Eventually it would be great to have randomly generated descriptions
+ // for randarts.
+ const unrandart_entry *unrand = _seekunrandart( item );
+
+ return ((which_descrip == 0) ? unrand->desc :
+ (which_descrip == 1) ? unrand->desc_id :
+ (which_descrip == 2) ? unrand->desc_end
+ : "Unknown.");
+}
+
+void artefact_set_properties( item_def &item,
+ artefact_properties_t &proprt )
+{
+ ASSERT( is_random_artefact( item ) );
+ ASSERT( item.props.exists( ARTEFACT_PROPS_KEY ) );
+
+ CrawlVector &rap_vec = item.props[ARTEFACT_PROPS_KEY].get_vector();
+ ASSERT( rap_vec.get_type() == SV_SHORT );
+ ASSERT( rap_vec.size() == ART_PROPERTIES);
+ ASSERT( rap_vec.get_max_size() == ART_PROPERTIES);
+
+ for (vec_size i = 0; i < ART_PROPERTIES; i++)
+ rap_vec[i].get_short() = proprt[i];
+}
+
+void artefact_set_property( item_def &item,
+ artefact_prop_type prop,
+ int val )
+{
+ ASSERT( is_random_artefact( item ) );
+ ASSERT( item.props.exists( ARTEFACT_PROPS_KEY ) );
+
+ CrawlVector &rap_vec = item.props[ARTEFACT_PROPS_KEY].get_vector();
+ ASSERT( rap_vec.get_type() == SV_SHORT );
+ ASSERT( rap_vec.size() == ART_PROPERTIES);
+ ASSERT( rap_vec.get_max_size() == ART_PROPERTIES);
+
+ rap_vec[prop].get_short() = val;
+}