diff options
-rw-r--r-- | crawl-ref/source/acr.cc | 28 | ||||
-rw-r--r-- | crawl-ref/source/command.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/dat/database/randname.txt | 62 | ||||
-rw-r--r-- | crawl-ref/source/dat/descript/items.txt | 15 | ||||
-rw-r--r-- | crawl-ref/source/debug.cc | 6 | ||||
-rw-r--r-- | crawl-ref/source/effects.cc | 121 | ||||
-rw-r--r-- | crawl-ref/source/enum.h | 73 | ||||
-rw-r--r-- | crawl-ref/source/externs.h | 4 | ||||
-rw-r--r-- | crawl-ref/source/invent.cc | 3 | ||||
-rw-r--r-- | crawl-ref/source/itemname.cc | 15 | ||||
-rw-r--r-- | crawl-ref/source/makeitem.cc | 46 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.cc | 18 | ||||
-rw-r--r-- | crawl-ref/source/randart.cc | 66 | ||||
-rw-r--r-- | crawl-ref/source/religion.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/spl-book.cc | 168 | ||||
-rw-r--r-- | crawl-ref/source/stuff.h | 8 | ||||
-rw-r--r-- | crawl-ref/source/tags.cc | 4 |
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); |