Implements saving of loot (items / money) contained inside lootable inventory items.
* Unlooted items / money persist across player sessions
* Loot inside items is tied to the item rather than the player so if trading
partially looted items becomes possible, this implementation will still work
* New tables added:
characters_database.sql (first time users)
characters_create_item_loot.sql (existing users)
Implementation Can be tested with:
Watertight Trunk [21113]
Bulging Sack of Gems [25422]
Fat Sack of Coins [11937]
Closes #2048
This commit is contained in:
1 parent
1f869ce3a5
commit
04f08d26a7
10 files changed
+398
-5
No files matched your search
@@ -1954,6 +1954,41 @@ LOCK TABLES `item_instance` WRITE;
|
|||||||
/*!40000 ALTER TABLE `item_instance` ENABLE KEYS */;
|
/*!40000 ALTER TABLE `item_instance` ENABLE KEYS */;
|
||||||
UNLOCK TABLES;
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `item_loot_items`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `item_loot_items`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `item_loot_items` (
|
||||||
|
`container_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'guid of container (item_instance.guid)',
|
||||||
|
`item_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'loot item entry (item_instance.itemEntry)',
|
||||||
|
`item_count` int(10) NOT NULL DEFAULT '0' COMMENT 'stack size',
|
||||||
|
`follow_rules` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'follow loot rules',
|
||||||
|
`ffa` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'free-for-all',
|
||||||
|
`blocked` tinyint(1) NOT NULL DEFAULT '0',
|
||||||
|
`counted` tinyint(1) NOT NULL DEFAULT '0',
|
||||||
|
`under_threshold` tinyint(1) NOT NULL DEFAULT '0',
|
||||||
|
`needs_quest` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'quest drop',
|
||||||
|
`rnd_prop` int(10) NOT NULL DEFAULT '0' COMMENT 'random enchantment added when originally rolled',
|
||||||
|
`rnd_suffix` int(10) NOT NULL DEFAULT '0' COMMENT 'random suffix added when originally rolled'
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `item_loot_money`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `item_loot_money`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `item_loot_money` (
|
||||||
|
`container_id` int(10) NOT NULL DEFAULT '0' COMMENT 'guid of container (item_instance.guid)',
|
||||||
|
`money` int(10) NOT NULL DEFAULT '0' COMMENT 'money loot (in copper)'
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Table structure for table `item_refund_instance`
|
-- Table structure for table `item_refund_instance`
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
SQLyog Ultimate v9.63
|
||||||
|
MySQL - 5.5.21 : Database - characters
|
||||||
|
*********************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!40101 SET NAMES utf8 */;
|
||||||
|
|
||||||
|
/*!40101 SET SQL_MODE=''*/;
|
||||||
|
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `item_loot_items`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `item_loot_items`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `item_loot_items` (
|
||||||
|
`container_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'guid of container (item_instance.guid)',
|
||||||
|
`item_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'loot item entry (item_instance.itemEntry)',
|
||||||
|
`item_count` int(10) NOT NULL DEFAULT '0' COMMENT 'stack size',
|
||||||
|
`follow_rules` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'follow loot rules',
|
||||||
|
`ffa` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'free-for-all',
|
||||||
|
`blocked` tinyint(1) NOT NULL DEFAULT '0',
|
||||||
|
`counted` tinyint(1) NOT NULL DEFAULT '0',
|
||||||
|
`under_threshold` tinyint(1) NOT NULL DEFAULT '0',
|
||||||
|
`needs_quest` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'quest drop',
|
||||||
|
`rnd_prop` int(10) NOT NULL DEFAULT '0' COMMENT 'random enchantment added when originally rolled',
|
||||||
|
`rnd_suffix` int(10) NOT NULL DEFAULT '0' COMMENT 'random suffix added when originally rolled'
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `item_loot_money`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `item_loot_money`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `item_loot_money` (
|
||||||
|
`container_id` int(10) NOT NULL DEFAULT '0' COMMENT 'guid of container (item_instance.guid)',
|
||||||
|
`money` int(10) NOT NULL DEFAULT '0' COMMENT 'money loot (in copper)'
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||||
|
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||||
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
@@ -378,6 +378,10 @@ void Item::SaveToDB(SQLTransaction& trans)
|
|||||||
if (!isInTransaction)
|
if (!isInTransaction)
|
||||||
CharacterDatabase.CommitTransaction(trans);
|
CharacterDatabase.CommitTransaction(trans);
|
||||||
|
|
||||||
|
// Delete the items if this is a container
|
||||||
|
if (!loot.isLooted())
|
||||||
|
ItemContainerDeleteLootMoneyAndLootItemsFromDB();
|
||||||
|
|
||||||
delete this;
|
delete this;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -483,6 +487,10 @@ void Item::DeleteFromDB(SQLTransaction& trans, uint32 itemGuid)
|
|||||||
void Item::DeleteFromDB(SQLTransaction& trans)
|
void Item::DeleteFromDB(SQLTransaction& trans)
|
||||||
{
|
{
|
||||||
DeleteFromDB(trans, GetGUIDLow());
|
DeleteFromDB(trans, GetGUIDLow());
|
||||||
|
|
||||||
|
// Delete the items if this is a container
|
||||||
|
if (!loot.isLooted())
|
||||||
|
ItemContainerDeleteLootMoneyAndLootItemsFromDB();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static*/
|
/*static*/
|
||||||
@@ -1198,3 +1206,191 @@ bool Item::CheckSoulboundTradeExpire()
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Item::ItemContainerSaveLootToDB()
|
||||||
|
{
|
||||||
|
// Saves the money and item loot associated with an openable item to the DB
|
||||||
|
|
||||||
|
if (loot.isLooted()) // no money and no loot
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32 container_id = GetGUIDLow();
|
||||||
|
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||||
|
|
||||||
|
loot.containerID = container_id; // Save this for when a LootItem is removed
|
||||||
|
|
||||||
|
// Save money
|
||||||
|
if (loot.gold > 0)
|
||||||
|
{
|
||||||
|
PreparedStatement* stmt_money = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY);
|
||||||
|
stmt_money->setUInt32(0, container_id);
|
||||||
|
trans->Append(stmt_money);
|
||||||
|
|
||||||
|
stmt_money = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_MONEY);
|
||||||
|
stmt_money->setUInt32(0, container_id);
|
||||||
|
stmt_money->setUInt32(1, loot.gold);
|
||||||
|
trans->Append(stmt_money);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save items
|
||||||
|
if (!loot.isLooted())
|
||||||
|
{
|
||||||
|
|
||||||
|
PreparedStatement* stmt_items = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEMS);
|
||||||
|
stmt_items->setUInt32(0, container_id);
|
||||||
|
trans->Append(stmt_items);
|
||||||
|
|
||||||
|
// Now insert the items
|
||||||
|
for (LootItemList::const_iterator _li = loot.items.begin(); _li != loot.items.end(); _li++)
|
||||||
|
{
|
||||||
|
// When an item is looted, it doesn't get removed from the items collection
|
||||||
|
// but we don't want to resave it.
|
||||||
|
if (!_li->canSave)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
stmt_items = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_ITEMS);
|
||||||
|
|
||||||
|
// container_id, item_id, item_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix
|
||||||
|
stmt_items->setUInt32(0, container_id);
|
||||||
|
stmt_items->setUInt32(1, _li->itemid);
|
||||||
|
stmt_items->setUInt32(2, _li->count);
|
||||||
|
stmt_items->setBool(3, _li->follow_loot_rules);
|
||||||
|
stmt_items->setBool(4, _li->freeforall);
|
||||||
|
stmt_items->setBool(5, _li->is_blocked);
|
||||||
|
stmt_items->setBool(6, _li->is_counted);
|
||||||
|
stmt_items->setBool(7, _li->is_underthreshold);
|
||||||
|
stmt_items->setBool(8, _li->needs_quest);
|
||||||
|
stmt_items->setUInt32(9, _li->randomPropertyId);
|
||||||
|
stmt_items->setUInt32(10, _li->randomSuffix);
|
||||||
|
trans->Append(stmt_items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterDatabase.CommitTransaction(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Item::ItemContainerLoadLootFromDB()
|
||||||
|
{
|
||||||
|
// Loads the money and item loot associated with an openable item from the DB
|
||||||
|
|
||||||
|
// Default. If there are no records for this item then it will be rolled for in Player::SendLoot()
|
||||||
|
m_lootGenerated = false;
|
||||||
|
|
||||||
|
uint32 container_id = GetGUIDLow();
|
||||||
|
|
||||||
|
// Save this for later use
|
||||||
|
loot.containerID = container_id;
|
||||||
|
|
||||||
|
// First, see if there was any money loot. This gets added directly to the container.
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_MONEY);
|
||||||
|
stmt->setUInt32(0, container_id);
|
||||||
|
PreparedQueryResult money_result = CharacterDatabase.Query(stmt);
|
||||||
|
|
||||||
|
if (money_result)
|
||||||
|
{
|
||||||
|
Field* fields = money_result->Fetch();
|
||||||
|
loot.gold = fields[0].GetUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, load any items that were saved
|
||||||
|
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_ITEMS);
|
||||||
|
stmt->setUInt32(0, container_id);
|
||||||
|
PreparedQueryResult item_result = CharacterDatabase.Query(stmt);
|
||||||
|
|
||||||
|
if (item_result)
|
||||||
|
{
|
||||||
|
// Get a LootTemplate for the container item. This is where
|
||||||
|
// the saved loot was originally rolled from, we will copy conditions from it
|
||||||
|
LootTemplate const* lt = LootTemplates_Item.GetLootFor(GetEntry());
|
||||||
|
|
||||||
|
if (lt)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Create an empty LootItem
|
||||||
|
LootItem loot_item = LootItem();
|
||||||
|
|
||||||
|
// Fill in the rest of the LootItem from the DB
|
||||||
|
Field* fields = item_result->Fetch();
|
||||||
|
|
||||||
|
// item_id, itm_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix
|
||||||
|
loot_item.itemid = fields[0].GetUInt32();
|
||||||
|
loot_item.count = fields[1].GetUInt32();
|
||||||
|
loot_item.follow_loot_rules = fields[2].GetBool();
|
||||||
|
loot_item.freeforall = fields[3].GetBool();
|
||||||
|
loot_item.is_blocked = fields[4].GetBool();
|
||||||
|
loot_item.is_counted = fields[5].GetBool();
|
||||||
|
loot_item.canSave = true;
|
||||||
|
loot_item.is_underthreshold = fields[6].GetBool();
|
||||||
|
loot_item.needs_quest = fields[7].GetBool();
|
||||||
|
loot_item.randomPropertyId = fields[8].GetUInt32();
|
||||||
|
loot_item.randomSuffix = fields[9].GetUInt32();
|
||||||
|
|
||||||
|
// Copy the extra loot conditions from the item in the loot template
|
||||||
|
lt->CopyConditions(&loot_item);
|
||||||
|
|
||||||
|
// If container item is in a bag, add that player as an allowed looter
|
||||||
|
if (GetBagSlot())
|
||||||
|
loot_item.allowedGUIDs.insert(GetOwner()->GetGUIDLow());
|
||||||
|
|
||||||
|
// Finally add the LootItem to the container
|
||||||
|
loot.items.push_back(loot_item);
|
||||||
|
|
||||||
|
// Increment unlooted count
|
||||||
|
loot.unlootedCount++;
|
||||||
|
|
||||||
|
} while (item_result->NextRow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the item if it has loot so it won't be generated again on open
|
||||||
|
m_lootGenerated = !loot.isLooted();
|
||||||
|
|
||||||
|
return m_lootGenerated;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Item::ItemContainerDeleteLootItemsFromDB()
|
||||||
|
{
|
||||||
|
// Deletes items associated with an openable item from the DB
|
||||||
|
|
||||||
|
uint32 containerId = GetGUIDLow();
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEMS);
|
||||||
|
stmt->setUInt32(0, containerId);
|
||||||
|
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||||
|
trans->Append(stmt);
|
||||||
|
CharacterDatabase.CommitTransaction(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Item::ItemContainerDeleteLootItemFromDB(uint32 itemID)
|
||||||
|
{
|
||||||
|
// Deletes a single item associated with an openable item from the DB
|
||||||
|
|
||||||
|
uint32 containerId = GetGUIDLow();
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEM);
|
||||||
|
stmt->setUInt32(0, containerId);
|
||||||
|
stmt->setUInt32(1, itemID);
|
||||||
|
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||||
|
trans->Append(stmt);
|
||||||
|
CharacterDatabase.CommitTransaction(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Item::ItemContainerDeleteLootMoneyFromDB()
|
||||||
|
{
|
||||||
|
// Deletes the money loot associated with an openable item from the DB
|
||||||
|
|
||||||
|
uint32 containerId = GetGUIDLow();
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY);
|
||||||
|
stmt->setUInt32(0, containerId);
|
||||||
|
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||||
|
trans->Append(stmt);
|
||||||
|
CharacterDatabase.CommitTransaction(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Item::ItemContainerDeleteLootMoneyAndLootItemsFromDB()
|
||||||
|
{
|
||||||
|
// Deletes money and items associated with an openable item from the DB
|
||||||
|
|
||||||
|
ItemContainerDeleteLootMoneyFromDB();
|
||||||
|
ItemContainerDeleteLootItemsFromDB();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -231,6 +231,15 @@ class Item : public Object
|
|||||||
static void DeleteFromDB(SQLTransaction& trans, uint32 itemGuid);
|
static void DeleteFromDB(SQLTransaction& trans, uint32 itemGuid);
|
||||||
virtual void DeleteFromDB(SQLTransaction& trans);
|
virtual void DeleteFromDB(SQLTransaction& trans);
|
||||||
static void DeleteFromInventoryDB(SQLTransaction& trans, uint32 itemGuid);
|
static void DeleteFromInventoryDB(SQLTransaction& trans, uint32 itemGuid);
|
||||||
|
|
||||||
|
// Lootable items and their contents
|
||||||
|
void ItemContainerSaveLootToDB();
|
||||||
|
bool ItemContainerLoadLootFromDB();
|
||||||
|
void ItemContainerDeleteLootItemsFromDB();
|
||||||
|
void ItemContainerDeleteLootItemFromDB(uint32 itemID);
|
||||||
|
void ItemContainerDeleteLootMoneyFromDB();
|
||||||
|
void ItemContainerDeleteLootMoneyAndLootItemsFromDB();
|
||||||
|
|
||||||
void DeleteFromInventoryDB(SQLTransaction& trans);
|
void DeleteFromInventoryDB(SQLTransaction& trans);
|
||||||
void SaveRefundDataToDB();
|
void SaveRefundDataToDB();
|
||||||
void DeleteRefundDataFromDB(SQLTransaction* trans);
|
void DeleteRefundDataFromDB(SQLTransaction* trans);
|
||||||
|
|||||||
@@ -8903,7 +8903,9 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
|
|||||||
|
|
||||||
loot = &item->loot;
|
loot = &item->loot;
|
||||||
|
|
||||||
if (!item->m_lootGenerated)
|
// If item doesn't already have loot, attempt to load it. If that
|
||||||
|
// fails then this is first time opening, generate loot
|
||||||
|
if (!item->m_lootGenerated && !item->ItemContainerLoadLootFromDB())
|
||||||
{
|
{
|
||||||
item->m_lootGenerated = true;
|
item->m_lootGenerated = true;
|
||||||
loot->clear();
|
loot->clear();
|
||||||
@@ -8922,6 +8924,12 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
|
|||||||
default:
|
default:
|
||||||
loot->generateMoneyLoot(item->GetTemplate()->MinMoneyLoot, item->GetTemplate()->MaxMoneyLoot);
|
loot->generateMoneyLoot(item->GetTemplate()->MinMoneyLoot, item->GetTemplate()->MaxMoneyLoot);
|
||||||
loot->FillLoot(item->GetEntry(), LootTemplates_Item, this, true, loot->gold != 0);
|
loot->FillLoot(item->GetEntry(), LootTemplates_Item, this, true, loot->gold != 0);
|
||||||
|
|
||||||
|
// Force save the loot and money items that were just rolled
|
||||||
|
// Also saves the container item ID in Loot struct (not to DB)
|
||||||
|
if (loot->gold > 0 || loot->unlootedCount > 0)
|
||||||
|
item->ItemContainerSaveLootToDB();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12675,6 +12683,12 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update)
|
|||||||
else if (Bag* pBag = GetBagByPos(bag))
|
else if (Bag* pBag = GetBagByPos(bag))
|
||||||
pBag->RemoveItem(slot, update);
|
pBag->RemoveItem(slot, update);
|
||||||
|
|
||||||
|
// Delete rolled money / loot from db.
|
||||||
|
// MUST be done before RemoveFromWorld() or GetTemplate() fails
|
||||||
|
if (ItemTemplate const* pTmp = pItem->GetTemplate())
|
||||||
|
if (pTmp->Flags & ITEM_PROTO_FLAG_OPENABLE)
|
||||||
|
pItem->ItemContainerDeleteLootMoneyAndLootItemsFromDB();
|
||||||
|
|
||||||
if (IsInWorld() && update)
|
if (IsInWorld() && update)
|
||||||
{
|
{
|
||||||
pItem->RemoveFromWorld();
|
pItem->RemoveFromWorld();
|
||||||
@@ -24055,6 +24069,11 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot)
|
|||||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count);
|
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count);
|
||||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item->count);
|
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item->count);
|
||||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count);
|
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count);
|
||||||
|
|
||||||
|
// LootItem is being removed (looted) from the container, delete it from the DB.
|
||||||
|
if (loot->containerID > 0)
|
||||||
|
loot->DeleteLootItemFromContainerItemDB(item->itemid);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
SendEquipError(msg, NULL, NULL, item->itemid);
|
SendEquipError(msg, NULL, NULL, item->itemid);
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recvData)
|
|||||||
}
|
}
|
||||||
|
|
||||||
player->StoreLootItem(lootSlot, loot);
|
player->StoreLootItem(lootSlot, loot);
|
||||||
|
|
||||||
|
// If player is removing the last LootItem, delete the empty container.
|
||||||
|
if (loot->isLooted() && IS_ITEM_GUID(lguid))
|
||||||
|
player->GetSession()->DoLootRelease(lguid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recvData*/)
|
void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recvData*/)
|
||||||
@@ -200,6 +204,14 @@ void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recvData*/)
|
|||||||
}
|
}
|
||||||
|
|
||||||
loot->gold = 0;
|
loot->gold = 0;
|
||||||
|
|
||||||
|
// Delete the money loot record from the DB
|
||||||
|
if (loot->containerID > 0)
|
||||||
|
loot->DeleteLootMoneyFromContainerItemDB();
|
||||||
|
|
||||||
|
// Delete container if empty
|
||||||
|
if (loot->isLooted() && IS_ITEM_GUID(guid))
|
||||||
|
player->GetSession()->DoLootRelease(guid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,8 +393,8 @@ void WorldSession::DoLootRelease(uint64 lguid)
|
|||||||
player->DestroyItemCount(pItem, count, true);
|
player->DestroyItemCount(pItem, count, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// FIXME: item must not be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or cheating possible.
|
if (pItem->loot.isLooted()) // Only delete item if no loot or money (unlooted loot is saved to db)
|
||||||
player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
|
player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
|
||||||
return; // item can be looted only single player
|
return; // item can be looted only single player
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -334,6 +334,7 @@ LootItem::LootItem(LootStoreItem const& li)
|
|||||||
is_blocked = 0;
|
is_blocked = 0;
|
||||||
is_underthreshold = 0;
|
is_underthreshold = 0;
|
||||||
is_counted = 0;
|
is_counted = 0;
|
||||||
|
canSave = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basic checks for player/item compatibility - if false no chance to see the item in the loot
|
// Basic checks for player/item compatibility - if false no chance to see the item in the loot
|
||||||
@@ -654,6 +655,33 @@ void Loot::generateMoneyLoot(uint32 minAmount, uint32 maxAmount)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Loot::DeleteLootItemFromContainerItemDB(uint32 itemID)
|
||||||
|
{
|
||||||
|
// Deletes a single item associated with an openable item from the DB
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEM);
|
||||||
|
stmt->setUInt32(0, containerID);
|
||||||
|
stmt->setUInt32(1, itemID);
|
||||||
|
CharacterDatabase.Execute(stmt);
|
||||||
|
|
||||||
|
// Mark the item looted to prevent resaving
|
||||||
|
for (LootItemList::iterator _itr = items.begin(); _itr != items.end(); _itr++)
|
||||||
|
{
|
||||||
|
if (!_itr->itemid == itemID)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_itr->canSave = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Loot::DeleteLootMoneyFromContainerItemDB()
|
||||||
|
{
|
||||||
|
// Deletes money loot associated with an openable item from the DB
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY);
|
||||||
|
stmt->setUInt32(0, containerID);
|
||||||
|
CharacterDatabase.Execute(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem* *qitem, QuestItem* *ffaitem, QuestItem* *conditem)
|
LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem* *qitem, QuestItem* *ffaitem, QuestItem* *conditem)
|
||||||
{
|
{
|
||||||
LootItem* item = NULL;
|
LootItem* item = NULL;
|
||||||
@@ -1239,6 +1267,19 @@ void LootTemplate::CopyConditions(ConditionList conditions)
|
|||||||
i->CopyConditions(conditions);
|
i->CopyConditions(conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LootTemplate::CopyConditions(LootItem* li) const
|
||||||
|
{
|
||||||
|
// Copies the conditions list from a template item to a LootItem
|
||||||
|
for (LootStoreItemList::const_iterator _iter = Entries.begin(); _iter != Entries.end(); ++_iter)
|
||||||
|
{
|
||||||
|
if (!_iter->itemid == li->itemid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
li->conditions = _iter->conditions;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Rolls for every item in the template and adds the rolled items the the loot
|
// Rolls for every item in the template and adds the rolled items the the loot
|
||||||
void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId) const
|
void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -141,14 +141,17 @@ struct LootItem
|
|||||||
bool is_counted : 1;
|
bool is_counted : 1;
|
||||||
bool needs_quest : 1; // quest drop
|
bool needs_quest : 1; // quest drop
|
||||||
bool follow_loot_rules : 1;
|
bool follow_loot_rules : 1;
|
||||||
|
bool canSave;
|
||||||
|
|
||||||
// Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties
|
// Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties
|
||||||
// Should be called for non-reference LootStoreItem entries only (mincountOrRef > 0)
|
// Should be called for non-reference LootStoreItem entries only (mincountOrRef > 0)
|
||||||
explicit LootItem(LootStoreItem const& li);
|
explicit LootItem(LootStoreItem const& li);
|
||||||
|
|
||||||
|
// Empty constructor for creating an empty LootItem to be filled in with DB data
|
||||||
|
LootItem() : canSave(true){};
|
||||||
|
|
||||||
// Basic checks for player/item compatibility - if false no chance to see the item in the loot
|
// Basic checks for player/item compatibility - if false no chance to see the item in the loot
|
||||||
bool AllowedForPlayer(Player const* player) const;
|
bool AllowedForPlayer(Player const* player) const;
|
||||||
|
|
||||||
void AddAllowedLooter(Player const* player);
|
void AddAllowedLooter(Player const* player);
|
||||||
const AllowedLooterSet & GetAllowedLooters() const { return allowedGUIDs; }
|
const AllowedLooterSet & GetAllowedLooters() const { return allowedGUIDs; }
|
||||||
};
|
};
|
||||||
@@ -223,6 +226,7 @@ class LootTemplate
|
|||||||
// Rolls for every item in the template and adds the rolled items the the loot
|
// Rolls for every item in the template and adds the rolled items the the loot
|
||||||
void Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId = 0) const;
|
void Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId = 0) const;
|
||||||
void CopyConditions(ConditionList conditions);
|
void CopyConditions(ConditionList conditions);
|
||||||
|
void CopyConditions(LootItem* li) const;
|
||||||
|
|
||||||
// True if template includes at least 1 quest drop entry
|
// True if template includes at least 1 quest drop entry
|
||||||
bool HasQuestDrop(LootTemplateMap const& store, uint8 groupId = 0) const;
|
bool HasQuestDrop(LootTemplateMap const& store, uint8 groupId = 0) const;
|
||||||
@@ -286,10 +290,18 @@ struct Loot
|
|||||||
uint8 unlootedCount;
|
uint8 unlootedCount;
|
||||||
uint64 roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released.
|
uint64 roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released.
|
||||||
LootType loot_type; // required for achievement system
|
LootType loot_type; // required for achievement system
|
||||||
|
|
||||||
|
// GUIDLow of container that holds this loot (item_instance.entry)
|
||||||
|
// Only set for inventory items that can be right-click looted
|
||||||
|
uint32 containerID;
|
||||||
|
|
||||||
Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0), loot_type(LOOT_CORPSE) {}
|
Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0), loot_type(LOOT_CORPSE), containerID(0) {}
|
||||||
~Loot() { clear(); }
|
~Loot() { clear(); }
|
||||||
|
|
||||||
|
// For deleting items at loot removal since there is no backward interface to the Item()
|
||||||
|
void DeleteLootItemFromContainerItemDB(uint32 itemID);
|
||||||
|
void DeleteLootMoneyFromContainerItemDB();
|
||||||
|
|
||||||
// if loot becomes invalid this reference is used to inform the listener
|
// if loot becomes invalid this reference is used to inform the listener
|
||||||
void addLootValidatorRef(LootValidatorRef* pLootValidatorRef)
|
void addLootValidatorRef(LootValidatorRef* pLootValidatorRef)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -540,6 +540,15 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
|||||||
PREPARE_STATEMENT(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC, "DELETE FROM character_action WHERE spec<>? AND guid = ?", CONNECTION_ASYNC);
|
PREPARE_STATEMENT(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC, "DELETE FROM character_action WHERE spec<>? AND guid = ?", CONNECTION_ASYNC);
|
||||||
PREPARE_STATEMENT(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
|
PREPARE_STATEMENT(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
|
||||||
|
|
||||||
|
// Items that hold loot or money
|
||||||
|
PREPARE_STATEMENT(CHAR_SEL_ITEMCONTAINER_ITEMS, "SELECT item_id, item_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix FROM item_loot_items WHERE container_id = ?", CONNECTION_SYNCH);
|
||||||
|
PREPARE_STATEMENT(CHAR_DEL_ITEMCONTAINER_ITEMS, "DELETE FROM item_loot_items WHERE container_id = ?", CONNECTION_ASYNC);
|
||||||
|
PREPARE_STATEMENT(CHAR_DEL_ITEMCONTAINER_ITEM, "DELETE FROM item_loot_items WHERE container_id = ? AND item_id = ?", CONNECTION_ASYNC);
|
||||||
|
PREPARE_STATEMENT(CHAR_INS_ITEMCONTAINER_ITEMS, "INSERT INTO item_loot_items (container_id, item_id, item_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix) VALUES (?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC);
|
||||||
|
PREPARE_STATEMENT(CHAR_SEL_ITEMCONTAINER_MONEY, "SELECT money FROM item_loot_money WHERE container_id = ?", CONNECTION_SYNCH);
|
||||||
|
PREPARE_STATEMENT(CHAR_DEL_ITEMCONTAINER_MONEY, "DELETE FROM item_loot_money WHERE container_id = ?", CONNECTION_ASYNC);
|
||||||
|
PREPARE_STATEMENT(CHAR_INS_ITEMCONTAINER_MONEY," INSERT INTO item_loot_money (container_id, money) VALUES (?,?)", CONNECTION_ASYNC);
|
||||||
|
|
||||||
// Calendar
|
// Calendar
|
||||||
PREPARE_STATEMENT(CHAR_REP_CALENDAR_EVENT, "REPLACE INTO calendar_events (id, creator, title, description, type, dungeon, eventtime, flags, time2) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
PREPARE_STATEMENT(CHAR_REP_CALENDAR_EVENT, "REPLACE INTO calendar_events (id, creator, title, description, type, dungeon, eventtime, flags, time2) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||||
PREPARE_STATEMENT(CHAR_DEL_CALENDAR_EVENT, "DELETE FROM calendar_events WHERE id = ?", CONNECTION_ASYNC);
|
PREPARE_STATEMENT(CHAR_DEL_CALENDAR_EVENT, "DELETE FROM calendar_events WHERE id = ?", CONNECTION_ASYNC);
|
||||||
|
|||||||
@@ -505,6 +505,14 @@ enum CharacterDatabaseStatements
|
|||||||
CHAR_REP_CALENDAR_INVITE,
|
CHAR_REP_CALENDAR_INVITE,
|
||||||
CHAR_DEL_CALENDAR_INVITE,
|
CHAR_DEL_CALENDAR_INVITE,
|
||||||
|
|
||||||
|
CHAR_SEL_ITEMCONTAINER_ITEMS,
|
||||||
|
CHAR_DEL_ITEMCONTAINER_ITEMS,
|
||||||
|
CHAR_DEL_ITEMCONTAINER_ITEM,
|
||||||
|
CHAR_INS_ITEMCONTAINER_ITEMS,
|
||||||
|
CHAR_SEL_ITEMCONTAINER_MONEY,
|
||||||
|
CHAR_DEL_ITEMCONTAINER_MONEY,
|
||||||
|
CHAR_INS_ITEMCONTAINER_MONEY,
|
||||||
|
|
||||||
MAX_CHARACTERDATABASE_STATEMENTS
|
MAX_CHARACTERDATABASE_STATEMENTS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user