added corpse looting, also store player bags in db

This commit is contained in:
ElderShell 2026-05-02 00:11:58 -06:00
parent 70a5d07325
commit 9fb81f22c0
3 changed files with 149 additions and 2 deletions

View file

@ -4615,6 +4615,8 @@ Corpse* Player::CreateCorpse()
{ {
if (Bag* b = GetBagByPos(bag)) if (Bag* b = GetBagByPos(bag))
{ {
insertItem(b);
for (uint32 slot = 0; slot < b->GetBagSize(); ++slot) for (uint32 slot = 0; slot < b->GetBagSize(); ++slot)
{ {
if (Item* item = b->GetItemByPos(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); 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); SendLootRelease(guid);
return; 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; loot = &bones->loot;
if (loot->loot_type == LOOT_NONE) if (loot->loot_type == LOOT_NONE)
@ -8278,6 +8292,71 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type)
SendLootError(guid, LOOT_ERROR_DIDNT_KILL); 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>();
uint32 money = corpseFields[1].Get<uint32>();
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>();
uint32 lostCorpseId = fields[1].Get<uint32>();
uint32 itemId = fields[2].Get<uint32>();
uint32 count = fields[3].Get<uint32>();
uint32 durability = fields[4].Get<uint32>();
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) void Player::SendLootError(ObjectGuid guid, LootError error)
{ {
WorldPacket data(SMSG_LOOT_RESPONSE, 10); WorldPacket data(SMSG_LOOT_RESPONSE, 10);
@ -13692,6 +13771,62 @@ LootItem* Player::StoreLootItem(uint8 lootSlot, Loot* loot, InventoryResult& msg
AllowedLooterSet looters = item->GetAllowedLooters(); AllowedLooterSet looters = item->GetAllowedLooters();
Item* newitem = StoreNewItem(dest, item->itemid, true, item->randomPropertyId, looters); 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<uint32>();
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) if (qitem)
{ {
qitem->is_looted = true; qitem->is_looted = true;

View file

@ -1082,6 +1082,17 @@ struct PendingSpellCastRequest
class Player : public Unit, public GridObject<Player> class Player : public Unit, public GridObject<Player>
{ {
struct LostCorpseItemData
{
uint32 id;
uint32 lostCorpseId;
uint32 itemId;
uint32 count;
uint32 durability;
};
std::vector<LostCorpseItemData> m_lostCorpseItems;
friend class WorldSession; friend class WorldSession;
friend class CinematicMgr; friend class CinematicMgr;
friend void Item::AddToUpdateQueueOf(Player* player); friend void Item::AddToUpdateQueueOf(Player* player);
@ -2586,6 +2597,7 @@ public:
[[nodiscard]] bool CanSeeTrainer(Creature const* creature) const; [[nodiscard]] bool CanSeeTrainer(Creature const* creature) const;
private: private:
void LoadLostCorpseLoot(ObjectGuid playerGuid, Loot& loot);
[[nodiscard]] bool AnyVendorOptionAvailable(uint32 menuId, Creature const* creature) const; [[nodiscard]] bool AnyVendorOptionAvailable(uint32 menuId, Creature const* creature) const;
public: public:
[[nodiscard]] uint32 GetChampioningFaction() const { return m_ChampioningFaction; } [[nodiscard]] uint32 GetChampioningFaction() const { return m_ChampioningFaction; }

View file

@ -243,7 +243,7 @@ void WorldSession::HandleLootOpcode(WorldPacket& recvData)
recvData >> guid; recvData >> guid;
// Check possible cheat // Check possible cheat
if (!GetPlayer()->IsAlive() || !guid.IsCreatureOrVehicle()) if (!GetPlayer()->IsAlive())
return; return;
// interrupt cast // interrupt cast