From ce2ce86070891f3ec22da84eebcb2582237ac351 Mon Sep 17 00:00:00 2001 From: qndel Date: Mon, 6 Mar 2023 18:00:55 +0100 Subject: [PATCH] inspect players command --- Source/control.cpp | 40 +++++++++++++ Source/controls/touch/renderers.cpp | 2 +- Source/cursor.cpp | 2 +- Source/diablo.cpp | 8 +-- Source/inv.cpp | 17 ++++-- Source/multi.cpp | 5 ++ Source/panels/charpanel.cpp | 90 ++++++++++++++--------------- Source/panels/spell_book.cpp | 14 ++--- Source/player.cpp | 1 + Source/player.h | 7 +++ 10 files changed, 122 insertions(+), 64 deletions(-) diff --git a/Source/control.cpp b/Source/control.cpp index 245d99c42..6e8423ecd 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -407,10 +407,45 @@ std::string TextCmdArenaPot(const string_view parameter) return ret; } +std::string TextCmdInspect(const string_view parameter) +{ + std::string ret; + if (!gbIsMultiplayer) { + StrAppend(ret, _("Inspecting only supported in multiplayer.")); + return ret; + } + + if (parameter.empty()) { + StrAppend(ret, _("Stopped inspecting players.")); + InspectPlayer = MyPlayer; + return ret; + } + + std::string param { parameter.data() }; + std::transform(param.begin(), param.end(), param.begin(), [](unsigned char c) { return std::tolower(c); }); + for (auto &player : Players) { + std::string playerName { player._pName }; + std::transform(playerName.begin(), playerName.end(), playerName.begin(), [](unsigned char c) { return std::tolower(c); }); + if (playerName.find(param) != std::string::npos) { + InspectPlayer = &player; + StrAppend(ret, _("Inspecting player: ")); + StrAppend(ret, player._pName); + OpenCharPanel(); + if (!sbookflag) + invflag = true; + RedrawEverything(); + return ret; + } + } + StrAppend(ret, _("No players found with such a name")); + return ret; +} + std::vector TextCmdList = { { N_("/help"), N_("Prints help overview or help for a specific command."), N_("({command})"), &TextCmdHelp }, { N_("/arena"), N_("Enter a PvP Arena."), N_("{arena-number}"), &TextCmdArena }, { N_("/arenapot"), N_("Gives Arena Potions."), N_("{number}"), &TextCmdArenaPot }, + { N_("/inspect"), N_("Inspects stats and equipment of another player."), N_("{player name}"), &TextCmdInspect }, }; bool CheckTextCommand(const string_view text) @@ -609,6 +644,11 @@ void OpenCharPanel() void CloseCharPanel() { chrflag = false; + if (IsInspectingPlayer()) { + InspectPlayer = MyPlayer; + RedrawEverything(); + InitDiabloMsg(_("Stopped inspecting players.")); + } } void ToggleCharPanel() diff --git a/Source/controls/touch/renderers.cpp b/Source/controls/touch/renderers.cpp index 08988d27a..af55e6474 100644 --- a/Source/controls/touch/renderers.cpp +++ b/Source/controls/touch/renderers.cpp @@ -376,7 +376,7 @@ void PotionButtonRenderer::RenderPotion(RenderFunction renderFunction, const But std::optional PotionButtonRenderer::GetPotionType() { - for (const Item &item : MyPlayer->SpdList) { + for (const Item &item : InspectPlayer->SpdList) { if (item.isEmpty()) { continue; } diff --git a/Source/cursor.cpp b/Source/cursor.cpp index 1cfd16f94..7600b44b2 100644 --- a/Source/cursor.cpp +++ b/Source/cursor.cpp @@ -243,7 +243,7 @@ void FreeHalfSizeItemSprites() void DrawItem(const Item &item, const Surface &out, Point position, ClxSprite clx) { - const bool usable = item._iStatFlag; + const bool usable = !IsInspectingPlayer() ? item._iStatFlag : InspectPlayer->CanUseItem(item); if (usable) { ClxDraw(out, position, clx); } else { diff --git a/Source/diablo.cpp b/Source/diablo.cpp index c0c29242c..926f70b6c 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -2461,7 +2461,7 @@ bool TryIconCurs() Player &myPlayer = *MyPlayer; if (pcurs == CURSOR_IDENTIFY) { - if (pcursinvitem != -1) + if (pcursinvitem != -1 && !IsInspectingPlayer()) CheckIdentify(myPlayer, pcursinvitem); else if (pcursstashitem != StashStruct::EmptyCell) { Item &item = Stash.stashList[pcursstashitem]; @@ -2472,7 +2472,7 @@ bool TryIconCurs() } if (pcurs == CURSOR_REPAIR) { - if (pcursinvitem != -1) + if (pcursinvitem != -1 && !IsInspectingPlayer()) DoRepair(myPlayer, pcursinvitem); else if (pcursstashitem != StashStruct::EmptyCell) { Item &item = Stash.stashList[pcursstashitem]; @@ -2483,7 +2483,7 @@ bool TryIconCurs() } if (pcurs == CURSOR_RECHARGE) { - if (pcursinvitem != -1) + if (pcursinvitem != -1 && !IsInspectingPlayer()) DoRecharge(myPlayer, pcursinvitem); else if (pcursstashitem != StashStruct::EmptyCell) { Item &item = Stash.stashList[pcursstashitem]; @@ -2495,7 +2495,7 @@ bool TryIconCurs() if (pcurs == CURSOR_OIL) { bool changeCursor = true; - if (pcursinvitem != -1) + if (pcursinvitem != -1 && !IsInspectingPlayer()) changeCursor = DoOil(myPlayer, pcursinvitem); else if (pcursstashitem != StashStruct::EmptyCell) { Item &item = Stash.stashList[pcursstashitem]; diff --git a/Source/inv.cpp b/Source/inv.cpp index 7ca449d3e..ecf33aaf3 100644 --- a/Source/inv.cpp +++ b/Source/inv.cpp @@ -1015,13 +1015,13 @@ void InvDrawSlotBack(const Surface &out, Point targetPosition, Size size, item_q if (pix >= PAL16_GRAY) { switch (itemQuality) { case ITEM_QUALITY_MAGIC: - pix -= PAL16_GRAY - PAL16_BLUE - 1; + pix -= PAL16_GRAY - (!IsInspectingPlayer() ? PAL16_BLUE : PAL16_ORANGE) - 1; break; case ITEM_QUALITY_UNIQUE: - pix -= PAL16_GRAY - PAL16_YELLOW - 1; + pix -= PAL16_GRAY - (!IsInspectingPlayer() ? PAL16_YELLOW : PAL16_ORANGE) - 1; break; default: - pix -= PAL16_GRAY - PAL16_BEIGE - 1; + pix -= PAL16_GRAY - (!IsInspectingPlayer() ? PAL16_BEIGE : PAL16_ORANGE) - 1; break; } } @@ -1087,7 +1087,7 @@ void DrawInv(const Surface &out) { 133, 160 }, // chest }; - Player &myPlayer = *MyPlayer; + Player &myPlayer = *InspectPlayer; for (int slot = INVLOC_HEAD; slot < NUM_INVLOC; slot++) { if (!myPlayer.InvBody[slot].isEmpty()) { @@ -1163,7 +1163,7 @@ void DrawInvBelt(const Surface &out) DrawPanelBox(out, { 205, 21, 232, 28 }, mainPanelPosition + Displacement { 205, 5 }); - Player &myPlayer = *MyPlayer; + Player &myPlayer = *InspectPlayer; for (int i = 0; i < MaxBeltItems; i++) { if (myPlayer.SpdList[i].isEmpty()) { @@ -1522,6 +1522,8 @@ void TransferItemToStash(Player &player, int location) void CheckInvItem(bool isShiftHeld, bool isCtrlHeld) { + if (IsInspectingPlayer()) + return; if (!MyPlayer->HoldItem.isEmpty()) { CheckInvPaste(*MyPlayer, MousePosition); } else if (IsStashOpen && isCtrlHeld) { @@ -1843,7 +1845,7 @@ int8_t CheckInvHLight() int8_t rv = -1; InfoColor = UiFlags::ColorWhite; Item *pi = nullptr; - Player &myPlayer = *MyPlayer; + Player &myPlayer = *InspectPlayer; if (r == SLOTXY_HEAD) { rv = INVLOC_HEAD; @@ -1978,6 +1980,9 @@ Item &GetInventoryItem(Player &player, int location) bool UseInvItem(int cii) { + if (IsInspectingPlayer()) + return false; + Player &player = *MyPlayer; if (player._pInvincible && player._pHitPoints == 0 && &player == MyPlayer) diff --git a/Source/multi.cpp b/Source/multi.cpp index cb5e596e0..3022c600d 100644 --- a/Source/multi.cpp +++ b/Source/multi.cpp @@ -244,6 +244,9 @@ void PlayerLeftMsg(int pnum, bool left) { Player &player = Players[pnum]; + if (&player == InspectPlayer) + InspectPlayer = MyPlayer; + if (&player == MyPlayer) return; if (!player.plractive) @@ -437,6 +440,7 @@ bool InitSingle(GameData *gameData) MyPlayerId = 0; MyPlayer = &Players[MyPlayerId]; + InspectPlayer = MyPlayer; gbIsMultiplayer = false; pfile_read_player_from_save(gSaveNumber, *MyPlayer); @@ -467,6 +471,7 @@ bool InitMulti(GameData *gameData) } MyPlayerId = playerId; MyPlayer = &Players[MyPlayerId]; + InspectPlayer = MyPlayer; gbIsMultiplayer = true; pfile_read_player_from_save(gSaveNumber, *MyPlayer); diff --git a/Source/panels/charpanel.cpp b/Source/panels/charpanel.cpp index 391043290..10097e31d 100644 --- a/Source/panels/charpanel.cpp +++ b/Source/panels/charpanel.cpp @@ -41,7 +41,7 @@ struct PanelEntry { UiFlags GetBaseStatColor(CharacterAttribute attr) { UiFlags style = UiFlags::ColorWhite; - if (MyPlayer->GetBaseAttributeValue(attr) == MyPlayer->GetMaximumAttributeValue(attr)) + if (InspectPlayer->GetBaseAttributeValue(attr) == InspectPlayer->GetMaximumAttributeValue(attr)) style = UiFlags::ColorWhitegold; return style; } @@ -49,8 +49,8 @@ UiFlags GetBaseStatColor(CharacterAttribute attr) UiFlags GetCurrentStatColor(CharacterAttribute attr) { UiFlags style = UiFlags::ColorWhite; - int current = MyPlayer->GetCurrentAttributeValue(attr); - int base = MyPlayer->GetBaseAttributeValue(attr); + int current = InspectPlayer->GetCurrentAttributeValue(attr); + int base = InspectPlayer->GetBaseAttributeValue(attr); if (current > base) style = UiFlags::ColorBlue; if (current < base) @@ -70,24 +70,24 @@ UiFlags GetValueColor(int value, bool flip = false) UiFlags GetMaxManaColor() { - return MyPlayer->_pMaxMana > MyPlayer->_pMaxManaBase ? UiFlags::ColorBlue : UiFlags::ColorWhite; + return InspectPlayer->_pMaxMana > InspectPlayer->_pMaxManaBase ? UiFlags::ColorBlue : UiFlags::ColorWhite; } UiFlags GetMaxHealthColor() { - return MyPlayer->_pMaxHP > MyPlayer->_pMaxHPBase ? UiFlags::ColorBlue : UiFlags::ColorWhite; + return InspectPlayer->_pMaxHP > InspectPlayer->_pMaxHPBase ? UiFlags::ColorBlue : UiFlags::ColorWhite; } std::pair GetDamage() { - int damageMod = MyPlayer->_pIBonusDamMod; - if (MyPlayer->InvBody[INVLOC_HAND_LEFT]._itype == ItemType::Bow && MyPlayer->_pClass != HeroClass::Rogue) { - damageMod += MyPlayer->_pDamageMod / 2; + int damageMod = InspectPlayer->_pIBonusDamMod; + if (InspectPlayer->InvBody[INVLOC_HAND_LEFT]._itype == ItemType::Bow && InspectPlayer->_pClass != HeroClass::Rogue) { + damageMod += InspectPlayer->_pDamageMod / 2; } else { - damageMod += MyPlayer->_pDamageMod; + damageMod += InspectPlayer->_pDamageMod; } - int mindam = MyPlayer->_pIMinDam + MyPlayer->_pIBonusDam * MyPlayer->_pIMinDam / 100 + damageMod; - int maxdam = MyPlayer->_pIMaxDam + MyPlayer->_pIBonusDam * MyPlayer->_pIMaxDam / 100 + damageMod; + int mindam = InspectPlayer->_pIMinDam + InspectPlayer->_pIBonusDam * InspectPlayer->_pIMinDam / 100 + damageMod; + int maxdam = InspectPlayer->_pIMaxDam + InspectPlayer->_pIBonusDam * InspectPlayer->_pIMaxDam / 100 + damageMod; return { mindam, maxdam }; } @@ -117,78 +117,78 @@ constexpr unsigned GoldHeaderEntryIndex = 16; PanelEntry panelEntries[] = { { "", { 9, 14 }, 150, 0, - []() { return StyledText { UiFlags::ColorWhite, MyPlayer->_pName }; } }, + []() { return StyledText { UiFlags::ColorWhite, InspectPlayer->_pName }; } }, { "", { 161, 14 }, 149, 0, - []() { return StyledText { UiFlags::ColorWhite, std::string(_(PlayersData[static_cast(MyPlayer->_pClass)].className)) }; } }, + []() { return StyledText { UiFlags::ColorWhite, std::string(_(PlayersData[static_cast(InspectPlayer->_pClass)].className)) }; } }, { N_("Level"), { 57, 52 }, 57, 45, - []() { return StyledText { UiFlags::ColorWhite, StrCat(MyPlayer->_pLevel) }; } }, + []() { return StyledText { UiFlags::ColorWhite, StrCat(InspectPlayer->_pLevel) }; } }, { N_("Experience"), { TopRightLabelX, 52 }, 99, 91, []() { - int spacing = ((MyPlayer->_pExperience >= 1000000000) ? 0 : 1); - return StyledText { UiFlags::ColorWhite, FormatInteger(MyPlayer->_pExperience), spacing }; + int spacing = ((InspectPlayer->_pExperience >= 1000000000) ? 0 : 1); + return StyledText { UiFlags::ColorWhite, FormatInteger(InspectPlayer->_pExperience), spacing }; } }, { N_("Next level"), { TopRightLabelX, 80 }, 99, 198, []() { - if (MyPlayer->_pLevel == MaxCharacterLevel) { + if (InspectPlayer->_pLevel == MaxCharacterLevel) { return StyledText { UiFlags::ColorWhitegold, std::string(_("None")) }; } - int spacing = ((MyPlayer->_pNextExper >= 1000000000) ? 0 : 1); - return StyledText { UiFlags::ColorWhite, FormatInteger(MyPlayer->_pNextExper), spacing }; + int spacing = ((InspectPlayer->_pNextExper >= 1000000000) ? 0 : 1); + return StyledText { UiFlags::ColorWhite, FormatInteger(InspectPlayer->_pNextExper), spacing }; } }, { N_("Base"), { LeftColumnLabelX, /* set dynamically */ 0 }, 0, 44, {} }, { N_("Now"), { 135, /* set dynamically */ 0 }, 0, 44, {} }, { N_("Strength"), { LeftColumnLabelX, 135 }, 45, LeftColumnLabelWidth, - []() { return StyledText { GetBaseStatColor(CharacterAttribute::Strength), StrCat(MyPlayer->_pBaseStr) }; } }, + []() { return StyledText { GetBaseStatColor(CharacterAttribute::Strength), StrCat(InspectPlayer->_pBaseStr) }; } }, { "", { 135, 135 }, 45, 0, - []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Strength), StrCat(MyPlayer->_pStrength) }; } }, + []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Strength), StrCat(InspectPlayer->_pStrength) }; } }, { N_("Magic"), { LeftColumnLabelX, 163 }, 45, LeftColumnLabelWidth, - []() { return StyledText { GetBaseStatColor(CharacterAttribute::Magic), StrCat(MyPlayer->_pBaseMag) }; } }, + []() { return StyledText { GetBaseStatColor(CharacterAttribute::Magic), StrCat(InspectPlayer->_pBaseMag) }; } }, { "", { 135, 163 }, 45, 0, - []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Magic), StrCat(MyPlayer->_pMagic) }; } }, - { N_("Dexterity"), { LeftColumnLabelX, 191 }, 45, LeftColumnLabelWidth, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Dexterity), StrCat(MyPlayer->_pBaseDex) }; } }, + []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Magic), StrCat(InspectPlayer->_pMagic) }; } }, + { N_("Dexterity"), { LeftColumnLabelX, 191 }, 45, LeftColumnLabelWidth, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Dexterity), StrCat(InspectPlayer->_pBaseDex) }; } }, { "", { 135, 191 }, 45, 0, - []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Dexterity), StrCat(MyPlayer->_pDexterity) }; } }, - { N_("Vitality"), { LeftColumnLabelX, 219 }, 45, LeftColumnLabelWidth, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Vitality), StrCat(MyPlayer->_pBaseVit) }; } }, + []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Dexterity), StrCat(InspectPlayer->_pDexterity) }; } }, + { N_("Vitality"), { LeftColumnLabelX, 219 }, 45, LeftColumnLabelWidth, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Vitality), StrCat(InspectPlayer->_pBaseVit) }; } }, { "", { 135, 219 }, 45, 0, - []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Vitality), StrCat(MyPlayer->_pVitality) }; } }, + []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Vitality), StrCat(InspectPlayer->_pVitality) }; } }, { N_("Points to distribute"), { LeftColumnLabelX, 248 }, 45, LeftColumnLabelWidth, []() { - MyPlayer->_pStatPts = std::min(CalcStatDiff(*MyPlayer), MyPlayer->_pStatPts); - return StyledText { UiFlags::ColorRed, (MyPlayer->_pStatPts > 0 ? StrCat(MyPlayer->_pStatPts) : "") }; + InspectPlayer->_pStatPts = std::min(CalcStatDiff(*InspectPlayer), InspectPlayer->_pStatPts); + return StyledText { UiFlags::ColorRed, (InspectPlayer->_pStatPts > 0 ? StrCat(InspectPlayer->_pStatPts) : "") }; } }, { N_("Gold"), { TopRightLabelX, /* set dynamically */ 0 }, 0, 98, {} }, { "", { TopRightLabelX, 127 }, 99, 0, - []() { return StyledText { UiFlags::ColorWhite, FormatInteger(MyPlayer->_pGold) }; } }, + []() { return StyledText { UiFlags::ColorWhite, FormatInteger(InspectPlayer->_pGold) }; } }, { N_("Armor class"), { RightColumnLabelX, 163 }, 57, RightColumnLabelWidth, - []() { return StyledText { GetValueColor(MyPlayer->_pIBonusAC), StrCat(MyPlayer->GetArmor() + MyPlayer->_pLevel * 2) }; } }, + []() { return StyledText { GetValueColor(InspectPlayer->_pIBonusAC), StrCat(InspectPlayer->GetArmor() + InspectPlayer->_pLevel * 2) }; } }, { N_("To hit"), { RightColumnLabelX, 191 }, 57, RightColumnLabelWidth, - []() { return StyledText { GetValueColor(MyPlayer->_pIBonusToHit), StrCat(MyPlayer->InvBody[INVLOC_HAND_LEFT]._itype == ItemType::Bow ? MyPlayer->GetRangedToHit() : MyPlayer->GetMeleeToHit(), "%") }; } }, + []() { return StyledText { GetValueColor(InspectPlayer->_pIBonusToHit), StrCat(InspectPlayer->InvBody[INVLOC_HAND_LEFT]._itype == ItemType::Bow ? InspectPlayer->GetRangedToHit() : InspectPlayer->GetMeleeToHit(), "%") }; } }, { N_("Damage"), { RightColumnLabelX, 219 }, 57, RightColumnLabelWidth, []() { std::pair dmg = GetDamage(); int spacing = ((dmg.first >= 100) ? -1 : 1); - return StyledText { GetValueColor(MyPlayer->_pIBonusDam), StrCat(dmg.first, "-", dmg.second), spacing }; + return StyledText { GetValueColor(InspectPlayer->_pIBonusDam), StrCat(dmg.first, "-", dmg.second), spacing }; } }, { N_("Life"), { LeftColumnLabelX, 284 }, 45, LeftColumnLabelWidth, - []() { return StyledText { GetMaxHealthColor(), StrCat(MyPlayer->_pMaxHP >> 6) }; } }, + []() { return StyledText { GetMaxHealthColor(), StrCat(InspectPlayer->_pMaxHP >> 6) }; } }, { "", { 135, 284 }, 45, 0, - []() { return StyledText { (MyPlayer->_pHitPoints != MyPlayer->_pMaxHP ? UiFlags::ColorRed : GetMaxHealthColor()), StrCat(MyPlayer->_pHitPoints >> 6) }; } }, + []() { return StyledText { (InspectPlayer->_pHitPoints != InspectPlayer->_pMaxHP ? UiFlags::ColorRed : GetMaxHealthColor()), StrCat(InspectPlayer->_pHitPoints >> 6) }; } }, { N_("Mana"), { LeftColumnLabelX, 312 }, 45, LeftColumnLabelWidth, - []() { return StyledText { GetMaxManaColor(), StrCat(MyPlayer->_pMaxMana >> 6) }; } }, + []() { return StyledText { GetMaxManaColor(), StrCat(InspectPlayer->_pMaxMana >> 6) }; } }, { "", { 135, 312 }, 45, 0, - []() { return StyledText { (MyPlayer->_pMana != MyPlayer->_pMaxMana ? UiFlags::ColorRed : GetMaxManaColor()), StrCat(MyPlayer->_pMana >> 6) }; } }, + []() { return StyledText { (InspectPlayer->_pMana != InspectPlayer->_pMaxMana ? UiFlags::ColorRed : GetMaxManaColor()), StrCat(InspectPlayer->_pMana >> 6) }; } }, { N_("Resist magic"), { RightColumnLabelX, 256 }, 57, RightColumnLabelWidth, - []() { return GetResistInfo(MyPlayer->_pMagResist); } }, + []() { return GetResistInfo(InspectPlayer->_pMagResist); } }, { N_("Resist fire"), { RightColumnLabelX, 284 }, 57, RightColumnLabelWidth, - []() { return GetResistInfo(MyPlayer->_pFireResist); } }, + []() { return GetResistInfo(InspectPlayer->_pFireResist); } }, { N_("Resist lightning"), { RightColumnLabelX, 313 }, 57, RightColumnLabelWidth, - []() { return GetResistInfo(MyPlayer->_pLghtResist); } }, + []() { return GetResistInfo(InspectPlayer->_pLghtResist); } }, }; OptionalOwnedClxSpriteList Panel; @@ -241,14 +241,14 @@ void DrawShadowString(const Surface &out, const PanelEntry &entry) void DrawStatButtons(const Surface &out) { - if (MyPlayer->_pStatPts > 0) { - if (MyPlayer->_pBaseStr < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Strength)) + if (InspectPlayer->_pStatPts > 0 && !IsInspectingPlayer()) { + if (InspectPlayer->_pBaseStr < InspectPlayer->GetMaximumAttributeValue(CharacterAttribute::Strength)) ClxDraw(out, GetPanelPosition(UiPanels::Character, { 137, 157 }), (*pChrButtons)[chrbtn[static_cast(CharacterAttribute::Strength)] ? 2 : 1]); - if (MyPlayer->_pBaseMag < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Magic)) + if (InspectPlayer->_pBaseMag < InspectPlayer->GetMaximumAttributeValue(CharacterAttribute::Magic)) ClxDraw(out, GetPanelPosition(UiPanels::Character, { 137, 185 }), (*pChrButtons)[chrbtn[static_cast(CharacterAttribute::Magic)] ? 4 : 3]); - if (MyPlayer->_pBaseDex < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Dexterity)) + if (InspectPlayer->_pBaseDex < InspectPlayer->GetMaximumAttributeValue(CharacterAttribute::Dexterity)) ClxDraw(out, GetPanelPosition(UiPanels::Character, { 137, 214 }), (*pChrButtons)[chrbtn[static_cast(CharacterAttribute::Dexterity)] ? 6 : 5]); - if (MyPlayer->_pBaseVit < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Vitality)) + if (InspectPlayer->_pBaseVit < InspectPlayer->GetMaximumAttributeValue(CharacterAttribute::Vitality)) ClxDraw(out, GetPanelPosition(UiPanels::Character, { 137, 242 }), (*pChrButtons)[chrbtn[static_cast(CharacterAttribute::Vitality)] ? 8 : 7]); } } diff --git a/Source/panels/spell_book.cpp b/Source/panels/spell_book.cpp index e12918af2..e73b26acb 100644 --- a/Source/panels/spell_book.cpp +++ b/Source/panels/spell_book.cpp @@ -43,7 +43,7 @@ SpellID GetSpellFromSpellPage(size_t page, size_t entry) { assert(page <= SpellBookPages && entry <= SpellBookPageEntries); if (page == 0 && entry == 0) { - switch (MyPlayer->_pClass) { + switch (InspectPlayer->_pClass) { case HeroClass::Warrior: return SpellID::ItemRepair; case HeroClass::Rogue: @@ -75,7 +75,7 @@ void PrintSBookStr(const Surface &out, Point position, string_view text, UiFlags SpellType GetSBookTrans(SpellID ii, bool townok) { - Player &player = *MyPlayer; + Player &player = *InspectPlayer; if ((player._pClass == HeroClass::Monk) && (ii == SpellID::Search)) return SpellType::Skill; SpellType st = SpellType::Spell; @@ -86,7 +86,7 @@ SpellType GetSBookTrans(SpellID ii, bool townok) st = SpellType::Skill; } if (st == SpellType::Spell) { - if (CheckSpell(*MyPlayer, ii, st, true) != SpellCheckResult::Success) { + if (CheckSpell(*InspectPlayer, ii, st, true) != SpellCheckResult::Success) { st = SpellType::Invalid; } if (player.GetSpellLevel(ii) == 0) { @@ -129,7 +129,7 @@ void DrawSpellBook(const Surface &out) } ClxDraw(out, GetPanelPosition(UiPanels::Spell, { sx, 348 }), (*pSBkBtnCel)[sbooktab]); } - Player &player = *MyPlayer; + Player &player = *InspectPlayer; uint64_t spl = player._pMemSpells | player._pISpells | player._pAblSpells; const int lineHeight = 18; @@ -143,7 +143,7 @@ void DrawSpellBook(const Surface &out) SetSpellTrans(st); const Point spellCellPosition = GetPanelPosition(UiPanels::Spell, { 11, yp + SpellBookDescription.height }); DrawSmallSpellIcon(out, spellCellPosition, sn); - if (sn == player._pRSpell && st == player._pRSplType) { + if (sn == player._pRSpell && st == player._pRSplType && !IsInspectingPlayer()) { SetSpellTrans(SpellType::Skill); DrawSmallSpellIconBorder(out, spellCellPosition); } @@ -196,9 +196,9 @@ void CheckSBook() // enough to the height of the space given to spell descriptions that we can reuse that value and subtract the // padding from the end of the area. Rectangle iconArea = { GetPanelPosition(UiPanels::Spell, { 11, 18 }), Size { 37, SpellBookDescription.height * 7 - 5 } }; - if (iconArea.contains(MousePosition)) { + if (iconArea.contains(MousePosition) && !IsInspectingPlayer()) { SpellID sn = GetSpellFromSpellPage(sbooktab, (MousePosition.y - iconArea.position.y) / SpellBookDescription.height); - Player &player = *MyPlayer; + Player &player = *InspectPlayer; uint64_t spl = player._pMemSpells | player._pISpells | player._pAblSpells; if (IsValidSpell(sn) && (spl & GetSpellBitmask(sn)) != 0) { SpellType st = SpellType::Spell; diff --git a/Source/player.cpp b/Source/player.cpp index f19160f00..fc7cbf353 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -52,6 +52,7 @@ namespace devilution { size_t MyPlayerId; Player *MyPlayer; std::vector Players; +Player *InspectPlayer; bool MyPlayerIsDead; /** Specifies the X-coordinate delta from the player start location in Tristram. */ diff --git a/Source/player.h b/Source/player.h index 2709be9d0..c23bbc869 100644 --- a/Source/player.h +++ b/Source/player.h @@ -762,6 +762,13 @@ struct Player { extern DVL_API_FOR_TEST size_t MyPlayerId; extern DVL_API_FOR_TEST Player *MyPlayer; extern DVL_API_FOR_TEST std::vector Players; +/** @brief What Player items and stats should be displayed? Normally this is identical to MyPlayer but can differ when /inspect was used. */ +extern Player *InspectPlayer; +/** @brief Do we currently inspect a remote player (/inspect was used)? In this case the (remote) players items and stats can't be modified. */ +inline bool IsInspectingPlayer() +{ + return MyPlayer != InspectPlayer; +} extern bool MyPlayerIsDead; Player *PlayerAtPosition(Point position);