summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-07 07:53:33 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-07 07:53:33 +0000
commit0f4b659dddc446658c1cb6387dfdd03bcb758db5 (patch)
treecc9b007258e1b62f34cc2caffeca20d865387d7d /crawl-ref
parentfea3c8df37716c2bb4c033d463b9a796098e0a83 (diff)
downloadcrawl-ref-0f4b659dddc446658c1cb6387dfdd03bcb758db5.tar.gz
crawl-ref-0f4b659dddc446658c1cb6387dfdd03bcb758db5.zip
Breaks savefile compatibility.
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
Diffstat (limited to 'crawl-ref')
-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);