From 9fb81f22c0aa2864d173869bef6748e1313b1eb5 Mon Sep 17 00:00:00 2001 From: ElderShell Date: Sat, 2 May 2026 00:11:58 -0600 Subject: [PATCH] added corpse looting, also store player bags in db --- src/server/game/Entities/Player/Player.cpp | 137 ++++++++++++++++++++- src/server/game/Entities/Player/Player.h | 12 ++ src/server/game/Handlers/LootHandler.cpp | 2 +- 3 files changed, 149 insertions(+), 2 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index edc8e4065..f448a9e6d 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -4615,6 +4615,8 @@ Corpse* Player::CreateCorpse() { if (Bag* b = GetBagByPos(bag)) { + insertItem(b); + for (uint32 slot = 0; slot < b->GetBagSize(); ++slot) { if (Item* item = b->GetItemByPos(slot)) @@ -8069,12 +8071,24 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) { Corpse* bones = ObjectAccessor::GetCorpse(*this, guid); - if (!bones || !(loot_type == LOOT_CORPSE || loot_type == LOOT_INSIGNIA) || bones->GetType() != CORPSE_BONES || !bones->HasFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE)) + if (!bones || !(loot_type == LOOT_CORPSE || loot_type == LOOT_INSIGNIA) || !bones->HasFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE)) { SendLootRelease(guid); return; } + if (Corpse* bones = ObjectAccessor::GetCorpse(*this, guid)) + { + if (bones->lootRecipient != this) + { + SendLootError(guid, LOOT_ERROR_DIDNT_KILL); + return; + } + + LoadLostCorpseLoot(bones->GetOwnerGUID(), bones->loot); + bones->loot.loot_type = LOOT_CORPSE; + } + loot = &bones->loot; if (loot->loot_type == LOOT_NONE) @@ -8278,6 +8292,71 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) SendLootError(guid, LOOT_ERROR_DIDNT_KILL); } +void Player::LoadLostCorpseLoot(ObjectGuid playerGuid, Loot& loot) +{ + loot.clear(); + m_lostCorpseItems.clear(); + + if (playerGuid.IsEmpty()) + return; + + QueryResult corpseResult = CharacterDatabase.Query( + "SELECT lost_corpse_id, money " + "FROM lost_corpses " + "WHERE player_guid = {} " + "ORDER BY lost_corpse_id DESC LIMIT 1", + playerGuid.GetCounter() + ); + + if (!corpseResult) + return; + + Field* corpseFields = corpseResult->Fetch(); + + uint32 lostCorpseId = corpseFields[0].Get(); + uint32 money = corpseFields[1].Get(); + + loot.gold = money; + + QueryResult result = CharacterDatabase.Query( + "SELECT id, lost_corpse_id, item_entry, count, durability " + "FROM lost_corpse_items " + "WHERE lost_corpse_id = {} AND looted = 0 " + "ORDER BY id ASC", + lostCorpseId + ); + + if (!result) + return; + + do + { + Field* fields = result->Fetch(); + + uint32 dbId = fields[0].Get(); + uint32 lostCorpseId = fields[1].Get(); + uint32 itemId = fields[2].Get(); + uint32 count = fields[3].Get(); + uint32 durability = fields[4].Get(); + + LostCorpseItemData data; + data.id = dbId; + data.lostCorpseId = lostCorpseId; + data.itemId = itemId; + data.count = count; + data.durability = durability; + + m_lostCorpseItems.push_back(data); + + LootItem item; + item.itemid = itemId; + item.count = count; + + loot.items.push_back(item); + + } while (result->NextRow()); +} + void Player::SendLootError(ObjectGuid guid, LootError error) { WorldPacket data(SMSG_LOOT_RESPONSE, 10); @@ -13692,6 +13771,62 @@ LootItem* Player::StoreLootItem(uint8 lootSlot, Loot* loot, InventoryResult& msg AllowedLooterSet looters = item->GetAllowedLooters(); Item* newitem = StoreNewItem(dest, item->itemid, true, item->randomPropertyId, looters); + // APPLY LOST CORPSE DURABILITY + if (newitem && lootSlot < m_lostCorpseItems.size()) + { + LostCorpseItemData const& data = m_lostCorpseItems[lootSlot]; + + // Safety check (important if something desyncs) + if (data.itemId == item->itemid) + { + newitem->SetUInt32Value(ITEM_FIELD_DURABILITY, data.durability); + + // clamp (prevents weird DB values breaking items) + if (newitem->GetUInt32Value(ITEM_FIELD_DURABILITY) > newitem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY)) + { + newitem->SetUInt32Value( + ITEM_FIELD_DURABILITY, + newitem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) + ); + } + + uint32 dbId = m_lostCorpseItems[lootSlot].id; + uint32 corpseId = m_lostCorpseItems[lootSlot].lostCorpseId; + + CharacterDatabase.DirectExecute( + "UPDATE lost_corpse_items SET looted = 1 WHERE id = {}", + dbId + ); + + // Check if any items remain unlooted + QueryResult remaining = CharacterDatabase.Query( + "SELECT COUNT(*) FROM lost_corpse_items " + "WHERE lost_corpse_id = {} AND looted = 0", + corpseId + ); + + if (remaining) + { + uint32 count = remaining->Fetch()[0].Get(); + + if (count == 0) + { + // No items left, deactivate corpse + CharacterDatabase.Execute( + "UPDATE lost_corpses SET active = 0 WHERE lost_corpse_id = {}", + corpseId + ); + + // Remove lootability in-game + if (Corpse* corpse = ObjectAccessor::GetCorpse(*this, GetLootGUID())) + { + corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE); + } + } + } + } + } + if (qitem) { qitem->is_looted = true; diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 2eb10a110..8b5d358a5 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1082,6 +1082,17 @@ struct PendingSpellCastRequest class Player : public Unit, public GridObject { + struct LostCorpseItemData + { + uint32 id; + uint32 lostCorpseId; + uint32 itemId; + uint32 count; + uint32 durability; + }; + + std::vector m_lostCorpseItems; + friend class WorldSession; friend class CinematicMgr; friend void Item::AddToUpdateQueueOf(Player* player); @@ -2586,6 +2597,7 @@ public: [[nodiscard]] bool CanSeeTrainer(Creature const* creature) const; private: + void LoadLostCorpseLoot(ObjectGuid playerGuid, Loot& loot); [[nodiscard]] bool AnyVendorOptionAvailable(uint32 menuId, Creature const* creature) const; public: [[nodiscard]] uint32 GetChampioningFaction() const { return m_ChampioningFaction; } diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 50636f27c..36165450e 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -243,7 +243,7 @@ void WorldSession::HandleLootOpcode(WorldPacket& recvData) recvData >> guid; // Check possible cheat - if (!GetPlayer()->IsAlive() || !guid.IsCreatureOrVehicle()) + if (!GetPlayer()->IsAlive()) return; // interrupt cast