diff --git a/rsrc/dialogs/cast-spell.xml b/rsrc/dialogs/cast-spell.xml index 44810467..c3cf3b7c 100644 --- a/rsrc/dialogs/cast-spell.xml +++ b/rsrc/dialogs/cast-spell.xml @@ -3,80 +3,212 @@ Select a Spell: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pick spell to cast. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Caster: + + + + + + + + + + + + + + + + Target: + + + + + + + + -> + -> + -> + -> + -> + -> + + HP: + + + + + + + + SP: + + + + + + + + + Status: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - Caster: - Target: HP: SP: Status: + + Keyboard: @@ -89,63 +221,10 @@ key by spell to cast spell.
Alt-click spell name for description.
+ Pick spell to cast. +
diff --git a/src/dialogxml/dialogs/dialog.cpp b/src/dialogxml/dialogs/dialog.cpp index 755ccf3f..1979c327 100644 --- a/src/dialogxml/dialogs/dialog.cpp +++ b/src/dialogxml/dialogs/dialog.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "dialog.hpp" #include "gfx/tiling.hpp" // for bg #include "fileio/resmgr/res_dialog.hpp" @@ -45,6 +46,8 @@ cDialog* cDialog::topWindow = nullptr; void (*cDialog::redraw_everything)() = nullptr; std::mt19937 cDialog::ui_rand; +extern std::map colour_map; + extern bool check_for_interrupt(std::string); std::string cDialog::generateRandomString(){ @@ -86,39 +89,9 @@ sf::Color cControl::parseColor(string what){ } } clr.r = r, clr.g = g, clr.b = b; - }else if(what == "black") - clr.r = 0x00, clr.g = 0x00, clr.b = 0x00; - else if(what == "red") - clr.r = 0xFF, clr.g = 0x00, clr.b = 0x00; - else if(what == "lime") - clr.r = 0x00, clr.g = 0xFF, clr.b = 0x00; - else if(what == "blue") - clr.r = 0x00, clr.g = 0x00, clr.b = 0xFF; - else if(what == "yellow") - clr.r = 0xFF, clr.g = 0xFF, clr.b = 0x00; - else if(what == "aqua") - clr.r = 0x00, clr.g = 0xFF, clr.b = 0xFF; - else if(what == "fuchsia") - clr.r = 0xFF, clr.g = 0x00, clr.b = 0xFF; - else if(what == "white") - clr.r = 0xFF, clr.g = 0xFF, clr.b = 0xFF; - else if(what == "gray" || what == "grey") - clr.r = 0x80, clr.g = 0x80, clr.b = 0x80; - else if(what == "maroon") - clr.r = 0x80, clr.g = 0x00, clr.b = 0x00; - else if(what == "green") - clr.r = 0x00, clr.g = 0x80, clr.b = 0x00; - else if(what == "navy") - clr.r = 0x00, clr.g = 0x00, clr.b = 0x80; - else if(what == "olive") - clr.r = 0x80, clr.g = 0x80, clr.b = 0x00; - else if(what == "teal") - clr.r = 0x00, clr.g = 0x80, clr.b = 0x80; - else if(what == "purple") - clr.r = 0x80, clr.g = 0x00, clr.b = 0x80; - else if(what == "silver") - clr.r = 0xC0, clr.g = 0xC0, clr.b = 0xC0; - else throw -1; + }else if(colour_map.find(what) != colour_map.end()){ + return colour_map[what]; + }else throw -1; return clr; } diff --git a/src/game/boe.actions.cpp b/src/game/boe.actions.cpp index 216187d2..3aca9af6 100644 --- a/src/game/boe.actions.cpp +++ b/src/game/boe.actions.cpp @@ -266,6 +266,14 @@ void handle_spellcast(eSkill which_type, bool& did_something, bool& need_redraw, short store_sp[6]; extern short spec_target_fail; extern eSpecCtxType spec_target_type; + // Dual-caster recast hint toggle: + // Change the recast hint to mage if last spell wasn't mage + if(spell_forced && is_combat() && univ.current_pc().last_cast_type != which_type){ + spell_forced = false; + univ.current_pc().last_cast_type = which_type; + need_redraw = true; + return; + } if(!someone_awake()) { ASB("Everyone's asleep/paralyzed."); need_reprint = true; @@ -1864,6 +1872,7 @@ void handle_menu_spell(eSpell spell_picked) { spell_forced = true; pc_casting = univ.cur_pc; univ.current_pc().last_cast[spell_type] = spell_picked; + univ.current_pc().last_cast_type = spell_type; if(spell_type == eSkill::MAGE_SPELLS) store_mage = spell_picked; else store_priest = spell_picked; @@ -2472,6 +2481,7 @@ bool handle_keystroke(const sf::Event& event, cFramerateLimiter& fps_limiter){ // cast multi-target spell, set # targets to 0 so that space clicked doesn't matter num_targets_left = 0; handle_target_space(center, did_something, need_redraw, need_reprint); + advance_time(did_something, need_redraw, need_reprint); } else if(overall_mode == MODE_SPELL_TARGET) // Rotate a force wall spell_cast_hit_return(); diff --git a/src/game/boe.graphics.cpp b/src/game/boe.graphics.cpp index 17c7f121..a0f2b9e5 100644 --- a/src/game/boe.graphics.cpp +++ b/src/game/boe.graphics.cpp @@ -611,8 +611,42 @@ void draw_text_bar() { } if((is_combat()) && (univ.cur_pc < 6) && !monsters_going) { std::ostringstream sout; - sout << univ.current_pc().name << " (ap: " << univ.current_pc().ap << ')'; - put_text_bar(sout.str()); + + cPlayer& current_pc = univ.current_pc(); + sout << current_pc.name << " (ap: " << current_pc.ap << ')'; + + // Spellcasters print a hint for recasting. + // There's not enough space to print 2 hints for dual-casters, + // so just handle the last type cast. + eSkill type = current_pc.last_cast_type; + std::string hint_prefix = ""; + std::ostringstream hint_out; + switch(type){ + case eSkill::MAGE_SPELLS: + hint_prefix = "M"; + break; + case eSkill::PRIEST_SPELLS: + hint_prefix = "P"; + break; + // The only other expected value is eSkill::INVALID + default: + break; + } + if(!hint_prefix.empty()){ + hint_out << hint_prefix << ": "; + if(current_pc.last_cast[type] != eSpell::NONE){ + const cSpell& spell = (*current_pc.last_cast[type]); + if(pc_can_cast_spell(current_pc,type) && spell.cost <= current_pc.get_magic()) { + hint_out << "Recast " << spell.name(); + }else{ + hint_out << "Cannot recast"; + } + }else{ + hint_out << "No spell to recast"; + } + } + + put_text_bar(sout.str(), hint_out.str()); } if((is_combat()) && (monsters_going)) // Print bar for 1st monster with >0 ap - that is monster that is going @@ -623,7 +657,7 @@ void draw_text_bar() { } } -void put_text_bar(std::string str) { +void put_text_bar(std::string str, std::string right_str) { text_bar_gworld.setActive(false); auto& bar_gw = *ResMgr::graphics.get("textbar"); rect_draw_some_item(bar_gw, rectangle(bar_gw), text_bar_gworld, rectangle(bar_gw)); @@ -635,9 +669,13 @@ void put_text_bar(std::string str) { rectangle to_rect = rectangle(text_bar_gworld); to_rect.top += 7; to_rect.left += 5; + to_rect.right -= 5; win_draw_string(text_bar_gworld, to_rect, str, eTextMode::LEFT_TOP, style); - - if(!monsters_going) { + // the recast hint will replace status icons: + if(!right_str.empty()){ + // Style has to be wrap to get right-alignment + win_draw_string(text_bar_gworld, to_rect, right_str, eTextMode::WRAP, style, true); + }else if(!monsters_going) { sf::Texture& status_gworld = *ResMgr::graphics.get("staticons"); to_rect.top -= 2; to_rect.left = to_rect.right - 15; diff --git a/src/game/boe.graphics.hpp b/src/game/boe.graphics.hpp index be72abf7..c1966d6f 100644 --- a/src/game/boe.graphics.hpp +++ b/src/game/boe.graphics.hpp @@ -31,7 +31,7 @@ void redraw_screen(int refresh); void put_background(); void draw_text_bar(); void refresh_text_bar(); -void put_text_bar(std::string str); +void put_text_bar(std::string str, std::string right_str = ""); void draw_terrain(short mode = 0); void place_trim(short q,short r,location where,ter_num_t ter_type); void draw_trim(short q,short r,short which_trim,short which_mode); diff --git a/src/game/boe.main.cpp b/src/game/boe.main.cpp index 04a96748..3adf75fa 100644 --- a/src/game/boe.main.cpp +++ b/src/game/boe.main.cpp @@ -147,7 +147,7 @@ eStatMode stat_screen_mode; short anim_step = -1; // Spell casting globals -eSpell store_mage = eSpell::LIGHT, store_priest = eSpell::BLESS_MINOR; +eSpell store_mage = eSpell::NONE, store_priest = eSpell::NONE; short store_mage_lev = 0, store_priest_lev = 0; short store_spell_target = 6,pc_casting; short num_targets_left = 0; diff --git a/src/game/boe.party.cpp b/src/game/boe.party.cpp index 5684fc8e..a809d4af 100644 --- a/src/game/boe.party.cpp +++ b/src/game/boe.party.cpp @@ -52,6 +52,11 @@ short combat_percent[20] = { 70,70,67,62,57,52,47,42,40,40}; short who_cast,which_pc_displayed; +// Light can be cast in or out of combat +const eSpell DEFAULT_SELECTED_MAGE = eSpell::LIGHT; +// Bless can only be cast in combat, so separate defaults are needed +const eSpell DEFAULT_SELECTED_PRIEST = eSpell::HEAL_MINOR; +const eSpell DEFAULT_SELECTED_PRIEST_COMBAT = eSpell::BLESS_MINOR; eSpell town_spell; extern bool spell_freebie; extern eSpecCtxType spec_target_type; @@ -94,6 +99,9 @@ short spell_index[38] = {38,39,40,41,42,43,44,45,90,90,46,47,48,49,50,51,52,53,9 // Says which buttons hit which spells on second spell page, 90 means no button bool can_choose_caster; +const sf::Color SELECTED_COLOUR = Colours::LIGHT_GREEN; +const sf::Color DISABLED_COLOUR = Colours::GREY; + // Dialog vars short store_graphic_pc_num ; short store_graphic_mode ; @@ -490,6 +498,13 @@ bool repeat_cast_ok(eSkill type) { what_spell = univ.party[who_would_cast].last_cast[type]; else what_spell = type == eSkill::MAGE_SPELLS ? store_mage : store_priest; + if(what_spell == eSpell::NONE){ + std::ostringstream sout; + sout << "Repeat cast: No " << (type == eSkill::MAGE_SPELLS ? "mage" : "priest") << " spell stored."; + add_string_to_buf(sout.str()); + return false; + } + if(!pc_can_cast_spell(univ.party[who_would_cast],what_spell)) { add_string_to_buf("Repeat cast: Can't cast."); return false; @@ -1640,8 +1655,8 @@ static void draw_spell_info(cDialog& me, const eSkill store_situation, const sho } break; case SELECT_ANY: - // TODO: Split off party members should probably be excluded too? - if(univ.party[i].main_status != eMainStatus::ABSENT) { + // Absent party members and split-off party members are excluded + if(univ.party[i].main_status != eMainStatus::ABSENT && univ.party[i].main_status < eMainStatus::SPLIT) { me[id].show(); } else { @@ -1691,6 +1706,7 @@ static void draw_spell_pc_info(cDialog& me) { if(univ.party[i].main_status != eMainStatus::ABSENT) { me["pc" + n].setText(univ.party[i].name); + me["arrow" + n].hide(); if(univ.party[i].main_status == eMainStatus::ALIVE) { me["hp" + n].setTextToNum(univ.party[i].cur_health); me["sp" + n].setTextToNum(univ.party[i].cur_sp); @@ -1706,7 +1722,7 @@ static void put_pc_caster_buttons(cDialog& me) { std::string n = boost::lexical_cast(i + 1); if(me["caster" + n].isVisible()) { if(i == pc_casting) - me["pc" + n].setColour(Colours::RED); + me["pc" + n].setColour(SELECTED_COLOUR); else me["pc" + n].setColour(me.getDefTextClr()); } } @@ -1716,13 +1732,11 @@ static void put_pc_target_buttons(cDialog& me, short& store_last_target_darkened if(store_spell_target < 6) { std::string n = boost::lexical_cast(store_spell_target + 1); - me["hp" + n].setColour(Colours::RED); - me["sp" + n].setColour(Colours::RED); + me["arrow" + n].show(); } if((store_last_target_darkened < 6) && (store_last_target_darkened != store_spell_target)) { std::string n = boost::lexical_cast(store_last_target_darkened + 1); - me["hp" + n].setColour(me.getDefTextClr()); - me["sp" + n].setColour(me.getDefTextClr()); + me["arrow" + n].hide(); } store_last_target_darkened = store_spell_target; } @@ -1740,12 +1754,16 @@ static void put_spell_led_buttons(cDialog& me, const eSkill store_situation,cons eSpell spell = cSpell::fromNum(store_situation, spell_for_this_button); if(store_spell == spell_for_this_button) { led.setState(led_green); + // Text color: + led.setColour(SELECTED_COLOUR); } else if(pc_can_cast_spell(univ.party[pc_casting],spell)) { led.setState(led_red); + led.setColour(me.getDefTextClr()); } else { led.setState(led_off); + led.setColour(DISABLED_COLOUR); } } } @@ -1862,9 +1880,7 @@ static bool pick_spell_select_led(cDialog& me, std::string id, eKeyMod mods, con me["feedback"].setText(bad_spell); } else { - if(store_situation == eSkill::MAGE_SPELLS) - store_spell = (on_which_spell_page == 0) ? item_hit : spell_index[item_hit]; - else store_spell = (on_which_spell_page == 0) ? item_hit : spell_index[item_hit]; + store_spell = (on_which_spell_page == 0) ? item_hit : spell_index[item_hit]; draw_spell_info(me, store_situation, store_spell); put_spell_led_buttons(me, store_situation, store_spell); @@ -1914,6 +1930,7 @@ static bool finish_pick_spell(cDialog& me, bool spell_toast, const short store_s if(store_situation == eSkill::MAGE_SPELLS && (*picked_spell).need_select == SELECT_NO) { store_last_cast_mage = pc_casting; univ.party[pc_casting].last_cast[store_situation] = picked_spell; + univ.party[pc_casting].last_cast_type = store_situation; me.toast(false); me.setResult(store_spell); return true; @@ -1921,6 +1938,7 @@ static bool finish_pick_spell(cDialog& me, bool spell_toast, const short store_s if(store_situation == eSkill::PRIEST_SPELLS && (*picked_spell).need_select == SELECT_NO) { store_last_cast_priest = pc_casting; univ.party[pc_casting].last_cast[store_situation] = picked_spell; + univ.party[pc_casting].last_cast_type = store_situation; me.toast(false); me.setResult(store_spell); return true; @@ -1938,6 +1956,7 @@ static bool finish_pick_spell(cDialog& me, bool spell_toast, const short store_s store_last_cast_mage = pc_casting; else store_last_cast_priest = pc_casting; univ.party[pc_casting].last_cast[store_situation] = picked_spell; + univ.party[pc_casting].last_cast_type = store_situation; me.toast(true); return true; } @@ -1947,7 +1966,7 @@ static bool finish_pick_spell(cDialog& me, bool spell_toast, const short store_s //short situation; // 0 - out 1 - town 2 - combat eSpell pick_spell(short pc_num,eSkill type) { // 70 - no spell OW spell num using namespace std::placeholders; - eSpell store_spell = type == eSkill::MAGE_SPELLS ? store_mage : store_priest; + eSpell default_spell = type == eSkill::MAGE_SPELLS ? store_mage : store_priest; short former_target = store_spell_target; short dark = 6; @@ -1995,29 +2014,38 @@ eSpell pick_spell(short pc_num,eSkill type) { // 70 - no spell OW spell num // If in combat, make the spell being cast this PCs most recent spell if(is_combat()) { if(type == eSkill::MAGE_SPELLS) - store_spell = univ.party[pc_casting].last_cast[eSkill::MAGE_SPELLS]; - else store_spell = univ.party[pc_casting].last_cast[eSkill::PRIEST_SPELLS]; + default_spell = univ.party[pc_casting].last_cast[eSkill::MAGE_SPELLS]; + else{ + default_spell = univ.party[pc_casting].last_cast[eSkill::PRIEST_SPELLS]; + if(default_spell == eSpell::NONE){ + default_spell = DEFAULT_SELECTED_PRIEST_COMBAT; + } + } } + if(default_spell == eSpell::NONE){ + default_spell = type == eSkill::MAGE_SPELLS ? DEFAULT_SELECTED_MAGE : DEFAULT_SELECTED_PRIEST; + } + // Keep the stored spell, if it's still castable - if(!pc_can_cast_spell(univ.party[pc_casting],store_spell)) { + if(!pc_can_cast_spell(univ.party[pc_casting],default_spell)) { if(type == eSkill::MAGE_SPELLS) { - store_spell = eSpell::LIGHT; + default_spell = DEFAULT_SELECTED_MAGE; } else { - store_spell = eSpell::HEAL_MINOR; + default_spell = DEFAULT_SELECTED_PRIEST; } } // If a target is needed, keep the same target if that PC still targetable if(store_spell_target < 6) { - if((*store_spell).need_select != SELECT_NO) { + if((*default_spell).need_select != SELECT_NO) { if(univ.party[store_spell_target].main_status != eMainStatus::ALIVE) store_spell_target = 6; } else store_spell_target = 6; } - short former_spell = int(store_spell) % 100; + short former_spell = int(default_spell) % 100; // Set the spell page, based on starting spell if(former_spell >= 38) on_which_spell_page = 1; else on_which_spell_page = 0; @@ -2042,9 +2070,7 @@ eSpell pick_spell(short pc_num,eSkill type) { // 70 - no spell OW spell num cLed& led = dynamic_cast(castSpell[id]); led.attachKey(key); castSpell.addLabelFor(id, {static_cast(i > 25 ? toupper(key.c) : key.c)}, LABEL_LEFT, 8, true); - if(spell_index[i] == 90){ - continue; - } + // All LEDs should get the click handler and set state, because page 2 will hide them if necessary led.setState((pc_can_cast_spell(univ.party[pc_casting],cSpell::fromNum(type,on_which_spell_page == 0 ? i : spell_index[i]))) ? led_red : led_green); led.attachClickHandler(std::bind(pick_spell_select_led, _1, _2, _3, type, std::ref(dark), std::ref(former_spell))); diff --git a/src/gfx/render_shapes.cpp b/src/gfx/render_shapes.cpp index e284547b..bc08df31 100644 --- a/src/gfx/render_shapes.cpp +++ b/src/gfx/render_shapes.cpp @@ -17,6 +17,36 @@ using boost::math::constants::pi; using pt_idx_t = decltype(((sf::Shape*)nullptr)->getPointCount()); +std::map colour_map = { + {"white", Colours::WHITE}, + {"black", Colours::BLACK}, + {"grey", Colours::GREY}, + {"gray", Colours::GREY}, + {"red", Colours::RED}, + {"green", Colours::GREEN}, + {"blue", Colours::BLUE}, + {"teal", Colours::TEAL}, + {"pink", Colours::PINK}, + {"yellow", Colours::YELLOW}, + {"orange", Colours::ORANGE}, + {"light-blue", Colours::LIGHT_BLUE}, + {"shadow", Colours::SHADOW}, + {"title-blue", Colours::TITLE_BLUE}, + {"navy", Colours::NAVY}, + {"dark_blue", Colours::DARK_BLUE}, + {"dark-green", Colours::DARK_GREEN}, + {"light-green", Colours::LIGHT_GREEN}, + {"dark-red", Colours::DARK_RED}, + // Extra Colors + {"lime", Colours::LIME}, + {"aqua", Colours::AQUA}, + {"fuchsia", Colours::FUCHSIA}, + {"maroon", Colours::MAROON}, + {"olive", Colours::OLIVE}, + {"purple", Colours::PURPLE}, + {"silver", Colours::SILVER} +}; + // TODO: Put these classes in a header? class EllipseShape : public sf::Shape { float divSz; diff --git a/src/gfx/render_shapes.hpp b/src/gfx/render_shapes.hpp index 20ca52a3..d56610ae 100644 --- a/src/gfx/render_shapes.hpp +++ b/src/gfx/render_shapes.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include "location.hpp" @@ -48,6 +49,7 @@ void undo_clip(sf::RenderTarget& where); namespace Colours { const sf::Color WHITE = sf::Color::White; const sf::Color BLACK = sf::Color::Black; + const sf::Color GREY { 0x80, 0x80, 0x80}; const sf::Color RED { 0xdd, 0x00, 0x00}; const sf::Color GREEN { 0x00, 0x88, 0x00}; const sf::Color BLUE { 0x00, 0x00, 0xdd}; @@ -55,6 +57,7 @@ namespace Colours { const sf::Color PINK { 0xff, 0x00, 0x99}; const sf::Color YELLOW { 0xff, 0xff, 0x31}; const sf::Color ORANGE { 0xff, 0x80, 0x00}; + const sf::Color LIGHT_BLUE { 0xad, 0xd8, 0xe6 }; // Spell points on dark background // Text colours for shopping / talking // TODO: The Windows version appears to use completely different colours? const sf::Color SHADOW { 0x00, 0x00, 0x68}; // formerly c[3] QD colour = {0,0,26623} (shop/character name shadow, shop subtitle) @@ -64,6 +67,14 @@ namespace Colours { const sf::Color DARK_GREEN { 0x00, 0x60, 0x00}; // formerly c[5] QD colour = {0,40959,0} (talking buttons) const sf::Color LIGHT_GREEN { 0x00, 0xa0, 0x00}; // formerly c[6] QD colour = {0,24575,0} (talking buttons pressed) const sf::Color DARK_RED { 0xa0, 0x00, 0x14}; // formerly c[7] (clickable text, new in OBoE) + // Extra colors + const sf::Color LIME { 0x00, 0xFF, 0x00}; + const sf::Color AQUA { 0x00, 0xFF, 0xFF}; + const sf::Color FUCHSIA { 0xFF, 0x00, 0xFF}; + const sf::Color MAROON { 0x80, 0x00, 0x00}; + const sf::Color OLIVE { 0x80, 0x80, 0x00}; + const sf::Color PURPLE { 0x80, 0x00, 0x80}; + const sf::Color SILVER { 0xC0, 0xC0, 0xC0}; } const sf::Color PRESET_WORD_ON = Colours::DARK_GREEN; diff --git a/src/gfx/render_text.cpp b/src/gfx/render_text.cpp index e7a6d22c..e0e55a49 100644 --- a/src/gfx/render_text.cpp +++ b/src/gfx/render_text.cpp @@ -149,7 +149,7 @@ static void win_draw_string(sf::RenderTarget& dest_window,rectangle dest_rect,st short total_width = str_to_draw.getLocalBounds().width; eTextMode mode = options.mode; - if(mode == eTextMode::WRAP && total_width < dest_rect.width()) + if(mode == eTextMode::WRAP && total_width < dest_rect.width() && !options.right_align) mode = eTextMode::LEFT_TOP; if(mode == eTextMode::LEFT_TOP && str.find('|') != std::string::npos) mode = eTextMode::WRAP; diff --git a/src/spell.cpp b/src/spell.cpp index 30c9311c..b454b157 100644 --- a/src/spell.cpp +++ b/src/spell.cpp @@ -92,6 +92,10 @@ eSpell cSpell::fromNum(int num) { return check; } +// NOTE: +// asPeaceful() marks a spell as castable by pacifist PCs. It doesn't mean "only in peace mode" +// (which would make a lot of these contradictory/broken) + // Mage Spells cSpell M_LIGHT = cSpell(eSpell::LIGHT).asType(eSkill::MAGE_SPELLS).asLevel(1) .withCost(1).when(WHEN_COMBAT).when(WHEN_TOWN).when(WHEN_OUTDOORS).asPeaceful().finish(); @@ -211,7 +215,7 @@ cSpell M_FLIGHT = cSpell(eSpell::FLIGHT).asType(eSkill::MAGE_SPELLS).asLevel(6) cSpell M_SHOCKWAVE = cSpell(eSpell::SHOCKWAVE).asType(eSkill::MAGE_SPELLS).asLevel(7) .withCost(12).withRefer(REFER_IMMED).when(WHEN_COMBAT).finish(); cSpell M_BLESS_MAJOR = cSpell(eSpell::BLESS_MAJOR).asType(eSkill::MAGE_SPELLS).asLevel(7) - .withCost(8).withRefer(REFER_IMMED).when(WHEN_COMBAT).when(WHEN_TOWN).asPeaceful().finish(); + .withCost(8).withRefer(REFER_IMMED).when(WHEN_COMBAT).asPeaceful().finish(); cSpell M_PARALYSIS_MASS = cSpell(eSpell::PARALYSIS_MASS).asType(eSkill::MAGE_SPELLS).asLevel(7) .withRange(8).withCost(20).withRefer(REFER_IMMED).when(WHEN_COMBAT).finish(); cSpell M_PROTECTION = cSpell(eSpell::PROTECTION).asType(eSkill::MAGE_SPELLS).asLevel(7) diff --git a/src/universe/pc.hpp b/src/universe/pc.hpp index cd6b69c2..0235fba7 100644 --- a/src/universe/pc.hpp +++ b/src/universe/pc.hpp @@ -105,7 +105,10 @@ public: eRace race; long unique_id; // transient stuff - std::map last_cast; + std::map last_cast = {{ eSkill::MAGE_SPELLS, eSpell::NONE}, { eSkill::PRIEST_SPELLS, eSpell::NONE }}; + // There is already a global last_spellcast_type, but that variable is for the whole party. + // This one is per-PC + eSkill last_cast_type = eSkill::INVALID; location combat_pos; short parry = 0; iLiving* last_attacked = nullptr; // Note: Currently this is assigned but never read diff --git a/test/init.cpp b/test/init.cpp index d35e2733..48cbda46 100644 --- a/test/init.cpp +++ b/test/init.cpp @@ -258,7 +258,7 @@ TEST_CASE("Construction sanity test for player character") { CHECK(pc.traits.empty()); CHECK(pc.race == eRace::HUMAN); // Skip unique_id since it's non-deterministic - CHECK(pc.last_cast.empty()); + CHECK(pc.last_cast.size() == 2); CHECK(pc.combat_pos == loc(-1,-1)); CHECK(pc.parry == 0); CHECK(pc.last_attacked == nullptr);