Implement group loot for chest having GroupLootRules (go type=3, data15=1).
Implement round robin loot. Implement round robin for underthreshold items (group loot and need befor greed). Fix "all players pass" bug on creature. Add SMSG_LOOT_LIST message to indicate looter (round robin or master). And some other minor loot bugs. Fixes issue #167. Fixes issue #247. --HG-- branch : trunk
This commit is contained in:
+17
-18
@@ -485,7 +485,22 @@ void Creature::Update(uint32 diff)
|
||||
if (m_isDeadByDefault)
|
||||
break;
|
||||
|
||||
if( m_deathTimer <= diff )
|
||||
if (m_groupLootTimer && lootingGroupLeaderGUID)
|
||||
{
|
||||
// for delayed spells
|
||||
m_Events.Update(diff);
|
||||
|
||||
if (m_groupLootTimer <= diff)
|
||||
{
|
||||
Group* group = objmgr.GetGroupByLeader(lootingGroupLeaderGUID);
|
||||
if (group)
|
||||
group->EndRoll(&loot);
|
||||
m_groupLootTimer = 0;
|
||||
lootingGroupLeaderGUID = 0;
|
||||
}
|
||||
else m_groupLootTimer -= diff;
|
||||
}
|
||||
else if (m_deathTimer <= diff)
|
||||
{
|
||||
RemoveCorpse();
|
||||
DEBUG_LOG("Removing corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY));
|
||||
@@ -493,24 +508,8 @@ void Creature::Update(uint32 diff)
|
||||
else
|
||||
{
|
||||
// for delayed spells
|
||||
m_Events.Update( diff );
|
||||
|
||||
m_Events.Update(diff);
|
||||
m_deathTimer -= diff;
|
||||
if (m_groupLootTimer && lootingGroupLeaderGUID)
|
||||
{
|
||||
if(diff <= m_groupLootTimer)
|
||||
{
|
||||
m_groupLootTimer -= diff;
|
||||
}
|
||||
else
|
||||
{
|
||||
Group* group = objmgr.GetGroupByLeader(lootingGroupLeaderGUID);
|
||||
if (group)
|
||||
group->EndRoll();
|
||||
m_groupLootTimer = 0;
|
||||
lootingGroupLeaderGUID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
+18
-2
@@ -61,6 +61,9 @@ GameObject::GameObject() : WorldObject(), m_goValue(new GameObjectValue)
|
||||
m_DBTableGuid = 0;
|
||||
m_rotation = 0;
|
||||
|
||||
m_groupLootTimer = 0;
|
||||
lootingGroupLeaderGUID = 0;
|
||||
|
||||
ResetLootMode(); // restore default loot mode
|
||||
}
|
||||
|
||||
@@ -210,7 +213,7 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMa
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameObject::Update(uint32 /*p_time*/)
|
||||
void GameObject::Update(uint32 diff)
|
||||
{
|
||||
if (IS_MO_TRANSPORT(GetGUID()))
|
||||
{
|
||||
@@ -432,6 +435,19 @@ void GameObject::Update(uint32 /*p_time*/)
|
||||
m_cooldownTime = 0;
|
||||
}
|
||||
break;
|
||||
case GAMEOBJECT_TYPE_CHEST:
|
||||
if (m_groupLootTimer)
|
||||
{
|
||||
if (m_groupLootTimer <= diff)
|
||||
{
|
||||
Group* group = objmgr.GetGroupByLeader(lootingGroupLeaderGUID);
|
||||
if (group)
|
||||
group->EndRoll(&loot);
|
||||
m_groupLootTimer = 0;
|
||||
lootingGroupLeaderGUID = 0;
|
||||
}
|
||||
else m_groupLootTimer -= diff;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1111,7 +1127,7 @@ void GameObject::Use(Unit* user)
|
||||
data << GetGUID();
|
||||
player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
else if (info->questgiver.gossipID)
|
||||
else if (info->goober.gossipID)
|
||||
{
|
||||
player->PrepareGossipMenu(this, info->goober.gossipID);
|
||||
player->SendPreparedGossip(this);
|
||||
|
||||
@@ -708,6 +708,9 @@ class GameObject : public WorldObject, public GridObject<GameObject>
|
||||
|
||||
Loot loot;
|
||||
|
||||
uint32 m_groupLootTimer; // (msecs)timer used for group loot
|
||||
uint64 lootingGroupLeaderGUID; // used to find group which is looting
|
||||
|
||||
bool hasQuest(uint32 quest_id) const;
|
||||
bool hasInvolvedQuest(uint32 quest_id) const;
|
||||
bool ActivateToQuest(Player *pTarget) const;
|
||||
|
||||
+185
-146
@@ -560,16 +560,34 @@ void Group::SendLootAllPassed(uint32 NumberOfPlayers, const Roll &r)
|
||||
}
|
||||
}
|
||||
|
||||
void Group::GroupLoot(const uint64& playerGUID, Loot *loot, Creature *creature)
|
||||
// notify group members which player is the allowed looter for the given creature
|
||||
void Group::SendLooter(Creature *pCreature, Player *pLooter)
|
||||
{
|
||||
assert(pCreature);
|
||||
|
||||
WorldPacket data(SMSG_LOOT_LIST, (8+8));
|
||||
data << uint64(pCreature->GetGUID());
|
||||
data << uint8(0); // unk1
|
||||
|
||||
if (pLooter)
|
||||
data.append(pLooter->GetPackGUID());
|
||||
else
|
||||
data << uint8(0);
|
||||
|
||||
BroadcastPacket(&data, false);
|
||||
}
|
||||
|
||||
void Group::GroupLoot(Loot *loot, WorldObject* pLootedObject)
|
||||
{
|
||||
std::vector<LootItem>::iterator i;
|
||||
ItemPrototype const *item;
|
||||
uint8 itemSlot = 0;
|
||||
Player *player = objmgr.GetPlayer(playerGUID);
|
||||
Group *group = player->GetGroup();
|
||||
|
||||
for (i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot)
|
||||
for (i = loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot)
|
||||
{
|
||||
if (i->freeforall)
|
||||
continue;
|
||||
|
||||
item = objmgr.GetItemPrototype(i->itemid);
|
||||
if (!item)
|
||||
{
|
||||
@@ -578,73 +596,23 @@ void Group::GroupLoot(const uint64& playerGUID, Loot *loot, Creature *creature)
|
||||
}
|
||||
|
||||
//roll for over-threshold item if it's one-player loot
|
||||
if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall)
|
||||
if (item->Quality >= uint32(m_lootThreshold))
|
||||
{
|
||||
uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM);
|
||||
Roll* r=new Roll(newitemGUID,*i);
|
||||
Roll* r = new Roll(newitemGUID,*i);
|
||||
|
||||
//a vector is filled with only near party members
|
||||
for (GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player *member = itr->getSource();
|
||||
if(!member || !member->GetSession())
|
||||
if (!member || !member->GetSession())
|
||||
continue;
|
||||
if ( i->AllowedForPlayer(member) )
|
||||
if (i->AllowedForPlayer(member))
|
||||
{
|
||||
if (member->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
if (member->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
{
|
||||
r->playerVote[member->GetGUID()] = NOT_EMITED_YET;
|
||||
++r->totalPlayersRolling;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r->setLoot(loot);
|
||||
r->itemSlot = itemSlot;
|
||||
|
||||
group->SendLootStartRoll(60000, *r);
|
||||
|
||||
loot->items[itemSlot].is_blocked = true;
|
||||
creature->m_groupLootTimer = 60000;
|
||||
creature->lootingGroupLeaderGUID = GetLeaderGUID();
|
||||
|
||||
RollId.push_back(r);
|
||||
}
|
||||
else
|
||||
i->is_underthreshold=1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Group::NeedBeforeGreed(const uint64& playerGUID, Loot *loot, Creature *creature)
|
||||
{
|
||||
ItemPrototype const *item;
|
||||
Player *player = objmgr.GetPlayer(playerGUID);
|
||||
Group *group = player->GetGroup();
|
||||
|
||||
uint8 itemSlot = 0;
|
||||
for (std::vector<LootItem>::iterator i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot)
|
||||
{
|
||||
item = objmgr.GetItemPrototype(i->itemid);
|
||||
|
||||
//only roll for one-player items, not for ones everyone can get
|
||||
if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall)
|
||||
{
|
||||
uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM);
|
||||
Roll* r=new Roll(newitemGUID,*i);
|
||||
|
||||
for (GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player *playerToRoll = itr->getSource();
|
||||
if(!playerToRoll || !playerToRoll->GetSession())
|
||||
continue;
|
||||
|
||||
if (playerToRoll->CanUseItem(item) && i->AllowedForPlayer(playerToRoll) )
|
||||
{
|
||||
if (playerToRoll->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
{
|
||||
r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET;
|
||||
++r->totalPlayersRolling;
|
||||
r->totalPlayersRolling++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -654,11 +622,22 @@ void Group::NeedBeforeGreed(const uint64& playerGUID, Loot *loot, Creature *crea
|
||||
r->setLoot(loot);
|
||||
r->itemSlot = itemSlot;
|
||||
|
||||
group->SendLootStartRoll(60000, *r);
|
||||
|
||||
loot->items[itemSlot].is_blocked = true;
|
||||
|
||||
SendLootStartRoll(60000, *r);
|
||||
|
||||
RollId.push_back(r);
|
||||
|
||||
if (Creature* creature = dynamic_cast<Creature *>(pLootedObject))
|
||||
{
|
||||
creature->m_groupLootTimer = 60000;
|
||||
creature->lootingGroupLeaderGUID = GetLeaderGUID();
|
||||
}
|
||||
else if (GameObject* go = dynamic_cast<GameObject *>(pLootedObject))
|
||||
{
|
||||
go->m_groupLootTimer = 60000;
|
||||
go->lootingGroupLeaderGUID = GetLeaderGUID();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -670,13 +649,69 @@ void Group::NeedBeforeGreed(const uint64& playerGUID, Loot *loot, Creature *crea
|
||||
}
|
||||
}
|
||||
|
||||
void Group::MasterLoot(const uint64& playerGUID, Loot* /*loot*/, Creature *creature)
|
||||
void Group::NeedBeforeGreed(Loot *loot, WorldObject* pLootedObject)
|
||||
{
|
||||
Player *player = objmgr.GetPlayer(playerGUID);
|
||||
if(!player)
|
||||
return;
|
||||
ItemPrototype const *item;
|
||||
uint8 itemSlot = 0;
|
||||
for (std::vector<LootItem>::iterator i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot)
|
||||
{
|
||||
if (i->freeforall)
|
||||
continue;
|
||||
|
||||
sLog.outDebug("Group::MasterLoot (SMSG_LOOT_MASTER_LIST, 330) player = [%s].", player->GetName());
|
||||
item = objmgr.GetItemPrototype(i->itemid);
|
||||
|
||||
//roll for over-threshold item if it's one-player loot
|
||||
if (item->Quality >= uint32(m_lootThreshold))
|
||||
{
|
||||
uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM);
|
||||
Roll* r=new Roll(newitemGUID,*i);
|
||||
|
||||
for (GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player *playerToRoll = itr->getSource();
|
||||
if (!playerToRoll || !playerToRoll->GetSession())
|
||||
continue;
|
||||
|
||||
if (playerToRoll->CanUseItem(item) && i->AllowedForPlayer(playerToRoll))
|
||||
{
|
||||
if (playerToRoll->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
{
|
||||
r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET;
|
||||
r->totalPlayersRolling++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r->totalPlayersRolling > 0)
|
||||
{
|
||||
r->setLoot(loot);
|
||||
r->itemSlot = itemSlot;
|
||||
|
||||
loot->items[itemSlot].is_blocked = true;
|
||||
|
||||
SendLootStartRoll(60000, *r);
|
||||
|
||||
RollId.push_back(r);
|
||||
|
||||
if (Creature* creature = dynamic_cast<Creature *>(pLootedObject))
|
||||
{
|
||||
creature->m_groupLootTimer = 60000;
|
||||
creature->lootingGroupLeaderGUID = GetLeaderGUID();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete r;
|
||||
}
|
||||
}
|
||||
else
|
||||
i->is_underthreshold=1;
|
||||
}
|
||||
}
|
||||
|
||||
void Group::MasterLoot(Loot* /*loot*/, WorldObject* pLootedObject)
|
||||
{
|
||||
sLog.outDebug("Group::MasterLoot (SMSG_LOOT_MASTER_LIST, 330)");
|
||||
|
||||
uint32 real_count = 0;
|
||||
|
||||
@@ -689,7 +724,7 @@ void Group::MasterLoot(const uint64& playerGUID, Loot* /*loot*/, Creature *creat
|
||||
if (!looter->IsInWorld())
|
||||
continue;
|
||||
|
||||
if (looter->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
if (looter->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
{
|
||||
data << looter->GetGUID();
|
||||
++real_count;
|
||||
@@ -701,12 +736,12 @@ void Group::MasterLoot(const uint64& playerGUID, Loot* /*loot*/, Creature *creat
|
||||
for (GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player *looter = itr->getSource();
|
||||
if (looter->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
if (looter->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
looter->GetSession()->SendPacket(&data);
|
||||
}
|
||||
}
|
||||
|
||||
void Group::CountRollVote(const uint64& playerGUID, const uint64& Guid, uint32 NumberOfPlayers, uint8 Choise)
|
||||
void Group::CountRollVote(const uint64& playerGUID, const uint64& Guid, uint32 NumberOfPlayers, uint8 Choice)
|
||||
{
|
||||
Rolls::iterator rollI = GetRoll(Guid);
|
||||
if (rollI == RollId.end())
|
||||
@@ -722,37 +757,30 @@ void Group::CountRollVote(const uint64& playerGUID, const uint64& Guid, uint32 N
|
||||
if (roll->getLoot()->items.empty())
|
||||
return;
|
||||
|
||||
switch (Choise)
|
||||
switch (Choice)
|
||||
{
|
||||
case ROLL_PASS: // Player choose pass
|
||||
{
|
||||
SendLootRoll(0, playerGUID, 128, ROLL_PASS, *roll);
|
||||
++roll->totalPass;
|
||||
itr->second = PASS;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case ROLL_NEED: // player choose Need
|
||||
{
|
||||
SendLootRoll(0, playerGUID, 0, 0, *roll);
|
||||
++roll->totalNeed;
|
||||
itr->second = NEED;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case ROLL_GREED: // player choose Greed
|
||||
{
|
||||
SendLootRoll(0, playerGUID, 128, ROLL_GREED, *roll);
|
||||
++roll->totalGreed;
|
||||
itr->second = GREED;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case ROLL_DISENCHANT: // player choose Disenchant
|
||||
{
|
||||
SendLootRoll(0, playerGUID, 128, ROLL_DISENCHANT, *roll);
|
||||
++roll->totalGreed;
|
||||
itr->second = DISENCHANT;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
if (roll->totalPass + roll->totalNeed + roll->totalGreed >= roll->totalPlayersRolling)
|
||||
{
|
||||
CountTheRoll(rollI, NumberOfPlayers);
|
||||
@@ -760,21 +788,23 @@ void Group::CountRollVote(const uint64& playerGUID, const uint64& Guid, uint32 N
|
||||
}
|
||||
|
||||
//called when roll timer expires
|
||||
void Group::EndRoll()
|
||||
void Group::EndRoll(Loot *pLoot)
|
||||
{
|
||||
Rolls::iterator itr;
|
||||
while(!RollId.empty())
|
||||
for (Rolls::iterator itr = RollId.begin(); itr != RollId.end(); )
|
||||
{
|
||||
//need more testing here, if rolls disappear
|
||||
itr = RollId.begin();
|
||||
CountTheRoll(itr, GetMembersCount()); //i don't have to edit player votes, who didn't vote ... he will pass
|
||||
if ((*itr)->getLoot() == pLoot) {
|
||||
CountTheRoll(itr, GetMembersCount()); //i don't have to edit player votes, who didn't vote ... he will pass
|
||||
itr = RollId.begin();
|
||||
}
|
||||
else
|
||||
itr++;
|
||||
}
|
||||
}
|
||||
|
||||
void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
|
||||
{
|
||||
Roll* roll = *rollI;
|
||||
if(!roll->isValid()) // is loot already deleted ?
|
||||
if (!roll->isValid()) // is loot already deleted ?
|
||||
{
|
||||
RollId.erase(rollI);
|
||||
delete roll;
|
||||
@@ -783,7 +813,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
|
||||
//end of the roll
|
||||
if (roll->totalNeed > 0)
|
||||
{
|
||||
if(!roll->playerVote.empty())
|
||||
if (!roll->playerVote.empty())
|
||||
{
|
||||
uint8 maxresul = 0;
|
||||
uint64 maxguid = (*roll->playerVote.begin()).first;
|
||||
@@ -794,7 +824,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
|
||||
if (itr->second != NEED)
|
||||
continue;
|
||||
|
||||
uint8 randomN = urand(1, 99);
|
||||
uint8 randomN = urand(1, 100);
|
||||
SendLootRoll(0, itr->first, randomN, ROLL_NEED, *roll);
|
||||
if (maxresul < randomN)
|
||||
{
|
||||
@@ -805,18 +835,18 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
|
||||
SendLootRollWon(0, maxguid, maxresul, ROLL_NEED, *roll);
|
||||
player = objmgr.GetPlayer(maxguid);
|
||||
|
||||
if(player && player->GetSession())
|
||||
if (player && player->GetSession())
|
||||
{
|
||||
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT, roll->itemid, maxresul);
|
||||
|
||||
ItemPosCountVec dest;
|
||||
LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
|
||||
uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count );
|
||||
if ( msg == EQUIP_ERR_OK )
|
||||
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count);
|
||||
if (msg == EQUIP_ERR_OK)
|
||||
{
|
||||
item->is_looted = true;
|
||||
roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
|
||||
--roll->getLoot()->unlootedCount;
|
||||
roll->getLoot()->unlootedCount--;
|
||||
player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId);
|
||||
}
|
||||
else
|
||||
@@ -829,7 +859,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
|
||||
}
|
||||
else if (roll->totalGreed > 0)
|
||||
{
|
||||
if(!roll->playerVote.empty())
|
||||
if (!roll->playerVote.empty())
|
||||
{
|
||||
uint8 maxresul = 0;
|
||||
uint64 maxguid = (*roll->playerVote.begin()).first;
|
||||
@@ -837,12 +867,12 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
|
||||
RollVote rollvote;
|
||||
|
||||
Roll::PlayerVote::iterator itr;
|
||||
for (itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr)
|
||||
for (itr = roll->playerVote.begin(); itr != roll->playerVote.end(); ++itr)
|
||||
{
|
||||
if (itr->second != GREED && itr->second != DISENCHANT)
|
||||
continue;
|
||||
|
||||
uint8 randomN = urand(1, 99);
|
||||
uint8 randomN = urand(1, 100);
|
||||
SendLootRoll(0, itr->first, randomN, itr->second, *roll);
|
||||
if (maxresul < randomN)
|
||||
{
|
||||
@@ -854,34 +884,34 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
|
||||
SendLootRollWon(0, maxguid, maxresul, rollvote, *roll);
|
||||
player = objmgr.GetPlayer(maxguid);
|
||||
|
||||
if(player && player->GetSession())
|
||||
if (player && player->GetSession())
|
||||
{
|
||||
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT, roll->itemid, maxresul);
|
||||
|
||||
LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
|
||||
|
||||
if(rollvote == GREED)
|
||||
if (rollvote == GREED)
|
||||
{
|
||||
ItemPosCountVec dest;
|
||||
uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count );
|
||||
if ( msg == EQUIP_ERR_OK )
|
||||
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count);
|
||||
if (msg == EQUIP_ERR_OK)
|
||||
{
|
||||
item->is_looted = true;
|
||||
roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
|
||||
--roll->getLoot()->unlootedCount;
|
||||
player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId);
|
||||
roll->getLoot()->unlootedCount--;
|
||||
player->StoreNewItem(dest, roll->itemid, true, item->randomPropertyId);
|
||||
}
|
||||
else
|
||||
{
|
||||
item->is_blocked = false;
|
||||
player->SendEquipError( msg, NULL, NULL );
|
||||
player->SendEquipError(msg, NULL, NULL);
|
||||
}
|
||||
}
|
||||
else if(rollvote == DISENCHANT)
|
||||
{
|
||||
item->is_looted = true;
|
||||
roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
|
||||
--roll->getLoot()->unlootedCount;
|
||||
roll->getLoot()->unlootedCount--;
|
||||
ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(roll->itemid);
|
||||
player->AutoStoreLoot(pProto->DisenchantID, LootTemplates_Disenchant, true);
|
||||
}
|
||||
@@ -891,8 +921,11 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
|
||||
else
|
||||
{
|
||||
SendLootAllPassed(NumberOfPlayers, *roll);
|
||||
|
||||
// remove is_blocked so that the item is lootable by all players
|
||||
LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
|
||||
if(item) item->is_blocked = false;
|
||||
if (item)
|
||||
item->is_blocked = false;
|
||||
}
|
||||
RollId.erase(rollI);
|
||||
delete roll;
|
||||
@@ -1372,7 +1405,18 @@ void Group::ChangeMembersGroup(Player *player, const uint8 &group)
|
||||
}
|
||||
}
|
||||
|
||||
void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
|
||||
// Retrieve the next Round-Roubin player for the group
|
||||
//
|
||||
// No update done if loot method is Master or FFA.
|
||||
//
|
||||
// If the RR player is not yet set for the group, the first group member becomes the round-robin player.
|
||||
// If the RR player is set, the next player in group becomes the round-robin player.
|
||||
//
|
||||
// If ifneed is true,
|
||||
// the current RR player is checked to be near the looted object.
|
||||
// if yes, no update done.
|
||||
// if not, he looses his turn.
|
||||
void Group::UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed)
|
||||
{
|
||||
switch (GetLootMethod())
|
||||
{
|
||||
@@ -1385,64 +1429,59 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
|
||||
break;
|
||||
}
|
||||
|
||||
member_citerator guid_itr = _getMemberCSlot(GetLooterGuid());
|
||||
if(guid_itr != m_memberSlots.end())
|
||||
uint64 oldLooterGUID = GetLooterGuid();
|
||||
member_citerator guid_itr = _getMemberCSlot(oldLooterGUID);
|
||||
if (guid_itr != m_memberSlots.end())
|
||||
{
|
||||
if(ifneed)
|
||||
if (ifneed)
|
||||
{
|
||||
// not update if only update if need and ok
|
||||
Player* looter = ObjectAccessor::FindPlayer(guid_itr->guid);
|
||||
if(looter && looter->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
if (looter && looter->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
return;
|
||||
}
|
||||
++guid_itr;
|
||||
}
|
||||
|
||||
// search next after current
|
||||
if(guid_itr != m_memberSlots.end())
|
||||
Player *pNewLooter = NULL;
|
||||
for (member_citerator itr = guid_itr; itr != m_memberSlots.end(); ++itr)
|
||||
{
|
||||
for (member_citerator itr = guid_itr; itr != m_memberSlots.end(); ++itr)
|
||||
{
|
||||
if(Player* pl = ObjectAccessor::FindPlayer(itr->guid))
|
||||
if (Player* pl = ObjectAccessor::FindPlayer(itr->guid))
|
||||
if (pl->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
{
|
||||
if (pl->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
pNewLooter = pl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pNewLooter)
|
||||
{
|
||||
// search from start
|
||||
for (member_citerator itr = m_memberSlots.begin(); itr != guid_itr; ++itr)
|
||||
{
|
||||
if (Player* pl = ObjectAccessor::FindPlayer(itr->guid))
|
||||
if (pl->IsWithinDist(pLootedObject,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
{
|
||||
bool refresh = pl->GetLootGUID()==creature->GetGUID();
|
||||
|
||||
//if(refresh) // update loot for new looter
|
||||
// pl->GetSession()->DoLootRelease(pl->GetLootGUID());
|
||||
SetLooterGuid(pl->GetGUID());
|
||||
SendUpdate();
|
||||
if(refresh) // update loot for new looter
|
||||
pl->SendLoot(creature->GetGUID(),LOOT_CORPSE);
|
||||
return;
|
||||
pNewLooter = pl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search from start
|
||||
for (member_citerator itr = m_memberSlots.begin(); itr != guid_itr; ++itr)
|
||||
if (pNewLooter)
|
||||
{
|
||||
if(Player* pl = ObjectAccessor::FindPlayer(itr->guid))
|
||||
if (oldLooterGUID != pNewLooter->GetGUID())
|
||||
{
|
||||
if (pl->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
|
||||
{
|
||||
bool refresh = pl->GetLootGUID()==creature->GetGUID();
|
||||
|
||||
//if(refresh) // update loot for new looter
|
||||
// pl->GetSession()->DoLootRelease(pl->GetLootGUID());
|
||||
SetLooterGuid(pl->GetGUID());
|
||||
SendUpdate();
|
||||
if(refresh) // update loot for new looter
|
||||
pl->SendLoot(creature->GetGUID(),LOOT_CORPSE);
|
||||
return;
|
||||
}
|
||||
SetLooterGuid(pNewLooter->GetGUID());
|
||||
SendUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
SetLooterGuid(0);
|
||||
SendUpdate();
|
||||
else
|
||||
{
|
||||
SetLooterGuid(0);
|
||||
SendUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Group::CanJoinBattleGroundQueue(BattleGround const* bgOrTemplate, BattleGroundQueueTypeId bgQueueTypeId, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot)
|
||||
|
||||
+6
-5
@@ -184,7 +184,7 @@ class Group
|
||||
void ChangeLeader(const uint64 &guid);
|
||||
void SetLootMethod(LootMethod method) { m_lootMethod = method; }
|
||||
void SetLooterGuid(const uint64 &guid) { m_looterGuid = guid; }
|
||||
void UpdateLooterGuid( Creature* creature, bool ifneed = false );
|
||||
void UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed = false);
|
||||
void SetLootThreshold(ItemQualities threshold) { m_lootThreshold = threshold; }
|
||||
void Disband(bool hideDestroy=false);
|
||||
|
||||
@@ -324,9 +324,10 @@ class Group
|
||||
void SendLootRoll(const uint64& SourceGuid, const uint64& TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r);
|
||||
void SendLootRollWon(const uint64& SourceGuid, const uint64& TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r);
|
||||
void SendLootAllPassed(uint32 NumberOfPlayers, const Roll &r);
|
||||
void GroupLoot(const uint64& playerGUID, Loot *loot, Creature *creature);
|
||||
void NeedBeforeGreed(const uint64& playerGUID, Loot *loot, Creature *creature);
|
||||
void MasterLoot(const uint64& playerGUID, Loot *loot, Creature *creature);
|
||||
void SendLooter(Creature *pCreature, Player *pLooter);
|
||||
void GroupLoot(Loot *loot, WorldObject* pLootedObject);
|
||||
void NeedBeforeGreed(Loot *loot, WorldObject* pLootedObject);
|
||||
void MasterLoot(Loot *loot, WorldObject* pLootedObject);
|
||||
Rolls::iterator GetRoll(uint64 Guid)
|
||||
{
|
||||
Rolls::iterator iter;
|
||||
@@ -341,7 +342,7 @@ class Group
|
||||
}
|
||||
void CountTheRoll(Rolls::iterator roll, uint32 NumberOfPlayers);
|
||||
void CountRollVote(const uint64& playerGUID, const uint64& Guid, uint32 NumberOfPlayers, uint8 Choise);
|
||||
void EndRoll();
|
||||
void EndRoll(Loot *loot);
|
||||
|
||||
void LinkMember(GroupReference *pRef) { m_memberMgr.insertFirst(pRef); }
|
||||
void DelinkMember(GroupReference* /*pRef*/ ) { }
|
||||
|
||||
+47
-17
@@ -274,7 +274,7 @@ void WorldSession::HandleLootReleaseOpcode( WorldPacket & recv_data )
|
||||
DoLootRelease(lguid);
|
||||
}
|
||||
|
||||
void WorldSession::DoLootRelease( uint64 lguid )
|
||||
void WorldSession::DoLootRelease(uint64 lguid)
|
||||
{
|
||||
Player *player = GetPlayer();
|
||||
Loot *loot;
|
||||
@@ -284,7 +284,7 @@ void WorldSession::DoLootRelease( uint64 lguid )
|
||||
|
||||
player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
|
||||
|
||||
if(!player->IsInWorld())
|
||||
if (!player->IsInWorld())
|
||||
return;
|
||||
|
||||
if (IS_GAMEOBJECT_GUID(lguid))
|
||||
@@ -364,13 +364,29 @@ void WorldSession::DoLootRelease( uint64 lguid )
|
||||
loot->clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// not fully looted object
|
||||
go->SetLootState(GO_ACTIVATED);
|
||||
|
||||
// if the round robin player release, reset it.
|
||||
if (player->GetGUID() == loot->roundRobinPlayer)
|
||||
{
|
||||
if (Group* pGroup = player->GetGroup())
|
||||
{
|
||||
if (pGroup->GetLootMethod() != MASTER_LOOT)
|
||||
{
|
||||
loot->roundRobinPlayer = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
loot->roundRobinPlayer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IS_CORPSE_GUID(lguid)) // ONLY remove insignia at BG
|
||||
{
|
||||
Corpse *corpse = ObjectAccessor::GetCorpse(*player, lguid);
|
||||
if (!corpse || !corpse->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
|
||||
if (!corpse || !corpse->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
|
||||
return;
|
||||
|
||||
loot = &corpse->loot;
|
||||
@@ -384,13 +400,13 @@ void WorldSession::DoLootRelease( uint64 lguid )
|
||||
else if (IS_ITEM_GUID(lguid))
|
||||
{
|
||||
Item *pItem = player->GetItemByGuid(lguid );
|
||||
if(!pItem)
|
||||
if (!pItem)
|
||||
return;
|
||||
|
||||
ItemPrototype const* proto = pItem->GetProto();
|
||||
|
||||
// destroy only 5 items from stack in case prospecting and milling
|
||||
if( (proto->BagFamily & (BAG_FAMILY_MASK_MINING_SUPP|BAG_FAMILY_MASK_HERBS)) &&
|
||||
if ((proto->BagFamily & (BAG_FAMILY_MASK_MINING_SUPP|BAG_FAMILY_MASK_HERBS)) &&
|
||||
proto->Class == ITEM_CLASS_TRADE_GOODS)
|
||||
{
|
||||
pItem->m_lootGenerated = false;
|
||||
@@ -399,14 +415,14 @@ void WorldSession::DoLootRelease( uint64 lguid )
|
||||
uint32 count = pItem->GetCount();
|
||||
|
||||
// >=5 checked in spell code, but will work for cheating cases also with removing from another stacks.
|
||||
if(count > 5)
|
||||
if (count > 5)
|
||||
count = 5;
|
||||
|
||||
player->DestroyItemCount(pItem, count, true);
|
||||
}
|
||||
else
|
||||
// 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);
|
||||
// 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.
|
||||
player->DestroyItem(pItem->GetBagSlot(),pItem->GetSlot(), true);
|
||||
return; // item can be looted only single player
|
||||
}
|
||||
else
|
||||
@@ -414,26 +430,40 @@ void WorldSession::DoLootRelease( uint64 lguid )
|
||||
Creature* pCreature = GetPlayer()->GetMap()->GetCreature(lguid);
|
||||
|
||||
bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed);
|
||||
if ( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
|
||||
if (!ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
|
||||
return;
|
||||
|
||||
loot = &pCreature->loot;
|
||||
|
||||
// update next looter
|
||||
if(Player *recipient = pCreature->GetLootRecipient())
|
||||
if(Group* group = recipient->GetGroup())
|
||||
if (group->GetLooterGuid() == player->GetGUID())
|
||||
group->UpdateLooterGuid(pCreature);
|
||||
|
||||
if (loot->isLooted())
|
||||
{
|
||||
// skip pickpocketing loot for speed, skinning timer redunction is no-op in fact
|
||||
if(!pCreature->isAlive())
|
||||
if (!pCreature->isAlive())
|
||||
pCreature->AllLootRemovedFromCorpse();
|
||||
|
||||
pCreature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
|
||||
pCreature->SetLootRecipient(NULL);
|
||||
loot->clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the round robin player release, reset it.
|
||||
if (player->GetGUID() == loot->roundRobinPlayer)
|
||||
{
|
||||
if (Group* pGroup = player->GetGroup())
|
||||
{
|
||||
if (pGroup->GetLootMethod() != MASTER_LOOT)
|
||||
{
|
||||
loot->roundRobinPlayer = 0;
|
||||
pGroup->SendLooter(pCreature, NULL);
|
||||
|
||||
// force update of dynamic flags, otherwise other group's players still not able to loot.
|
||||
pCreature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS);
|
||||
}
|
||||
}
|
||||
else
|
||||
loot->roundRobinPlayer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Player is not looking at loot list, he doesn't need to see updates on the loot list
|
||||
|
||||
+107
-11
@@ -408,11 +408,20 @@ bool Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner,
|
||||
|
||||
// Setting access rights for group loot case
|
||||
Group * pGroup = loot_owner->GetGroup();
|
||||
if(!personal && pGroup)
|
||||
if (!personal && pGroup)
|
||||
{
|
||||
roundRobinPlayer = loot_owner->GetGUID();
|
||||
|
||||
for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
if(Player* pl = itr->getSource())
|
||||
FillNotNormalLootFor(pl);
|
||||
|
||||
for (uint8 i = 0; i < items.size(); ++i)
|
||||
{
|
||||
if (ItemPrototype const *proto = sItemStorage.LookupEntry<ItemPrototype>(items[i].itemid))
|
||||
if (proto->Quality < uint32(pGroup->GetLootThreshold()))
|
||||
items[i].is_underthreshold = true;
|
||||
}
|
||||
}
|
||||
// ... for personal loot
|
||||
else
|
||||
@@ -672,6 +681,63 @@ uint32 Loot::GetMaxSlotInLootFor(Player* player) const
|
||||
return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0);
|
||||
}
|
||||
|
||||
// return true if there is any FFA, quest or conditional item for the player.
|
||||
bool Loot::hasItemFor(Player* player) const
|
||||
{
|
||||
QuestItemMap const& lootPlayerQuestItems = GetPlayerQuestItems();
|
||||
QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(player->GetGUIDLow());
|
||||
if (q_itr != lootPlayerQuestItems.end())
|
||||
{
|
||||
QuestItemList *q_list = q_itr->second;
|
||||
for (QuestItemList::const_iterator qi = q_list->begin() ; qi != q_list->end(); ++qi)
|
||||
{
|
||||
const LootItem &item = quest_items[qi->index];
|
||||
if (!qi->is_looted && !item.is_looted)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QuestItemMap const& lootPlayerFFAItems = GetPlayerFFAItems();
|
||||
QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(player->GetGUIDLow());
|
||||
if (ffa_itr != lootPlayerFFAItems.end())
|
||||
{
|
||||
QuestItemList *ffa_list = ffa_itr->second;
|
||||
for (QuestItemList::const_iterator fi = ffa_list->begin() ; fi != ffa_list->end(); ++fi)
|
||||
{
|
||||
const LootItem &item = items[fi->index];
|
||||
if (!fi->is_looted && !item.is_looted)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems();
|
||||
QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(player->GetGUIDLow());
|
||||
if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end())
|
||||
{
|
||||
QuestItemList *conditional_list = nn_itr->second;
|
||||
for (QuestItemList::const_iterator ci = conditional_list->begin() ; ci != conditional_list->end(); ++ci)
|
||||
{
|
||||
const LootItem &item = items[ci->index];
|
||||
if (!ci->is_looted && !item.is_looted)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if there is any item over the group threshold (i.e. not underthreshold).
|
||||
bool Loot::hasOverThresholdItem() const
|
||||
{
|
||||
for (uint8 i = 0; i < items.size(); ++i)
|
||||
{
|
||||
if (!items[i].is_looted && !items[i].is_underthreshold && !items[i].freeforall)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li)
|
||||
{
|
||||
b << uint32(li.itemid);
|
||||
@@ -706,16 +772,46 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
{
|
||||
case GROUP_PERMISSION:
|
||||
{
|
||||
// You are not the items proprietary, so you can only see
|
||||
// if you are not the round-robin group looter, you can only see
|
||||
// blocked rolled items and quest items, and !ffa items
|
||||
for (uint8 i = 0; i < l.items.size(); ++i)
|
||||
{
|
||||
if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer))
|
||||
{
|
||||
uint8 slot_type = (l.items[i].is_blocked || l.items[i].is_underthreshold) ? 0 : 1;
|
||||
uint8 slot_type;
|
||||
|
||||
b << uint8(i) << l.items[i]; //send the index and the item if it's not looted, and blocked or under threshold, free for all items will be sent later, only one-player loots here
|
||||
b << uint8(slot_type); // 0 - get 1 - look only
|
||||
if (l.items[i].is_blocked)
|
||||
slot_type = LOOT_SLOT_TYPE_ROLL_ONGOING;
|
||||
else if (l.roundRobinPlayer == 0 || !l.items[i].is_underthreshold || lv.viewer->GetGUID() == l.roundRobinPlayer)
|
||||
{
|
||||
// no round robin owner or he has released the loot
|
||||
// or it IS the round robin group owner
|
||||
// => item is lootable
|
||||
slot_type = LOOT_SLOT_TYPE_ALLOW_LOOT;
|
||||
}
|
||||
else
|
||||
// item shall not be displayed.
|
||||
continue;
|
||||
|
||||
b << uint8(i) << l.items[i];
|
||||
b << uint8(slot_type);
|
||||
++itemsShown;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ROUND_ROBIN_PERMISSION:
|
||||
{
|
||||
for (uint8 i = 0; i < l.items.size(); ++i)
|
||||
{
|
||||
if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer))
|
||||
{
|
||||
if (l.roundRobinPlayer != 0 && lv.viewer->GetGUID() != l.roundRobinPlayer)
|
||||
// item shall not be displayed.
|
||||
continue;
|
||||
|
||||
b << uint8(i) << l.items[i];
|
||||
b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT);
|
||||
++itemsShown;
|
||||
}
|
||||
}
|
||||
@@ -724,13 +820,13 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
case ALL_PERMISSION:
|
||||
case MASTER_PERMISSION:
|
||||
{
|
||||
uint8 slot_type = (lv.permission==MASTER_PERMISSION) ? 2 : 0;
|
||||
uint8 slot_type = (lv.permission==MASTER_PERMISSION) ? LOOT_SLOT_TYPE_MASTER : LOOT_SLOT_TYPE_ALLOW_LOOT;
|
||||
for (uint8 i = 0; i < l.items.size(); ++i)
|
||||
{
|
||||
if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer))
|
||||
{
|
||||
b << uint8(i) << l.items[i]; //only send one-player loot items now, free for all will be sent later
|
||||
b << uint8(slot_type); // 0 - get 2 - master selection
|
||||
b << uint8(i) << l.items[i];
|
||||
b << uint8(slot_type);
|
||||
++itemsShown;
|
||||
}
|
||||
}
|
||||
@@ -752,7 +848,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
{
|
||||
b << uint8(l.items.size() + (qi - q_list->begin()));
|
||||
b << item;
|
||||
b << uint8(0); // allow loot
|
||||
b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT);
|
||||
++itemsShown;
|
||||
}
|
||||
}
|
||||
@@ -769,7 +865,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
if (!fi->is_looted && !item.is_looted)
|
||||
{
|
||||
b << uint8(fi->index) << item;
|
||||
b << uint8(0); // allow loot
|
||||
b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT);
|
||||
++itemsShown;
|
||||
}
|
||||
}
|
||||
@@ -786,7 +882,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
if (!ci->is_looted && !item.is_looted)
|
||||
{
|
||||
b << uint8(ci->index) << item;
|
||||
b << uint8(0); // allow loot
|
||||
b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT);
|
||||
++itemsShown;
|
||||
}
|
||||
}
|
||||
|
||||
+18
-4
@@ -56,10 +56,11 @@ enum LootMethod
|
||||
|
||||
enum PermissionTypes
|
||||
{
|
||||
ALL_PERMISSION = 0,
|
||||
GROUP_PERMISSION = 1,
|
||||
MASTER_PERMISSION = 2,
|
||||
NONE_PERMISSION = 3
|
||||
ALL_PERMISSION = 0,
|
||||
GROUP_PERMISSION = 1,
|
||||
MASTER_PERMISSION = 2,
|
||||
ROUND_ROBIN_PERMISSION = 3,
|
||||
NONE_PERMISSION = 4,
|
||||
};
|
||||
|
||||
enum LootType
|
||||
@@ -77,6 +78,15 @@ enum LootType
|
||||
LOOT_INSIGNIA = 21 // unsupported by client, sending LOOT_CORPSE instead
|
||||
};
|
||||
|
||||
// type of Loot Item in Loot View
|
||||
enum LootSlotType
|
||||
{
|
||||
LOOT_SLOT_TYPE_ALLOW_LOOT = 0, // player can loot the item.
|
||||
LOOT_SLOT_TYPE_ROLL_ONGOING = 1, // roll is ongoing. player cannot loot.
|
||||
LOOT_SLOT_TYPE_MASTER = 2, // item can only be distributed by group loot master.
|
||||
LOOT_SLOT_TYPE_LOCKED = 3, // item is shown in red. player cannot loot.
|
||||
};
|
||||
|
||||
class Player;
|
||||
class LootStore;
|
||||
|
||||
@@ -248,6 +258,7 @@ struct Loot
|
||||
std::vector<LootItem> items;
|
||||
uint32 gold;
|
||||
uint8 unlootedCount;
|
||||
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
|
||||
|
||||
Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0), loot_type(LOOT_CORPSE) {}
|
||||
@@ -279,6 +290,7 @@ struct Loot
|
||||
quest_items.clear();
|
||||
gold = 0;
|
||||
unlootedCount = 0;
|
||||
roundRobinPlayer = 0;
|
||||
i_LootValidatorRefManager.clearReferences();
|
||||
}
|
||||
|
||||
@@ -299,6 +311,8 @@ struct Loot
|
||||
|
||||
LootItem* LootItemInSlot(uint32 lootslot, Player* player, QuestItem** qitem = NULL, QuestItem** ffaitem = NULL, QuestItem** conditem = NULL);
|
||||
uint32 GetMaxSlotInLootFor(Player* player) const;
|
||||
bool hasItemFor(Player* player) const;
|
||||
bool hasOverThresholdItem() const;
|
||||
|
||||
private:
|
||||
void FillNotNormalLootFor(Player* player);
|
||||
|
||||
+122
-47
@@ -7917,10 +7917,10 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
|
||||
uint32 lootid = go->GetGOInfo()->GetLootId();
|
||||
|
||||
//TODO: fix this big hack
|
||||
if((go->GetEntry() == BG_AV_OBJECTID_MINE_N || go->GetEntry() == BG_AV_OBJECTID_MINE_S))
|
||||
if( BattleGround *bg = GetBattleGround())
|
||||
if(bg->GetTypeID() == BATTLEGROUND_AV)
|
||||
if(!(((BattleGroundAV*)bg)->PlayerCanDoMineQuest(go->GetEntry(),GetTeam())))
|
||||
if ((go->GetEntry() == BG_AV_OBJECTID_MINE_N || go->GetEntry() == BG_AV_OBJECTID_MINE_S))
|
||||
if (BattleGround *bg = GetBattleGround())
|
||||
if (bg->GetTypeID() == BATTLEGROUND_AV)
|
||||
if (!(((BattleGroundAV*)bg)->PlayerCanDoMineQuest(go->GetEntry(),GetTeam())))
|
||||
{
|
||||
SendLootRelease(guid);
|
||||
return;
|
||||
@@ -7928,16 +7928,71 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
|
||||
|
||||
if (lootid)
|
||||
{
|
||||
sLog.outDebug(" if(lootid)");
|
||||
loot->clear();
|
||||
loot->FillLoot(lootid, LootTemplates_Gameobject, this, false, false, go->GetLootMode());
|
||||
|
||||
Group* group = GetGroup();
|
||||
bool groupRules = (group && go->GetGOInfo()->type == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules);
|
||||
|
||||
// check current RR player and get next if necessary
|
||||
if (groupRules)
|
||||
group->UpdateLooterGuid(go, true);
|
||||
|
||||
loot->FillLoot(lootid, LootTemplates_Gameobject, this, !groupRules, false, go->GetLootMode());
|
||||
|
||||
// get next RR player (for next loot)
|
||||
if (groupRules)
|
||||
group->UpdateLooterGuid(go);
|
||||
}
|
||||
|
||||
if (loot_type == LOOT_FISHING)
|
||||
go->getFishLoot(loot,this);
|
||||
|
||||
if (go->GetGOInfo()->type == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules)
|
||||
{
|
||||
if (Group* group = GetGroup())
|
||||
{
|
||||
switch (group->GetLootMethod())
|
||||
{
|
||||
case GROUP_LOOT:
|
||||
// GroupLoot: rolls items over threshold. Items with quality < threshold, round robin
|
||||
group->GroupLoot(loot, go);
|
||||
break;
|
||||
case NEED_BEFORE_GREED:
|
||||
group->NeedBeforeGreed(loot, go);
|
||||
break;
|
||||
case MASTER_LOOT:
|
||||
group->MasterLoot(loot, go);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
go->SetLootState(GO_ACTIVATED);
|
||||
}
|
||||
|
||||
if (go->getLootState() == GO_ACTIVATED)
|
||||
{
|
||||
if (Group* group = GetGroup())
|
||||
{
|
||||
switch (group->GetLootMethod())
|
||||
{
|
||||
case MASTER_LOOT:
|
||||
permission = MASTER_PERMISSION;
|
||||
break;
|
||||
case FREE_FOR_ALL:
|
||||
permission = ALL_PERMISSION;
|
||||
break;
|
||||
case ROUND_ROBIN:
|
||||
permission = ROUND_ROBIN_PERMISSION;
|
||||
break;
|
||||
default:
|
||||
permission = GROUP_PERMISSION;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
permission = ALL_PERMISSION;
|
||||
}
|
||||
}
|
||||
else if (IS_ITEM_GUID(guid))
|
||||
{
|
||||
@@ -8019,7 +8074,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
|
||||
return;
|
||||
}
|
||||
|
||||
loot = &creature->loot;
|
||||
loot = &creature->loot;
|
||||
|
||||
if (loot_type == LOOT_PICKPOCKETING)
|
||||
{
|
||||
@@ -8029,7 +8084,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
|
||||
loot->clear();
|
||||
|
||||
if (uint32 lootid = creature->GetCreatureInfo()->pickpocketLootId)
|
||||
loot->FillLoot(lootid, LootTemplates_Pickpocketing, this, false);
|
||||
loot->FillLoot(lootid, LootTemplates_Pickpocketing, this, true);
|
||||
|
||||
// Generate extra money for pick pocket loot
|
||||
const uint32 a = urand(0, creature->getLevel()/2);
|
||||
@@ -8047,37 +8102,25 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
|
||||
recipient = this;
|
||||
}
|
||||
|
||||
if (creature->lootForPickPocketed)
|
||||
{
|
||||
creature->lootForPickPocketed = false;
|
||||
loot->clear();
|
||||
}
|
||||
|
||||
if (!creature->lootForBody)
|
||||
{
|
||||
creature->lootForBody = true;
|
||||
loot->clear();
|
||||
|
||||
if (uint32 lootid = creature->GetCreatureInfo()->lootid)
|
||||
loot->FillLoot(lootid, LootTemplates_Creature, recipient, false, false, creature->GetLootMode());
|
||||
|
||||
loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold);
|
||||
// for creature, loot is filled when creature is killed.
|
||||
|
||||
if (Group* group = recipient->GetGroup())
|
||||
{
|
||||
group->UpdateLooterGuid(creature,true);
|
||||
|
||||
switch (group->GetLootMethod())
|
||||
{
|
||||
case GROUP_LOOT:
|
||||
// GroupLoot delete items over threshold (threshold even not implemented), and roll them. Items with quality<threshold, round robin
|
||||
group->GroupLoot(recipient->GetGUID(), loot, creature);
|
||||
// GroupLoot: rolls items over threshold. Items with quality < threshold, round robin
|
||||
group->GroupLoot(loot, creature);
|
||||
break;
|
||||
case NEED_BEFORE_GREED:
|
||||
group->NeedBeforeGreed(recipient->GetGUID(), loot, creature);
|
||||
group->NeedBeforeGreed(loot, creature);
|
||||
break;
|
||||
case MASTER_LOOT:
|
||||
group->MasterLoot(recipient->GetGUID(), loot, creature);
|
||||
group->MasterLoot(loot, creature);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -8089,26 +8132,30 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
|
||||
if (loot_type == LOOT_SKINNING)
|
||||
{
|
||||
loot->clear();
|
||||
loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, this, false);
|
||||
loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, this, true);
|
||||
}
|
||||
// set group rights only for loot_type != LOOT_SKINNING
|
||||
else
|
||||
{
|
||||
if(Group* group = GetGroup())
|
||||
if (Group* group = GetGroup())
|
||||
{
|
||||
if (group == recipient->GetGroup())
|
||||
{
|
||||
if (group->GetLootMethod() == FREE_FOR_ALL)
|
||||
permission = ALL_PERMISSION;
|
||||
else if (group->GetLooterGuid() == GetGUID())
|
||||
switch (group->GetLootMethod())
|
||||
{
|
||||
if (group->GetLootMethod() == MASTER_LOOT)
|
||||
case MASTER_LOOT:
|
||||
permission = MASTER_PERMISSION;
|
||||
else
|
||||
break;
|
||||
case FREE_FOR_ALL:
|
||||
permission = ALL_PERMISSION;
|
||||
break;
|
||||
case ROUND_ROBIN:
|
||||
permission = ROUND_ROBIN_PERMISSION;
|
||||
break;
|
||||
default:
|
||||
permission = GROUP_PERMISSION;
|
||||
break;
|
||||
}
|
||||
else
|
||||
permission = GROUP_PERMISSION;
|
||||
}
|
||||
else
|
||||
permission = NONE_PERMISSION;
|
||||
@@ -16077,22 +16124,50 @@ bool Player::isAllowedToLoot(Creature* creature)
|
||||
if (creature->isDead() && !creature->IsDamageEnoughForLootingAndReward())
|
||||
return false;
|
||||
|
||||
if (Player* recipient = creature->GetLootRecipient())
|
||||
{
|
||||
if (recipient == this)
|
||||
return true;
|
||||
if (Group* otherGroup = recipient->GetGroup())
|
||||
{
|
||||
Group* thisGroup = GetGroup();
|
||||
if (!thisGroup)
|
||||
return false;
|
||||
return thisGroup == otherGroup;
|
||||
}
|
||||
Loot* loot = &creature->loot;
|
||||
if (loot->items.size() == 0)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
||||
Player* recipient = creature->GetLootRecipient();
|
||||
if (!recipient)
|
||||
// prevent other players from looting if the recipient got disconnected
|
||||
return !creature->hasLootRecipient();
|
||||
|
||||
Group* recipientGroup = recipient->GetGroup();
|
||||
if (!recipientGroup)
|
||||
return (this == recipient);
|
||||
|
||||
Group* thisGroup = GetGroup();
|
||||
if (!thisGroup || thisGroup != recipientGroup)
|
||||
return false;
|
||||
|
||||
switch(thisGroup->GetLootMethod())
|
||||
{
|
||||
case FREE_FOR_ALL:
|
||||
return true;
|
||||
case ROUND_ROBIN:
|
||||
case MASTER_LOOT:
|
||||
// may only loot if the player is the loot roundrobin player
|
||||
// or if there are free/quest/conditional item for the player
|
||||
if (loot->roundRobinPlayer == 0 || loot->roundRobinPlayer == GetGUID())
|
||||
return true;
|
||||
|
||||
return loot->hasItemFor(this);
|
||||
case GROUP_LOOT:
|
||||
case NEED_BEFORE_GREED:
|
||||
// may only loot if the player is the loot roundrobin player
|
||||
// or item over threshold (so roll(s) can be launched)
|
||||
// or if there are free/quest/conditional item for the player
|
||||
if (loot->roundRobinPlayer == 0 || loot->roundRobinPlayer == GetGUID())
|
||||
return true;
|
||||
|
||||
if (loot->hasOverThresholdItem())
|
||||
return true;
|
||||
|
||||
return loot->hasItemFor(this);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Player::_LoadActions(QueryResult_AutoPtr result, bool startup)
|
||||
|
||||
+55
-7
@@ -14560,29 +14560,77 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
|
||||
|
||||
// find player: owner of controlled `this` or `this` itself maybe
|
||||
Player *player = GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
Creature *creature = pVictim->ToCreature();
|
||||
|
||||
bool bRewardIsAllowed = true;
|
||||
if (pVictim->GetTypeId() == TYPEID_UNIT)
|
||||
if (creature)
|
||||
{
|
||||
bRewardIsAllowed = pVictim->ToCreature()->IsDamageEnoughForLootingAndReward();
|
||||
bRewardIsAllowed = creature->IsDamageEnoughForLootingAndReward();
|
||||
if (!bRewardIsAllowed)
|
||||
pVictim->ToCreature()->SetLootRecipient(NULL);
|
||||
creature->SetLootRecipient(NULL);
|
||||
}
|
||||
|
||||
if (bRewardIsAllowed && pVictim->GetTypeId() == TYPEID_UNIT && pVictim->ToCreature()->GetLootRecipient())
|
||||
player = pVictim->ToCreature()->GetLootRecipient();
|
||||
if (bRewardIsAllowed && creature && creature->GetLootRecipient())
|
||||
player = creature->GetLootRecipient();
|
||||
|
||||
// Reward player, his pets, and group/raid members
|
||||
// call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
|
||||
if (bRewardIsAllowed && player && player!=pVictim)
|
||||
if (bRewardIsAllowed && player && player != pVictim)
|
||||
{
|
||||
WorldPacket data(SMSG_PARTYKILLLOG, (8+8)); //send event PARTY_KILL
|
||||
data << uint64(player->GetGUID()); //player with killing blow
|
||||
data << uint64(pVictim->GetGUID()); //victim
|
||||
if (Group *group = player->GetGroup())
|
||||
|
||||
Player* pLooter = player;
|
||||
|
||||
if (Group *group = player->GetGroup())
|
||||
{
|
||||
group->BroadcastPacket(&data, group->GetMemberGroup(player->GetGUID()));
|
||||
|
||||
if (creature)
|
||||
{
|
||||
group->UpdateLooterGuid(creature, true);
|
||||
if (group->GetLooterGuid())
|
||||
{
|
||||
pLooter = objmgr.GetPlayer(group->GetLooterGuid());
|
||||
if (pLooter)
|
||||
{
|
||||
creature->SetLootRecipient(pLooter); // update creature loot recipient to the allowed looter.
|
||||
group->SendLooter(creature, pLooter);
|
||||
}
|
||||
else
|
||||
group->SendLooter(creature, NULL);
|
||||
}
|
||||
else
|
||||
group->SendLooter(creature, NULL);
|
||||
|
||||
group->UpdateLooterGuid(creature);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
player->SendDirectMessage(&data);
|
||||
|
||||
WorldPacket data2(SMSG_LOOT_LIST, (8+1+1));
|
||||
data2 << uint64(creature->GetGUID());
|
||||
data2 << uint8(0); // unk1
|
||||
data2 << uint8(0); // no group looter
|
||||
player->SendMessageToSetInRange(&data2, GetMap()->GetVisibilityDistance(), true);
|
||||
}
|
||||
|
||||
if (creature)
|
||||
{
|
||||
Loot* loot = &creature->loot;
|
||||
if (creature->lootForPickPocketed)
|
||||
creature->lootForPickPocketed = false;
|
||||
|
||||
loot->clear();
|
||||
if (uint32 lootid = creature->GetCreatureInfo()->lootid)
|
||||
loot->FillLoot(lootid, LootTemplates_Creature, pLooter, false, false, creature->GetLootMode());
|
||||
|
||||
loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold);
|
||||
}
|
||||
|
||||
if (player->RewardPlayerAndGroupAtKill(pVictim))
|
||||
player->ProcDamageAndSpell(pVictim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, 0);
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user