summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/acr.cc28
-rw-r--r--crawl-ref/source/command.cc2
-rw-r--r--crawl-ref/source/dat/database/randname.txt62
-rw-r--r--crawl-ref/source/dat/descript/items.txt15
-rw-r--r--crawl-ref/source/debug.cc6
-rw-r--r--crawl-ref/source/effects.cc121
-rw-r--r--crawl-ref/source/enum.h73
-rw-r--r--crawl-ref/source/externs.h4
-rw-r--r--crawl-ref/source/invent.cc3
-rw-r--r--crawl-ref/source/itemname.cc15
-rw-r--r--crawl-ref/source/makeitem.cc46
-rw-r--r--crawl-ref/source/mapdef.cc18
-rw-r--r--crawl-ref/source/randart.cc66
-rw-r--r--crawl-ref/source/religion.cc2
-rw-r--r--crawl-ref/source/spl-book.cc168
-rw-r--r--crawl-ref/source/stuff.h8
-rw-r--r--crawl-ref/source/tags.cc4
17 files changed, 505 insertions, 136 deletions
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index 6b40481dd6..c896fceb19 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -433,6 +433,22 @@ static bool _item_type_can_be_artefact( int type)
|| type == OBJ_BOOKS);
}
+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);
+}
+
static void _do_wizard_command(int wiz_command, bool silent_fail)
{
ASSERT(you.wizard);
@@ -567,8 +583,16 @@ static void _do_wizard_command(int wiz_command, bool silent_fail)
item.orig_monnum = -god;
}
}
-
- 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 ))
{
mpr("Failed to turn item into randart.");
break;
diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc
index cdc00ca7cf..db48b3ce26 100644
--- a/crawl-ref/source/command.cc
+++ b/crawl-ref/source/command.cc
@@ -1196,7 +1196,7 @@ static bool _append_books(std::string &desc, item_def &item, std::string key)
std::vector<std::string> rods;
item.base_type = OBJ_BOOKS;
- for (int i = 0; i < NUM_BOOKS; i++)
+ for (int i = 0; i < NUM_FIXED_BOOKS; i++)
for (int j = 0; j < 8; j++)
if (which_spell_in_book(i, j) == type)
{
diff --git a/crawl-ref/source/dat/database/randname.txt b/crawl-ref/source/dat/database/randname.txt
index 0e8dd2fbf2..bac648890f 100644
--- a/crawl-ref/source/dat/database/randname.txt
+++ b/crawl-ref/source/dat/database/randname.txt
@@ -801,13 +801,69 @@ amulet appearance
@jewellery appearance@ @amulet material@
%%%%
######################################################
-# Books (NOTE: just a placeholder for now)
+# Books
######################################################
+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.
book
-@armour@
+Book of Bugginess
%%%%
book appearance
-@armour appearance@
+@book_adjective@
%%%%
diff --git a/crawl-ref/source/dat/descript/items.txt b/crawl-ref/source/dat/descript/items.txt
index 22570b2772..ac591095b6 100644
--- a/crawl-ref/source/dat/descript/items.txt
+++ b/crawl-ref/source/dat/descript/items.txt
@@ -183,6 +183,11 @@ book of changes
A book of magic spells. Beware, for some of the more powerful
grimoires are not to be toyed with.
%%%%
+book of card effects
+
+A book of magic spells. Beware, for some of the more powerful
+grimoires are not to be toyed with.
+%%%%
book of charms
A book of magic spells. Beware, for some of the more powerful
@@ -228,6 +233,16 @@ book of envenomations
A book of magic spells. Beware, for some of the more powerful
grimoires are not to be toyed with.
%%%%
+book of fixed level
+
+A book of magic spells. Beware, for some of the more powerful
+grimoires are not to be toyed with.
+%%%%
+book of fixed theme
+
+A book of magic spells. Beware, for some of the more powerful
+grimoires are not to be toyed with.
+%%%%
book of fire
A book of magic spells. Beware, for some of the more powerful
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index b5c1a9b113..563b08c752 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -1121,7 +1121,7 @@ static bool _book_from_spell(const char* specs, item_def &item)
if (type == SPELL_NO_SPELL)
return (false);
- for (int i = 0; i < NUM_BOOKS; i++)
+ for (int i = 0; i < NUM_FIXED_BOOKS; i++)
for (int j = 0; j < 8; j++)
if (which_spell_in_book(i, j) == type)
{
@@ -1512,6 +1512,10 @@ bool get_item_by_name(item_def *item, char* specs,
else
mpr( "Sorry, no books on that skill today." );
}
+ else if (type_wanted == BOOK_RANDART_THEME)
+ make_book_theme_randart(*item);
+ else if (type_wanted == BOOK_RANDART_LEVEL)
+ make_book_level_randart(*item);
break;
case OBJ_WANDS:
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index fdc3f628a7..0fd397660d 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -1262,11 +1262,11 @@ static int _find_acquirement_subtype(object_class_type class_wanted,
}
// If we don't have a book, try and get a new one.
- if (type_wanted == NUM_BOOKS)
+ if (type_wanted > MAX_FIXED_BOOK)
{
do
{
- type_wanted = random2(NUM_BOOKS);
+ type_wanted = random2(NUM_NORMAL_BOOKS);
if (one_chance_in(500))
break;
}
@@ -1274,12 +1274,8 @@ static int _find_acquirement_subtype(object_class_type class_wanted,
}
// If the book is invalid find any valid one.
- 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);
break;
case OBJ_STAVES:
@@ -1421,6 +1417,93 @@ static int _find_acquirement_subtype(object_class_type class_wanted,
return (type_wanted);
}
+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);
+ }
+}
+
bool acquirement(object_class_type class_wanted, int agent,
bool quiet, int* item_index)
{
@@ -1450,17 +1533,26 @@ bool acquirement(object_class_type class_wanted, int agent,
case 'f': class_wanted = OBJ_FOOD; break;
case 'g': class_wanted = OBJ_MISCELLANY; break;
case 'h': class_wanted = OBJ_GOLD; break;
- default: break;
+ default:
+ // Lets wizards escape out of accidently choosing acquirement.
+ if (agent == AQ_WIZMODE)
+ {
+ canned_msg(MSG_OK);
+ return (false);
+ }
+ break;
}
}
+ const bool god_agent = agent > GOD_NO_GOD && agent < NUM_GODS;
+
if (grid_destroys_items(grd(you.pos())))
{
// How sad (and stupid).
if (!silenced(you.pos()) && !quiet)
mprf(MSGCH_SOUND, grid_item_destruction_message(grd(you.pos())));
- if (agent > GOD_NO_GOD && agent < NUM_GODS)
+ if (god_agent)
{
if (agent == GOD_XOM)
simple_god_message(" snickers.", GOD_XOM);
@@ -1487,7 +1579,11 @@ bool acquirement(object_class_type class_wanted, int agent,
if (you.species == SP_VAMPIRE && class_wanted == OBJ_FOOD)
class_wanted = OBJ_POTIONS;
- thing_created = items( 1, class_wanted, type_wanted, true,
+ // Don't generate randart books in items(), we do that
+ // ourselves.
+ int want_arts = class_wanted == OBJ_BOOKS ? 0 : 1;
+
+ thing_created = items( want_arts, class_wanted, type_wanted, true,
MAKE_GOOD_ITEM, MAKE_ITEM_RANDOM_RACE,
0, 0, agent );
@@ -1577,7 +1673,10 @@ bool acquirement(object_class_type class_wanted, int agent,
do_uncurse_item(thing);
if (thing.base_type == OBJ_BOOKS)
+ {
+ _do_book_acquirement(thing, agent);
mark_had_book(thing);
+ }
else if (thing.base_type == OBJ_JEWELLERY)
{
switch (thing.sub_type)
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 6ed738ef2f..dbc544072f 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -183,76 +183,78 @@ enum quiver_type
enum beam_type // beam[].flavour
{
- BEAM_NONE,
+ BEAM_NONE, // 0
BEAM_MISSILE,
BEAM_MMISSILE, // and similarly irresistible things
BEAM_FIRE,
BEAM_COLD,
- BEAM_MAGIC,
+ BEAM_MAGIC, // 5
BEAM_ELECTRICITY,
BEAM_POISON,
BEAM_NEG,
BEAM_ACID,
- BEAM_MIASMA,
+ BEAM_MIASMA, // 10
BEAM_SPORE,
BEAM_POISON_ARROW,
BEAM_HELLFIRE,
BEAM_NAPALM,
- BEAM_STEAM,
+ BEAM_STEAM, // 15
BEAM_HELLFROST,
BEAM_ENERGY,
BEAM_HOLY,
BEAM_FRAG,
- BEAM_LAVA,
+ BEAM_LAVA, // 20
BEAM_ICE,
BEAM_NUKE,
- BEAM_RANDOM, // 25 - currently translates into FIRE..ACID
+ BEAM_RANDOM, // currently translates into FIRE..ACID
+ // 24
// Enchantments
- BEAM_FIRST_ENCHANTMENT,
+ BEAM_FIRST_ENCHANTMENT = 25, // 25
BEAM_SLOW = BEAM_FIRST_ENCHANTMENT,
BEAM_HASTE,
BEAM_HEALING,
BEAM_PARALYSIS,
BEAM_CONFUSION,
- BEAM_INVISIBILITY,
+ BEAM_INVISIBILITY, // 30
BEAM_DIGGING,
BEAM_TELEPORT,
BEAM_POLYMORPH,
BEAM_CHARM,
- BEAM_BANISH,
+ BEAM_BANISH, // 35
BEAM_DEGENERATE,
BEAM_ENSLAVE_UNDEAD,
BEAM_ENSLAVE_SOUL,
BEAM_PAIN,
- BEAM_DISPEL_UNDEAD,
+ BEAM_DISPEL_UNDEAD, // 40
BEAM_DISINTEGRATION,
BEAM_ENSLAVE_DEMON,
BEAM_BLINK,
BEAM_PETRIFY,
- BEAM_BACKLIGHT,
+ BEAM_BACKLIGHT, // 45
BEAM_SLEEP,
BEAM_LAST_ENCHANTMENT = BEAM_SLEEP,
// new beams for evaporate
- BEAM_POTION_STINKING_CLOUD,
+ BEAM_POTION_STINKING_CLOUD, // 47
BEAM_POTION_POISON,
BEAM_POTION_MIASMA,
- BEAM_POTION_STEAM,
+ BEAM_POTION_STEAM, // 50
BEAM_POTION_FIRE,
BEAM_POTION_COLD,
BEAM_POTION_BLACK_SMOKE,
BEAM_POTION_GREY_SMOKE,
- BEAM_POTION_BLUE_SMOKE,
+ BEAM_POTION_BLUE_SMOKE, // 55
BEAM_POTION_PURP_SMOKE,
BEAM_POTION_RANDOM,
BEAM_TORMENT_DAMAGE, // Pseudo-beam for damage flavour.
BEAM_STEAL_FOOD, // Pseudo-beam for harpyes stealing food.
- BEAM_LINE_OF_SIGHT // only used for checking monster LOS
+ BEAM_LINE_OF_SIGHT, // 60 - only used for checking monster LOS
+ NUM_BEAMS
};
enum book_type
@@ -281,33 +283,50 @@ enum book_type
BOOK_CLOUDS,
BOOK_HEALING, // XXX: not used
BOOK_NECROMANCY,
- BOOK_NECRONOMICON,
- BOOK_CALLINGS, // 25
- BOOK_CHARMS,
- BOOK_DEMONOLOGY,
+ BOOK_CALLINGS,
+ BOOK_CHARMS, // 25
BOOK_AIR,
BOOK_SKY,
- BOOK_DIVINATIONS, // 30
+ BOOK_DIVINATIONS,
BOOK_WARP,
- BOOK_ENVENOMATIONS,
- BOOK_ANNIHILATIONS,
+ BOOK_ENVENOMATIONS, // 30
BOOK_UNLIFE,
- BOOK_DESTRUCTION, // 35
BOOK_CONTROL,
BOOK_MUTATIONS,
BOOK_TUKIMA,
- BOOK_GEOMANCY,
- BOOK_EARTH, // 40
- BOOK_MANUAL,
+ BOOK_GEOMANCY, // 35
+ BOOK_EARTH,
BOOK_WIZARDRY,
BOOK_POWER,
BOOK_CANTRIPS, //jmf: 04jan2000
- BOOK_PARTY_TRICKS, // 45 //jmf: 04jan2000
+ BOOK_PARTY_TRICKS, // 40 //jmf: 04jan2000
BOOK_BEASTS,
BOOK_STALKING, // renamed -- assassination was confusing -- bwr
+ 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
NUM_BOOKS
};
+#define NUM_NORMAL_BOOKS (MAX_NORMAL_BOOK + 1)
+#define NUM_FIXED_BOOKS (MAX_FIXED_BOOK + 1)
+#define NUM_MEMORISABLE_BOOK (MAX_MEMORISABLE_BOOK + 1)
+
enum branch_type // you.where_are_you
{
BRANCH_MAIN_DUNGEON, // 0
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index 08114ea0f0..fe7983ae6a 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -876,8 +876,8 @@ public:
FixedVector<unsigned char, NUM_MUTATIONS> demon_pow;
unsigned char magic_contamination;
- FixedVector<bool, NUM_BOOKS> had_book;
- FixedVector<bool, NUM_SPELLS> seen_spell;
+ FixedVector<bool, NUM_FIXED_BOOKS> had_book;
+ FixedVector<bool, NUM_SPELLS> seen_spell;
unsigned char normal_vision; // how far the species gets to see
unsigned char current_vision; // current sight radius (cells)
diff --git a/crawl-ref/source/invent.cc b/crawl-ref/source/invent.cc
index 7543eac61b..c49792734d 100644
--- a/crawl-ref/source/invent.cc
+++ b/crawl-ref/source/invent.cc
@@ -116,7 +116,8 @@ const bool InvEntry::is_item_glowing() const
|| (is_artefact(*item)
&& (item->base_type == OBJ_WEAPONS
|| item->base_type == OBJ_MISSILES
- || item->base_type == OBJ_ARMOUR))));
+ || item->base_type == OBJ_ARMOUR
+ || item->base_type == OBJ_BOOKS))));
}
const bool InvEntry::is_item_ego() const
diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc
index 6e81c530ea..fdddccfe4e 100644
--- a/crawl-ref/source/itemname.cc
+++ b/crawl-ref/source/itemname.cc
@@ -841,6 +841,9 @@ static const char* book_type_name(int booktype)
case BOOK_CANTRIPS: return "Cantrips";
case BOOK_PARTY_TRICKS: return "Party Tricks";
case BOOK_STALKING: return "Stalking";
+ case BOOK_RANDART_LEVEL: return "Fixed Level";
+ case BOOK_RANDART_THEME: return "Fixed Theme";
+ case BOOK_CARD_EFFECT: return "Card Effects";
default: return "Bugginess";
}
}
@@ -1476,15 +1479,9 @@ std::string item_def::name_aux( description_level_type desc,
case OBJ_BOOKS:
if (is_random_artefact( *this ) && !dbname && !basename)
{
- 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";
break;
}
if (basename)
diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc
index 80f2b20771..00bb475f0d 100644
--- a/crawl-ref/source/makeitem.cc
+++ b/crawl-ref/source/makeitem.cc
@@ -2397,8 +2397,8 @@ static void _generate_scroll_item(item_def& item, int force_type,
item.plus = 0;
}
-static void _generate_book_item(item_def& item, int force_type,
- int item_level)
+static void _generate_book_item(item_def& item, int allow_uniques,
+ int force_type, int item_level)
{
// determine special (description)
item.special = random2(5);
@@ -2412,11 +2412,9 @@ static void _generate_book_item(item_def& item, int force_type,
{
do
{
- item.sub_type = random2(NUM_BOOKS);
+ item.sub_type = random2(NUM_NORMAL_BOOKS);
- if (item.sub_type != BOOK_DESTRUCTION
- && item.sub_type != BOOK_MANUAL
- && book_rarity(item.sub_type) != 100
+ if (book_rarity(item.sub_type) != 100
&& one_chance_in(10))
{
item.sub_type = coinflip() ? BOOK_WIZARDRY : BOOK_POWER;
@@ -2428,9 +2426,7 @@ static void _generate_book_item(item_def& item, int force_type,
item.sub_type = BOOK_DESTRUCTION; // continue trying
}
}
- while (item.sub_type == BOOK_DESTRUCTION
- || item.sub_type == BOOK_MANUAL
- || book_rarity(item.sub_type) == 100);
+ while (book_rarity(item.sub_type) == 100);
// Tome of destruction: rare!
if (item_level > 10 && x_chance_in_y(21 + item_level, 7000))
@@ -2456,6 +2452,36 @@ static void _generate_book_item(item_def& item, int force_type,
// Set number of reads possible before it "crumbles to dust".
item.plus2 = 3 + random2(15);
}
+
+ // 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);
+ }
}
static void _generate_staff_item(item_def& item, int force_type)
@@ -2756,7 +2782,7 @@ int items( int allow_uniques, // not just true-false,
break;
case OBJ_BOOKS:
- _generate_book_item(item, force_type, item_level);
+ _generate_book_item(item, allow_uniques, force_type, item_level);
break;
case OBJ_STAVES:
diff --git a/crawl-ref/source/mapdef.cc b/crawl-ref/source/mapdef.cc
index da1afa86b9..44cfbdd282 100644
--- a/crawl-ref/source/mapdef.cc
+++ b/crawl-ref/source/mapdef.cc
@@ -2782,8 +2782,22 @@ void item_list::parse_random_by_class(std::string c, item_spec &spec)
if (c == "manual")
{
spec.base_type = OBJ_BOOKS;
- spec.sub_type = BOOK_MANUAL;
- spec.plus = -1;
+ spec.sub_type = BOOK_MANUAL;
+ spec.plus = -1;
+ return;
+ }
+ else if (c == "fixed theme book")
+ {
+ spec.base_type = OBJ_BOOKS;
+ spec.sub_type = BOOK_RANDART_THEME;
+ spec.plus = -1;
+ return;
+ }
+ else if (c == "fixed level book")
+ {
+ spec.base_type = OBJ_BOOKS;
+ spec.sub_type = BOOK_RANDART_LEVEL;
+ spec.plus = -1;
return;
}
diff --git a/crawl-ref/source/randart.cc b/crawl-ref/source/randart.cc
index 9ef3270d34..5be03b5185 100644
--- a/crawl-ref/source/randart.cc
+++ b/crawl-ref/source/randart.cc
@@ -1119,9 +1119,66 @@ void static _get_randart_properties(const item_def &item,
}
}
+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_randart_book(item_def &book)
{
- 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);
}
static bool _init_randart_properties(item_def &item)
@@ -2037,8 +2094,11 @@ bool make_item_randart( item_def &item )
if (item.base_type == OBJ_BOOKS)
{
- if (item.sub_type == BOOK_MANUAL || item.sub_type == BOOK_DESTRUCTION)
- return (false);
+ if (item.sub_type != BOOK_RANDART_LEVEL
+ && item.sub_type != BOOK_RANDART_THEME)
+ {
+ return (false);
+ }
}
// already is a randart
diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc
index da9afeb7a7..0c7dc58b7f 100644
--- a/crawl-ref/source/religion.cc
+++ b/crawl-ref/source/religion.cc
@@ -3535,7 +3535,7 @@ bool god_dislikes_spell_type(spell_type spell, god_type god)
break;
case GOD_ELYVILON:
- // A peaceful god of healing wouldn't like combat spells. All
+ // A peaceful god of healing wouldn't like combat spells.
if (disciplines & SPTYP_CONJURATION)
return (true);
diff --git a/crawl-ref/source/spl-book.cc b/crawl-ref/source/spl-book.cc
index 4afa74bfab..bf2c3313bb 100644
--- a/crawl-ref/source/spl-book.cc
+++ b/crawl-ref/source/spl-book.cc
@@ -20,6 +20,7 @@
#include "externs.h"
#include "cio.h"
+#include "database.h"
#include "debug.h"
#include "delay.h"
#include "food.h"
@@ -968,7 +969,7 @@ void init_spell_rarities()
for (int i = 0; i < NUM_SPELLS; i++)
_lowest_rarity[i] = 255;
- for (int i = 0; i < NUM_BOOKS; i++)
+ for (int i = 0; i < NUM_FIXED_BOOKS; i++)
{
const int rarity = book_rarity(i);
@@ -1114,18 +1115,26 @@ void mark_had_book(const item_def &book)
you.seen_spell[stype] = true;
}
+ if (book.sub_type == BOOK_RANDART_LEVEL)
+ {
+ god_type god;
+ int level = book.plus;
+ ASSERT(level > 0 && level <= 9);
+
+ if (origin_is_acquirement(book) ||
+ origin_is_god_gift(book, &god) && god == GOD_SIF_MUNA)
+ {
+ you.attribute[ATTR_RND_LVL_BOOKS] |= (1 << level);
+ }
+ }
+
if (!book.props.exists( SPELL_LIST_KEY ))
mark_had_book(book.book_number());
}
void mark_had_book(int booktype)
{
- ASSERT(booktype >= 0 && booktype < NUM_BOOKS);
-
- if (booktype == BOOK_MANUAL || booktype == BOOK_DESTRUCTION)
- {
- return;
- }
+ ASSERT(booktype >= 0 && booktype <= MAX_FIXED_BOOK);
you.had_book[booktype] = true;
@@ -1689,7 +1698,7 @@ static bool _compare_spells(spell_type a, spell_type b)
return (strcmp(spell_title(a), spell_title(b)));
}
-static bool _is_memorized(spell_type spell)
+static bool _is_memorised(spell_type spell)
{
for (int i = 0; i < 25; i++)
{
@@ -1781,47 +1790,46 @@ bool make_book_level_randart(item_def &book, int level,
int num_spells)
{
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 completely_random =
god == GOD_XOM || (god == GOD_NO_GOD && !origin_is_acquirement(book));
- const bool track_levels_had = origin_is_acquirement(book)
- || god == GOD_SIF_MUNA;
- if (level == -1)
+ if (!is_random_artefact(book))
{
- 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);
}
+ ASSERT(level > 0 && level <= 9);
+
+ if (num_spells == -1)
+ num_spells = SPELLBOOK_SIZE;
+ ASSERT(num_spells > 0 && num_spells <= SPELLBOOK_SIZE);
+
+ book.plus = level;
+ book.plus2 = num_spells;
+
+ book.sub_type = BOOK_RANDART_LEVEL;
+ return (make_item_randart(book));
}
+
+ // Being called from make_item_randart()
+
+ ASSERT(book.sub_type == BOOK_RANDART_LEVEL);
+ ASSERT(level == -1 && num_spells == -1);
+
+ level = book.plus;
ASSERT(level > 0 && level <= 9);
- if (num_spells == -1)
- num_spells = SPELLBOOK_SIZE;
+ num_spells = book.plus2;
ASSERT(num_spells > 0 && num_spells <= SPELLBOOK_SIZE);
int god_discard = 0;
@@ -1896,7 +1904,7 @@ bool make_book_level_randart(item_def &book, int level,
spell_type spell = spell_list[spell_pos];
ASSERT(spell != SPELL_NO_SPELL);
- if (avoid_memorised[spell_pos] && _is_memorized(spell))
+ if (avoid_memorised[spell_pos] && _is_memorised(spell))
{
// Only once.
avoid_memorised[spell_pos] = false;
@@ -1911,6 +1919,7 @@ bool make_book_level_randart(item_def &book, int level,
ASSERT(chosen_spells[0] != SPELL_NO_SPELL);
CrawlHashTable &props = book.props;
+ props.erase(SPELL_LIST_KEY);
props[SPELL_LIST_KEY].new_vector(SV_LONG).resize(SPELLBOOK_SIZE);
CrawlVector &spell_vec = props[SPELL_LIST_KEY];
@@ -1919,8 +1928,7 @@ bool make_book_level_randart(item_def &book, int level,
for (int i = 0; i < SPELLBOOK_SIZE; i++)
spell_vec[i] = (long) chosen_spells[i];
- if (track_levels_had)
- you.attribute[ATTR_RND_LVL_BOOKS] |= (1 << level);
+ set_randart_name(book, getRandNameString("level book"));
return (true);
}
@@ -2044,7 +2052,7 @@ static void _get_weighted_spells(bool completely_random, god_type god,
int c = 1;
if (!you.seen_spell[spell])
c = 4;
- else if (!_is_memorized(spell))
+ else if (!_is_memorised(spell))
c = 2;
int total_skill = 0;
@@ -2103,10 +2111,6 @@ 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);
@@ -2114,25 +2118,63 @@ bool make_book_theme_randart(item_def &book, int disc1, int disc2,
const bool completely_random =
god == GOD_XOM || (god == GOD_NO_GOD && !origin_is_acquirement(book));
- 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;
ASSERT(num_spells > 0 && num_spells <= SPELLBOOK_SIZE);
- if (max_levels == -1)
- max_levels = INT_MAX;
+ max_levels = (book.plus >> 8) & 0xFF;
ASSERT(max_levels > 0);
- 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;
int god_discard = 0;
int uncastable_discard = 0;
@@ -2156,6 +2198,7 @@ bool make_book_theme_randart(item_def &book, int disc1, int disc2,
ASSERT(chosen_spells[0] != SPELL_NO_SPELL);
CrawlHashTable &props = book.props;
+ props.erase(SPELL_LIST_KEY);
props[SPELL_LIST_KEY].new_vector(SV_LONG).resize(SPELLBOOK_SIZE);
CrawlVector &spell_vec = props[SPELL_LIST_KEY];
@@ -2169,10 +2212,12 @@ bool make_book_theme_randart(item_def &book, int disc1, int disc2,
if (god != GOD_NO_GOD)
{
name = '"';
- name += god_name(god, false) + "'s book ";
+ name += god_name(god, false) + "'s ";
}
- name += "of ";
+ name += getRandNameString("book_noun");
+
+ name += " of ";
name += spelltype_long_name(disc1);
if (disc1 != disc2)
@@ -2186,6 +2231,9 @@ bool make_book_theme_randart(item_def &book, int disc1, int disc2,
set_randart_name(book, name);
+ book.plus = disc1;
+ book.plus2 = disc2;
+
return (true);
}
diff --git a/crawl-ref/source/stuff.h b/crawl-ref/source/stuff.h
index 7682bd2fe5..996809b4ab 100644
--- a/crawl-ref/source/stuff.h
+++ b/crawl-ref/source/stuff.h
@@ -212,16 +212,22 @@ public:
template<typename Iterator>
int choose_random_weighted(Iterator beg, const Iterator end)
{
+ ASSERT(beg < end);
+
int totalweight = 0;
- int count = 0, result = 0;
+ int count = 0, result = 0, times_set = 0;
while ( beg != end )
{
totalweight += *beg;
if ( random2(totalweight) < *beg )
+ {
result = count;
+ times_set++;
+ }
++count;
++beg;
}
+ ASSERT(times_set > 0);
return result;
}
diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc
index a1004bf4be..98eb7be697 100644
--- a/crawl-ref/source/tags.cc
+++ b/crawl-ref/source/tags.cc
@@ -1078,8 +1078,8 @@ static void tag_construct_you_items(writer &th)
for (j = 0; j < 50; ++j)
marshallByte(th,you.unique_items[j]);
- marshallByte(th, NUM_BOOKS);
- for (j = 0; j < NUM_BOOKS; ++j)
+ marshallByte(th, NUM_FIXED_BOOKS);
+ for (j = 0; j < NUM_FIXED_BOOKS; ++j)
marshallByte(th,you.had_book[j]);
marshallShort(th, NUM_SPELLS);