Re-arranged book_type so that books you might find on the floor come first, then books only given out by certain gods, and so on. Added book types BOOK_RANDART_LEVEL, BOOK_RANDART_THEME and BOOK_CARD_EFFECT.
Can now get randart books both from acquirement and shops/floor. Acquirement books have a chance of being a manual with a spell discipline skill.
Randart books have their own appearances now, and fixed level books their own naming scheme. Needs more entries.
Randart books aren't hilited in the menu like other randarts are; don't know why.
Added some assertions to choose_random_weighted().
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7761 c06c8d41-db1a-0410-9941-cceddc491573
CCRQESB4ADT4WA7FGLNZZXAJ6G5QMCTYCZIWORBN45P6ZPILC34AC
OMUS66AHB2COOG2U4LPC5O6QMDOGA53V4UGAHITGTFDDS744AD7QC
AP62FIMTAYB6TLR7EHW3MFH4I6JWPYMFP3RT3GLD5P3SOCNCZKAAC
6F6OFJCUOBUP7QTVWSMSQPW25RLVRYVXO3VO5GLMFRY6K5RMOWFAC
I2B33Z7NZGC33AMDSSK446AZZYWKPHWLAGULVHKKZU4MVB4BNJOAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
UKN6HTZXDUUOWKNWNKWPHKGUGL474JIAQN5JU3DM3DU26WGMNP4AC
IIN7AVA6JYRBXH6ZYRR7BY7TV6PW7ANAQ2A3PD55FKBKKQFEEF2AC
QO5ZJWQ3JK3PEGBPTQSAYIPEJEHG2M2KTD74227G5VG7DVXUL3BQC
BSI5DB3LVY42ZHOS46X2CAPPVOSOTTQWFGLTMAKRFTROI5BQWFDQC
FWNNTOEERPUKXPE4OC52UABFZLKIU3O5GRNNLDK4QI4HR2IOU36QC
46MRRHVYJ3BS74R2BEVUWEWJCI4FCRBSLZ3NWMQCE6FUCNE5P73QC
XI7X6SNTHG67D4NQWM75HWB6TVRXVFDPGNSQLTXN6JAZBZIVZXIQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
DTO3EUKWHZ5RJNGNCFYXSOVTIPVXPP637F2W7WFGYKJ7JK7VNKNQC
TZ2SH2SPO5TFSFL2OCV5A32P2X4CRHEKEK7ENUFCBVF3RKUWAD4AC
Q3DNEB5OOJ34P5ML4CMK3L6SCP7RLW7DDOZEG24KZBX3C7BJRQDAC
7YUGK5Q64KG5O7GJGTUBRRLHAHBCJ5YOE23YUPT6UBKUSB67CYAQC
RNJX2RDBDA62DSAUIWVVPFS7YNIU3GEOXDWJUABDS5DS5QGS3LAQC
VNHFP63ZLLZU3A3PLXP4BITX57DUIYDHFOHQYK3BOBHV3S64G26QC
BW3XFNOS6LDAQLHOZ6RXARCMKCY5JVLVDSXDSSAX4DSYM3FANQBAC
5FHWTG7M6FW4B3I33YI7QSM3OZIB6ZGC6TI6JISSLY5Y43HI56VAC
JCWJWGMQIKQGSSFJUQRKNIWW3HBOJSHYDTOPPE5BWOJTIJTDYUTAC
PR42BCP5BPRFD2MP5H6CIJP7E57Q6TKL6SOXZWFKMFVR2OZWHT7AC
K33CV7EYR37TTSEXQWQ6QPHSEUFO545AIPUZOA2C47QTUCUWFPAAC
WFMQVPMMOPG5SBJD5LUBOIYWRMXVWK3FXENK7SAEGZ5T6XWFKERQC
NQXYTPHC5ZBLJDIC5OVAUIDYYOXLPSGWUVD464ODH4KBHYGRDPPAC
Z3QC4SVHJ6DOPJBXBILBUKAMMZU4MR3BUGFYHNESRVHADM3CFYEAC
5UVDIVD4NSXA52U4QMQIVST3GSZJ2A2YZK3RUEXKPM43YVQ7LI5AC
4FQAKUKUO6PCAZ3N4HUR5XL6E4VA5UQUZ3AEDGRBLVY7W2LMWI7QC
FIYBXLWALQINNQTHG2KNDUUTAQAZRDDLXW2XOVSKDKBADJ3XCJ4AC
3KCTZBUKYPJXQPPNQZHA4YYZN36ZTUSKA7NY3VR7OR35TDSXMCXQC
52XHD5LKS6UVLXBYUXMPTMVMTXQ6FBUFXJ2TAW6R7CSJY7OXWVJAC
KQNIGKATHT4YSPJFPJGIGPD6VNR5B753SE2JN2LCXZZJNHCGY3DQC
JJVROJMJVKS7VN5HJNB4ZNCG7Y6EMHJQZR3MDN2VZCFTZC73HOZQC
SWT4O2TCOAQOVFA6WRA7MCU3KMTMJWFEMIHO64N4PWL5FNHDPADAC
LOAF3UOCD7BGVGFWA5IPJT55URGZSKJJDMUIP6CN2VVWM5UHWTBQC
HNPSSHGZFQ3E2I6X6VTKZ3WBBM2G25P2D7SIL2SZYKV2CCEA2ADAC
VMIKJGB6CSFVZS6VMQNP33ALEDEO2TARDICVGDJFMZ4WSPTV3LFAC
VCG3BRIYRTNNWYC3LOXD6KFGXOX37HAFW2HNV7WXVG2V7EUHLDZQC
I7QLYOTE6DLQZM7YWUWYLKHRJRB2A3STQ42ALSRGQICEWKD2QTEQC
EJKHYV2Z6UPRVYUAL4WRW33GBNHYBFPMPA57HMBX2LQKXHIUO5VQC
KNO4TZR76DMOYJCF24PSVQW7FUZOTMOJTL7I7J74SM4IHOGDX6TAC
Y2NYY7HWFZ2LQDK3ACSLGS37F2J2IJ5LRGCIMZYXLEOSVPD3A4DAC
int max_level =
completely_random ? 9 : std::min(9, you.get_experience_level());
level = random_range(1, max_level);
// Give a book of a level not seen before, preferably one with
// spells of a low enough level for the player to cast, or the
// lowest aviable level if all levels which the player can cast
// have already been given.
if (track_levels_had)
// Stuf parameters into book.plus and book.plus2, then call
// make_item_randart(), which will call us back.
if (level == -1)
unsigned int seen_levels = you.attribute[ATTR_RND_LVL_BOOKS];
std::vector<int> vec;
for (int i = 1; i <= 9 && (vec.empty() || i <= max_level); i++)
{
if (!(seen_levels & (1 << i)))
vec.push_back(i);
}
if (vec.size() > 0)
level = vec[random2(vec.size())];
int max_level =
completely_random ? 9 : std::min(9, you.get_experience_level());
level = random_range(1, max_level);
if (num_spells == -1)
num_spells = SPELLBOOK_SIZE;
if (!is_random_artefact(book))
{
// Stuff parameters into book.plus and book.plus2, then call
// make_item_randart(), which will then call us back.
if (num_spells == -1)
num_spells = SPELLBOOK_SIZE;
ASSERT(num_spells > 0 && num_spells <= SPELLBOOK_SIZE);
if (max_levels == -1)
max_levels = 255;
if (disc1 == 0 && disc2 == 0)
{
if (!_get_weighted_discs(completely_random, god, disc1, disc2))
return (false);
}
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 disc1_pos = 0, disc2_pos = 0;
for (int i = 0; i <= SPTYP_LAST_EXPONENT; i++)
{
if (disc1 & (1 << i))
disc1_pos = i;
if (disc2 & (1 << i))
disc2_pos = i;
}
book.plus = num_spells | (max_levels << 8);
book.plus2 = disc1_pos | (disc2_pos << 8);
book.sub_type = BOOK_RANDART_THEME;
return (make_item_randart(book));
}
// We're being called from make_item_randart()
ASSERT(book.sub_type == BOOK_RANDART_THEME);
ASSERT(disc1 == 0 && disc2 == 0);
ASSERT(num_spells == -1 && max_levels == -1);
num_spells = book.plus & 0xFF;
if (disc1 == 0 && disc2 == 0)
{
if (!_get_weighted_discs(completely_random, god, disc1, disc2))
return (false);
}
else if (disc2 == 0)
disc2 = disc1;
int disc1_pos = book.plus2 & 0xFF;
ASSERT(disc1_pos <= SPTYP_LAST_EXPONENT);
disc1 = 1 << disc1_pos;
ASSERT(disc1 < (1 << (SPTYP_LAST_EXPONENT + 1)));
ASSERT(disc2 < (1 << (SPTYP_LAST_EXPONENT + 1)));
ASSERT(count_bits(disc1) == 1 && count_bits(disc2) == 1);
int disc2_pos = (book.plus2 >> 8) & 0xFF;
ASSERT(disc2_pos <= SPTYP_LAST_EXPONENT);
disc2 = 1 << disc2_pos;
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);
}
return make_book_theme_randart(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);
}
// Manuals and books of destruction are rare enough without replacing
// them with randart books.
if (item.sub_type == BOOK_MANUAL || item.sub_type == BOOK_DESTRUCTION)
return;
// Only randomly generate randart books for OBJ_RANDOM, since randart
// spellbooks aren't merely of-the-same-type-but-better, but
// have an entirely different set of spells.
if (allow_uniques && item_level > 2 && force_type == OBJ_RANDOM
&& x_chance_in_y(101 + item_level * 3, 4000))
{
// Same relative weights as acquirement
int choice = random_choose_weighted(
55, BOOK_RANDART_THEME,
12, BOOK_RANDART_LEVEL,
0);
item.sub_type = choice;
if (item.sub_type == BOOK_RANDART_THEME)
make_book_theme_randart(item, 0, 0, 7, 25);
else if (item.sub_type == BOOK_RANDART_LEVEL)
{
int max_level = std::min( 9, std::max(1, item_level / 3) );
int spell_level = random_range(1, max_level);
int num_spells = 7 - (spell_level + 1) / 2 + random_range(1, 2);
make_book_level_randart(item, spell_level, num_spells);
}
if (know_type)
{
if (book_has_title(*this))
buff << get_artefact_name(*this);
else
buff << "book " << get_artefact_name(*this);
}
else
buff << get_artefact_name(*this) << "book";
buff << get_artefact_name(*this);
if (!know_type)
buff << "book";
MAX_NORMAL_BOOK = BOOK_STALKING,
MIN_GOD_ONLY_BOOK, // 43
BOOK_ANNIHILATIONS = MIN_GOD_ONLY_BOOK, // 43
BOOK_DEMONOLOGY,
BOOK_NECRONOMICON, // 45
MAX_GOD_ONLY_BOOK = BOOK_NECRONOMICON,
MAX_FIXED_BOOK = MAX_GOD_ONLY_BOOK,
BOOK_RANDART_LEVEL, // 46
BOOK_RANDART_THEME,
BOOK_CARD_EFFECT,
MAX_MEMORISABLE_BOOK = BOOK_CARD_EFFECT,
BOOK_MANUAL,
BOOK_DESTRUCTION, // 50
while (book_rarity(type_wanted) == 100
|| type_wanted == BOOK_DESTRUCTION
|| type_wanted == BOOK_MANUAL)
{
type_wanted = random2(NUM_BOOKS);
}
while (book_rarity(type_wanted) == 100)
type_wanted = random2(NUM_NORMAL_BOOKS);
static void _do_book_acquirement(item_def &book, int agent)
{
// items() shouldn't make book a randart for acquirement items.
ASSERT(!is_random_artefact(book));
// Non-normal books are rare enough without turning them into
// randart books.
if (book.sub_type > MAX_NORMAL_BOOK)
return;
int level = (you.skills[SK_SPELLCASTING] + 2) / 3;
unsigned int seen_levels = you.attribute[ATTR_RND_LVL_BOOKS];
if (agent == GOD_XOM)
level = random_range(1, 9);
else if (seen_levels & (1 << level))
{
// Give a book of a level not seen before, preferably one with
// spells of a low enough level for the player to cast, or the
// lowest aviable level if all levels which the player can cast
// have already been given.
int max_level = std::min(9, you.get_experience_level());
std::vector<int> vec;
for (int i = 1; i <= 9 && (vec.empty() || i <= max_level); i++)
{
if (!(seen_levels & (1 << i)))
vec.push_back(i);
}
if (vec.size() > 0)
level = vec[random2(vec.size())];
else
level = -1;
}
int choice = random_choose_weighted(
55, 0, // fixed themed
24, 1, // leave alone
level == -1 ? 0 : 12, 2, // fixed level
agent == GOD_XOM ? 0 : 6, 3, // manual (too useful for Xom)
0);
switch(choice)
{
case 0:
make_book_theme_randart(book, 0, 0, 7, 25);
break;
case 1:
// Leave alone
break;
case 2:
{
int num_spells = 7 - (level + 1) / 2 + random_range(1, 2);
make_book_level_randart(book, level, num_spells);
break;
}
// Spell discipline manual
case 3:
{
int weights[SK_POISON_MAGIC - SK_CONJURATIONS + 1];
for (int i = SK_CONJURATIONS; i <= SK_POISON_MAGIC; i++)
{
int w = std::max(0, 24 - you.skills[i])
* 100 / species_skills(i, you.species);
weights[i - SK_CONJURATIONS] = w;
}
int skill = choose_random_weighted(weights,
weights + ARRAYSZ(weights));
skill += SK_CONJURATIONS;
book.sub_type = BOOK_MANUAL;
book.plus = skill;
// Set number of reads possible before it "crumbles to dust".
book.plus2 = 3 + random2(15);
break;
}
default:
ASSERT(false);
}
}
book_noun
Book
Tome
Grimoir
Almanach
Guide
Volume
Compendium
Handbook
%%%%
book_adjective
glistering
levitating
droning
conspicuous
inconspicuous
%%%%
book_magic
Magic
Casting
Wizardry
Bewitchment
Sorcery
%%%%
level book
"@book_magic@ 101"
"Easy @book_magic@"
"@book_magic@ in Simple Steps"
"Thorough Guide to @book_magic@"
"Last Secrets of @book_magic@"
%%%%
# Book name should be set in make_book_level_randart() or
# make_book_theme_randart(), so if this gets picked there's a bug.
}
static bool _make_book_randart(item_def &book)
{
char type;
do
{
mpr("Make book fixed [t]heme or fixed [l]evel? ", MSGCH_PROMPT);
type = tolower(getch());
} while (type != 't' && type != 'l');
if (type == 'l')
return make_book_level_randart(book);
else
return make_book_theme_randart(book);
if (!make_item_randart( item ))
if (item.base_type == OBJ_BOOKS)
{
if (!_make_book_randart(item))
{
mpr("Failed to turn book into randart.");
break;
}
}
else if (!make_item_randart( item ))