From e35086d6b21baae2a1050aa2e62cc03a7dc346cc Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Wed, 14 May 2025 15:40:54 -0500 Subject: [PATCH] resurrection spells hide invalid targets & hide res spells when all alive --- src/damage.hpp | 3 ++ src/game/boe.items.cpp | 6 ++++ src/game/boe.items.hpp | 1 + src/game/boe.party.cpp | 63 +++++++++++++++++++++++++++------------ src/game/boe.specials.cpp | 2 ++ src/spell.cpp | 6 ++-- src/spell.hpp | 2 +- 7 files changed, 60 insertions(+), 23 deletions(-) diff --git a/src/damage.hpp b/src/damage.hpp index efa8ec2f..6be4db4e 100644 --- a/src/damage.hpp +++ b/src/damage.hpp @@ -11,6 +11,7 @@ #include #include +#include enum class eDamageType { WEAPON = 0, @@ -92,6 +93,8 @@ enum class eMainStatus { SPLIT_WON = SPLIT + WON, }; +const std::set dead_statuses = { eMainStatus::DEAD, eMainStatus::STONE, eMainStatus::DUST }; + inline eMainStatus exceptSplit(eMainStatus stat) { if(int(stat) >= 10) return (eMainStatus) (-10 + (int)stat); diff --git a/src/game/boe.items.cpp b/src/game/boe.items.cpp index 36697609..9b3a1cf5 100644 --- a/src/game/boe.items.cpp +++ b/src/game/boe.items.cpp @@ -1002,6 +1002,12 @@ short select_pc(eSelectPC mode, std::string title, eSkill highlight_highest, boo extra_info = ""; } break; + case eSelectPC::ONLY_STONE: + if(univ.party[i].main_status != eMainStatus::STONE){ + can_pick = false; + extra_info = ""; + } + break; case eSelectPC::ONLY_DEAD: if(univ.party[i].main_status == eMainStatus::ALIVE){ can_pick = false; diff --git a/src/game/boe.items.hpp b/src/game/boe.items.hpp index 12e306b9..f7870fe0 100644 --- a/src/game/boe.items.hpp +++ b/src/game/boe.items.hpp @@ -55,6 +55,7 @@ enum class eSelectPC { // Must have skill points ONLY_CAN_TRAIN, ONLY_DEAD, + ONLY_STONE, }; // Prompt the player to choose a party member. Returns 0-5 for a pc, 6 for cancel, 7 for all, or 8 if no PCs fit the mode's filter. // Pass a string poiner to no_choice_reason to get the reason why no choices were available, if none are. diff --git a/src/game/boe.party.cpp b/src/game/boe.party.cpp index e284ce93..74d50b59 100644 --- a/src/game/boe.party.cpp +++ b/src/game/boe.party.cpp @@ -1598,6 +1598,20 @@ bool pc_can_cast_spell(const cPlayer& pc,eSpell spell_num) { eSkill type = (*spell_num).type; cSpell spell = *spell_num; + if(spell.need_select == eSpellSelect::SELECT_DEAD){ + bool any_dead = false; + for(int i = 0; i < 6; ++i){ + if(dead_statuses.count(univ.party[i].main_status)) any_dead = true; + } + if(!any_dead) return false; + }else if(spell.need_select == eSpellSelect::SELECT_STONE){ + bool any_stone = false; + for(int i = 0; i < 6; ++i){ + if(univ.party[i].main_status == eMainStatus::STONE) any_stone = true; + } + if(!any_stone) return false; + } + level = spell.level; int effective_skill = pc.skill(type); if(pc.status[eStatus::DUMB] < 0) @@ -1696,7 +1710,21 @@ static void draw_spell_info(cDialog& me, const eSkill store_situation, const sho me[id].hide(); } break; - + case SELECT_DEAD: + if(dead_statuses.count(univ.party[i].main_status)) { + me[id].show(); + }else{ + me[id].hide(); + } + break; + case SELECT_STONE: + if(univ.party[i].main_status == eMainStatus::STONE) { + me[id].show(); + } + else { + me[id].hide(); + } + break; } } } @@ -1887,22 +1915,12 @@ static bool pick_spell_caster(cDialog& me, std::string id, const eSkill store_si } static bool pick_spell_target(cDialog& me, std::string id, const eSkill store_situation, short& last_darkened, const short& store_spell) { - static const char*const no_target = " No target needed."; - static const char*const bad_target = " Can't cast on them."; static const char*const got_target = " Target selected."; short item_hit = id[id.length() - 1] - '1'; - std::string casting = id; - casting[casting.length() - 1] = pc_casting + '1'; - if(!me[casting].isVisible()) { - me["feedback"].setText(no_target); - } else if(!me[id].isVisible()) { - me["feedback"].setText(bad_target); - } else { - me["feedback"].setText(got_target); - store_spell_target = item_hit; - draw_spell_info(me, store_situation, store_spell); - put_pc_target_buttons(me, last_darkened); - } + me["feedback"].setText(got_target); + store_spell_target = item_hit; + draw_spell_info(me, store_situation, store_spell); + put_pc_target_buttons(me, last_darkened); return true; } @@ -1941,14 +1959,21 @@ static bool pick_spell_select_led(cDialog& me, std::string id, eKeyMod mods, con put_pc_target_buttons(me, last_darkened); } } - // Cute trick now... if a target is needed, caster can always be picked - std::string targ = "target" + boost::lexical_cast(pc_casting + 1); - if((store_spell_target == 6) && me[targ].isVisible()) { + bool any_targets = false; + for(int i = 0; i < 6; ++i){ + std::string targ = "target" + boost::lexical_cast(i + 1); + if(me[targ].isVisible()){ + any_targets = true; + break; + } + } + if((store_spell_target == 6) && any_targets) { me["feedback"].setText(choose_target); draw_spell_info(me, store_situation, store_spell); play_sound(45); // formerly force_play_sound } - else if(!me[targ].isVisible()) { + else{ + me["feedback"].setText(""); store_spell_target = 6; put_pc_target_buttons(me, last_darkened); } diff --git a/src/game/boe.specials.cpp b/src/game/boe.specials.cpp index c86942f2..a97938e4 100644 --- a/src/game/boe.specials.cpp +++ b/src/game/boe.specials.cpp @@ -1133,6 +1133,8 @@ void use_item(short pc,short item) { case SELECT_NO: break; case SELECT_ACTIVE: store_spell_target = select_pc(eSelectPC::ONLY_LIVING); break; case SELECT_ANY: store_spell_target = select_pc(eSelectPC::ANY); break; + case SELECT_DEAD: store_spell_target = select_pc(eSelectPC::ONLY_DEAD); break; + case SELECT_STONE: store_spell_target = select_pc(eSelectPC::ONLY_STONE); break; } if(overall_mode == MODE_COMBAT) { bool priest = (*spell).is_priest(); diff --git a/src/spell.cpp b/src/spell.cpp index 2c9da089..bf6be596 100644 --- a/src/spell.cpp +++ b/src/spell.cpp @@ -328,7 +328,7 @@ cSpell P_BLESS_PARTY = cSpell(eSpell::BLESS_PARTY).asType(eSkill::PRIEST_SPELLS) cSpell P_HEAL_MAJOR = cSpell(eSpell::HEAL_MAJOR).asType(eSkill::PRIEST_SPELLS).asLevel(5) .withCost(7).needsSelect().when(WHEN_COMBAT).when(WHEN_TOWN).when(WHEN_OUTDOORS).finish(); cSpell P_RAISE_DEAD = cSpell(eSpell::RAISE_DEAD).asType(eSkill::PRIEST_SPELLS).asLevel(5) - .withCost(25).needsSelect(SELECT_ANY).when(WHEN_TOWN).when(WHEN_OUTDOORS).finish(); + .withCost(25).needsSelect(SELECT_DEAD).when(WHEN_TOWN).when(WHEN_OUTDOORS).finish(); cSpell P_FLAMESTRIKE = cSpell(eSpell::FLAMESTRIKE).asType(eSkill::PRIEST_SPELLS).asLevel(5) .withRange(9).withTargetLock().withCost(8).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell P_SANCTUARY_MASS = cSpell(eSpell::SANCTUARY_MASS).asType(eSkill::PRIEST_SPELLS).asLevel(5) @@ -348,7 +348,7 @@ cSpell P_REVIVE = cSpell(eSpell::REVIVE).asType(eSkill::PRIEST_SPELLS).asLevel(6 cSpell P_HYPERACTIVITY = cSpell(eSpell::HYPERACTIVITY).asType(eSkill::PRIEST_SPELLS).asLevel(6) .withCost(8).when(WHEN_COMBAT).when(WHEN_TOWN).when(WHEN_OUTDOORS).asPeaceful().finish(); cSpell P_DESTONE = cSpell(eSpell::DESTONE).asType(eSkill::PRIEST_SPELLS).asLevel(6) - .withCost(8).needsSelect(SELECT_ANY).when(WHEN_TOWN).when(WHEN_OUTDOORS).finish(); + .withCost(8).needsSelect(SELECT_STONE).when(WHEN_TOWN).when(WHEN_OUTDOORS).finish(); cSpell P_SUMMON_GUARDIAN = cSpell(eSpell::SUMMON_GUARDIAN).asType(eSkill::PRIEST_SPELLS).asLevel(6) .withRange(4).withCost(14).withRefer(REFER_TARGET).when(WHEN_COMBAT).when(WHEN_TOWN).finish(); // This is always centered on the caster: @@ -366,7 +366,7 @@ cSpell P_REVIVE_ALL = cSpell(eSpell::REVIVE_ALL).asType(eSkill::PRIEST_SPELLS).a cSpell P_RAVAGE_SPIRIT = cSpell(eSpell::RAVAGE_SPIRIT).asType(eSkill::PRIEST_SPELLS).asLevel(7) .withRange(4).withTargetLock().withCost(10).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell P_RESURRECT = cSpell(eSpell::RESURRECT).asType(eSkill::PRIEST_SPELLS).asLevel(7) - .withCost(35).needsSelect(SELECT_ANY).when(WHEN_TOWN).when(WHEN_OUTDOORS).finish(); + .withCost(35).needsSelect(SELECT_DEAD).when(WHEN_TOWN).when(WHEN_OUTDOORS).finish(); cSpell P_DIVINE_THUD = cSpell(eSpell::DIVINE_THUD).asType(eSkill::PRIEST_SPELLS).asLevel(7) .withRange(12).withTargetLock().withCost(10).withRefer(REFER_TARGET).when(WHEN_COMBAT).finish(); cSpell P_AVATAR = cSpell(eSpell::AVATAR).asType(eSkill::PRIEST_SPELLS).asLevel(7) diff --git a/src/spell.hpp b/src/spell.hpp index 8f7d5fa3..d22746a7 100644 --- a/src/spell.hpp +++ b/src/spell.hpp @@ -16,7 +16,7 @@ // This controls how a spell is cast in combat; YES means it's the same as in town, IMMED means it has a special implementation in town, and TARGET / FANCY both mean it requires target selection. enum eSpellRefer {REFER_YES, REFER_IMMED, REFER_TARGET, REFER_FANCY}; // This specifies whether a spell targets a party member and whether dead PCs can be chosen. -enum eSpellSelect {SELECT_NO, SELECT_ACTIVE, SELECT_ANY}; +enum eSpellSelect {SELECT_NO, SELECT_ACTIVE, SELECT_ANY, SELECT_DEAD, SELECT_STONE}; // This one is meant for indexing a bit field enum eSpellWhen {WHEN_COMBAT = 1, WHEN_TOWN = 2, WHEN_OUTDOORS = 4};