diff --git a/Source/inv.cpp b/Source/inv.cpp index 4290f494a..09181aca5 100644 --- a/Source/inv.cpp +++ b/Source/inv.cpp @@ -1671,17 +1671,7 @@ void RemoveInvItem(int pnum, int iv) CalcPlrScrolls(pnum); - if (plr[pnum]._pRSplType == RSPLTYPE_SCROLL) { - if (plr[pnum]._pRSpell != SPL_INVALID) { - if (!( - plr[pnum]._pScrlSpells - & (1ULL << (plr[pnum]._pRSpell - 1)))) { - plr[pnum]._pRSpell = SPL_INVALID; - } - - force_redraw = 255; - } - } + EnsureValidReadiedSpell(plr[pnum]); } #ifdef HELLFIRE @@ -1733,15 +1723,7 @@ void RemoveSpdBarItem(int pnum, int iv) CalcPlrScrolls(pnum); - if (plr[pnum]._pRSplType == RSPLTYPE_SCROLL) { - if (plr[pnum]._pRSpell != SPL_INVALID) { - if (!( - plr[pnum]._pScrlSpells - & (1ULL << (plr[pnum]._pRSpell - 1)))) { - plr[pnum]._pRSpell = SPL_INVALID; - } - } - } + EnsureValidReadiedSpell(plr[pnum]); force_redraw = 255; } diff --git a/Source/items.cpp b/Source/items.cpp index 05c176233..fd55cb786 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -906,13 +906,7 @@ void CalcPlrItemVals(int p, BOOL Loadgfx) plr[p]._pISpells = spl; - // check if the current RSplType is a valid/allowed spell - if (plr[p]._pRSplType == RSPLTYPE_CHARGES - && !(spl & (1ULL << (plr[p]._pRSpell - 1)))) { - plr[p]._pRSpell = SPL_INVALID; - plr[p]._pRSplType = RSPLTYPE_INVALID; - force_redraw = 255; - } + EnsureValidReadiedSpell(plr[p]); plr[p]._pISplLvlAdd = spllvladd; plr[p]._pIEnAc = enac; @@ -1164,13 +1158,7 @@ void CalcPlrScrolls(int p) plr[p]._pScrlSpells |= 1ULL << (plr[p].SpdList[j]._iSpell - 1); } } - if (plr[p]._pRSplType == RSPLTYPE_SCROLL) { - if (!(plr[p]._pScrlSpells & 1ULL << (plr[p]._pRSpell - 1))) { - plr[p]._pRSpell = SPL_INVALID; - plr[p]._pRSplType = RSPLTYPE_INVALID; - force_redraw = 255; - } - } + EnsureValidReadiedSpell(plr[p]); } void CalcPlrStaff(int p) diff --git a/Source/player.cpp b/Source/player.cpp index 5bd6cb5b2..1e523a6cc 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -3188,23 +3188,7 @@ BOOL PM_DoSpell(int pnum) plr[pnum]._pVar4); if (!plr[pnum]._pSplFrom) { - if (plr[pnum]._pRSplType == RSPLTYPE_SCROLL) { - if (!(plr[pnum]._pScrlSpells - & 1ULL << (plr[pnum]._pRSpell - 1))) { - plr[pnum]._pRSpell = SPL_INVALID; - plr[pnum]._pRSplType = RSPLTYPE_INVALID; - force_redraw = 255; - } - } - - if (plr[pnum]._pRSplType == RSPLTYPE_CHARGES) { - if (!(plr[pnum]._pISpells - & 1ULL << (plr[pnum]._pRSpell - 1))) { - plr[pnum]._pRSpell = SPL_INVALID; - plr[pnum]._pRSplType = RSPLTYPE_INVALID; - force_redraw = 255; - } - } + EnsureValidReadiedSpell(plr[pnum]); } } diff --git a/Source/spells.cpp b/Source/spells.cpp index f4206a7ca..0be87e222 100644 --- a/Source/spells.cpp +++ b/Source/spells.cpp @@ -93,6 +93,85 @@ void UseMana(int id, int sn) } } +/** + * @brief Gets a value that represents the specified spellID in 64bit bitmask format. + * For example: + * - spell ID 1: 0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0001 + * - spell ID 43: 0000.0000.0000.0000.0000.0100.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000 + * @param spellId The id of the spell to get a bitmask for. + * @return A 64bit bitmask representation for the specified spell. + */ +unsigned long long GetSpellBitmask(int spellId) +{ + return 1ULL << (spellId - 1); +} + +/** + * @brief Gets a value indicating whether the player's current readied spell is a valid spell. Readied spells can be + * invalidaded in a few scenarios where the spell comes from items, for example (like dropping the only scroll that + * provided the spell). + * @param player The player whose readied spell is to be checked. + * @return 'true' when the readied spell is currently valid, and 'false' otherwise. + */ +bool IsReadiedSpellValid(const PlayerStruct &player) +{ + switch (player._pRSplType) { + case RSPLTYPE_SKILL: + case RSPLTYPE_SPELL: + case RSPLTYPE_INVALID: + return true; + + case RSPLTYPE_CHARGES: + return player._pISpells & GetSpellBitmask(player._pRSpell); + + case RSPLTYPE_SCROLL: + return player._pScrlSpells & GetSpellBitmask(player._pRSpell); + + default: + return false; + } +} + +/** + * @brief Clears the current player's readied spell selection. + * @note Will force a UI redraw in case the values actually change, so that the new spell reflects on the bottom panel. + * @param player The player whose readied spell is to be cleared. + */ +void ClearReadiedSpell(PlayerStruct &player) +{ + bool needsRedraw = false; + + int &readiedSpell = player._pRSpell; + if (readiedSpell != SPL_INVALID) { + readiedSpell = SPL_INVALID; + needsRedraw = true; + } + + char &readiedSpellType = player._pRSplType; + if (readiedSpellType != RSPLTYPE_INVALID) { + readiedSpellType = RSPLTYPE_INVALID; + needsRedraw = true; + } + + if (needsRedraw) { + force_redraw = 255; + } +} + +/** + * @brief Ensures the player's current readied spell is a valid selection for the character. If the current selection is + * incompatible with the player's items and spell (for example, if the player does not currently have access to the spell), + * the selection is cleared. + * @note Will force a UI redraw in case the values actually change, so that the new spell reflects on the bottom panel. + * @param player The player whose readied spell is to be checked. + */ +void EnsureValidReadiedSpell(PlayerStruct &player) +{ + if (!IsReadiedSpellValid(player)) { + ClearReadiedSpell(player); + } +} + BOOL CheckSpell(int id, int sn, char st, BOOL manaonly) { BOOL result; diff --git a/Source/spells.h b/Source/spells.h index 16588c8fd..3d7f7c81f 100644 --- a/Source/spells.h +++ b/Source/spells.h @@ -15,6 +15,7 @@ extern "C" { int GetManaAmount(int id, int sn); void UseMana(int id, int sn); BOOL CheckSpell(int id, int sn, char st, BOOL manaonly); +void EnsureValidReadiedSpell(PlayerStruct &player); void CastSpell(int id, int spl, int sx, int sy, int dx, int dy, int caster, int spllvl); void DoResurrect(int pnum, int rid); void DoHealOther(int pnum, int rid);