mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-05 00:33:51 +00:00
Merge commit '32bb0becc8' into chore/game-screen-extract
This commit is contained in:
commit
43aecab1ef
145 changed files with 3237 additions and 2849 deletions
|
|
@ -216,7 +216,9 @@ void AuthScreen::render(auth::AuthHandler& authHandler) {
|
|||
if (music) {
|
||||
if (!loginMusicVolumeAdjusted_) {
|
||||
savedMusicVolume_ = music->getVolume();
|
||||
int loginVolume = (savedMusicVolume_ * 80) / 100; // reduce auth music by 20%
|
||||
// Reduce music to 80% during login so UI button clicks and error sounds
|
||||
// remain audible over the background track
|
||||
int loginVolume = (savedMusicVolume_ * 80) / 100;
|
||||
if (loginVolume < 0) loginVolume = 0;
|
||||
if (loginVolume > 100) loginVolume = 100;
|
||||
music->setVolume(loginVolume);
|
||||
|
|
|
|||
|
|
@ -1344,6 +1344,8 @@ void GameScreen::processTargetInput(game::GameHandler& gameHandler) {
|
|||
if (gameHandler.hasTarget()) {
|
||||
auto target = gameHandler.getTarget();
|
||||
if (target && target->getType() == game::ObjectType::GAMEOBJECT) {
|
||||
LOG_WARNING("[GO-DIAG] Right-click: re-interacting with targeted GO 0x",
|
||||
std::hex, target->getGuid(), std::dec);
|
||||
gameHandler.setTarget(target->getGuid());
|
||||
gameHandler.interactWithGameObject(target->getGuid());
|
||||
return;
|
||||
|
|
@ -1416,6 +1418,18 @@ void GameScreen::processTargetInput(game::GameHandler& gameHandler) {
|
|||
hitCenter = core::coords::canonicalToRender(
|
||||
glm::vec3(entity->getX(), entity->getY(), entity->getZ()));
|
||||
hitCenter.z += heightOffset;
|
||||
// Log each unique GO's raypick position once
|
||||
if (t == game::ObjectType::GAMEOBJECT) {
|
||||
static std::unordered_set<uint64_t> goPickLog;
|
||||
if (goPickLog.insert(guid).second) {
|
||||
auto go = std::static_pointer_cast<game::GameObject>(entity);
|
||||
LOG_WARNING("[GO-DIAG] Raypick GO: guid=0x", std::hex, guid, std::dec,
|
||||
" entry=", go->getEntry(), " name='", go->getName(),
|
||||
"' pos=(", entity->getX(), ",", entity->getY(), ",", entity->getZ(),
|
||||
") center=(", hitCenter.x, ",", hitCenter.y, ",", hitCenter.z,
|
||||
") r=", hitRadius);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hitRadius = std::max(hitRadius * 1.1f, 0.6f);
|
||||
}
|
||||
|
|
@ -1476,6 +1490,8 @@ void GameScreen::processTargetInput(game::GameHandler& gameHandler) {
|
|||
|
||||
if (closestGuid != 0) {
|
||||
if (closestType == game::ObjectType::GAMEOBJECT) {
|
||||
LOG_WARNING("[GO-DIAG] Right-click: raypick hit GO 0x",
|
||||
std::hex, closestGuid, std::dec);
|
||||
gameHandler.setTarget(closestGuid);
|
||||
gameHandler.interactWithGameObject(closestGuid);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,45 @@ constexpr const char* kResistNames[6] = {
|
|||
"Frost Resistance", "Shadow Resistance", "Arcane Resistance"
|
||||
};
|
||||
|
||||
// Render "Classes: Warrior, Paladin" or "Races: Human, Orc" restriction text.
|
||||
// Shared between quest info and item info tooltips — both use the same WoW
|
||||
// allowableClass/allowableRace bitmask format with identical display logic.
|
||||
void renderClassRestriction(uint32_t allowableMask, uint8_t playerClass) {
|
||||
const auto& entries = ui::kClassMasks;
|
||||
int mc = 0;
|
||||
for (const auto& e : entries) if (allowableMask & e.mask) ++mc;
|
||||
if (mc <= 0 || mc >= 10) return; // all classes allowed or none matched
|
||||
char buf[128] = "Classes: "; bool first = true;
|
||||
for (const auto& e : entries) {
|
||||
if (!(allowableMask & e.mask)) continue;
|
||||
if (!first) strncat(buf, ", ", sizeof(buf) - strlen(buf) - 1);
|
||||
strncat(buf, e.name, sizeof(buf) - strlen(buf) - 1);
|
||||
first = false;
|
||||
}
|
||||
uint32_t pm = (playerClass > 0 && playerClass <= 10) ? (1u << (playerClass - 1)) : 0;
|
||||
bool ok = (pm == 0 || (allowableMask & pm));
|
||||
ImGui::TextColored(ok ? ImVec4(1,1,1,0.75f) : colors::kPaleRed, "%s", buf);
|
||||
}
|
||||
|
||||
void renderRaceRestriction(uint32_t allowableMask, uint8_t playerRace) {
|
||||
constexpr uint32_t kAllPlayable = 1|2|4|8|16|32|64|128|512|1024;
|
||||
if ((allowableMask & kAllPlayable) == kAllPlayable) return;
|
||||
const auto& entries = ui::kRaceMasks;
|
||||
int mc = 0;
|
||||
for (const auto& e : entries) if (allowableMask & e.mask) ++mc;
|
||||
if (mc <= 0) return;
|
||||
char buf[160] = "Races: "; bool first = true;
|
||||
for (const auto& e : entries) {
|
||||
if (!(allowableMask & e.mask)) continue;
|
||||
if (!first) strncat(buf, ", ", sizeof(buf) - strlen(buf) - 1);
|
||||
strncat(buf, e.name, sizeof(buf) - strlen(buf) - 1);
|
||||
first = false;
|
||||
}
|
||||
uint32_t pm = (playerRace > 0 && playerRace <= 11) ? (1u << (playerRace - 1)) : 0;
|
||||
bool ok = (pm == 0 || (allowableMask & pm));
|
||||
ImGui::TextColored(ok ? ImVec4(1,1,1,0.75f) : colors::kPaleRed, "%s", buf);
|
||||
}
|
||||
|
||||
// Socket types from shared ui_colors.hpp (ui::kSocketTypes)
|
||||
|
||||
const game::ItemSlot* findComparableEquipped(const game::Inventory& inventory, uint8_t inventoryType) {
|
||||
|
|
@ -2847,47 +2886,10 @@ void InventoryScreen::renderItemTooltip(const game::ItemDef& item, const game::I
|
|||
rankName,
|
||||
fIt != s_factionNamesB.end() ? fIt->second.c_str() : "Unknown Faction");
|
||||
}
|
||||
// Class restriction
|
||||
if (qInfo->allowableClass != 0) {
|
||||
const auto& kClassesB = ui::kClassMasks;
|
||||
int mc = 0;
|
||||
for (const auto& kc : kClassesB) if (qInfo->allowableClass & kc.mask) ++mc;
|
||||
if (mc > 0 && mc < 10) {
|
||||
char buf[128] = "Classes: "; bool first = true;
|
||||
for (const auto& kc : kClassesB) {
|
||||
if (!(qInfo->allowableClass & kc.mask)) continue;
|
||||
if (!first) strncat(buf, ", ", sizeof(buf)-strlen(buf)-1);
|
||||
strncat(buf, kc.name, sizeof(buf)-strlen(buf)-1);
|
||||
first = false;
|
||||
}
|
||||
uint8_t pc = gameHandler_->getPlayerClass();
|
||||
uint32_t pm = (pc > 0 && pc <= 10) ? (1u << (pc-1)) : 0;
|
||||
bool ok = (pm == 0 || (qInfo->allowableClass & pm));
|
||||
ImGui::TextColored(ok ? ImVec4(1,1,1,0.75f) : ImVec4(1,0.5f,0.5f,1), "%s", buf);
|
||||
}
|
||||
}
|
||||
// Race restriction
|
||||
if (qInfo->allowableRace != 0) {
|
||||
const auto& kRacesB = ui::kRaceMasks;
|
||||
constexpr uint32_t kAll = 1|2|4|8|16|32|64|128|512|1024;
|
||||
if ((qInfo->allowableRace & kAll) != kAll) {
|
||||
int mc = 0;
|
||||
for (const auto& kr : kRacesB) if (qInfo->allowableRace & kr.mask) ++mc;
|
||||
if (mc > 0) {
|
||||
char buf[160] = "Races: "; bool first = true;
|
||||
for (const auto& kr : kRacesB) {
|
||||
if (!(qInfo->allowableRace & kr.mask)) continue;
|
||||
if (!first) strncat(buf, ", ", sizeof(buf)-strlen(buf)-1);
|
||||
strncat(buf, kr.name, sizeof(buf)-strlen(buf)-1);
|
||||
first = false;
|
||||
}
|
||||
uint8_t pr = gameHandler_->getPlayerRace();
|
||||
uint32_t pm = (pr > 0 && pr <= 11) ? (1u << (pr-1)) : 0;
|
||||
bool ok = (pm == 0 || (qInfo->allowableRace & pm));
|
||||
ImGui::TextColored(ok ? ImVec4(1,1,1,0.75f) : ImVec4(1,0.5f,0.5f,1), "%s", buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (qInfo->allowableClass != 0)
|
||||
renderClassRestriction(qInfo->allowableClass, gameHandler_->getPlayerClass());
|
||||
if (qInfo->allowableRace != 0)
|
||||
renderRaceRestriction(qInfo->allowableRace, gameHandler_->getPlayerRace());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3361,64 +3363,10 @@ void InventoryScreen::renderItemTooltip(const game::ItemQueryResponseData& info,
|
|||
fIt != s_factionNames.end() ? fIt->second.c_str() : "Unknown Faction");
|
||||
}
|
||||
|
||||
// Class restriction (e.g. "Classes: Paladin, Warrior")
|
||||
if (info.allowableClass != 0) {
|
||||
const auto& kClasses = ui::kClassMasks;
|
||||
// Count matching classes
|
||||
int matchCount = 0;
|
||||
for (const auto& kc : kClasses)
|
||||
if (info.allowableClass & kc.mask) ++matchCount;
|
||||
// Only show if restricted to a subset (not all classes)
|
||||
if (matchCount > 0 && matchCount < 10) {
|
||||
char classBuf[128] = "Classes: ";
|
||||
bool first = true;
|
||||
for (const auto& kc : kClasses) {
|
||||
if (!(info.allowableClass & kc.mask)) continue;
|
||||
if (!first) strncat(classBuf, ", ", sizeof(classBuf) - strlen(classBuf) - 1);
|
||||
strncat(classBuf, kc.name, sizeof(classBuf) - strlen(classBuf) - 1);
|
||||
first = false;
|
||||
}
|
||||
// Check if player's class is allowed
|
||||
bool playerAllowed = true;
|
||||
if (gameHandler_) {
|
||||
uint8_t pc = gameHandler_->getPlayerClass();
|
||||
uint32_t pmask = (pc > 0 && pc <= 10) ? (1u << (pc - 1)) : 0;
|
||||
playerAllowed = (pmask == 0 || (info.allowableClass & pmask));
|
||||
}
|
||||
ImVec4 clColor = playerAllowed ? ImVec4(1.0f, 1.0f, 1.0f, 0.75f) : ui::colors::kPaleRed;
|
||||
ImGui::TextColored(clColor, "%s", classBuf);
|
||||
}
|
||||
}
|
||||
|
||||
// Race restriction (e.g. "Races: Night Elf, Human")
|
||||
if (info.allowableRace != 0) {
|
||||
const auto& kRaces = ui::kRaceMasks;
|
||||
constexpr uint32_t kAllPlayable = 1|2|4|8|16|32|64|128|512|1024;
|
||||
// Only show if not all playable races are allowed
|
||||
if ((info.allowableRace & kAllPlayable) != kAllPlayable) {
|
||||
int matchCount = 0;
|
||||
for (const auto& kr : kRaces)
|
||||
if (info.allowableRace & kr.mask) ++matchCount;
|
||||
if (matchCount > 0) {
|
||||
char raceBuf[160] = "Races: ";
|
||||
bool first = true;
|
||||
for (const auto& kr : kRaces) {
|
||||
if (!(info.allowableRace & kr.mask)) continue;
|
||||
if (!first) strncat(raceBuf, ", ", sizeof(raceBuf) - strlen(raceBuf) - 1);
|
||||
strncat(raceBuf, kr.name, sizeof(raceBuf) - strlen(raceBuf) - 1);
|
||||
first = false;
|
||||
}
|
||||
bool playerAllowed = true;
|
||||
if (gameHandler_) {
|
||||
uint8_t pr = gameHandler_->getPlayerRace();
|
||||
uint32_t pmask = (pr > 0 && pr <= 11) ? (1u << (pr - 1)) : 0;
|
||||
playerAllowed = (pmask == 0 || (info.allowableRace & pmask));
|
||||
}
|
||||
ImVec4 rColor = playerAllowed ? ImVec4(1.0f, 1.0f, 1.0f, 0.75f) : ui::colors::kPaleRed;
|
||||
ImGui::TextColored(rColor, "%s", raceBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info.allowableClass != 0 && gameHandler_)
|
||||
renderClassRestriction(info.allowableClass, gameHandler_->getPlayerClass());
|
||||
if (info.allowableRace != 0 && gameHandler_)
|
||||
renderRaceRestriction(info.allowableRace, gameHandler_->getPlayerRace());
|
||||
|
||||
// Spell effects
|
||||
for (const auto& sp : info.spells) {
|
||||
|
|
|
|||
|
|
@ -175,9 +175,11 @@ void SpellbookScreen::loadSpellDBC(pipeline::AssetManager* assetManager) {
|
|||
"expansion layout");
|
||||
}
|
||||
|
||||
// If dbc_layouts.json was missing or its field names didn't match, retry with
|
||||
// hard-coded WotLK field indices as a safety net. fieldCount >= 200 distinguishes
|
||||
// WotLK (234 fields) from Classic (148) to avoid misreading shorter DBCs.
|
||||
if (spellData.empty() && fieldCount >= 200) {
|
||||
LOG_INFO("Spellbook: Retrying with WotLK field indices (DBC has ", fieldCount, " fields)");
|
||||
// WotLK Spell.dbc field indices (verified against 3.3.5a schema); SchoolMask at field 225
|
||||
schoolField_ = 225;
|
||||
isSchoolEnum_ = false;
|
||||
tryLoad(0, 4, 133, 136, 153, 139, 14, 39, 47, 49, "WotLK fallback");
|
||||
|
|
@ -441,7 +443,9 @@ VkDescriptorSet SpellbookScreen::getSpellIcon(uint32_t iconId, pipeline::AssetMa
|
|||
static int lastImGuiFrame = -1;
|
||||
int curFrame = ImGui::GetFrameCount();
|
||||
if (curFrame != lastImGuiFrame) { loadsThisFrame = 0; lastImGuiFrame = curFrame; }
|
||||
if (loadsThisFrame >= 4) return VK_NULL_HANDLE; // defer — do NOT cache null here
|
||||
// Defer without caching — returning null here allows retry next frame when
|
||||
// the budget resets, rather than permanently blacklisting the icon as missing
|
||||
if (loadsThisFrame >= 4) return VK_NULL_HANDLE;
|
||||
|
||||
auto pit = spellIconPaths.find(iconId);
|
||||
if (pit == spellIconPaths.end()) {
|
||||
|
|
|
|||
|
|
@ -76,7 +76,9 @@ void TalentScreen::renderTalentTrees(game::GameHandler& gameHandler) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Get talent tabs for this class, sorted by orderIndex
|
||||
// Get talent tabs for this class, sorted by orderIndex.
|
||||
// WoW class IDs are 1-indexed (Warrior=1..Druid=11); convert to bitmask for
|
||||
// TalentTab.classMask matching (Warrior=0x1, Paladin=0x2, Hunter=0x4, etc.)
|
||||
uint32_t classMask = 1u << (playerClass - 1);
|
||||
std::vector<const game::GameHandler::TalentTabEntry*> classTabs;
|
||||
for (const auto& [tabId, tab] : gameHandler.getAllTalentTabs()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue