From 3ede9fff7fa316a1d441a840533613226b720e8a Mon Sep 17 00:00:00 2001 From: j-p-e-g Date: Sun, 3 May 2009 22:04:54 +0000 Subject: Some spellcasting modifications, as discussed on c-r-d. * Spells are marked grey if you lack the necessary magic or if there are no visible monsters within range. * z bails out if there are no monsters in range * Z is the same as the old z behaviour * Wands now need to be e(V)oked. Feedback welcome! git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@9727 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/spl-cast.cc | 176 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 140 insertions(+), 36 deletions(-) (limited to 'crawl-ref/source/spl-cast.cc') diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index 4bdc390c61..6c700ea721 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -57,6 +57,9 @@ REVISION("$Rev$"); #include #endif +static int _calc_spell_range(spell_type spell, int power = 0, + bool real_cast = false); + static bool _surge_identify_boosters(spell_type spell) { const unsigned int typeflags = get_spell_disciplines(spell); @@ -66,12 +69,12 @@ static bool _surge_identify_boosters(spell_type spell) // Note that robes of the Archmagi identify on wearing, // so that's less of an issue. const item_def* wpn = player_weapon(); - if ( wpn == NULL || - wpn->base_type != OBJ_STAVES || - item_ident(*wpn, ISFLAG_KNOW_PROPERTIES) ) + if (wpn == NULL + || wpn->base_type != OBJ_STAVES + || item_ident(*wpn, ISFLAG_KNOW_PROPERTIES)) { int num_unknown = 0; - for ( int i = EQ_LEFT_RING; i <= EQ_RIGHT_RING; ++i ) + for (int i = EQ_LEFT_RING; i <= EQ_RIGHT_RING; ++i) { if (you.equip[i] != -1 && !item_ident(you.inv[you.equip[i]], ISFLAG_KNOW_PROPERTIES)) @@ -82,10 +85,10 @@ static bool _surge_identify_boosters(spell_type spell) // We can also identify cases with two unknown rings, both // of fire (or both of ice)...let's skip it. - if ( num_unknown == 1 ) + if (num_unknown == 1) { - for ( int i = EQ_LEFT_RING; i <= EQ_RIGHT_RING; ++i ) - if ( you.equip[i] != -1 ) + for (int i = EQ_LEFT_RING; i <= EQ_RIGHT_RING; ++i) + if (player_wearing_slot(i)) { item_def& ring = you.inv[you.equip[i]]; if (!item_ident(ring, ISFLAG_KNOW_PROPERTIES) @@ -127,10 +130,12 @@ static void _surge_power(spell_type spell) } } -static std::string _spell_base_description(spell_type spell) +static std::string _spell_base_description(spell_type spell, bool grey = false) { std::ostringstream desc; + if (grey) + desc << ""; desc << std::left; // spell name @@ -138,7 +143,7 @@ static std::string _spell_base_description(spell_type spell) // spell schools bool already = false; - for ( int i = 0; i <= SPTYP_LAST_EXPONENT; ++i) + for (int i = 0; i <= SPTYP_LAST_EXPONENT; ++i) { if (spell_typematch(spell, (1<"; return desc.str(); } -static std::string _spell_extra_description(spell_type spell) +static std::string _spell_extra_description(spell_type spell, bool grey = false) { std::ostringstream desc; + if (grey) + desc << ""; desc << std::left; // spell name @@ -177,10 +186,42 @@ static std::string _spell_extra_description(spell_type spell) << std::setw(12) << spell_hunger_string(spell) << spell_difficulty(spell); + if (grey) + desc << ""; + return desc.str(); } -int list_spells(bool toggle_with_I, bool viewing) +static bool _spell_no_hostile_in_range(spell_type spell, int minRange) +{ + if (minRange < 0) + return (false); + + switch (spell) + { + case SPELL_APPORTATION: + case SPELL_PROJECTED_NOISE: + // These don't target monsters. + return (false); + default: + break; + } + + // The healing spells. + if (testbits(get_spell_flags(spell), SPFLAG_HELPFUL)) + return (false); + + const int range = _calc_spell_range(spell); + if (range < 0) + return (false); + + if (range < minRange) + return (true); + + return (false); +} + +int list_spells(bool toggle_with_I, bool viewing, int minRange) { ToggleableMenu spell_menu(MF_SINGLESELECT | MF_ANYPRINTABLE | MF_ALWAYS_SHOW_MORE | MF_ALLOW_FORMATTING); @@ -221,15 +262,26 @@ int list_spells(bool toggle_with_I, bool viewing) more_str += "to toggle spell view."; spell_menu.set_more(formatted_string(more_str)); + bool grey = false; // Needs to be greyed out? for (int i = 0; i < 52; ++i) { const char letter = index_to_letter(i); const spell_type spell = get_spell_by_letter(letter); + if (!viewing) + { + if (spell_mana(spell) > you.magic_points + || _spell_no_hostile_in_range(spell, minRange)) + { + grey = true; + } + else + grey = false; + } if (spell != SPELL_NO_SPELL) { ToggleableMenuEntry* me = - new ToggleableMenuEntry(_spell_base_description(spell), - _spell_extra_description(spell), + new ToggleableMenuEntry(_spell_base_description(spell, grey), + _spell_extra_description(spell, grey), MEL_ITEM, 1, letter); spell_menu.add_entry(me); } @@ -550,8 +602,39 @@ void inspect_spells() list_spells(true, true); } +static int _get_dist_to_nearest_monster() +{ + int minRange = LOS_RADIUS + 1; + for (radius_iterator ri(you.pos(), LOS_RADIUS, true, false, true); ri; ++ri) + { + if (!in_bounds(*ri)) + continue; + + if (!see_grid(*ri)) + continue; + + const monsters *mon = monster_at(*ri); + if (mon == NULL) + continue; + + if (!player_monster_visible(mon) + || mons_is_unknown_mimic(mon)) + { + continue; + } + + if (mons_wont_attack(mon)) + continue; + + int dist = grid_distance(you.pos(), *ri); + if (dist < minRange) + minRange = dist; + } + return (minRange); +} + // Returns false if spell failed, and true otherwise. -bool cast_a_spell() +bool cast_a_spell(bool check_range) { if (!you.spell_no) { @@ -574,6 +657,14 @@ bool cast_a_spell() return (false); } + if (you.magic_points < 1) + { + mpr("You don't have enough magic to cast spells."); + return (false); + } + + const int minRange = (check_range ? _get_dist_to_nearest_monster() : -1); + int keyin = 0; // silence stupid compilers while (true) @@ -584,7 +675,7 @@ bool cast_a_spell() if (keyin == '?' || keyin == '*') { - keyin = list_spells(); + keyin = list_spells(true, false, minRange); if (!keyin) keyin = ESCAPE; @@ -621,12 +712,18 @@ bool cast_a_spell() return (false); } - if (spell_mana( spell ) > you.magic_points) + if (spell_mana(spell) > you.magic_points) { mpr("You don't have enough magic to cast that spell."); return (false); } + if (_spell_no_hostile_in_range(spell, minRange)) + { + mpr("There are no visible monsters within range! (Use Z to cast anyway.)"); + return (false); + } + if (you.is_undead != US_UNDEAD && you.species != SP_VAMPIRE && (you.hunger_state == HS_STARVING || you.hunger <= spell_hunger( spell ))) @@ -1047,11 +1144,7 @@ spret_type your_spells(spell_type spell, int powc, bool allow_fail) const bool dont_cancel_me = testbits(flags, SPFLAG_AREA); - - // FIXME: Code duplication (see similar line below). - int range_power = (powc == 0 ? calc_spell_power(spell, true) : powc); - - const int range = spell_range(spell, range_power, false); + const int range = _calc_spell_range(spell, powc, false); if (!spell_direction(spd, beam, dir, targ, range, needs_path, true, dont_cancel_me, prompt, @@ -1060,14 +1153,14 @@ spret_type your_spells(spell_type spell, int powc, bool allow_fail) return (SPRET_ABORT); } - beam.range = spell_range(spell, range_power, true); + beam.range = _calc_spell_range(spell, powc, true); if (testbits(flags, SPFLAG_NOT_SELF) && spd.isMe) { if (spell == SPELL_TELEPORT_OTHER || spell == SPELL_POLYMORPH_OTHER || spell == SPELL_BANISHMENT) { - mpr( "Sorry, this spell works on others only." ); + mpr("Sorry, this spell works on others only."); } else canned_msg(MSG_UNTHINKING_ACT); @@ -2165,14 +2258,14 @@ const char* spell_hunger_string( spell_type spell ) int spell_power_colour(spell_type spell) { const int powercap = spell_power_cap(spell); - if ( powercap == 0 ) + if (powercap == 0) return DARKGREY; const int power = calc_spell_power(spell, true); - if ( power >= powercap ) + if (power >= powercap) return WHITE; - if ( power * 3 < powercap ) + if (power * 3 < powercap) return RED; - if ( power * 3 < powercap * 2 ) + if (power * 3 < powercap * 2) return YELLOW; return GREEN; } @@ -2199,23 +2292,34 @@ std::string spell_power_string(spell_type spell) { const int numbars = spell_power_bars(spell); const int capbars = _power_to_barcount(spell_power_cap(spell)); - ASSERT( numbars <= capbars ); - if ( numbars < 0 ) + ASSERT(numbars <= capbars); + if (numbars < 0) return "N/A"; else return std::string(numbars, '#') + std::string(capbars - numbars, '.'); } +static int _calc_spell_range(spell_type spell, int power, bool real_cast) +{ + if (power == 0) + power = calc_spell_power(spell, true); + const int range = spell_range(spell, power, real_cast); + + return (range); +} + std::string spell_range_string(spell_type spell) { - const int cap = spell_power_cap(spell); - const int power = calc_spell_power(spell, true); - const int range = spell_range(spell, power, false); + const int cap = spell_power_cap(spell); + const int range = _calc_spell_range(spell); const int maxrange = spell_range(spell, cap, false); + if (range < 0) return "N/A"; else + { return std::string("@") + std::string(range, '.') - + "" + std::string(maxrange - range, '.') - + ""; + + "" + std::string(maxrange - range, '.') + + ""; + } } -- cgit v1.2.3-54-g00ecf