[10662] Implement partly item looting

* Not deleted normal item loot at first loot window close.
* Save current item loot content to DB for later continue looting.
* Cleanup spell effect SPELL_EFFECT_OPEN_LOCK check code
* Cleanup Player::AutoStoreLoot calls
This commit is contained in:
VladimirMangos 2010-11-01 00:02:51 +03:00
parent 9a5accf8a9
commit f48e768a70
21 changed files with 392 additions and 132 deletions

View file

@ -21,7 +21,7 @@
DROP TABLE IF EXISTS `character_db_version`; DROP TABLE IF EXISTS `character_db_version`;
CREATE TABLE `character_db_version` ( CREATE TABLE `character_db_version` (
`required_10655_01_characters_character_queststatus_monthly` bit(1) default NULL `required_10662_01_characters_item_loot` bit(1) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB';
-- --
@ -1455,6 +1455,31 @@ 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`
--
DROP TABLE IF EXISTS `item_loot`;
CREATE TABLE `item_loot` (
`guid` int(11) unsigned NOT NULL default '0',
`owner_guid` int(11) unsigned NOT NULL default '0',
`itemid` int(11) unsigned NOT NULL default '0',
`amount` int(11) unsigned NOT NULL default '0',
`suffix` int(11) unsigned NOT NULL default '0',
`property` int(11) NOT NULL default '0',
PRIMARY KEY (`guid`,`itemid`),
KEY `idx_owner_guid` (`owner_guid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Item System';
--
-- Dumping data for table `item_instance`
--
LOCK TABLES `item_loot` WRITE;
/*!40000 ALTER TABLE `item_loot` DISABLE KEYS */;
/*!40000 ALTER TABLE `item_loot` ENABLE KEYS */;
UNLOCK TABLES;
-- --
-- Table structure for table `mail` -- Table structure for table `mail`
-- --

View file

@ -0,0 +1,13 @@
ALTER TABLE character_db_version CHANGE COLUMN required_10655_01_characters_character_queststatus_monthly required_10662_01_characters_item_loot bit;
DROP TABLE IF EXISTS `item_loot`;
CREATE TABLE `item_loot` (
`guid` int(11) unsigned NOT NULL default '0',
`owner_guid` int(11) unsigned NOT NULL default '0',
`itemid` int(11) unsigned NOT NULL default '0',
`amount` int(11) unsigned NOT NULL default '0',
`suffix` int(11) unsigned NOT NULL default '0',
`property` int(11) NOT NULL default '0',
PRIMARY KEY (`guid`,`itemid`),
KEY `idx_owner_guid` (`owner_guid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Item System';

View file

@ -112,6 +112,7 @@ pkgdata_DATA = \
10654_01_mangos_game_event_creature_quest.sql \ 10654_01_mangos_game_event_creature_quest.sql \
10655_01_characters_character_queststatus_monthly.sql \ 10655_01_characters_character_queststatus_monthly.sql \
10660_01_mangos_game_event_quest.sql \ 10660_01_mangos_game_event_quest.sql \
10662_01_characters_item_loot.sql \
README README
## Additional files to include when running 'make dist' ## Additional files to include when running 'make dist'
@ -204,4 +205,5 @@ EXTRA_DIST = \
10654_01_mangos_game_event_creature_quest.sql \ 10654_01_mangos_game_event_creature_quest.sql \
10655_01_characters_character_queststatus_monthly.sql \ 10655_01_characters_character_queststatus_monthly.sql \
10660_01_mangos_game_event_quest.sql \ 10660_01_mangos_game_event_quest.sql \
10662_01_characters_item_loot.sql \
README README

View file

@ -318,7 +318,7 @@ void AuctionHouseMgr::LoadAuctionItems()
Item *item = NewItemOrBag(proto); Item *item = NewItemOrBag(proto);
if(!item->LoadFromDB(item_guid,0, result)) if(!item->LoadFromDB(item_guid,0, fields))
{ {
delete item; delete item;
continue; continue;

View file

@ -94,9 +94,9 @@ void Bag::SaveToDB()
Item::SaveToDB(); Item::SaveToDB();
} }
bool Bag::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result) bool Bag::LoadFromDB(uint32 guid, uint64 owner_guid, Field *fields)
{ {
if(!Item::LoadFromDB(guid, owner_guid, result)) if(!Item::LoadFromDB(guid, owner_guid, fields))
return false; return false;
// cleanup bag content related item value fields (its will be filled correctly from `character_inventory`) // cleanup bag content related item value fields (its will be filled correctly from `character_inventory`)

View file

@ -57,7 +57,7 @@ class Bag : public Item
// overwrite virtual Item::SaveToDB // overwrite virtual Item::SaveToDB
void SaveToDB(); void SaveToDB();
// overwrite virtual Item::LoadFromDB // overwrite virtual Item::LoadFromDB
bool LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result); bool LoadFromDB(uint32 guid, uint64 owner_guid, Field *fields);
// overwrite virtual Item::DeleteFromDB // overwrite virtual Item::DeleteFromDB
void DeleteFromDB(); void DeleteFromDB();
@ -71,6 +71,10 @@ class Bag : public Item
inline Item* NewItemOrBag(ItemPrototype const * proto) inline Item* NewItemOrBag(ItemPrototype const * proto)
{ {
return (proto->InventoryType == INVTYPE_BAG) ? new Bag : new Item; if (proto->InventoryType == INVTYPE_BAG)
return new Bag;
return new Item;
} }
#endif #endif

View file

@ -82,6 +82,7 @@ bool LoginQueryHolder::Initialize()
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMONTHLYQUESTSTATUS,"SELECT quest FROM character_queststatus_monthly WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMONTHLYQUESTSTATUS,"SELECT quest FROM character_queststatus_monthly WHERE guid = '%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADINVENTORY, "SELECT data,text,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADINVENTORY, "SELECT data,text,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADITEMLOOT, "SELECT guid,itemid,amount,suffix,property FROM item_loot WHERE owner_guid = '%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT spec,button,action,type FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT spec,button,action,type FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADHOMEBIND, "SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADHOMEBIND, "SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(m_guid));

View file

@ -1174,7 +1174,7 @@ void Guild::LoadGuildBankFromDB()
} }
Item *pItem = NewItemOrBag(proto); Item *pItem = NewItemOrBag(proto);
if (!pItem->LoadFromDB(ItemGuid, 0, result)) if (!pItem->LoadFromDB(ItemGuid, 0, fields))
{ {
CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid='%u' AND TabId='%u' AND SlotId='%u'", m_Id, uint32(TabId), uint32(SlotId)); CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid='%u' AND TabId='%u' AND SlotId='%u'", m_Id, uint32(TabId), uint32(SlotId));
sLog.outError("Item GUID %u not found in item_instance, deleting from Guild Bank!", ItemGuid); sLog.outError("Item GUID %u not found in item_instance, deleting from Guild Bank!", ItemGuid);

View file

@ -241,8 +241,8 @@ Item::Item( )
uState = ITEM_NEW; uState = ITEM_NEW;
uQueuePos = -1; uQueuePos = -1;
m_container = NULL; m_container = NULL;
m_lootGenerated = false;
mb_in_trade = false; mb_in_trade = false;
m_lootState = ITEM_LOOT_NONE;
} }
bool Item::Create( uint32 guidlow, uint32 itemid, Player const* owner) bool Item::Create( uint32 guidlow, uint32 itemid, Player const* owner)
@ -326,40 +326,66 @@ void Item::SaveToDB()
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", guid); CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", guid);
if (HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED)) if (HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED))
CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", GetGUIDLow()); CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", GetGUIDLow());
if (HasSavedLoot())
CharacterDatabase.PExecute("DELETE FROM item_loot WHERE guid = '%u'", GetGUIDLow());
delete this; delete this;
return; return;
} }
case ITEM_UNCHANGED: case ITEM_UNCHANGED:
break; return;
} }
if (m_lootState == ITEM_LOOT_CHANGED || m_lootState == ITEM_LOOT_REMOVED)
CharacterDatabase.PExecute("DELETE FROM item_loot WHERE guid = '%u'", GetGUIDLow());
if (m_lootState == ITEM_LOOT_NEW || m_lootState == ITEM_LOOT_CHANGED)
{
if(Player* owner = GetOwner())
{
// save money as 0 itemid data
if (loot.gold)
CharacterDatabase.PExecute("INSERT INTO item_loot (guid,owner_guid,itemid,amount,suffix,property) "
"VALUES (%u, %u, 0, %u, 0, 0)",
GetGUIDLow(), owner->GetGUIDLow(), loot.gold);
// save items and quest items (at load its all will added as normal, but this not important for item loot case)
for (size_t i = 0; i < loot.GetMaxSlotInLootFor(owner); ++i)
{
QuestItem *qitem = NULL;
LootItem *item = loot.LootItemInSlot(i,owner,&qitem);
if(!item)
continue;
// questitems use the blocked field for other purposes
if (!qitem && item->is_blocked)
continue;
CharacterDatabase.PExecute("INSERT INTO item_loot (guid,owner_guid,itemid,amount,suffix,property) "
"VALUES (%u, %u, %u, %u, %u, %i)",
GetGUIDLow(), owner->GetGUIDLow(), item->itemid, item->count, item->randomSuffix, item->randomPropertyId);
}
}
}
if (m_lootState != ITEM_LOOT_NONE && m_lootState != ITEM_LOOT_TEMPORARY)
SetLootState(ITEM_LOOT_UNCHANGED);
SetState(ITEM_UNCHANGED); SetState(ITEM_UNCHANGED);
} }
bool Item::LoadFromDB(uint32 guidLow, uint64 owner_guid, QueryResult *result) bool Item::LoadFromDB(uint32 guidLow, uint64 owner_guid, Field *fields)
{ {
// create item before any checks for store correct guid // create item before any checks for store correct guid
// and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB // and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB
Object::_Create(guidLow, 0, HIGHGUID_ITEM); Object::_Create(guidLow, 0, HIGHGUID_ITEM);
bool delete_result = false;
if(!result)
{
result = CharacterDatabase.PQuery("SELECT data FROM item_instance WHERE guid = '%u'", guidLow);
delete_result = true;
}
if (!result)
{
sLog.outError("Item (GUID: %u owner: %u) not found in table `item_instance`, can't load. ", guidLow, GUID_LOPART(owner_guid));
return false;
}
Field *fields = result->Fetch();
if (!LoadValues(fields[0].GetString())) if (!LoadValues(fields[0].GetString()))
{ {
sLog.outError("Item #%d have broken data in `data` field. Can't be loaded.", guidLow); sLog.outError("Item #%d have broken data in `data` field. Can't be loaded.", guidLow);
if (delete_result) delete result;
return false; return false;
} }
@ -373,9 +399,6 @@ bool Item::LoadFromDB(uint32 guidLow, uint64 owner_guid, QueryResult *result)
need_save = true; need_save = true;
} }
if (delete_result)
delete result;
ItemPrototype const* proto = GetProto(); ItemPrototype const* proto = GetProto();
if(!proto) if(!proto)
return false; return false;
@ -446,6 +469,37 @@ bool Item::LoadFromDB(uint32 guidLow, uint64 owner_guid, QueryResult *result)
return true; return true;
} }
void Item::LoadLootFromDB(Field *fields)
{
uint32 item_id = fields[1].GetUInt32();
uint32 item_amount = fields[2].GetUInt32();
uint32 item_suffix = fields[3].GetUInt32();
int32 item_propid = fields[4].GetInt32();
// money value special case
if (item_id == 0)
{
loot.gold = item_amount;
SetLootState(ITEM_LOOT_UNCHANGED);
return;
}
// normal item case
ItemPrototype const* proto = ObjectMgr::GetItemPrototype(item_id);
if(!proto)
{
CharacterDatabase.PExecute("DELETE FROM item_loot WHERE guid = '%u' AND itemid = '%u'", GetGUIDLow(), item_id);
sLog.outError("Item::LoadLootFromDB: %s has an unknown item (id: #%u) in item_loot, deleted.", ObjectGuid(GetOwnerGUID()).GetString().c_str(), item_id);
return;
}
loot.items.push_back(LootItem(item_id, item_amount, item_suffix, item_propid));
++loot.unlootedCount;
SetLootState(ITEM_LOOT_UNCHANGED);
}
void Item::DeleteFromDB() void Item::DeleteFromDB()
{ {
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'",GetGUIDLow()); CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'",GetGUIDLow());
@ -725,9 +779,6 @@ bool Item::IsEquipped() const
bool Item::CanBeTraded(bool mail) const bool Item::CanBeTraded(bool mail) const
{ {
if (m_lootGenerated)
return false;
if ((!mail || !IsBoundAccountWide()) && IsSoulBound()) if ((!mail || !IsBoundAccountWide()) && IsSoulBound())
return false; return false;
@ -742,6 +793,9 @@ bool Item::CanBeTraded(bool mail) const
return false; return false;
} }
if (HasGeneratedLoot())
return false;
if (IsBoundByEnchant()) if (IsBoundByEnchant())
return false; return false;
@ -1003,6 +1057,10 @@ bool Item::IsBindedNotWith( Player const* player ) const
if(GetOwnerGUID()== player->GetGUID()) if(GetOwnerGUID()== player->GetGUID())
return false; return false;
// has loot with diff owner
if (HasGeneratedLoot())
return true;
// not BOA item case // not BOA item case
if(!IsBoundAccountWide()) if(!IsBoundAccountWide())
return true; return true;
@ -1050,7 +1108,7 @@ uint8 Item::CanBeMergedPartlyWith( ItemPrototype const* proto ) const
return EQUIP_ERR_ITEM_CANT_STACK; return EQUIP_ERR_ITEM_CANT_STACK;
// not allow merge looting currently items // not allow merge looting currently items
if (m_lootGenerated) if (HasGeneratedLoot())
return EQUIP_ERR_ALREADY_LOOTED; return EQUIP_ERR_ALREADY_LOOTED;
return EQUIP_ERR_OK; return EQUIP_ERR_OK;
@ -1099,3 +1157,47 @@ void Item::RestoreCharges()
} }
} }
} }
void Item::SetLootState( ItemLootUpdateState state )
{
// ITEM_LOOT_NONE -> ITEM_LOOT_TEMPORARY -> ITEM_LOOT_NONE
// ITEM_LOOT_NONE -> ITEM_LOOT_NEW -> ITEM_LOOT_NONE
// ITEM_LOOT_NONE -> ITEM_LOOT_NEW -> ITEM_LOOT_UNCHANGED [<-> ITEM_LOOT_CHANGED] -> ITEM_LOOT_REMOVED -> ITEM_LOOT_NONE
switch(state)
{
case ITEM_LOOT_NONE:
case ITEM_LOOT_NEW:
assert(false); // not used in state change calls
return;
case ITEM_LOOT_TEMPORARY:
assert(m_lootState == ITEM_LOOT_NONE); // called only for not generated yet loot case
m_lootState = ITEM_LOOT_TEMPORARY;
break;
case ITEM_LOOT_CHANGED:
// new loot must stay in new state until saved, temporary must stay until remove
if (m_lootState != ITEM_LOOT_NEW && m_lootState != ITEM_LOOT_TEMPORARY)
m_lootState = m_lootState == ITEM_LOOT_NONE ? ITEM_LOOT_NEW : state;
break;
case ITEM_LOOT_UNCHANGED:
// expected that called after DB update or load
if (m_lootState == ITEM_LOOT_REMOVED)
m_lootState = ITEM_LOOT_NONE;
// temporary must stay until remove (ignore any changes)
else if (m_lootState != ITEM_LOOT_TEMPORARY)
m_lootState = ITEM_LOOT_UNCHANGED;
break;
case ITEM_LOOT_REMOVED:
// if loot not saved then it existence in past can be just ignored
if (m_lootState == ITEM_LOOT_NEW || m_lootState == ITEM_LOOT_TEMPORARY)
{
m_lootState = ITEM_LOOT_NONE;
return;
}
m_lootState = ITEM_LOOT_REMOVED;
break;
}
if (m_lootState != ITEM_LOOT_NONE && m_lootState != ITEM_LOOT_UNCHANGED && m_lootState != ITEM_LOOT_TEMPORARY)
SetState(ITEM_CHANGED);
}

View file

@ -26,6 +26,7 @@
struct SpellEntry; struct SpellEntry;
class Bag; class Bag;
class Field;
class QueryResult; class QueryResult;
class Unit; class Unit;
@ -202,6 +203,15 @@ enum ItemUpdateState
ITEM_REMOVED = 3 ITEM_REMOVED = 3
}; };
enum ItemLootUpdateState
{
ITEM_LOOT_NONE = 0, // loot not generated
ITEM_LOOT_TEMPORARY = 1, // generated loot is temporary (will deleted at loot window close)
ITEM_LOOT_UNCHANGED = 2,
ITEM_LOOT_CHANGED = 3,
ITEM_LOOT_NEW = 4,
ITEM_LOOT_REMOVED = 5
};
// masks for ITEM_FIELD_FLAGS field // masks for ITEM_FIELD_FLAGS field
enum ItemDynFlags enum ItemDynFlags
@ -282,9 +292,10 @@ class MANGOS_DLL_SPEC Item : public Object
bool IsBindedNotWith(Player const* player) const; bool IsBindedNotWith(Player const* player) const;
bool IsBoundByEnchant() const; bool IsBoundByEnchant() const;
virtual void SaveToDB(); virtual void SaveToDB();
virtual bool LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result); virtual bool LoadFromDB(uint32 guid, uint64 owner_guid, Field *fields);
virtual void DeleteFromDB(); virtual void DeleteFromDB();
void DeleteFromInventoryDB(); void DeleteFromInventoryDB();
void LoadLootFromDB(Field *fields);
bool IsBag() const { return GetProto()->InventoryType == INVTYPE_BAG; } bool IsBag() const { return GetProto()->InventoryType == INVTYPE_BAG; }
bool IsBroken() const { return GetUInt32Value(ITEM_FIELD_MAXDURABILITY) > 0 && GetUInt32Value(ITEM_FIELD_DURABILITY) == 0; } bool IsBroken() const { return GetUInt32Value(ITEM_FIELD_MAXDURABILITY) > 0 && GetUInt32Value(ITEM_FIELD_DURABILITY) == 0; }
@ -344,7 +355,10 @@ class MANGOS_DLL_SPEC Item : public Object
void RestoreCharges(); void RestoreCharges();
Loot loot; Loot loot;
bool m_lootGenerated;
void SetLootState(ItemLootUpdateState state);
bool HasGeneratedLoot() const { return m_lootState != ITEM_LOOT_NONE && m_lootState != ITEM_LOOT_REMOVED; }
bool HasSavedLoot() const { return m_lootState != ITEM_LOOT_NONE && m_lootState != ITEM_LOOT_NEW && m_lootState != ITEM_LOOT_TEMPORARY; }
// Update States // Update States
ItemUpdateState GetState() const { return uState; } ItemUpdateState GetState() const { return uState; }
@ -373,5 +387,7 @@ class MANGOS_DLL_SPEC Item : public Object
ItemUpdateState uState; ItemUpdateState uState;
int16 uQueuePos; int16 uQueuePos;
bool mb_in_trade; // true if item is currently in trade-window bool mb_in_trade; // true if item is currently in trade-window
ItemLootUpdateState m_lootState;
}; };
#endif #endif

View file

@ -38,6 +38,7 @@ void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data )
ObjectGuid lguid = player->GetLootGUID(); ObjectGuid lguid = player->GetLootGUID();
Loot *loot; Loot *loot;
uint8 lootSlot; uint8 lootSlot;
Item* pItem = NULL;
recv_data >> lootSlot; recv_data >> lootSlot;
@ -59,9 +60,9 @@ void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data )
} }
case HIGHGUID_ITEM: case HIGHGUID_ITEM:
{ {
Item *pItem = player->GetItemByGuid( lguid ); pItem = player->GetItemByGuid( lguid );
if (!pItem) if (!pItem || !pItem->HasGeneratedLoot())
{ {
player->SendLootRelease(lguid); player->SendLootRelease(lguid);
return; return;
@ -122,6 +123,9 @@ void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data )
return; return;
} }
if (pItem)
pItem->SetLootState(ITEM_LOOT_CHANGED);
ItemPosCountVec dest; ItemPosCountVec dest;
uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count ); uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count );
if ( msg == EQUIP_ERR_OK ) if ( msg == EQUIP_ERR_OK )
@ -179,6 +183,7 @@ void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ )
return; return;
Loot *pLoot = NULL; Loot *pLoot = NULL;
Item* pItem = NULL;
switch(guid.GetHigh()) switch(guid.GetHigh())
{ {
@ -203,8 +208,11 @@ void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ )
} }
case HIGHGUID_ITEM: case HIGHGUID_ITEM:
{ {
if(Item *item = GetPlayer()->GetItemByGuid(guid)) pItem = GetPlayer()->GetItemByGuid(guid);
pLoot = &item->loot; if (!pItem || !pItem->HasGeneratedLoot())
return;
pLoot = &pItem->loot;
break; break;
} }
case HIGHGUID_UNIT: case HIGHGUID_UNIT:
@ -255,7 +263,12 @@ void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ )
player->ModifyMoney( pLoot->gold ); player->ModifyMoney( pLoot->gold );
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, pLoot->gold); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, pLoot->gold);
} }
pLoot->gold = 0; pLoot->gold = 0;
if (pItem)
pItem->SetLootState(ITEM_LOOT_CHANGED);
pLoot->NotifyMoneyRemoved(); pLoot->NotifyMoneyRemoved();
} }
} }
@ -400,29 +413,44 @@ void WorldSession::DoLootRelease(ObjectGuid lguid)
case HIGHGUID_ITEM: case HIGHGUID_ITEM:
{ {
Item *pItem = player->GetItemByGuid(lguid ); Item *pItem = player->GetItemByGuid(lguid );
if(!pItem) if (!pItem)
return; return;
ItemPrototype const* proto = pItem->GetProto(); ItemPrototype const* proto = pItem->GetProto();
// destroy only 5 items from stack in case prospecting and milling // destroy only 5 items from stack in case prospecting and milling
if ((proto->Flags & (ITEM_FLAG_PROSPECTABLE | ITEM_FLAG_MILLABLE))) switch (pItem->loot.loot_type)
{ {
pItem->m_lootGenerated = false; case LOOT_MILLING:
pItem->loot.clear(); case LOOT_PROSPECTING:
{
uint32 count = pItem->GetCount();
uint32 count = pItem->GetCount(); // >=5 checked in spell code, but will work for cheating cases also with removing from another stacks.
if(count > 5)
count = 5;
// >=5 checked in spell code, but will work for cheating cases also with removing from another stacks. player->DestroyItemCount(pItem, count, true);
if(count > 5) break;
count = 5; }
case LOOT_DISENCHANTING:
player->DestroyItemCount(pItem, count, true); {
if (!pItem->loot.isLooted())
player->AutoStoreLoot(pItem->loot); // can be lost if no space
pItem->loot.clear();
pItem->SetLootState(ITEM_LOOT_REMOVED);
player->DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), true);
break;
}
default:
{
// must be destroyed only if no loot
if (pItem->loot.isLooted())
player->DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), true);
break;
}
} }
else return; // item can be looted only single player
// FIXME: item don't must be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or checting possible.
player->DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), true);
return; // item can be looted only single player
} }
case HIGHGUID_UNIT: case HIGHGUID_UNIT:
{ {

View file

@ -333,6 +333,25 @@ LootItem::LootItem(LootStoreItem const& li)
is_counted = 0; is_counted = 0;
} }
LootItem::LootItem(uint32 itemid_, uint32 count_, uint32 randomSuffix_, int32 randomPropertyId_)
{
itemid = itemid_;
conditionId = 0;
ItemPrototype const* proto = ObjectMgr::GetItemPrototype(itemid);
freeforall = proto && (proto->Flags & ITEM_FLAG_PARTY_LOOT);
needs_quest = false;
count = count_;
randomSuffix = randomSuffix_;
randomPropertyId = randomPropertyId_;
is_looted = 0;
is_blocked = 0;
is_underthreshold = 0;
is_counted = 0;
}
// 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 LootItem::AllowedForPlayer(Player const * player) const bool LootItem::AllowedForPlayer(Player const * player) const
{ {

View file

@ -97,6 +97,8 @@ struct LootItem
// 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);
LootItem(uint32 itemid_, uint32 count_, uint32 randomSuffix_ = 0, int32 randomPropertyId_ = 0);
// 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;
}; };

View file

@ -1927,6 +1927,35 @@ void ObjectMgr::LoadItemPrototypes()
const_cast<ItemPrototype*>(proto)->InventoryType = INVTYPE_NON_EQUIP; const_cast<ItemPrototype*>(proto)->InventoryType = INVTYPE_NON_EQUIP;
} }
if (proto->InventoryType != INVTYPE_NON_EQUIP)
{
if(proto->Flags & ITEM_FLAG_LOOTABLE)
{
sLog.outErrorDb("Item container (Entry: %u) has not allowed for containers flag ITEM_FLAG_LOOTABLE (%u), flag removed.", i, ITEM_FLAG_LOOTABLE);
const_cast<ItemPrototype*>(proto)->Flags &= ~ITEM_FLAG_LOOTABLE;
}
if(proto->Flags & ITEM_FLAG_MILLABLE)
{
sLog.outErrorDb("Item container (Entry: %u) has not allowed for containers flag ITEM_FLAG_MILLABLE (%u), flag removed.", i, ITEM_FLAG_MILLABLE);
const_cast<ItemPrototype*>(proto)->Flags &= ~ITEM_FLAG_MILLABLE;
}
if(proto->Flags & ITEM_FLAG_PROSPECTABLE)
{
sLog.outErrorDb("Item container (Entry: %u) has not allowed for containers flag ITEM_FLAG_PROSPECTABLE (%u), flag removed.", i, ITEM_FLAG_PROSPECTABLE);
const_cast<ItemPrototype*>(proto)->Flags &= ~ITEM_FLAG_PROSPECTABLE;
}
}
else if (proto->InventoryType != INVTYPE_BAG)
{
if (proto->ContainerSlots > 0)
{
sLog.outErrorDb("Non-container item (Entry: %u) has ContainerSlots (%u), set to 0.", i, proto->ContainerSlots);
const_cast<ItemPrototype*>(proto)->ContainerSlots = 0;
}
}
if(proto->RequiredSkill >= MAX_SKILL_TYPE) if(proto->RequiredSkill >= MAX_SKILL_TYPE)
{ {
sLog.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)",i,proto->RequiredSkill); sLog.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)",i,proto->RequiredSkill);
@ -2005,17 +2034,11 @@ void ObjectMgr::LoadItemPrototypes()
if (proto->ContainerSlots) if (proto->ContainerSlots)
{ {
if(proto->ContainerSlots > MAX_BAG_SIZE) if (proto->ContainerSlots > MAX_BAG_SIZE)
{ {
sLog.outErrorDb("Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).",i,proto->ContainerSlots,MAX_BAG_SIZE); sLog.outErrorDb("Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).",i,proto->ContainerSlots,MAX_BAG_SIZE);
const_cast<ItemPrototype*>(proto)->ContainerSlots = MAX_BAG_SIZE; const_cast<ItemPrototype*>(proto)->ContainerSlots = MAX_BAG_SIZE;
} }
if(proto->Flags & ITEM_FLAG_LOOTABLE)
{
sLog.outErrorDb("Item container (Entry: %u) has not allowed for containers flag ITEM_FLAG_LOOTABLE (%u), flag removed.",i,ITEM_FLAG_LOOTABLE);
const_cast<ItemPrototype*>(proto)->Flags &= ~ITEM_FLAG_LOOTABLE;
}
} }
if(proto->StatsCount > MAX_ITEM_PROTO_STATS) if(proto->StatsCount > MAX_ITEM_PROTO_STATS)

View file

@ -4254,7 +4254,7 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe
} }
Item *pItem = NewItemOrBag(itemProto); Item *pItem = NewItemOrBag(itemProto);
if (!pItem->LoadFromDB(item_guidlow, playerguid.GetRawValue(), resultItems)) if (!pItem->LoadFromDB(item_guidlow, playerguid.GetRawValue(), fields2))
{ {
pItem->FSetState(ITEM_REMOVED); pItem->FSetState(ITEM_REMOVED);
pItem->SaveToDB(); // it also deletes item object ! pItem->SaveToDB(); // it also deletes item object !
@ -7897,25 +7897,28 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type)
loot = &item->loot; loot = &item->loot;
if (!item->m_lootGenerated) if (!item->HasGeneratedLoot())
{ {
item->m_lootGenerated = true; item->loot.clear();
loot->clear();
switch(loot_type) switch(loot_type)
{ {
case LOOT_DISENCHANTING: case LOOT_DISENCHANTING:
loot->FillLoot(item->GetProto()->DisenchantID, LootTemplates_Disenchant, this,true); loot->FillLoot(item->GetProto()->DisenchantID, LootTemplates_Disenchant, this,true);
item->SetLootState(ITEM_LOOT_TEMPORARY);
break; break;
case LOOT_PROSPECTING: case LOOT_PROSPECTING:
loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this,true); loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this,true);
item->SetLootState(ITEM_LOOT_TEMPORARY);
break; break;
case LOOT_MILLING: case LOOT_MILLING:
loot->FillLoot(item->GetEntry(), LootTemplates_Milling, this,true); loot->FillLoot(item->GetEntry(), LootTemplates_Milling, this,true);
item->SetLootState(ITEM_LOOT_TEMPORARY);
break; break;
default: default:
loot->FillLoot(item->GetEntry(), LootTemplates_Item, this,true); loot->FillLoot(item->GetEntry(), LootTemplates_Item, this,true);
loot->generateMoneyLoot(item->GetProto()->MinMoneyLoot,item->GetProto()->MaxMoneyLoot); loot->generateMoneyLoot(item->GetProto()->MinMoneyLoot,item->GetProto()->MaxMoneyLoot);
item->SetLootState(ITEM_LOOT_CHANGED);
break; break;
} }
} }
@ -9621,14 +9624,6 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
if (pItem) if (pItem)
{ {
// item used
if(pItem->m_lootGenerated)
{
if (no_space_count)
*no_space_count = count;
return EQUIP_ERR_ALREADY_LOOTED;
}
if (pItem->IsBindedNotWith(this)) if (pItem->IsBindedNotWith(this))
{ {
if (no_space_count) if (no_space_count)
@ -10116,10 +10111,6 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
if( !pProto ) if( !pProto )
return EQUIP_ERR_ITEM_NOT_FOUND; return EQUIP_ERR_ITEM_NOT_FOUND;
// item used
if(pItem->m_lootGenerated)
return EQUIP_ERR_ALREADY_LOOTED;
// item it 'bind' // item it 'bind'
if(pItem->IsBindedNotWith(this)) if(pItem->IsBindedNotWith(this))
return EQUIP_ERR_DONT_OWN_THAT_ITEM; return EQUIP_ERR_DONT_OWN_THAT_ITEM;
@ -10323,10 +10314,6 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo
ItemPrototype const *pProto = pItem->GetProto(); ItemPrototype const *pProto = pItem->GetProto();
if( pProto ) if( pProto )
{ {
// item used
if(pItem->m_lootGenerated)
return EQUIP_ERR_ALREADY_LOOTED;
if(pItem->IsBindedNotWith(this)) if(pItem->IsBindedNotWith(this))
return EQUIP_ERR_DONT_OWN_THAT_ITEM; return EQUIP_ERR_DONT_OWN_THAT_ITEM;
@ -10455,25 +10442,21 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo
uint8 Player::CanUnequipItem( uint16 pos, bool swap ) const uint8 Player::CanUnequipItem( uint16 pos, bool swap ) const
{ {
// Applied only to equipped items and bank bags // Applied only to equipped items and bank bags
if(!IsEquipmentPos(pos) && !IsBagPos(pos)) if (!IsEquipmentPos(pos) && !IsBagPos(pos))
return EQUIP_ERR_OK; return EQUIP_ERR_OK;
Item* pItem = GetItemByPos(pos); Item* pItem = GetItemByPos(pos);
// Applied only to existing equipped item // Applied only to existing equipped item
if( !pItem ) if (!pItem)
return EQUIP_ERR_OK; return EQUIP_ERR_OK;
DEBUG_LOG( "STORAGE: CanUnequipItem slot = %u, item = %u, count = %u", pos, pItem->GetEntry(), pItem->GetCount()); DEBUG_LOG( "STORAGE: CanUnequipItem slot = %u, item = %u, count = %u", pos, pItem->GetEntry(), pItem->GetCount());
ItemPrototype const *pProto = pItem->GetProto(); ItemPrototype const *pProto = pItem->GetProto();
if( !pProto ) if (!pProto)
return EQUIP_ERR_ITEM_NOT_FOUND; return EQUIP_ERR_ITEM_NOT_FOUND;
// item used
if(pItem->m_lootGenerated)
return EQUIP_ERR_ALREADY_LOOTED;
// do not allow unequipping gear except weapons, offhands, projectiles, relics in // do not allow unequipping gear except weapons, offhands, projectiles, relics in
// - combat // - combat
// - in-progress arenas // - in-progress arenas
@ -10505,10 +10488,6 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p
if (!pProto) if (!pProto)
return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND; return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
// item used
if(pItem->m_lootGenerated)
return EQUIP_ERR_ALREADY_LOOTED;
if (pItem->IsBindedNotWith(this)) if (pItem->IsBindedNotWith(this))
return EQUIP_ERR_DONT_OWN_THAT_ITEM; return EQUIP_ERR_DONT_OWN_THAT_ITEM;
@ -11593,13 +11572,13 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
uint8 dstslot = dst & 255; uint8 dstslot = dst & 255;
Item *pSrcItem = GetItemByPos( srcbag, srcslot ); Item *pSrcItem = GetItemByPos( srcbag, srcslot );
if( !pSrcItem ) if (!pSrcItem)
{ {
SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL ); SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL );
return; return;
} }
if(pSrcItem->m_lootGenerated) // prevent split looting item (item if (pSrcItem->HasGeneratedLoot()) // prevent split looting item (stackable items can has only temporary loot and this meaning that loot window open)
{ {
//best error message found for attempting to split while looting //best error message found for attempting to split while looting
SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL ); SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL );
@ -11607,14 +11586,14 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
} }
// not let split all items (can be only at cheating) // not let split all items (can be only at cheating)
if(pSrcItem->GetCount() == count) if (pSrcItem->GetCount() == count)
{ {
SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL ); SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL );
return; return;
} }
// not let split more existing items (can be only at cheating) // not let split more existing items (can be only at cheating)
if(pSrcItem->GetCount() < count) if (pSrcItem->GetCount() < count)
{ {
SendEquipError( EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT, pSrcItem, NULL ); SendEquipError( EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT, pSrcItem, NULL );
return; return;
@ -11622,20 +11601,20 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
DEBUG_LOG( "STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag, dstslot, pSrcItem->GetEntry(), count); DEBUG_LOG( "STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag, dstslot, pSrcItem->GetEntry(), count);
Item *pNewItem = pSrcItem->CloneItem( count, this ); Item *pNewItem = pSrcItem->CloneItem( count, this );
if( !pNewItem ) if (!pNewItem)
{ {
SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL ); SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL );
return; return;
} }
if( IsInventoryPos( dst ) ) if (IsInventoryPos(dst))
{ {
// change item amount before check (for unique max count check) // change item amount before check (for unique max count check)
pSrcItem->SetCount( pSrcItem->GetCount() - count ); pSrcItem->SetCount( pSrcItem->GetCount() - count );
ItemPosCountVec dest; ItemPosCountVec dest;
uint8 msg = CanStoreItem( dstbag, dstslot, dest, pNewItem, false ); uint8 msg = CanStoreItem( dstbag, dstslot, dest, pNewItem, false );
if( msg != EQUIP_ERR_OK ) if (msg != EQUIP_ERR_OK)
{ {
delete pNewItem; delete pNewItem;
pSrcItem->SetCount( pSrcItem->GetCount() + count ); pSrcItem->SetCount( pSrcItem->GetCount() + count );
@ -11643,12 +11622,12 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
return; return;
} }
if( IsInWorld() ) if (IsInWorld())
pSrcItem->SendCreateUpdateToPlayer( this ); pSrcItem->SendCreateUpdateToPlayer( this );
pSrcItem->SetState(ITEM_CHANGED, this); pSrcItem->SetState(ITEM_CHANGED, this);
StoreItem( dest, pNewItem, true); StoreItem( dest, pNewItem, true);
} }
else if( IsBankPos ( dst ) ) else if (IsBankPos (dst))
{ {
// change item amount before check (for unique max count check) // change item amount before check (for unique max count check)
pSrcItem->SetCount( pSrcItem->GetCount() - count ); pSrcItem->SetCount( pSrcItem->GetCount() - count );
@ -11663,19 +11642,19 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
return; return;
} }
if( IsInWorld() ) if (IsInWorld())
pSrcItem->SendCreateUpdateToPlayer( this ); pSrcItem->SendCreateUpdateToPlayer( this );
pSrcItem->SetState(ITEM_CHANGED, this); pSrcItem->SetState(ITEM_CHANGED, this);
BankItem( dest, pNewItem, true); BankItem( dest, pNewItem, true);
} }
else if( IsEquipmentPos ( dst ) ) else if (IsEquipmentPos (dst))
{ {
// change item amount before check (for unique max count check), provide space for splitted items // change item amount before check (for unique max count check), provide space for splitted items
pSrcItem->SetCount( pSrcItem->GetCount() - count ); pSrcItem->SetCount( pSrcItem->GetCount() - count );
uint16 dest; uint16 dest;
uint8 msg = CanEquipItem( dstslot, dest, pNewItem, false ); uint8 msg = CanEquipItem( dstslot, dest, pNewItem, false );
if( msg != EQUIP_ERR_OK ) if (msg != EQUIP_ERR_OK)
{ {
delete pNewItem; delete pNewItem;
pSrcItem->SetCount( pSrcItem->GetCount() + count ); pSrcItem->SetCount( pSrcItem->GetCount() + count );
@ -11683,7 +11662,7 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
return; return;
} }
if( IsInWorld() ) if (IsInWorld())
pSrcItem->SendCreateUpdateToPlayer( this ); pSrcItem->SendCreateUpdateToPlayer( this );
pSrcItem->SetState(ITEM_CHANGED, this); pSrcItem->SetState(ITEM_CHANGED, this);
EquipItem( dest, pNewItem, true); EquipItem( dest, pNewItem, true);
@ -15492,6 +15471,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
m_reputationMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADREPUTATION)); m_reputationMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADREPUTATION));
_LoadInventory(holder->GetResult(PLAYER_LOGIN_QUERY_LOADINVENTORY), time_diff); _LoadInventory(holder->GetResult(PLAYER_LOGIN_QUERY_LOADINVENTORY), time_diff);
_LoadItemLoot(holder->GetResult(PLAYER_LOGIN_QUERY_LOADITEMLOOT));
// update items with duration and realtime // update items with duration and realtime
UpdateItemDuration(time_diff, true); UpdateItemDuration(time_diff, true);
@ -15903,7 +15883,7 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
Item *item = NewItemOrBag(proto); Item *item = NewItemOrBag(proto);
if(!item->LoadFromDB(item_guid, GetGUID(), result)) if(!item->LoadFromDB(item_guid, GetGUID(), fields))
{ {
sLog.outError( "Player::_LoadInventory: Player %s has broken item (id: #%u) in inventory, deleted.", GetName(),item_id ); sLog.outError( "Player::_LoadInventory: Player %s has broken item (id: #%u) in inventory, deleted.", GetName(),item_id );
CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid); CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
@ -16027,10 +16007,39 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
draft.SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED); draft.SendMailTo(this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED);
} }
} }
//if(isAlive()) //if(isAlive())
_ApplyAllItemMods(); _ApplyAllItemMods();
} }
void Player::_LoadItemLoot(QueryResult *result)
{
//QueryResult *result = CharacterDatabase.PQuery("SELECT guid,itemid,amount,suffix,property FROM item_loot WHERE guid = '%u'", GetGUIDLow());
if (result)
{
do
{
Field *fields = result->Fetch();
uint32 item_guid = fields[0].GetUInt32();
Item* item = GetItemByGuid(ObjectGuid(HIGHGUID_ITEM, item_guid));
if (!item)
{
CharacterDatabase.PExecute("DELETE FROM item_loot WHERE guid = '%u'", item_guid);
sLog.outError("Player::_LoadItemLoot: Player %s has loot for not existed item (GUID: %u) in `item_loot`, deleted.", GetName(), item_guid );
continue;
}
item->LoadLootFromDB(fields);
} while (result->NextRow());
delete result;
}
}
// load mailed item which should receive current player // load mailed item which should receive current player
void Player::_LoadMailedItems(QueryResult *result) void Player::_LoadMailedItems(QueryResult *result)
{ {
@ -16064,7 +16073,7 @@ void Player::_LoadMailedItems(QueryResult *result)
Item *item = NewItemOrBag(proto); Item *item = NewItemOrBag(proto);
if(!item->LoadFromDB(item_guid_low, 0, result)) if(!item->LoadFromDB(item_guid_low, 0, fields))
{ {
sLog.outError( "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, item_guid_low); sLog.outError( "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, item_guid_low);
CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low); CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low);
@ -21175,11 +21184,16 @@ bool Player::IsBaseRuneSlotsOnCooldown( RuneType runeType ) const
return true; return true;
} }
void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast) void Player::AutoStoreLoot(uint32 loot_id, LootStore const& store, bool broadcast, uint8 bag, uint8 slot)
{ {
Loot loot; Loot loot;
loot.FillLoot (loot_id,store,this,true); loot.FillLoot (loot_id, store, this, true);
AutoStoreLoot(loot, broadcast, bag, slot);
}
void Player::AutoStoreLoot(Loot& loot, bool broadcast, uint8 bag, uint8 slot)
{
uint32 max_slot = loot.GetMaxSlotInLootFor(this); uint32 max_slot = loot.GetMaxSlotInLootFor(this);
for(uint32 i = 0; i < max_slot; ++i) for(uint32 i = 0; i < max_slot; ++i)
{ {

View file

@ -896,6 +896,7 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS, PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,
PLAYER_LOGIN_QUERY_LOADREPUTATION, PLAYER_LOGIN_QUERY_LOADREPUTATION,
PLAYER_LOGIN_QUERY_LOADINVENTORY, PLAYER_LOGIN_QUERY_LOADINVENTORY,
PLAYER_LOGIN_QUERY_LOADITEMLOOT,
PLAYER_LOGIN_QUERY_LOADACTIONS, PLAYER_LOGIN_QUERY_LOADACTIONS,
PLAYER_LOGIN_QUERY_LOADSOCIALLIST, PLAYER_LOGIN_QUERY_LOADSOCIALLIST,
PLAYER_LOGIN_QUERY_LOADHOMEBIND, PLAYER_LOGIN_QUERY_LOADHOMEBIND,
@ -1299,8 +1300,8 @@ class MANGOS_DLL_SPEC Player : public Unit
bool StoreNewItemInBestSlots(uint32 item_id, uint32 item_count); bool StoreNewItemInBestSlots(uint32 item_id, uint32 item_count);
Item* StoreNewItemInInventorySlot(uint32 itemEntry, uint32 amount); Item* StoreNewItemInInventorySlot(uint32 itemEntry, uint32 amount);
void AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast = false); void AutoStoreLoot(uint32 loot_id, LootStore const& store, bool broadcast = false, uint8 bag = NULL_BAG, uint8 slot = NULL_SLOT);
void AutoStoreLoot(uint32 loot_id, LootStore const& store, bool broadcast = false) { AutoStoreLoot(NULL_BAG,NULL_SLOT,loot_id,store,broadcast); } void AutoStoreLoot(Loot& loot, bool broadcast = false, uint8 bag = NULL_BAG, uint8 slot = NULL_SLOT);
uint8 _CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = NULL) const; uint8 _CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = NULL) const;
uint8 _CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item *pItem = NULL, bool swap = false, uint32* no_space_count = NULL ) const; uint8 _CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item *pItem = NULL, bool swap = false, uint32* no_space_count = NULL ) const;
@ -2442,6 +2443,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void _LoadAuras(QueryResult *result, uint32 timediff); void _LoadAuras(QueryResult *result, uint32 timediff);
void _LoadBoundInstances(QueryResult *result); void _LoadBoundInstances(QueryResult *result);
void _LoadInventory(QueryResult *result, uint32 timediff); void _LoadInventory(QueryResult *result, uint32 timediff);
void _LoadItemLoot(QueryResult *result);
void _LoadMails(QueryResult *result); void _LoadMails(QueryResult *result);
void _LoadMailedItems(QueryResult *result); void _LoadMailedItems(QueryResult *result);
void _LoadQuestStatus(QueryResult *result); void _LoadQuestStatus(QueryResult *result);

View file

@ -4989,34 +4989,43 @@ SpellCastResult Spell::CheckCast(bool strict)
} }
case SPELL_EFFECT_OPEN_LOCK: case SPELL_EFFECT_OPEN_LOCK:
{ {
if( m_spellInfo->EffectImplicitTargetA[i] != TARGET_GAMEOBJECT && if (m_caster->GetTypeId() != TYPEID_PLAYER) // only players can open locks, gather etc.
m_spellInfo->EffectImplicitTargetA[i] != TARGET_GAMEOBJECT_ITEM )
break;
if( m_caster->GetTypeId() != TYPEID_PLAYER // only players can open locks, gather etc.
// we need a go target in case of TARGET_GAMEOBJECT
|| m_spellInfo->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT && !m_targets.getGOTarget()
// we need a go target, or an openable item target in case of TARGET_GAMEOBJECT_ITEM
|| m_spellInfo->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT_ITEM && !m_targets.getGOTarget() &&
(!m_targets.getItemTarget() || m_targets.getItemTarget()->GetOwner() != m_caster ||
!m_targets.getItemTarget()->GetProto()->LockID || m_targets.getItemTarget()->HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_UNLOCKED)))
return SPELL_FAILED_BAD_TARGETS; return SPELL_FAILED_BAD_TARGETS;
// In BattleGround players can use only flags and banners // we need a go target in case of TARGET_GAMEOBJECT (for other targets acceptable GO and items)
if( ((Player*)m_caster)->InBattleGround() && if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT)
!((Player*)m_caster)->CanUseBattleGroundObject() ) {
return SPELL_FAILED_TRY_AGAIN; if (!m_targets.getGOTarget())
return SPELL_FAILED_BAD_TARGETS;
}
// get the lock entry // get the lock entry
uint32 lockId = 0; uint32 lockId = 0;
if (GameObject* go = m_targets.getGOTarget()) if (GameObject* go = m_targets.getGOTarget())
{ {
// In BattleGround players can use only flags and banners
if( ((Player*)m_caster)->InBattleGround() &&
!((Player*)m_caster)->CanUseBattleGroundObject() )
return SPELL_FAILED_TRY_AGAIN;
lockId = go->GetGOInfo()->GetLockId(); lockId = go->GetGOInfo()->GetLockId();
if (!lockId) if (!lockId)
return SPELL_FAILED_BAD_TARGETS; return SPELL_FAILED_ALREADY_OPEN;
} }
else if(Item* itm = m_targets.getItemTarget()) else if(Item* item = m_targets.getItemTarget())
lockId = itm->GetProto()->LockID; {
// not own (trade?)
if (item->GetOwner() != m_caster)
return SPELL_FAILED_ITEM_GONE;
lockId = item->GetProto()->LockID;
// if already unlocked
if (!lockId || item->HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_UNLOCKED))
return SPELL_FAILED_ALREADY_OPEN;
}
else
return SPELL_FAILED_BAD_TARGETS;
SkillType skillId = SKILL_NONE; SkillType skillId = SKILL_NONE;
int32 reqSkillValue = 0; int32 reqSkillValue = 0;

View file

@ -1302,7 +1302,7 @@ void Aura::TriggerSpell()
if (!creature->GetCreatureInfo()->SkinLootId) if (!creature->GetCreatureInfo()->SkinLootId)
return; return;
player->AutoStoreLoot(creature->GetCreatureInfo()->SkinLootId,LootTemplates_Skinning,true); player->AutoStoreLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, true);
creature->ForcedDespawn(); creature->ForcedDespawn();
} }

View file

@ -222,7 +222,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
// locked item // locked item
uint32 lockId = proto->LockID; uint32 lockId = proto->LockID;
if(lockId) if(lockId && !pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_UNLOCKED))
{ {
LockEntry const *lockInfo = sLockStore.LookupEntry(lockId); LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__ #ifndef __REVISION_NR_H__
#define __REVISION_NR_H__ #define __REVISION_NR_H__
#define REVISION_NR "10661" #define REVISION_NR "10662"
#endif // __REVISION_NR_H__ #endif // __REVISION_NR_H__

View file

@ -1,6 +1,6 @@
#ifndef __REVISION_SQL_H__ #ifndef __REVISION_SQL_H__
#define __REVISION_SQL_H__ #define __REVISION_SQL_H__
#define REVISION_DB_CHARACTERS "required_10655_01_characters_character_queststatus_monthly" #define REVISION_DB_CHARACTERS "required_10662_01_characters_item_loot"
#define REVISION_DB_MANGOS "required_10660_01_mangos_game_event_quest" #define REVISION_DB_MANGOS "required_10660_01_mangos_game_event_quest"
#define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version" #define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version"
#endif // __REVISION_SQL_H__ #endif // __REVISION_SQL_H__