/*
* File: artefact.cc
* Summary: Random and unrandom artefact functions.
* Written by: Linley Henzell
*/
#include "AppHdr.h"
#include "artefact.h"
#include <cstdlib>
#include <climits>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include "externs.h"
#include "options.h"
#include "areas.h"
#include "colour.h"
#include "coordit.h"
#include "database.h"
#include "describe.h"
#include "itemname.h"
#include "itemprop.h"
#include "items.h"
#include "makeitem.h"
#include "place.h"
#include "player.h"
#include "random.h"
#include "religion.h"
#include "species.h"
#include "spl-book.h"
#include "stuff.h"
#include "view.h" // Elemental colours for unrandarts
#include "shout.h"
// 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);
// Jellies can't eat artefacts, so their god won't make any.
if (which_god == GOD_JIYVA)
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;
case GOD_CHEIBRIADOS:
// Slow god: no quick blades, no berserking, no resist slowing.
if (item.base_type == OBJ_WEAPONS && item.sub_type == WPN_QUICK_BLADE)
type_bad = true;
if (item.base_type == OBJ_JEWELLERY && item.sub_type == AMU_RAGE)
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);
const int ego = get_armour_ego_type(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 || brand == SPWPN_REAPING
|| brand == SPWPN_CHAOS
|| is_demonic(item)
|| 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);
break;
case GOD_FEDHAS:
// Anti-necromancy god: nothing involving necromancy or
// necromantic spell use.
if (brand == SPWPN_DRAINING || brand == SPWPN_PAIN
|| brand == SPWPN_VAMPIRICISM || brand == SPWPN_REAPING
|| brand == SPWPN_CHAOS)
{
return (false);
}
break;
case GOD_CHEIBRIADOS:
// Slow god: no speed, no berserking.
if (brand == SPWPN_SPEED)
return (false);
if (ego == SPARM_RUNNING)
return (false);
if (artefact_wpn_property(item, ARTP_ANGRY)
|| artefact_wpn_property(item, ARTP_BERSERK))
{
return (false);
}
break;
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.
// Functions defined in art-func.h are referenced in art-data.h
#include "art-func.h"
static unrandart_entry unranddata[UNRAND_LAST] = {
#include "art-data.h"
};
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
&& (_seekunrandart(item)->flags & UNRAND_FLAG_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 at least some properties are still possible.
const object_class_type cl = item.base_type;
const int ty = item.sub_type;
const bool negench = one_chance_in(4);
const artefact_prop_type artprops[] = { ARTP_AC, ARTP_EVASION,
ARTP_STRENGTH, ARTP_INTELLIGENCE,
ARTP_DEXTERITY };
do
{
int prop = RANDOM_ELEMENT(artprops);
if (proprt[prop] != 0)
continue;
switch (prop)
{
case ARTP_AC:
if (cl == OBJ_ARMOUR
|| cl == OBJ_JEWELLERY && ty == RING_PROTECTION)
{
continue;
}
_randart_propset(proprt, ARTP_AC,
1 + random2(3) + random2(3) + random2(3),
negench);
break;
case ARTP_EVASION:
if (cl == OBJ_JEWELLERY && ty == RING_EVASION)
continue;
_randart_propset(proprt, ARTP_EVASION,
1 + random2(3) + random2(3) + random2(3),
negench);
break;
case ARTP_STRENGTH:
if (cl == OBJ_JEWELLERY && ty == RING_STRENGTH)
continue;
_randart_propset(proprt, ARTP_STRENGTH,
1 + random2(3) + random2(3),
negench);
break;
case ARTP_INTELLIGENCE:
if (cl == OBJ_JEWELLERY && ty == RING_INTELLIGENCE)
continue;
_randart_propset(proprt, ARTP_INTELLIGENCE,
1 + random2(3) + random2(3),
negench);
break;
case ARTP_DEXTERITY:
if (cl == OBJ_JEWELLERY && ty == RING_DEXTERITY)
continue;
_randart_propset(proprt, ARTP_DEXTERITY,
1 + random2(3) + random2(3),
negench);
break;
}
}
while (false);
return (negench ? -1 : 1);
}
// An artefact will pass this check if it has any non-stat properties, and
// also if it has enough stat (AC, EV, Str, Dex, Int, Acc, Dam) properties.
// Returns how many (more) stat properties we need to add.
static int _need_bonus_stat_props( const artefact_properties_t &proprt )
{
int num_stats = 0;
int num_acc_dam = 0;
for (int i = 0; i < ARTP_NUM_PROPERTIES; i++)
{
if (i == ARTP_CURSED)
continue;
if (proprt[i] == 0)
continue;
if (i >= ARTP_AC && i <= ARTP_DEXTERITY)
num_stats++;
else if (i >= ARTP_ACCURACY && i <= ARTP_DAMAGE)
num_acc_dam++;
else
return (0);
}
num_stats += num_acc_dam;
// If an artefact has no properties at all, something is wrong.
if (num_stats == 0)
return (2);
// Artefacts with two of more stat-only properties are fine.
if (num_stats >= 2)
return (0);
// If an artefact has exactly one stat property, we might want to add
// some more. (66% chance if it's Acc/Dam, else always.)
if (num_acc_dam > 0)
return (random2(3));
return (1 + random2(2));
}
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 (one_chance_in(6))
proprt[ARTP_BRAND] = SPWPN_VORPAL;
if (proprt[ARTP_BRAND] == SPWPN_PROTECTION || proprt[ARTP_BRAND] == SPWPN_EVASION)
proprt[ARTP_BRAND] = SPWPN_NORMAL; // no protection or evasion
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 >= 16) ? SPWPN_PENETRATION :
(tmp >= 13) ? SPWPN_REAPING :
(tmp >= 10) ? SPWPN_EVASION :
(tmp >= 7) ? SPWPN_VENOM
: SPWPN_VORPAL + random2(3);
if (atype == WPN_BLOWGUN
&& proprt[ARTP_BRAND] != SPWPN_SPEED
&& proprt[ARTP_BRAND] != SPWPN_EVASION)
{
proprt[ARTP_BRAND] = SPWPN_NORMAL;
}
// Removed slings from getting the venom attribute: they can
// be branded with it now using Poison Weapon, and perma-branded
// via vorpalise weapon.
if (atype == WPN_CROSSBOW && one_chance_in(5))
proprt[ARTP_BRAND] = SPWPN_ELECTROCUTION;
// XXX: Penetration is only allowed on crossbows. This may change
// in future.
if (atype != WPN_CROSSBOW && proprt[ARTP_BRAND] == SPWPN_PENETRATION)
proprt[ARTP_BRAND] = SPWPN_NORMAL;
// XXX: Only allow reaping brand on bows. This may change.
if (atype != WPN_BOW && atype != WPN_LONGBOW
&& proprt[ARTP_BRAND] == SPWPN_REAPING)
{
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 (!is_weapon_brand_ok(atype, proprt[ARTP_BRAND]))
{
proprt[ARTP_BRAND] = SPWPN_NORMAL;
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_BRAND] != SPWPN_REAPING)
{
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++;
}
// 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++;
}
if (!done_powers && one_chance_in(10) && aclass == OBJ_ARMOUR
&& (atype == ARM_CAP || atype == ARM_SHIELD))
{
proprt[ARTP_BRAND] = SPARM_SPIRIT_SHIELD;
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_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--;
}
}
// "Boring" artefacts (no properties, or only one stat property)
// get an additional property, or maybe two of them.
int add_prop = _need_bonus_stat_props(proprt);
while (0 < add_prop--)
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 parameters 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)
// The parameters to this call are in book.plus and plus2.
book_good = make_book_level_randart(book, book.plus, book.plus2);
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].get_vector();
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_num_props( const artefact_properties_t &proprt )
{
int num = 0;
// Count all properties, but exclude self-cursing.
for (int i = 0; i < ARTP_NUM_PROPERTIES; i++)
if (proprt[i] != 0 && i != ARTP_CURSED)
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 >= UNRAND_CEREBOV && index <= UNRAND_ASMODEUS)
{
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_START || unrand_index > UNRAND_LAST)
return (UNRANDSPEC_NORMAL);
if (unranddata[unrand_index].flags & UNRAND_FLAG_SPECIAL)
return (UNRANDSPEC_SPECIAL);
return (UNRANDSPEC_NORMAL);
}
unrand_special_type get_unrand_specialness(const item_def &item)
{
ASSERT(is_unrandom_artefact(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_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;
case AMU_STASIS:
provides = ARTP_PREVENT_TELEPORTATION;
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_WEAPONS
&& get_weapon_brand(item) == SPWPN_HOLY_WRATH
&& proprt[ARTP_CURSED] != 0)
{
return (true);
}
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_num_props( proprt ) == 0)
return (true);
if (item.base_type == OBJ_WEAPONS && proprt[ARTP_BRAND] == SPWPN_NORMAL)
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_randart( item_def &item, bool force_mundane )
{
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() && !one_chance_in(5) && !force_mundane)
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].get_vector();
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);
int randart_tries = 500;
do
{
item.special = (random_int() & RANDART_SEED_MASK);
// Now that we found something, initialise the props array.
if (--randart_tries <= 0 || !_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));
item.special = unrand_index;
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].get_vector();
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);
if (unrand->prpty[ARTP_CURSED] != 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);
if (unrand_index == UNRAND_VARIABILITY)
{
item.plus = random_range(-4, 16);
item.plus2 = random_range(-4, 16);
}
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_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_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;
}