From 0ef3ad81d99198caa93b6455fa4662f39ff90070 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Sat, 18 Jan 2025 16:42:16 -0600 Subject: [PATCH] Recast hint for spellcasters --- src/game/boe.actions.cpp | 1 + src/game/boe.graphics.cpp | 40 ++++++++++++++++++++++++++++++++++++--- src/game/boe.graphics.hpp | 2 +- src/game/boe.party.cpp | 3 +++ src/gfx/render_text.cpp | 2 +- src/universe/pc.hpp | 3 +++ 6 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/game/boe.actions.cpp b/src/game/boe.actions.cpp index 85b1b4c7..46ce13d8 100644 --- a/src/game/boe.actions.cpp +++ b/src/game/boe.actions.cpp @@ -1850,6 +1850,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; diff --git a/src/game/boe.graphics.cpp b/src/game/boe.graphics.cpp index 17c7f121..77f8ea14 100644 --- a/src/game/boe.graphics.cpp +++ b/src/game/boe.graphics.cpp @@ -611,8 +611,38 @@ 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 << ": "; + 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"; + } + } + + 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 +653,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,7 +665,11 @@ 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); + // Style has to be wrap to get right-alignment + win_draw_string(text_bar_gworld, to_rect, right_str, eTextMode::WRAP, style, true); + to_rect.right -= string_length(right_str, style); if(!monsters_going) { sf::Texture& status_gworld = *ResMgr::graphics.get("staticons"); 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.party.cpp b/src/game/boe.party.cpp index 138aa48a..766dbd6c 100644 --- a/src/game/boe.party.cpp +++ b/src/game/boe.party.cpp @@ -1926,6 +1926,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; @@ -1933,6 +1934,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; @@ -1950,6 +1952,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; } 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/universe/pc.hpp b/src/universe/pc.hpp index 037536b1..0235fba7 100644 --- a/src/universe/pc.hpp +++ b/src/universe/pc.hpp @@ -106,6 +106,9 @@ public: long unique_id; // transient stuff 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