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:
Trazom62
2010-03-11 22:55:02 +01:00
parent d9f257a18c
commit ed8c5ef6ff
10 changed files with 578 additions and 257 deletions

View File

@@ -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)