mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-02 15:53:51 +00:00
feat: track UNIT_FIELD_STAT0-4 from server update fields for accurate character stats
Add UNIT_FIELD_STAT0-4 (STR/AGI/STA/INT/SPI) to the UF enum and wire up per-expansion indices in all four expansion JSON files (WotLK: 84-88, Classic/Turtle: 138-142, TBC: 159-163). Read the values in both CREATE and VALUES player update handlers and store in playerStats_[5]. renderStatsPanel now uses the server-authoritative totals when available, falling back to the previous 20+level estimate only if the server hasn't sent UNIT_FIELD_STAT* yet. Item-query bonuses are still shown as (+N) alongside the server total for both paths.
This commit is contained in:
parent
d95abfb607
commit
99de1fa3e5
10 changed files with 115 additions and 30 deletions
|
|
@ -17,6 +17,11 @@
|
||||||
"UNIT_NPC_FLAGS": 147,
|
"UNIT_NPC_FLAGS": 147,
|
||||||
"UNIT_DYNAMIC_FLAGS": 143,
|
"UNIT_DYNAMIC_FLAGS": 143,
|
||||||
"UNIT_FIELD_RESISTANCES": 154,
|
"UNIT_FIELD_RESISTANCES": 154,
|
||||||
|
"UNIT_FIELD_STAT0": 138,
|
||||||
|
"UNIT_FIELD_STAT1": 139,
|
||||||
|
"UNIT_FIELD_STAT2": 140,
|
||||||
|
"UNIT_FIELD_STAT3": 141,
|
||||||
|
"UNIT_FIELD_STAT4": 142,
|
||||||
"UNIT_END": 188,
|
"UNIT_END": 188,
|
||||||
"PLAYER_FLAGS": 190,
|
"PLAYER_FLAGS": 190,
|
||||||
"PLAYER_BYTES": 191,
|
"PLAYER_BYTES": 191,
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@
|
||||||
"UNIT_NPC_FLAGS": 168,
|
"UNIT_NPC_FLAGS": 168,
|
||||||
"UNIT_DYNAMIC_FLAGS": 164,
|
"UNIT_DYNAMIC_FLAGS": 164,
|
||||||
"UNIT_FIELD_RESISTANCES": 185,
|
"UNIT_FIELD_RESISTANCES": 185,
|
||||||
|
"UNIT_FIELD_STAT0": 159,
|
||||||
|
"UNIT_FIELD_STAT1": 160,
|
||||||
|
"UNIT_FIELD_STAT2": 161,
|
||||||
|
"UNIT_FIELD_STAT3": 162,
|
||||||
|
"UNIT_FIELD_STAT4": 163,
|
||||||
"UNIT_END": 234,
|
"UNIT_END": 234,
|
||||||
"PLAYER_FLAGS": 236,
|
"PLAYER_FLAGS": 236,
|
||||||
"PLAYER_BYTES": 237,
|
"PLAYER_BYTES": 237,
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@
|
||||||
"UNIT_NPC_FLAGS": 147,
|
"UNIT_NPC_FLAGS": 147,
|
||||||
"UNIT_DYNAMIC_FLAGS": 143,
|
"UNIT_DYNAMIC_FLAGS": 143,
|
||||||
"UNIT_FIELD_RESISTANCES": 154,
|
"UNIT_FIELD_RESISTANCES": 154,
|
||||||
|
"UNIT_FIELD_STAT0": 138,
|
||||||
|
"UNIT_FIELD_STAT1": 139,
|
||||||
|
"UNIT_FIELD_STAT2": 140,
|
||||||
|
"UNIT_FIELD_STAT3": 141,
|
||||||
|
"UNIT_FIELD_STAT4": 142,
|
||||||
"UNIT_END": 188,
|
"UNIT_END": 188,
|
||||||
"PLAYER_FLAGS": 190,
|
"PLAYER_FLAGS": 190,
|
||||||
"PLAYER_BYTES": 191,
|
"PLAYER_BYTES": 191,
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@
|
||||||
"UNIT_NPC_FLAGS": 82,
|
"UNIT_NPC_FLAGS": 82,
|
||||||
"UNIT_DYNAMIC_FLAGS": 147,
|
"UNIT_DYNAMIC_FLAGS": 147,
|
||||||
"UNIT_FIELD_RESISTANCES": 99,
|
"UNIT_FIELD_RESISTANCES": 99,
|
||||||
|
"UNIT_FIELD_STAT0": 84,
|
||||||
|
"UNIT_FIELD_STAT1": 85,
|
||||||
|
"UNIT_FIELD_STAT2": 86,
|
||||||
|
"UNIT_FIELD_STAT3": 87,
|
||||||
|
"UNIT_FIELD_STAT4": 88,
|
||||||
"UNIT_END": 148,
|
"UNIT_END": 148,
|
||||||
"PLAYER_FLAGS": 150,
|
"PLAYER_FLAGS": 150,
|
||||||
"PLAYER_BYTES": 153,
|
"PLAYER_BYTES": 153,
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,13 @@ public:
|
||||||
// Server-authoritative armor (UNIT_FIELD_RESISTANCES[0])
|
// Server-authoritative armor (UNIT_FIELD_RESISTANCES[0])
|
||||||
int32_t getArmorRating() const { return playerArmorRating_; }
|
int32_t getArmorRating() const { return playerArmorRating_; }
|
||||||
|
|
||||||
|
// Server-authoritative primary stats (UNIT_FIELD_STAT0-4: STR, AGI, STA, INT, SPI).
|
||||||
|
// Returns -1 if the server hasn't sent the value yet.
|
||||||
|
int32_t getPlayerStat(int idx) const {
|
||||||
|
if (idx < 0 || idx > 4) return -1;
|
||||||
|
return playerStats_[idx];
|
||||||
|
}
|
||||||
|
|
||||||
// Inventory
|
// Inventory
|
||||||
Inventory& getInventory() { return inventory; }
|
Inventory& getInventory() { return inventory; }
|
||||||
const Inventory& getInventory() const { return inventory; }
|
const Inventory& getInventory() const { return inventory; }
|
||||||
|
|
@ -2109,6 +2116,8 @@ private:
|
||||||
std::unordered_map<uint64_t, float> recentLootMoneyAnnounceCooldowns_;
|
std::unordered_map<uint64_t, float> recentLootMoneyAnnounceCooldowns_;
|
||||||
uint64_t playerMoneyCopper_ = 0;
|
uint64_t playerMoneyCopper_ = 0;
|
||||||
int32_t playerArmorRating_ = 0;
|
int32_t playerArmorRating_ = 0;
|
||||||
|
// Server-authoritative primary stats: [0]=STR [1]=AGI [2]=STA [3]=INT [4]=SPI; -1 = not received yet
|
||||||
|
int32_t playerStats_[5] = {-1, -1, -1, -1, -1};
|
||||||
// Some servers/custom clients shift update field indices. We can auto-detect coinage by correlating
|
// Some servers/custom clients shift update field indices. We can auto-detect coinage by correlating
|
||||||
// money-notify deltas with update-field diffs and then overriding UF::PLAYER_FIELD_COINAGE at runtime.
|
// money-notify deltas with update-field diffs and then overriding UF::PLAYER_FIELD_COINAGE at runtime.
|
||||||
uint32_t pendingMoneyDelta_ = 0;
|
uint32_t pendingMoneyDelta_ = 0;
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,11 @@ enum class UF : uint16_t {
|
||||||
UNIT_NPC_FLAGS,
|
UNIT_NPC_FLAGS,
|
||||||
UNIT_DYNAMIC_FLAGS,
|
UNIT_DYNAMIC_FLAGS,
|
||||||
UNIT_FIELD_RESISTANCES, // Physical armor (index 0 of the resistance array)
|
UNIT_FIELD_RESISTANCES, // Physical armor (index 0 of the resistance array)
|
||||||
|
UNIT_FIELD_STAT0, // Strength (effective base, includes items)
|
||||||
|
UNIT_FIELD_STAT1, // Agility
|
||||||
|
UNIT_FIELD_STAT2, // Stamina
|
||||||
|
UNIT_FIELD_STAT3, // Intellect
|
||||||
|
UNIT_FIELD_STAT4, // Spirit
|
||||||
UNIT_END,
|
UNIT_END,
|
||||||
|
|
||||||
// Player fields
|
// Player fields
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,8 @@ private:
|
||||||
int bagIndex, float defaultX, float defaultY, uint64_t moneyCopper);
|
int bagIndex, float defaultX, float defaultY, uint64_t moneyCopper);
|
||||||
void renderEquipmentPanel(game::Inventory& inventory);
|
void renderEquipmentPanel(game::Inventory& inventory);
|
||||||
void renderBackpackPanel(game::Inventory& inventory, bool collapseEmptySections = false);
|
void renderBackpackPanel(game::Inventory& inventory, bool collapseEmptySections = false);
|
||||||
void renderStatsPanel(game::Inventory& inventory, uint32_t playerLevel, int32_t serverArmor = 0);
|
void renderStatsPanel(game::Inventory& inventory, uint32_t playerLevel, int32_t serverArmor = 0,
|
||||||
|
const int32_t* serverStats = nullptr);
|
||||||
void renderReputationPanel(game::GameHandler& gameHandler);
|
void renderReputationPanel(game::GameHandler& gameHandler);
|
||||||
|
|
||||||
void renderItemSlot(game::Inventory& inventory, const game::ItemSlot& slot,
|
void renderItemSlot(game::Inventory& inventory, const game::ItemSlot& slot,
|
||||||
|
|
|
||||||
|
|
@ -6098,6 +6098,7 @@ void GameHandler::selectCharacter(uint64_t characterGuid) {
|
||||||
onlineEquipDirty_ = false;
|
onlineEquipDirty_ = false;
|
||||||
playerMoneyCopper_ = 0;
|
playerMoneyCopper_ = 0;
|
||||||
playerArmorRating_ = 0;
|
playerArmorRating_ = 0;
|
||||||
|
std::fill(std::begin(playerStats_), std::end(playerStats_), -1);
|
||||||
knownSpells.clear();
|
knownSpells.clear();
|
||||||
spellCooldowns.clear();
|
spellCooldowns.clear();
|
||||||
actionBar = {};
|
actionBar = {};
|
||||||
|
|
@ -8142,6 +8143,11 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
||||||
const uint16_t ufCoinage = fieldIndex(UF::PLAYER_FIELD_COINAGE);
|
const uint16_t ufCoinage = fieldIndex(UF::PLAYER_FIELD_COINAGE);
|
||||||
const uint16_t ufArmor = fieldIndex(UF::UNIT_FIELD_RESISTANCES);
|
const uint16_t ufArmor = fieldIndex(UF::UNIT_FIELD_RESISTANCES);
|
||||||
const uint16_t ufPBytes2 = fieldIndex(UF::PLAYER_BYTES_2);
|
const uint16_t ufPBytes2 = fieldIndex(UF::PLAYER_BYTES_2);
|
||||||
|
const uint16_t ufStats[5] = {
|
||||||
|
fieldIndex(UF::UNIT_FIELD_STAT0), fieldIndex(UF::UNIT_FIELD_STAT1),
|
||||||
|
fieldIndex(UF::UNIT_FIELD_STAT2), fieldIndex(UF::UNIT_FIELD_STAT3),
|
||||||
|
fieldIndex(UF::UNIT_FIELD_STAT4)
|
||||||
|
};
|
||||||
for (const auto& [key, val] : block.fields) {
|
for (const auto& [key, val] : block.fields) {
|
||||||
if (key == ufPlayerXp) { playerXp_ = val; }
|
if (key == ufPlayerXp) { playerXp_ = val; }
|
||||||
else if (key == ufPlayerNextXp) { playerNextLevelXp_ = val; }
|
else if (key == ufPlayerNextXp) { playerNextLevelXp_ = val; }
|
||||||
|
|
@ -8170,6 +8176,14 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
||||||
uint8_t restStateByte = static_cast<uint8_t>((val >> 24) & 0xFF);
|
uint8_t restStateByte = static_cast<uint8_t>((val >> 24) & 0xFF);
|
||||||
isResting_ = (restStateByte != 0);
|
isResting_ = (restStateByte != 0);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
for (int si = 0; si < 5; ++si) {
|
||||||
|
if (ufStats[si] != 0xFFFF && key == ufStats[si]) {
|
||||||
|
playerStats_[si] = static_cast<int32_t>(val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Do not synthesize quest-log entries from raw update-field slots.
|
// Do not synthesize quest-log entries from raw update-field slots.
|
||||||
// Slot layouts differ on some classic-family realms and can produce
|
// Slot layouts differ on some classic-family realms and can produce
|
||||||
// phantom "already accepted" quests that block quest acceptance.
|
// phantom "already accepted" quests that block quest acceptance.
|
||||||
|
|
@ -8454,6 +8468,11 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
||||||
const uint16_t ufPlayerFlags = fieldIndex(UF::PLAYER_FLAGS);
|
const uint16_t ufPlayerFlags = fieldIndex(UF::PLAYER_FLAGS);
|
||||||
const uint16_t ufArmor = fieldIndex(UF::UNIT_FIELD_RESISTANCES);
|
const uint16_t ufArmor = fieldIndex(UF::UNIT_FIELD_RESISTANCES);
|
||||||
const uint16_t ufPBytes2v = fieldIndex(UF::PLAYER_BYTES_2);
|
const uint16_t ufPBytes2v = fieldIndex(UF::PLAYER_BYTES_2);
|
||||||
|
const uint16_t ufStatsV[5] = {
|
||||||
|
fieldIndex(UF::UNIT_FIELD_STAT0), fieldIndex(UF::UNIT_FIELD_STAT1),
|
||||||
|
fieldIndex(UF::UNIT_FIELD_STAT2), fieldIndex(UF::UNIT_FIELD_STAT3),
|
||||||
|
fieldIndex(UF::UNIT_FIELD_STAT4)
|
||||||
|
};
|
||||||
for (const auto& [key, val] : block.fields) {
|
for (const auto& [key, val] : block.fields) {
|
||||||
if (key == ufPlayerXp) {
|
if (key == ufPlayerXp) {
|
||||||
playerXp_ = val;
|
playerXp_ = val;
|
||||||
|
|
@ -8510,6 +8529,14 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
||||||
if (ghostStateCallback_) ghostStateCallback_(false);
|
if (ghostStateCallback_) ghostStateCallback_(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
for (int si = 0; si < 5; ++si) {
|
||||||
|
if (ufStatsV[si] != 0xFFFF && key == ufStatsV[si]) {
|
||||||
|
playerStats_[si] = static_cast<int32_t>(val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Do not auto-create quests from VALUES quest-log slot fields for the
|
// Do not auto-create quests from VALUES quest-log slot fields for the
|
||||||
// same reason as CREATE_OBJECT2 above (can be misaligned per realm).
|
// same reason as CREATE_OBJECT2 above (can be misaligned per realm).
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,11 @@ static const UFNameEntry kUFNames[] = {
|
||||||
{"UNIT_NPC_FLAGS", UF::UNIT_NPC_FLAGS},
|
{"UNIT_NPC_FLAGS", UF::UNIT_NPC_FLAGS},
|
||||||
{"UNIT_DYNAMIC_FLAGS", UF::UNIT_DYNAMIC_FLAGS},
|
{"UNIT_DYNAMIC_FLAGS", UF::UNIT_DYNAMIC_FLAGS},
|
||||||
{"UNIT_FIELD_RESISTANCES", UF::UNIT_FIELD_RESISTANCES},
|
{"UNIT_FIELD_RESISTANCES", UF::UNIT_FIELD_RESISTANCES},
|
||||||
|
{"UNIT_FIELD_STAT0", UF::UNIT_FIELD_STAT0},
|
||||||
|
{"UNIT_FIELD_STAT1", UF::UNIT_FIELD_STAT1},
|
||||||
|
{"UNIT_FIELD_STAT2", UF::UNIT_FIELD_STAT2},
|
||||||
|
{"UNIT_FIELD_STAT3", UF::UNIT_FIELD_STAT3},
|
||||||
|
{"UNIT_FIELD_STAT4", UF::UNIT_FIELD_STAT4},
|
||||||
{"UNIT_END", UF::UNIT_END},
|
{"UNIT_END", UF::UNIT_END},
|
||||||
{"PLAYER_FLAGS", UF::PLAYER_FLAGS},
|
{"PLAYER_FLAGS", UF::PLAYER_FLAGS},
|
||||||
{"PLAYER_BYTES", UF::PLAYER_BYTES},
|
{"PLAYER_BYTES", UF::PLAYER_BYTES},
|
||||||
|
|
|
||||||
|
|
@ -1086,7 +1086,10 @@ void InventoryScreen::renderCharacterScreen(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
if (ImGui::BeginTabItem("Stats")) {
|
if (ImGui::BeginTabItem("Stats")) {
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
renderStatsPanel(inventory, gameHandler.getPlayerLevel(), gameHandler.getArmorRating());
|
int32_t stats[5];
|
||||||
|
for (int i = 0; i < 5; ++i) stats[i] = gameHandler.getPlayerStat(i);
|
||||||
|
const int32_t* serverStats = (stats[0] >= 0) ? stats : nullptr;
|
||||||
|
renderStatsPanel(inventory, gameHandler.getPlayerLevel(), gameHandler.getArmorRating(), serverStats);
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1376,18 +1379,18 @@ void InventoryScreen::renderEquipmentPanel(game::Inventory& inventory) {
|
||||||
// Stats Panel
|
// Stats Panel
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
void InventoryScreen::renderStatsPanel(game::Inventory& inventory, uint32_t playerLevel, int32_t serverArmor) {
|
void InventoryScreen::renderStatsPanel(game::Inventory& inventory, uint32_t playerLevel,
|
||||||
// Sum equipment stats
|
int32_t serverArmor, const int32_t* serverStats) {
|
||||||
int32_t totalStr = 0, totalAgi = 0, totalSta = 0, totalInt = 0, totalSpi = 0;
|
// Sum equipment stats for item-query bonus display
|
||||||
|
int32_t itemStr = 0, itemAgi = 0, itemSta = 0, itemInt = 0, itemSpi = 0;
|
||||||
for (int s = 0; s < game::Inventory::NUM_EQUIP_SLOTS; s++) {
|
for (int s = 0; s < game::Inventory::NUM_EQUIP_SLOTS; s++) {
|
||||||
const auto& slot = inventory.getEquipSlot(static_cast<game::EquipSlot>(s));
|
const auto& slot = inventory.getEquipSlot(static_cast<game::EquipSlot>(s));
|
||||||
if (slot.empty()) continue;
|
if (slot.empty()) continue;
|
||||||
totalStr += slot.item.strength;
|
itemStr += slot.item.strength;
|
||||||
totalAgi += slot.item.agility;
|
itemAgi += slot.item.agility;
|
||||||
totalSta += slot.item.stamina;
|
itemSta += slot.item.stamina;
|
||||||
totalInt += slot.item.intellect;
|
itemInt += slot.item.intellect;
|
||||||
totalSpi += slot.item.spirit;
|
itemSpi += slot.item.spirit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use server-authoritative armor from UNIT_FIELD_RESISTANCES when available.
|
// Use server-authoritative armor from UNIT_FIELD_RESISTANCES when available.
|
||||||
|
|
@ -1399,9 +1402,6 @@ void InventoryScreen::renderStatsPanel(game::Inventory& inventory, uint32_t play
|
||||||
}
|
}
|
||||||
int32_t totalArmor = (serverArmor > 0) ? serverArmor : itemQueryArmor;
|
int32_t totalArmor = (serverArmor > 0) ? serverArmor : itemQueryArmor;
|
||||||
|
|
||||||
// Base stats: 20 + level
|
|
||||||
int32_t baseStat = 20 + static_cast<int32_t>(playerLevel);
|
|
||||||
|
|
||||||
ImVec4 green(0.0f, 1.0f, 0.0f, 1.0f);
|
ImVec4 green(0.0f, 1.0f, 0.0f, 1.0f);
|
||||||
ImVec4 white(1.0f, 1.0f, 1.0f, 1.0f);
|
ImVec4 white(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
ImVec4 gold(1.0f, 0.84f, 0.0f, 1.0f);
|
ImVec4 gold(1.0f, 0.84f, 0.0f, 1.0f);
|
||||||
|
|
@ -1414,23 +1414,41 @@ void InventoryScreen::renderStatsPanel(game::Inventory& inventory, uint32_t play
|
||||||
ImGui::TextColored(gray, "Armor: 0");
|
ImGui::TextColored(gray, "Armor: 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to render a stat line
|
if (serverStats) {
|
||||||
auto renderStat = [&](const char* name, int32_t equipBonus) {
|
// Server-authoritative stats from UNIT_FIELD_STAT0-4: show total and item bonus.
|
||||||
int32_t total = baseStat + equipBonus;
|
// serverStats[i] is the server's effective base stat (items included, buffs excluded).
|
||||||
if (equipBonus > 0) {
|
const char* statNames[5] = {"Strength", "Agility", "Stamina", "Intellect", "Spirit"};
|
||||||
ImGui::TextColored(white, "%s: %d", name, total);
|
const int32_t itemBonuses[5] = {itemStr, itemAgi, itemSta, itemInt, itemSpi};
|
||||||
ImGui::SameLine();
|
for (int i = 0; i < 5; ++i) {
|
||||||
ImGui::TextColored(green, "(+%d)", equipBonus);
|
int32_t total = serverStats[i];
|
||||||
} else {
|
int32_t bonus = itemBonuses[i];
|
||||||
ImGui::TextColored(gray, "%s: %d", name, total);
|
if (bonus > 0) {
|
||||||
|
ImGui::TextColored(white, "%s: %d", statNames[i], total);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(green, "(+%d)", bonus);
|
||||||
|
} else {
|
||||||
|
ImGui::TextColored(gray, "%s: %d", statNames[i], total);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
} else {
|
||||||
|
// Fallback: estimated base (20 + level) plus item query bonuses.
|
||||||
renderStat("Strength", totalStr);
|
int32_t baseStat = 20 + static_cast<int32_t>(playerLevel);
|
||||||
renderStat("Agility", totalAgi);
|
auto renderStat = [&](const char* name, int32_t equipBonus) {
|
||||||
renderStat("Stamina", totalSta);
|
int32_t total = baseStat + equipBonus;
|
||||||
renderStat("Intellect", totalInt);
|
if (equipBonus > 0) {
|
||||||
renderStat("Spirit", totalSpi);
|
ImGui::TextColored(white, "%s: %d", name, total);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(green, "(+%d)", equipBonus);
|
||||||
|
} else {
|
||||||
|
ImGui::TextColored(gray, "%s: %d", name, total);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
renderStat("Strength", itemStr);
|
||||||
|
renderStat("Agility", itemAgi);
|
||||||
|
renderStat("Stamina", itemSta);
|
||||||
|
renderStat("Intellect", itemInt);
|
||||||
|
renderStat("Spirit", itemSpi);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InventoryScreen::renderBackpackPanel(game::Inventory& inventory, bool collapseEmptySections) {
|
void InventoryScreen::renderBackpackPanel(game::Inventory& inventory, bool collapseEmptySections) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue