git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7746 c06c8d41-db1a-0410-9941-cceddc491573
QO5ZJWQ3JK3PEGBPTQSAYIPEJEHG2M2KTD74227G5VG7DVXUL3BQC
KK3VOK55GODD7XJKCQGQEXR7KUFXRDI5RRMTMLHRZTPXHYKVNUIQC
FWNNTOEERPUKXPE4OC52UABFZLKIU3O5GRNNLDK4QI4HR2IOU36QC
HH7B5SORPUW74EC3V2ZVZTMDH7UXG2FLAELXUQII4TNGGHDLPETAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
DDU4A3JGN5IUIPP5IASOODKPR2WBHSDSV4FITZ6HNXNSXXQACWAQC
7BREK7U6OWZ6YU3JDSJSH4CMNNULDYABCKCAUHGZIUJZBIRJS5WQC
NQMXQ6OQVUSC7Y7F7IL252QW4A5JED224EECNHWAM4ZZYVNY745AC
4HHCHQD26LSGYC5TSQV25D2KIWLRJDB4BJQHJPSIB3MWNSSE4TGAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
CHO4U5JC3RNTLXVIDXXJYZMOBZJ4VXW2GVJWDOTBRKK3AJ36LDLQC
46MRRHVYJ3BS74R2BEVUWEWJCI4FCRBSLZ3NWMQCE6FUCNE5P73QC
KOSAQXB3KF6VAYTG2TOTHGJBB3F7AL6O32EMCXPVZJ5WHQX6XR5AC
25VJTGR3AWZJ7OHKYWS53VJ6TVMMU6UT7BA6V3QVVWDGI6FYKD7QC
E6JXIMRH2TX5WHZ6BT2QZ3EANM3PWCHCVYC4XWRJGRBDSP42X2RAC
2KTJHQUX2LTU2BCLS5YXVRRKMOYKKIZAPF2LBKORFGSHEN5IO3IAC
XDJGQNFELURGXMUAOOVALQMSLAJVHMG63KPKVA33HTJFVZROGZ4AC
DF5LOTJFSXOT7UBDHLBDLGA22OY4L7ZF6CVFP6H3KL4N5CHB5C5QC
4QMMF3ZQ37PVVPLFUCNA6VJAL6S7QCFJ4QBU6V7ZFJR4FRDH3R6QC
LY7DLLD7IKL6ZQPVROLDJ46XRM5CMAAEBRFDKJ4M53CPC5GFCGSQC
XI7X6SNTHG67D4NQWM75HWB6TVRXVFDPGNSQLTXN6JAZBZIVZXIQC
SN3VSV7G6NF6NLX5E47QJQQFDIPC2LQUUYGZMH6AWBF3FOUHXCBQC
VCG3BRIYRTNNWYC3LOXD6KFGXOX37HAFW2HNV7WXVG2V7EUHLDZQC
VBG2GGMVC66LQM4OSI67VKXGAQK4GVOEHX3OL6V3IFOO52MQL72QC
LDBTCT5WIPLJPZWXS2RUQ26QKISCUUTLO77M464WOE6VSYSNPKYAC
static unsigned char _lowest_rarity[NUM_SPELLS];
void init_spell_rarities()
{
for (int i = 0; i < NUM_SPELLS; i++)
_lowest_rarity[i] = 255;
for (int i = 0; i < NUM_BOOKS; i++)
{
const int rarity = book_rarity(i);
// Manuals, books of destruction, books only created as gifts
// from specific gods, and the unused Book of Healing.
if (rarity >= 20)
continue;
for (int j = 0; j < SPELLBOOK_SIZE; j++)
{
spell_type spell = which_spell_in_book(i, j);
if (spell == SPELL_NO_SPELL)
continue;
#ifdef DEBUG
int unsigned flags = get_spell_flags(spell);
if (flags & (SPFLAG_MONSTER | SPFLAG_TESTING))
{
item_def item;
item.base_type = OBJ_BOOKS;
item.sub_type = i;
end(1, false, "Spellbook '%s' contains invalid spell "
"'%s'",
item.name(DESC_PLAIN, false, true).c_str(),
spell_title(spell));
}
#endif
static void _get_spell_list(std::vector<spell_type> &spell_list, int level,
unsigned int disc1, unsigned int disc2,
god_type god, bool avoid_uncastable,
int &god_discard, int &uncastable_discard)
{
for (int i = 0; i < NUM_SPELLS; i++)
{
const spell_type spell = (spell_type) i;
if (!is_valid_spell(spell))
continue;
// Only use spells available in books you might find laying about
// the dungeon.
if (spell_rarity(spell) == -1)
continue;
const unsigned int disciplines = get_spell_disciplines(spell);
if (level != -1)
{
if (spell_difficulty(spell) != level)
continue;
}
else if (!((disciplines & disc1) || (disciplines & disc2))
|| disciplines_conflict(disc1, disciplines)
|| disciplines_conflict(disc2, disciplines))
{
continue;
}
// Only wizards gets spells still under development.
const unsigned int flags = get_spell_flags(spell);
if (flags & SPFLAG_DEVEL)
{
#ifdef WIZARD
if (!you.wizard)
continue;
#else
continue;
#endif
}
if (avoid_uncastable && undead_cannot_memorise(spell, you.is_undead))
{
uncastable_discard++;
continue;
}
if (god_dislikes_spell_type(spell, god))
{
god_discard++;
continue;
}
// Passed all tests.
spell_list.push_back(spell);
}
}
static void _get_spell_list(std::vector<spell_type> &spell_list,
unsigned int disc1, unsigned int disc2,
god_type god, bool avoid_uncastable,
int &god_discard, int &uncastable_discard)
{
_get_spell_list(spell_list, -1, disc1, disc2,
god, avoid_uncastable, god_discard, uncastable_discard);
}
static void _get_spell_list(std::vector<spell_type> &spell_list, int level,
god_type god, bool avoid_uncastable,
int &god_discard, int &uncastable_discard)
{
_get_spell_list(spell_list, level, SPTYP_NONE, SPTYP_NONE,
god, avoid_uncastable, god_discard, uncastable_discard);
}
for (int i = 0; i < NUM_SPELLS; i++)
{
const spell_type spell = (spell_type) i;
if (!is_valid_spell(spell))
continue;
if (spell_difficulty(spell) != level)
continue;
const unsigned int flags = get_spell_flags(spell);
const unsigned int schools = get_spell_disciplines(spell);
// Don't include schoolless spells, like Smiting.
if (schools == 0)
continue;
// Holy spells don't show up in books.
if (schools & SPTYP_HOLY)
continue;
if (flags & (SPFLAG_MONSTER | SPFLAG_CARD | SPFLAG_TESTING))
continue;
// Only wizards gets spells still under development.
if (flags & SPFLAG_DEVEL)
{
#ifdef WIZARD
if (!you.wizard)
continue;
#else
continue;
#endif
}
if (avoid_uncastable && undead_cannot_memorise(spell, you.is_undead))
{
uncastable_discard++;
continue;
}
if (god_dislikes_spell_type(spell, god))
{
god_discard++;
continue;
}
// Passed all tests.
spell_list.push_back(spell);
}
_get_spell_list(spell_list, level, god, avoid_uncastable,
god_discard, uncastable_discard);
bool make_book_theme_randart(item_def &book, int disc1, int disc2,
int num_spells, int max_levels)
{
ASSERT(book.base_type == OBJ_BOOKS);
ASSERT(book.book_number() != BOOK_MANUAL
&& book.book_number() != BOOK_DESTRUCTION);
ASSERT(is_random_artefact(book));
ASSERT(!book.props.exists(SPELL_LIST_KEY));
god_type god;
(void) origin_is_god_gift(book, &god);
const bool avoid_uncastable = (god != GOD_NO_GOD && god != GOD_XOM)
|| origin_is_acquirement(book);
if (num_spells == -1)
num_spells = SPELLBOOK_SIZE;
ASSERT(num_spells > 0 && num_spells <= SPELLBOOK_SIZE);
if (max_levels == -1)
max_levels = INT_MAX;
ASSERT(max_levels > 0);
if (disc1 == 0 && disc2 == 0)
{
// Eliminate disciplines that the god disapproves of or from which
// all spells are discarded.
std::vector<int> ok_discs;
for (int i = 0; i < SPTYP_LAST_EXPONENT; i++)
{
int disc = 1 << i;
if (god_dislikes_spell_discipline(disc, god))
continue;
int junk1 = 0, junk2 = 0;
std::vector<spell_type> spell_list;
_get_spell_list(spell_list, disc, disc, god, avoid_uncastable,
junk1, junk2);
if(spell_list.empty())
continue;
ok_discs.push_back(i);
}
ASSERT( !ok_discs.empty() );
if (ok_discs.empty())
{
mpr("No valid disciplines with which to make a themed ranadart "
"spellbook.", MSGCH_ERROR);
return (false);
}
do
{
disc1 = 1 << ok_discs[random2(ok_discs.size())];
disc2 = 1 << ok_discs[random2(ok_discs.size())];
} while(disciplines_conflict(disc1, disc2));
}
else if (disc2 == 0)
disc2 = disc1;
ASSERT(disc1 < (1 << (SPTYP_LAST_EXPONENT + 1)));
ASSERT(disc2 < (1 << (SPTYP_LAST_EXPONENT + 1)));
ASSERT(count_bits(disc1) == 1 && count_bits(disc2) == 1);
int god_discard = 0;
int uncastable_discard = 0;
std::vector<spell_type> spell_list;
_get_spell_list(spell_list, disc1, disc2, god, avoid_uncastable,
god_discard, uncastable_discard);
if (num_spells > (int) spell_list.size())
num_spells = spell_list.size();
std::vector<bool> spell_used(spell_list.size(), false);
spell_type chosen_spells[SPELLBOOK_SIZE];
for (int i = 0; i < SPELLBOOK_SIZE; i++)
{
chosen_spells[i] = SPELL_NO_SPELL;
}
int book_pos = 0;
while (book_pos < num_spells && max_levels > 0)
{
int spell_pos = random2(spell_list.size());
if (spell_used[spell_pos])
continue;
spell_type spell = spell_list[spell_pos];
ASSERT(spell != SPELL_NO_SPELL);
int spell_level = spell_difficulty(spell);
if (spell_level > max_levels)
continue;
spell_used[spell_pos] = true;
chosen_spells[book_pos++] = spell;
max_levels -= spell_level;
}
std::sort(chosen_spells, chosen_spells + SPELLBOOK_SIZE,
_compare_spells);
ASSERT(chosen_spells[0] != SPELL_NO_SPELL);
CrawlHashTable &props = book.props;
props[SPELL_LIST_KEY].new_vector(SV_LONG).resize(SPELLBOOK_SIZE);
CrawlVector &spell_vec = props[SPELL_LIST_KEY];
spell_vec.set_max_size(SPELLBOOK_SIZE);
for (int i = 0; i < SPELLBOOK_SIZE; i++)
spell_vec[i] = (long) chosen_spells[i];
std::string name;
if (god != GOD_NO_GOD)
{
name = '"';
name += god_name(god, false) + "'s book";
}
name += " of ";
name += spelltype_long_name(disc1);
if (disc1 != disc2)
{
name += " and ";
name += spelltype_long_name(disc2);
}
if (god != GOD_NO_GOD)
name += '"';
set_randart_name(book, name);
return (true);
}
bool book_has_title(const item_def &book)
{
ASSERT(book.base_type == OBJ_BOOKS);
if (!is_artefact(book))
return (false);
return (get_artefact_name(book)[0] == '"');
}
{
int rarity = 0;
if (is_random_artefact(item))
{
// Consider spellbook as rare as its rarest spell.
// NOTE: This probably undervalues a book if it contains
// lots of rare spells.
for (int i = 0; i < SPELLBOOK_SIZE; i++)
{
spell_type spell = which_spell_in_book(item, i);
if (spell == SPELL_NO_SPELL)
continue;
if (rarity > spell_rarity(spell))
rarity = spell_rarity(spell);
}
}
else
rarity = book_rarity(item.sub_type);
/* ***********************************************************************
* called from: spl-book
* *********************************************************************** */
void set_randart_name( item_def &item, const std::string &name );
void set_randart_appearance( item_def &item, const std::string &appear );
}
void set_randart_name( item_def &item, const std::string &name )
{
ASSERT( is_random_artefact( item ));
ASSERT( !name.empty() );
item.props[RANDART_NAME_KEY].get_string() = name;
}
void set_randart_appearance( item_def &item, const std::string &appear )
{
ASSERT( is_random_artefact( item ));
ASSERT( !appear.empty() );
item.props[RANDART_APPEAR_KEY].get_string() = appear;
ASSERT(!item.props.exists( RANDART_NAME_KEY ));
item.props[RANDART_NAME_KEY].get_string() = artefact_name(item, false);
if (item.props.exists( RANDART_NAME_KEY ))
ASSERT(item.props[RANDART_NAME_KEY].get_type() == SV_STR);
else
item.props[RANDART_NAME_KEY].get_string() = artefact_name(item, false);
ASSERT(!item.props.exists( RANDART_APPEAR_KEY ));
item.props[RANDART_APPEAR_KEY].get_string() = artefact_name(item, true);
if (item.props.exists( RANDART_APPEAR_KEY ))
ASSERT(item.props[RANDART_APPEAR_KEY].get_type() == SV_STR);
else
item.props[RANDART_APPEAR_KEY].get_string() =
artefact_name(item, true);
mpr("Fake item as gift from which god (ENTER to leave alone): ",
MSGCH_PROMPT);
char name[80];
if (!cancelable_get_line( name, sizeof( name ) ) && name[0])
{
god_type god = string_to_god(name, false);
if (god == GOD_NO_GOD)
mpr("No such god, leaving item origin alone.");
else
{
mprf("God gift of %s.", god_name(god, false).c_str());
item.orig_monnum = -god;
}
}