mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix online item GUID resolution, async terrain loading, and inventory enrichment
Enrich online inventory from local DB when server data is incomplete, add resolveOnlineItemGuid fallback for sell/equip/use, use async enqueueTile for initial terrain load, improve walk/run animation fallbacks, clear target on loot close, and broaden equipability detection to include armor/subclass.
This commit is contained in:
parent
db4a40a4e6
commit
e38c0213e4
6 changed files with 119 additions and 8 deletions
|
|
@ -474,6 +474,7 @@ private:
|
|||
void rebuildOnlineInventory();
|
||||
void detectInventorySlotBases(const std::map<uint16_t, uint32_t>& fields);
|
||||
bool applyInventoryFields(const std::map<uint16_t, uint32_t>& fields);
|
||||
uint64_t resolveOnlineItemGuid(uint32_t itemId) const;
|
||||
|
||||
// ---- Phase 2 handlers ----
|
||||
void handleAttackStart(network::Packet& packet);
|
||||
|
|
|
|||
|
|
@ -143,6 +143,11 @@ public:
|
|||
*/
|
||||
bool loadTile(int x, int y);
|
||||
|
||||
/**
|
||||
* Enqueue a tile for async loading (returns false if previously failed).
|
||||
*/
|
||||
bool enqueueTile(int x, int y);
|
||||
|
||||
/**
|
||||
* Unload a tile
|
||||
* @param x Tile X coordinate
|
||||
|
|
|
|||
|
|
@ -3120,6 +3120,19 @@ void GameHandler::handleItemQueryResponse(network::Packet& packet) {
|
|||
}
|
||||
}
|
||||
|
||||
uint64_t GameHandler::resolveOnlineItemGuid(uint32_t itemId) const {
|
||||
if (itemId == 0) return 0;
|
||||
uint64_t found = 0;
|
||||
for (const auto& [guid, info] : onlineItems_) {
|
||||
if (info.entry != itemId) continue;
|
||||
if (found != 0) {
|
||||
return 0; // Ambiguous
|
||||
}
|
||||
found = guid;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void GameHandler::detectInventorySlotBases(const std::map<uint16_t, uint32_t>& fields) {
|
||||
if (invSlotBase_ >= 0 && packSlotBase_ >= 0) return;
|
||||
if (onlineItems_.empty() || fields.empty()) return;
|
||||
|
|
@ -3215,6 +3228,31 @@ void GameHandler::rebuildOnlineInventory() {
|
|||
def.name = "Item " + std::to_string(def.itemId);
|
||||
queryItemInfo(def.itemId, guid);
|
||||
}
|
||||
if (def.itemId != 0) {
|
||||
auto& db = getSinglePlayerLootDb();
|
||||
auto itTpl = db.itemTemplates.find(def.itemId);
|
||||
if (itTpl != db.itemTemplates.end()) {
|
||||
if (def.name.empty() || def.name.rfind("Item ", 0) == 0) def.name = itTpl->second.name;
|
||||
if (def.quality == ItemQuality::COMMON && itTpl->second.quality != 0) {
|
||||
def.quality = static_cast<ItemQuality>(itTpl->second.quality);
|
||||
}
|
||||
if (def.inventoryType == 0 && itTpl->second.inventoryType != 0) {
|
||||
def.inventoryType = itTpl->second.inventoryType;
|
||||
}
|
||||
if (def.maxStack <= 1 && itTpl->second.maxStack > 1) {
|
||||
def.maxStack = static_cast<uint32_t>(itTpl->second.maxStack);
|
||||
}
|
||||
if (def.displayInfoId == 0 && itTpl->second.displayId != 0) {
|
||||
def.displayInfoId = itTpl->second.displayId;
|
||||
}
|
||||
if (def.armor == 0 && itTpl->second.armor > 0) def.armor = itTpl->second.armor;
|
||||
if (def.stamina == 0 && itTpl->second.stamina > 0) def.stamina = itTpl->second.stamina;
|
||||
if (def.strength == 0 && itTpl->second.strength > 0) def.strength = itTpl->second.strength;
|
||||
if (def.agility == 0 && itTpl->second.agility > 0) def.agility = itTpl->second.agility;
|
||||
if (def.intellect == 0 && itTpl->second.intellect > 0) def.intellect = itTpl->second.intellect;
|
||||
if (def.spirit == 0 && itTpl->second.spirit > 0) def.spirit = itTpl->second.spirit;
|
||||
}
|
||||
}
|
||||
|
||||
inventory.setEquipSlot(static_cast<EquipSlot>(i), def);
|
||||
}
|
||||
|
|
@ -3250,6 +3288,31 @@ void GameHandler::rebuildOnlineInventory() {
|
|||
def.name = "Item " + std::to_string(def.itemId);
|
||||
queryItemInfo(def.itemId, guid);
|
||||
}
|
||||
if (def.itemId != 0) {
|
||||
auto& db = getSinglePlayerLootDb();
|
||||
auto itTpl = db.itemTemplates.find(def.itemId);
|
||||
if (itTpl != db.itemTemplates.end()) {
|
||||
if (def.name.empty() || def.name.rfind("Item ", 0) == 0) def.name = itTpl->second.name;
|
||||
if (def.quality == ItemQuality::COMMON && itTpl->second.quality != 0) {
|
||||
def.quality = static_cast<ItemQuality>(itTpl->second.quality);
|
||||
}
|
||||
if (def.inventoryType == 0 && itTpl->second.inventoryType != 0) {
|
||||
def.inventoryType = itTpl->second.inventoryType;
|
||||
}
|
||||
if (def.maxStack <= 1 && itTpl->second.maxStack > 1) {
|
||||
def.maxStack = static_cast<uint32_t>(itTpl->second.maxStack);
|
||||
}
|
||||
if (def.displayInfoId == 0 && itTpl->second.displayId != 0) {
|
||||
def.displayInfoId = itTpl->second.displayId;
|
||||
}
|
||||
if (def.armor == 0 && itTpl->second.armor > 0) def.armor = itTpl->second.armor;
|
||||
if (def.stamina == 0 && itTpl->second.stamina > 0) def.stamina = itTpl->second.stamina;
|
||||
if (def.strength == 0 && itTpl->second.strength > 0) def.strength = itTpl->second.strength;
|
||||
if (def.agility == 0 && itTpl->second.agility > 0) def.agility = itTpl->second.agility;
|
||||
if (def.intellect == 0 && itTpl->second.intellect > 0) def.intellect = itTpl->second.intellect;
|
||||
if (def.spirit == 0 && itTpl->second.spirit > 0) def.spirit = itTpl->second.spirit;
|
||||
}
|
||||
}
|
||||
|
||||
inventory.setBackpackSlot(i, def);
|
||||
}
|
||||
|
|
@ -3846,6 +3909,9 @@ void GameHandler::lootItem(uint8_t slotIndex) {
|
|||
void GameHandler::closeLoot() {
|
||||
if (!lootWindowOpen) return;
|
||||
lootWindowOpen = false;
|
||||
if (currentLoot.lootGuid != 0 && targetGuid == currentLoot.lootGuid) {
|
||||
clearTarget();
|
||||
}
|
||||
if (singlePlayerMode_ && currentLoot.lootGuid != 0) {
|
||||
auto st = localLootState_.find(currentLoot.lootGuid);
|
||||
if (st != localLootState_.end()) {
|
||||
|
|
@ -3987,8 +4053,13 @@ void GameHandler::sellItemBySlot(int backpackIndex) {
|
|||
notifyInventoryChanged();
|
||||
} else {
|
||||
uint64_t itemGuid = backpackSlotGuids_[backpackIndex];
|
||||
if (itemGuid == 0) {
|
||||
itemGuid = resolveOnlineItemGuid(slot.item.itemId);
|
||||
}
|
||||
if (itemGuid != 0 && currentVendorItems.vendorGuid != 0) {
|
||||
sellItem(currentVendorItems.vendorGuid, itemGuid, 1);
|
||||
} else if (itemGuid == 0) {
|
||||
LOG_WARNING("Sell failed: missing item GUID for slot ", backpackIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4004,9 +4075,14 @@ void GameHandler::autoEquipItemBySlot(int backpackIndex) {
|
|||
}
|
||||
|
||||
uint64_t itemGuid = backpackSlotGuids_[backpackIndex];
|
||||
if (itemGuid == 0) {
|
||||
itemGuid = resolveOnlineItemGuid(slot.item.itemId);
|
||||
}
|
||||
if (itemGuid != 0 && state == WorldState::IN_WORLD && socket) {
|
||||
auto packet = AutoEquipItemPacket::build(itemGuid);
|
||||
socket->send(packet);
|
||||
} else if (itemGuid == 0) {
|
||||
LOG_WARNING("Auto-equip failed: missing item GUID for slot ", backpackIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4021,9 +4097,14 @@ void GameHandler::useItemBySlot(int backpackIndex) {
|
|||
}
|
||||
|
||||
uint64_t itemGuid = backpackSlotGuids_[backpackIndex];
|
||||
if (itemGuid == 0) {
|
||||
itemGuid = resolveOnlineItemGuid(slot.item.itemId);
|
||||
}
|
||||
if (itemGuid != 0 && state == WorldState::IN_WORLD && socket) {
|
||||
auto packet = UseItemPacket::build(0xFF, static_cast<uint8_t>(backpackIndex), itemGuid);
|
||||
socket->send(packet);
|
||||
} else if (itemGuid == 0) {
|
||||
LOG_WARNING("Use item failed: missing item GUID for slot ", backpackIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -661,7 +661,7 @@ void Renderer::updateCharacterAnimation() {
|
|||
} else if (anyStrafeRight) {
|
||||
animId = pickFirstAvailable({ANIM_STRAFE_WALK_RIGHT, ANIM_STRAFE_RUN_RIGHT}, ANIM_WALK);
|
||||
} else {
|
||||
animId = ANIM_WALK;
|
||||
animId = pickFirstAvailable({ANIM_WALK, ANIM_RUN}, ANIM_STAND);
|
||||
}
|
||||
loop = true;
|
||||
break;
|
||||
|
|
@ -673,7 +673,7 @@ void Renderer::updateCharacterAnimation() {
|
|||
} else if (anyStrafeRight) {
|
||||
animId = pickFirstAvailable({ANIM_STRAFE_RUN_RIGHT}, ANIM_RUN);
|
||||
} else {
|
||||
animId = ANIM_RUN;
|
||||
animId = pickFirstAvailable({ANIM_RUN, ANIM_WALK}, ANIM_STAND);
|
||||
}
|
||||
loop = true;
|
||||
break;
|
||||
|
|
@ -1622,11 +1622,11 @@ bool Renderer::loadTestTerrain(pipeline::AssetManager* assetManager, const std::
|
|||
}
|
||||
}
|
||||
|
||||
LOG_INFO("Loading initial tile [", tileX, ",", tileY, "] via terrain manager");
|
||||
LOG_INFO("Enqueuing initial tile [", tileX, ",", tileY, "] via terrain manager");
|
||||
|
||||
// Load the initial tile through TerrainManager (properly tracked for streaming)
|
||||
if (!terrainManager->loadTile(tileX, tileY)) {
|
||||
LOG_ERROR("Failed to load initial tile [", tileX, ",", tileY, "]");
|
||||
// Enqueue the initial tile for async loading (avoids long sync stalls)
|
||||
if (!terrainManager->enqueueTile(tileX, tileY)) {
|
||||
LOG_ERROR("Failed to enqueue initial tile [", tileX, ",", tileY, "]");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -194,6 +194,27 @@ bool TerrainManager::loadTile(int x, int y) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool TerrainManager::enqueueTile(int x, int y) {
|
||||
TileCoord coord = {x, y};
|
||||
if (loadedTiles.find(coord) != loadedTiles.end()) {
|
||||
return true;
|
||||
}
|
||||
if (pendingTiles.find(coord) != pendingTiles.end()) {
|
||||
return true;
|
||||
}
|
||||
if (failedTiles.find(coord) != failedTiles.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queueMutex);
|
||||
loadQueue.push(coord);
|
||||
pendingTiles[coord] = true;
|
||||
}
|
||||
queueCV.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<PendingTile> TerrainManager::prepareTile(int x, int y) {
|
||||
TileCoord coord = {x, y};
|
||||
|
||||
|
|
|
|||
|
|
@ -1097,15 +1097,18 @@ void InventoryScreen::renderItemSlot(game::Inventory& inventory, const game::Ite
|
|||
inventoryDirty = true;
|
||||
}
|
||||
} else if (kind == SlotKind::BACKPACK && backpackIndex >= 0) {
|
||||
bool looksEquipable = (item.inventoryType > 0) ||
|
||||
(item.armor > 0) ||
|
||||
(!item.subclassName.empty());
|
||||
if (gameHandler_ && !gameHandler_->isSinglePlayerMode()) {
|
||||
if (item.inventoryType > 0) {
|
||||
if (looksEquipable) {
|
||||
// Auto-equip (online)
|
||||
gameHandler_->autoEquipItemBySlot(backpackIndex);
|
||||
} else {
|
||||
// Use consumable (online)
|
||||
gameHandler_->useItemBySlot(backpackIndex);
|
||||
}
|
||||
} else if (item.inventoryType > 0) {
|
||||
} else if (looksEquipable) {
|
||||
// Auto-equip (single-player)
|
||||
uint8_t equippingType = item.inventoryType;
|
||||
game::EquipSlot targetSlot = getEquipSlotForType(equippingType, inventory);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue