diff options
-rw-r--r-- | crawl-ref/source/command.cc | 8 | ||||
-rw-r--r-- | crawl-ref/source/describe.cc | 9 | ||||
-rw-r--r-- | crawl-ref/source/invent.cc | 6 | ||||
-rw-r--r-- | crawl-ref/source/invent.h | 17 | ||||
-rw-r--r-- | crawl-ref/source/spl-book.cc | 366 | ||||
-rw-r--r-- | crawl-ref/source/spl-book.h | 3 | ||||
-rw-r--r-- | crawl-ref/source/spl-cast.cc | 31 | ||||
-rw-r--r-- | crawl-ref/source/spl-cast.h | 1 |
8 files changed, 306 insertions, 135 deletions
diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc index 2152b851b0..bcee8f6413 100644 --- a/crawl-ref/source/command.cc +++ b/crawl-ref/source/command.cc @@ -1241,6 +1241,14 @@ static bool _append_books(std::string &desc, item_def &item, std::string key) itoa( spell_difficulty( type ), sval, 10 ); desc += sval; + if (you_cannot_memorise(type)) + { + desc += "$You cannot memorise or cast this spell because you " + "are a "; + desc += lowercase_string(species_name(you.species, 0)); + desc += "."; + } + set_ident_flags(item, ISFLAG_IDENT_MASK); std::vector<std::string> books; std::vector<std::string> rods; diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc index 796b75b7e1..52f0b4b3e2 100644 --- a/crawl-ref/source/describe.cc +++ b/crawl-ref/source/describe.cc @@ -2494,6 +2494,15 @@ void describe_spell(spell_type spelled) #endif } + if (you_cannot_memorise(spelled)) + { + description += "$$"; + description += "You cannot memorise or cast this spell because you " + "are a "; + description += lowercase_string(species_name(you.species, 0)); + description += "."; + } + print_description(description); mouse_control mc(MOUSE_MODE_MORE); diff --git a/crawl-ref/source/invent.cc b/crawl-ref/source/invent.cc index be0bbc721d..363fe1a48a 100644 --- a/crawl-ref/source/invent.cc +++ b/crawl-ref/source/invent.cc @@ -434,8 +434,6 @@ static std::string _no_selectables_message(int item_selector) } case OSEL_UNIDENT: return("You don't have any unidentified items."); - case OSEL_MEMORISE: - return("You aren't carrying any spellbooks."); case OSEL_RECHARGE: return("You aren't carrying any rechargeable items."); case OSEL_ENCH_ARM: @@ -1018,10 +1016,6 @@ static bool _item_class_selected(const item_def &i, int selector) case OSEL_BUTCHERY: return (itype == OBJ_WEAPONS && can_cut_meat(i)); - case OSEL_MEMORISE: - return (itype == OBJ_BOOKS && i.sub_type != BOOK_MANUAL - && (i.sub_type != BOOK_DESTRUCTION || !item_type_known(i))); - case OBJ_SCROLLS: return (itype == OBJ_SCROLLS || itype == OBJ_BOOKS); diff --git a/crawl-ref/source/invent.h b/crawl-ref/source/invent.h index ac58610c62..2f64cf9d59 100644 --- a/crawl-ref/source/invent.h +++ b/crawl-ref/source/invent.h @@ -22,15 +22,14 @@ enum object_selector OSEL_WIELD = -2, OSEL_UNIDENT = -3, OSEL_EQUIP = -4, - OSEL_MEMORISE = -5, - OSEL_RECHARGE = -6, - OSEL_ENCH_ARM = -7, - OSEL_VAMP_EAT = -8, - OSEL_DRAW_DECK = -9, - OSEL_THROWABLE = -10, - OSEL_BUTCHERY = -11, - OSEL_EVOKABLE = -12, - OSEL_WORN_ARMOUR = -13 + OSEL_RECHARGE = -5, + OSEL_ENCH_ARM = -6, + OSEL_VAMP_EAT = -7, + OSEL_DRAW_DECK = -8, + OSEL_THROWABLE = -9, + OSEL_BUTCHERY = -10, + OSEL_EVOKABLE = -11, + OSEL_WORN_ARMOUR = -12 }; #define PROMPT_ABORT -1 diff --git a/crawl-ref/source/spl-book.cc b/crawl-ref/source/spl-book.cc index 5cf25a4ab1..ca4d8ce51a 100644 --- a/crawl-ref/source/spl-book.cc +++ b/crawl-ref/source/spl-book.cc @@ -15,6 +15,7 @@ REVISION("$Rev$"); #include <stdio.h> #include <string.h> #include <algorithm> +#include <iomanip> #ifdef DOS #include <conio.h> @@ -814,7 +815,9 @@ int spellbook_contents( item_def &book, read_book_action_type action, } else { - if (knows_spell) + if (you_cannot_memorise(stype)) + colour = LIGHTRED; + else if (knows_spell) colour = LIGHTGREY; else if (you.experience_level >= level_diff && spell_levels >= levels_req @@ -875,11 +878,6 @@ int spellbook_contents( item_def &book, read_book_action_type action, out.cprintf( "Select a spell to cast." EOL ); break; - case RBOOK_MEMORISE: - out.cprintf( "Select a spell to memorise (%d level%s available)." EOL, - spell_levels, (spell_levels == 1) ? "" : "s" ); - break; - case RBOOK_READ_SPELL: out.cprintf( "Select a spell to read its description." EOL ); break; @@ -1058,47 +1056,6 @@ bool is_valid_spell_in_book( int splbook, int spell ) return which_spell_in_book(splbook, spell) != SPELL_NO_SPELL; } -static int _which_spellbook( void ) -{ - int book = -1; - const int avail_levels = player_spell_levels(); - - // Knowing delayed fireball will allow Fireball to be learned for free -bwr - if (avail_levels < 1 && !player_has_spell(SPELL_DELAYED_FIREBALL)) - { - mpr("You can't memorise any more spells yet."); - return (-1); - } - else if (inv_count() < 1) - { - canned_msg(MSG_NOTHING_CARRIED); - return (-1); - } - - mprf("You can memorise %d more level%s of spells.", - avail_levels, (avail_levels > 1) ? "s" : "" ); - - book = prompt_invent_item("Memorise from which spellbook?", MT_INVLIST, - OSEL_MEMORISE ); - if (prompt_failed(book)) - return (-1); - - if (you.inv[book].base_type != OBJ_BOOKS - || you.inv[book].sub_type == BOOK_MANUAL) - { - mpr("That isn't a spellbook!"); - return (-1); - } - - if (you.inv[book].sub_type == BOOK_DESTRUCTION) - { - tome_of_power( book ); - return (-1); - } - - return (book); -} - // Returns false if the player cannot read/memorise from the book, // and true otherwise. -- bwr bool player_can_read_spellbook( const item_def &book ) @@ -1333,112 +1290,305 @@ bool player_can_memorise(const item_def &book) return (false); } -bool learn_spell(int book) +static std::vector<spell_type> _get_mem_list() { - if (player_in_bat_form()) + std::set<spell_type> all_spells; + std::vector<spell_type> mem_spells; + + bool book_errors = false; + int num_books = 0; + int num_unreadable = 0; + + // Collect the list of all spells in all available spellbooks. + for (int i = 0; i < ENDOFPACK; i++) { - canned_msg(MSG_PRESENT_FORM); - return (false); - } + item_def& book(you.inv[i]); - int chance = 0; - int levels_needed = 0; - int index; + if (book.base_type != OBJ_BOOKS || book.sub_type == BOOK_DESTRUCTION + || book.sub_type == BOOK_MANUAL) + { + continue; + } - int i; - int j = 0; + num_books++; - for (i = SK_SPELLCASTING; i <= SK_POISON_MAGIC; i++) - if (you.skills[i]) - j++; + if (!player_can_read_spellbook(book)) + { + num_unreadable++; + continue; + } - if (j == 0) - { - mpr("You can't use spell magic! I'm afraid it's scrolls only for now."); - return (false); + mark_had_book(book); + set_ident_flags(book, ISFLAG_KNOW_TYPE); + set_ident_flags(book, ISFLAG_IDENT_MASK); + + int spells_in_book = 0; + for (int j = 0; j < SPELLBOOK_SIZE; j++) + { + if (!is_valid_spell_in_book(book, j)) + continue; + + all_spells.insert(which_spell_in_book(book, j)); + spells_in_book++; + } + + if (spells_in_book == 0) + { + mprf(MSGCH_ERROR, "Spellbook \"%s\" contains no spells! Please " + "file a bug report.", book.name(DESC_PLAIN).c_str()); + book_errors = true; + } } - if (you.confused()) + if (book_errors) + more(); + + if (num_books == 0) { - mpr("You are too confused!"); - return (false); + mpr("You aren't carrying any spellbooks.", MSGCH_PROMPT); + return (mem_spells); + } + else if (num_unreadable == num_books) + { + mpr("All of the spellbooks you're carrying are beyond your " + "current level of comprehension.", MSGCH_PROMPT); + return (mem_spells); + } + else if (all_spells.size() == 0) + { + mpr("None of the spellbooks you are carrying contain any spells.", + MSGCH_PROMPT); + return (mem_spells); } - if (you.duration[DUR_BERSERKER]) + unsigned int num_known = 0; + unsigned int num_race = 0; + unsigned int num_low_xl = 0; + unsigned int num_low_levels = 0; + unsigned int num_memable = 0; + + bool amnesia = false; + + for (std::set<spell_type>::iterator i = all_spells.begin(); + i != all_spells.end(); ++i) { - canned_msg(MSG_TOO_BERSERK); - return (false); + const spell_type spell = *i; + + if (player_knows_spell(spell)) + num_known++; + else if (you_cannot_memorise(spell)) + num_race++; + else + { + mem_spells.push_back(spell); + + if (spell_difficulty(spell) > you.experience_level) + num_low_xl++; + else if (player_spell_levels() < spell_levels_required(spell)) + num_low_levels++; + else + { + if (spell == SPELL_SELECTIVE_AMNESIA) + amnesia = true; + num_memable++; + } + } } - if (book < 0) - book = _which_spellbook(); + // You can always memorise selective amnesia. + if (num_memable > 0 && you.spell_no >= 21) + { + mem_spells.clear(); - if (book < 0) // still -1? - return (false); + if (amnesia) + { + mem_spells.push_back(SPELL_SELECTIVE_AMNESIA); + return (mem_spells); + } - int spell = read_book( you.inv[book], RBOOK_MEMORISE ); + mpr("Your head is already too full of spells!"); + return (mem_spells); + } + + if (num_memable) + return (mem_spells); + + // No spells to be memorized is indicated by an empty list. + mem_spells.clear(); + + // None of the spells can be memorized, tell the player why. + std::string prefix = + make_stringf("You cannot memorize any new spells. Out of %u " + "available spells ", all_spells.size()); + + std::vector<std::string> causes; + if (num_known) + causes.push_back(make_stringf("you already known %u of them", + num_known)); + if (num_race) + causes.push_back(make_stringf("%u cannot be memorized because of " + "your race", num_race)); + if (num_low_xl) + causes.push_back(make_stringf("%u cannot be memorized because of " + "your low experinece level", + num_low_xl)); + if (num_low_levels) + causes.push_back(make_stringf("%u cannot be memorized because you " + "don't have enough free spell levels", + num_low_levels)); + + unsigned int total = num_known + num_race + num_low_xl + num_low_levels; + if (total < all_spells.size()) + causes.push_back(make_stringf("%u cannot be accounted for (please " + "file a bug report)", + all_spells.size() - total)); + + mpr_comma_separated_list(prefix, causes, ", and ", ", ", MSGCH_PROMPT); + + if (num_unreadable) + mprf(MSGCH_PROMPT, "Additionally, %d of your spellbooks are beyond " + "your current level of understanding, and thus none of the " + "spells in them are avaible to you."); + + return (mem_spells); +} - if (!crawl_state.is_replaying_keys()) +static bool _sort_mem_spells(spell_type a, spell_type b) +{ + if (spell_fail(a) != spell_fail(b)) + return (spell_fail(a) < spell_fail(b)); + if (spell_difficulty(a) != spell_difficulty(b)) + return (spell_difficulty(a) < spell_difficulty(b)); + + return (stricmp(spell_title(a), spell_title(b)) < 0); +} + +static spell_type _choose_mem_spell(std::vector<spell_type> &spells) +{ + std::sort(spells.begin(), spells.end(), _sort_mem_spells); + + Menu spell_menu(MF_SINGLESELECT | MF_ANYPRINTABLE + | MF_ALWAYS_SHOW_MORE | MF_ALLOW_FORMATTING); +#ifdef USE_TILE { - clrscr(); - mesclr(true); - redraw_screen(); + // [enne] - Hack. Make title an item so that it's aligned. + MenuEntry* me = + new MenuEntry( + " Spells Type " + " Success Level", + MEL_ITEM); + me->colour = BLUE; + spell_menu.add_entry(me); } +#else + spell_menu.set_title( + new MenuEntry( + " Spells Type " + " Success Level", + MEL_TITLE)); +#endif - if ( !isalpha(spell) ) + spell_menu.set_highlighter(NULL); + spell_menu.set_tag("spell"); + + for (unsigned int i = 0; i < spells.size(); i++) { - canned_msg( MSG_HUH ); - return (false); + const spell_type spell = spells[i]; + const bool grey = spell_difficulty(spell) > you.experience_level + || player_spell_levels() < spell_levels_required(spell); + + std::ostringstream desc; + + if (grey) + desc << "<darkgrey>"; + desc << std::left; + desc << std::setw(30) << spell_title(spell); + desc << spell_schools_string(spell); + + int so_far = desc.str().length() - (grey ? 10 : 0); + if (so_far < 60) + desc << std::string(60 - so_far, ' '); + + desc << std::setw(12) << failure_rate_to_string(spell_fail(spell)) + << spell_difficulty(spell); + if (grey) + desc << "</darkgrey>"; + + MenuEntry* me = new MenuEntry(desc.str(), MEL_ITEM, 1, + index_to_letter(i % 52)); + me->data = (void*) i; + spell_menu.add_entry(me); } - index = letter_to_index( spell ); + while (true) + { + std::vector<MenuEntry*> sel = spell_menu.show(); + + if (!crawl_state.doing_prev_cmd_again) + redraw_screen(); + + if (sel.empty()) + return (SPELL_NO_SPELL); + + ASSERT(sel.size() == 1); + + const spell_type spell = spells[(int) sel[0]->data]; + ASSERT(is_valid_spell(spell)); + + return (spell); + } +} - if (index >= SPELLBOOK_SIZE - || !is_valid_spell_in_book( you.inv[book], index )) +bool learn_spell() +{ + if (player_in_bat_form()) { - canned_msg( MSG_HUH ); + canned_msg(MSG_PRESENT_FORM); return (false); } - spell_type specspell = which_spell_in_book(you.inv[book], index); + int chance = 0; - if (specspell == SPELL_NO_SPELL) + int i; + int j = 0; + + for (i = SK_SPELLCASTING; i <= SK_POISON_MAGIC; i++) + if (you.skills[i]) + j++; + + if (j == 0) { - canned_msg( MSG_HUH ); + mpr("You can't use spell magic! I'm afraid it's scrolls only for now."); return (false); } - // You can always memorise selective amnesia: - if (you.spell_no >= 21 && specspell != SPELL_SELECTIVE_AMNESIA) + if (you.confused()) { - mpr("Your head is already too full of spells!"); + mpr("You are too confused!"); return (false); } - if (player_is_unholy() && spell_typematch(specspell, SPTYP_HOLY)) + if (you.duration[DUR_BERSERKER]) { - mpr("You can't use this type of magic!"); + canned_msg(MSG_TOO_BERSERK); return (false); } - if (you_cannot_memorise(specspell)) - { - mpr("You cannot use this spell."); + std::vector<spell_type> spell_list = _get_mem_list(); + + if (spell_list.empty()) return (false); - } - for (i = 0; i < 25; i++) + spell_type specspell = _choose_mem_spell(spell_list); + + // MATT + if (specspell == SPELL_NO_SPELL) { - if (you.spells[i] == specspell) - { - mpr("You already know that spell!"); - return (false); - } + canned_msg( MSG_OK ); + return (false); } - levels_needed = spell_levels_required( specspell ); - - if (player_spell_levels() < levels_needed) + if (player_spell_levels() < spell_levels_required(specspell)) { mpr("You can't memorise that many levels of magic yet!"); return (false); @@ -1488,6 +1638,7 @@ bool learn_spell(int book) mpr("You fail to memorise the spell."); you.turn_is_over = true; +#if 0 if (you.inv[ book ].sub_type == BOOK_NECRONOMICON) { mpr("The pages of the Necronomicon glow with a dark malevolence..."); @@ -1509,6 +1660,7 @@ bool learn_spell(int book) 8, random2avg(88, 3), "reading the book of Annihilations" ); } +#endif #ifdef WIZARD if (!you.wizard) diff --git a/crawl-ref/source/spl-book.h b/crawl-ref/source/spl-book.h index 07d69e7d9c..3a05756785 100644 --- a/crawl-ref/source/spl-book.h +++ b/crawl-ref/source/spl-book.h @@ -20,7 +20,6 @@ class formatted_string; enum read_book_action_type { RBOOK_USE_STAFF, - RBOOK_MEMORISE, RBOOK_READ_SPELL }; @@ -50,7 +49,7 @@ int read_book( item_def &item, read_book_action_type action ); * called from: acr * *********************************************************************** */ bool player_can_memorise(const item_def &book); -bool learn_spell(int book = -1); +bool learn_spell(); bool player_can_read_spellbook( const item_def &book ); diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index 37d8e99a2b..800ddad4e7 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -143,17 +143,7 @@ static std::string _spell_base_description(spell_type spell, bool grey = false) desc << std::setw(30) << spell_title(spell); // spell schools - bool already = false; - for (int i = 0; i <= SPTYP_LAST_EXPONENT; ++i) - { - if (spell_typematch(spell, (1<<i))) - { - if (already) - desc << '/'; - desc << spelltype_name(1 << i); - already = true; - } - } + desc << spell_schools_string(spell); const int so_far = desc.str().length() - (grey ? 10 : 0); if (so_far < 60) @@ -2351,3 +2341,22 @@ std::string spell_range_string(spell_type spell) + "</darkgrey>"; } } + +std::string spell_schools_string(spell_type spell) +{ + std::string desc; + + bool already = false; + for (int i = 0; i <= SPTYP_LAST_EXPONENT; ++i) + { + if (spell_typematch(spell, (1<<i))) + { + if (already) + desc += "/"; + desc += spelltype_name(1 << i); + already = true; + } + } + + return (desc); +} diff --git a/crawl-ref/source/spl-cast.h b/crawl-ref/source/spl-cast.h index 5167719fe0..63d2276251 100644 --- a/crawl-ref/source/spl-cast.h +++ b/crawl-ref/source/spl-cast.h @@ -83,6 +83,7 @@ int spell_power_colour(spell_type spell); int spell_power_bars(spell_type spell); std::string spell_power_string(spell_type spell); std::string spell_range_string(spell_type spell); +std::string spell_schools_string(spell_type spell); const char* spell_hunger_string( spell_type spell ); #endif |