diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index db50a66d4..e8b4c443b 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -59,6 +59,9 @@ GameObject::GameObject() : WorldObject(), m_cooldownTime = 0; m_packedRotation = 0; + m_groupLootTimer = 0; + m_groupLootId = 0; + m_lootGroupRecipientId = 0; } GameObject::~GameObject() @@ -352,6 +355,15 @@ void GameObject::Update(uint32 update_diff, uint32 /*p_time*/) if (GetGOInfo()->GetAutoCloseTime() && (m_cooldownTime < time(NULL))) ResetDoorOrButton(); break; + case GAMEOBJECT_TYPE_CHEST: + if (m_groupLootTimer) + { + if (m_groupLootTimer <= update_diff) + StopGroupLoot(); + else + m_groupLootTimer -= update_diff; + } + break; case GAMEOBJECT_TYPE_GOOBER: if (m_cooldownTime < time(NULL)) { @@ -415,6 +427,7 @@ void GameObject::Update(uint32 update_diff, uint32 /*p_time*/) } loot.clear(); + SetLootRecipient(NULL); SetLootState(GO_READY); if (!m_respawnDelayTime) @@ -1804,6 +1817,86 @@ void GameObject::SetDisplayId(uint32 modelId) m_displayInfo = sGameObjectDisplayInfoStore.LookupEntry(modelId); } +void GameObject::StartGroupLoot(Group* group, uint32 timer) +{ + m_groupLootId = group->GetId(); + m_groupLootTimer = timer; +} + +void GameObject::StopGroupLoot() +{ + if (!m_groupLootId) + return; + + if (Group* group = sObjectMgr.GetGroupById(m_groupLootId)) + group->EndRoll(); + + m_groupLootTimer = 0; + m_groupLootId = 0; +} + +Player* GameObject::GetOriginalLootRecipient() const +{ + return m_lootRecipientGuid ? ObjectAccessor::FindPlayer(m_lootRecipientGuid) : NULL; +} + +Group* GameObject::GetGroupLootRecipient() const +{ + // original recipient group if set and not disbanded + return m_lootGroupRecipientId ? sObjectMgr.GetGroupById(m_lootGroupRecipientId) : NULL; +} + +Player* GameObject::GetLootRecipient() const +{ + // original recipient group if set and not disbanded + Group* group = GetGroupLootRecipient(); + + // original recipient player if online + Player* player = GetOriginalLootRecipient(); + + // if group not set or disbanded return original recipient player if any + if (!group) + return player; + + // group case + + // return player if it still be in original recipient group + if (player && player->GetGroup() == group) + return player; + + // find any in group + for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) + if (Player* newPlayer = itr->getSource()) + return newPlayer; + + return NULL; +} + +void GameObject::SetLootRecipient(Unit* pUnit) +{ + // set the player whose group should receive the right + // to loot the gameobject after its used + // should be set to NULL after the loot disappears + + if (!pUnit) + { + m_lootRecipientGuid.Clear(); + m_lootGroupRecipientId = 0; + return; + } + + Player* player = pUnit->GetCharmerOrOwnerPlayerOrPlayerItself(); + if (!player) // normal creature, no player involved + return; + + // set player for non group case or if group will disbanded + m_lootRecipientGuid = player->GetObjectGuid(); + + // set group for group existed case including if player will leave group at loot time + if (Group* group = player->GetGroup()) + m_lootGroupRecipientId = group->GetId(); +} + float GameObject::GetObjectBoundingRadius() const { //FIXME: diff --git a/src/game/GameObject.h b/src/game/GameObject.h index 8f1deba51..ed01d5224 100644 --- a/src/game/GameObject.h +++ b/src/game/GameObject.h @@ -681,7 +681,6 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject static void AddToRemoveListInMaps(uint32 db_guid, GameObjectData const* data); static void SpawnInMaps(uint32 db_guid, GameObjectData const* data); - void getFishLoot(Loot *loot, Player* loot_owner); GameobjectTypes GetGoType() const { return GameobjectTypes(GetByteValue(GAMEOBJECT_BYTES_1, 1)); } void SetGoType(GameobjectTypes type) { SetByteValue(GAMEOBJECT_BYTES_1, 1, type); } GOState GetGoState() const { return GOState(GetByteValue(GAMEOBJECT_BYTES_1, 0)); } @@ -719,7 +718,19 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject void SaveRespawnTime(); - Loot loot; + // Loot System + Loot loot; + void getFishLoot(Loot* loot, Player* loot_owner); + void StartGroupLoot(Group* group, uint32 timer); + + ObjectGuid GetLootRecipientGuid() const { return m_lootRecipientGuid; } + uint32 GetLootGroupRecipientId() const { return m_lootGroupRecipientId; } + Player* GetLootRecipient() const; // use group cases as prefered + Group* GetGroupLootRecipient() const; + bool HasLootRecipient() const { return m_lootGroupRecipientId || !m_lootRecipientGuid.IsEmpty(); } + bool IsGroupLootRecipient() const { return m_lootGroupRecipientId; } + void SetLootRecipient(Unit* pUnit); + Player* GetOriginalLootRecipient() const; // ignore group changes/etc, not for looting bool HasQuest(uint32 quest_id) const; bool HasInvolvedQuest(uint32 quest_id) const; @@ -763,6 +774,14 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject GameObjectDisplayInfoEntry const* m_displayInfo; int64 m_packedRotation; QuaternionData m_worldRotation; + + // Loot System + uint32 m_groupLootTimer; // (msecs)timer used for group loot + uint32 m_groupLootId; // used to find group which is looting + void StopGroupLoot(); + ObjectGuid m_lootRecipientGuid; // player who will have rights for looting if m_lootGroupRecipient==0 or group disbanded + uint32 m_lootGroupRecipientId; // group who will have rights for looting if set and exist + private: void SwitchDoorOrButton(bool activate, bool alternative = false); diff --git a/src/game/Group.cpp b/src/game/Group.cpp index b02f64207..e0cd4a65e 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -585,7 +585,7 @@ void Group::SendLootAllPassed(Roll const& r) } } -void Group::GroupLoot(Creature *creature, Loot *loot) +void Group::GroupLoot(WorldObject* pSource, Loot* loot) { uint32 maxEnchantingSkill = GetMaxSkillValueForGroup(SKILL_ENCHANTING); @@ -601,13 +601,13 @@ void Group::GroupLoot(Creature *creature, Loot *loot) //roll for over-threshold item if it's one-player loot if (itemProto->Quality >= uint32(m_lootThreshold) && !lootItem.freeforall) - StartLootRool(creature, GROUP_LOOT, loot, itemSlot, maxEnchantingSkill); + StartLootRool(pSource, GROUP_LOOT, loot, itemSlot, maxEnchantingSkill); else lootItem.is_underthreshold = 1; } } -void Group::NeedBeforeGreed(Creature *creature, Loot *loot) +void Group::NeedBeforeGreed(WorldObject* pSource, Loot* loot) { uint32 maxEnchantingSkill = GetMaxSkillValueForGroup(SKILL_ENCHANTING); @@ -623,13 +623,13 @@ void Group::NeedBeforeGreed(Creature *creature, Loot *loot) //only roll for one-player items, not for ones everyone can get if (itemProto->Quality >= uint32(m_lootThreshold) && !lootItem.freeforall) - StartLootRool(creature, NEED_BEFORE_GREED, loot, itemSlot, maxEnchantingSkill); + StartLootRool(pSource, NEED_BEFORE_GREED, loot, itemSlot, maxEnchantingSkill); else lootItem.is_underthreshold = 1; } } -void Group::MasterLoot(Creature *creature, Loot* loot) +void Group::MasterLoot(WorldObject* pSource, Loot* loot) { for (LootItemList::iterator i=loot->items.begin(); i != loot->items.end(); ++i) { @@ -651,7 +651,7 @@ void Group::MasterLoot(Creature *creature, Loot* loot) if (!looter->IsInWorld()) continue; - if (looter->IsWithinDist(creature, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE), false)) + if (looter->IsWithinDist(pSource, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE), false)) { data << looter->GetObjectGuid(); ++real_count; @@ -663,7 +663,7 @@ void Group::MasterLoot(Creature *creature, Loot* loot) for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) { Player *looter = itr->getSource(); - if (looter->IsWithinDist(creature, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE), false)) + if (looter->IsWithinDist(pSource, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE), false)) looter->GetSession()->SendPacket(&data); } } @@ -743,7 +743,7 @@ bool Group::CountRollVote(ObjectGuid const& playerGUID, Rolls::iterator& rollI, return false; } -void Group::StartLootRool(Creature* lootTarget, LootMethod method, Loot* loot, uint8 itemSlot, uint32 maxEnchantingSkill) +void Group::StartLootRool(WorldObject* lootTarget, LootMethod method, Loot* loot, uint8 itemSlot, uint32 maxEnchantingSkill) { if (itemSlot >= loot->items.size()) return; @@ -778,11 +778,15 @@ void Group::StartLootRool(Creature* lootTarget, LootMethod method, Loot* loot, u r->playerVote.begin()->second = ROLL_NEED; else { + // Only GO-group looting and NPC-group looting possible + MANGOS_ASSERT(lootTarget->isType(TYPEMASK_CREATURE_OR_GAMEOBJECT)); + r->CalculateCommonVoteMask(maxEnchantingSkill); // dependent from item and possible skill SendLootStartRoll(LOOT_ROLL_TIMEOUT, lootTarget->GetMapId(), *r); loot->items[itemSlot].is_blocked = true; - lootTarget->StartGroupLoot(this,LOOT_ROLL_TIMEOUT); + + lootTarget->StartGroupLoot(this, LOOT_ROLL_TIMEOUT); } RollId.push_back(r); @@ -1488,7 +1492,7 @@ uint32 Group::GetMaxSkillValueForGroup( SkillType skill ) return maxvalue; } -void Group::UpdateLooterGuid( Creature* creature, bool ifneed ) +void Group::UpdateLooterGuid(WorldObject* pSource, bool ifneed) { switch (GetLootMethod()) { @@ -1508,7 +1512,7 @@ void Group::UpdateLooterGuid( Creature* creature, bool 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_FLOAT_GROUP_XP_DISTANCE), false)) + if (looter && looter->IsWithinDist(pSource, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE), false)) return; } ++guid_itr; @@ -1521,16 +1525,16 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed ) { if (Player* pl = ObjectAccessor::FindPlayer(itr->guid)) { - if (pl->IsWithinDist(creature, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE), false)) + if (pl->IsWithinDist(pSource, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE), false)) { - bool refresh = pl->GetLootGuid() == creature->GetObjectGuid(); + bool refresh = pl->GetLootGuid() == pSource->GetObjectGuid(); //if(refresh) // update loot for new looter // pl->GetSession()->DoLootRelease(pl->GetLootGUID()); SetLooterGuid(pl->GetObjectGuid()); SendUpdate(); if (refresh) // update loot for new looter - pl->SendLoot(creature->GetObjectGuid(), LOOT_CORPSE); + pl->SendLoot(pSource->GetObjectGuid(), LOOT_CORPSE); return; } } @@ -1542,16 +1546,16 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed ) { if (Player* pl = ObjectAccessor::FindPlayer(itr->guid)) { - if (pl->IsWithinDist(creature, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE), false)) + if (pl->IsWithinDist(pSource, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE), false)) { - bool refresh = pl->GetLootGuid() == creature->GetObjectGuid(); + bool refresh = pl->GetLootGuid() == pSource->GetObjectGuid(); //if(refresh) // update loot for new looter // pl->GetSession()->DoLootRelease(pl->GetLootGUID()); SetLooterGuid(pl->GetObjectGuid()); SendUpdate(); if (refresh) // update loot for new looter - pl->SendLoot(creature->GetObjectGuid(), LOOT_CORPSE); + pl->SendLoot(pSource->GetObjectGuid(), LOOT_CORPSE); return; } } diff --git a/src/game/Group.h b/src/game/Group.h index 640b03a1d..b67438937 100644 --- a/src/game/Group.h +++ b/src/game/Group.h @@ -223,7 +223,7 @@ class MANGOS_DLL_SPEC Group void ChangeLeader(ObjectGuid guid); void SetLootMethod(LootMethod method) { m_lootMethod = method; } void SetLooterGuid(ObjectGuid guid) { m_looterGuid = guid; } - void UpdateLooterGuid( Creature* creature, bool ifneed = false ); + void UpdateLooterGuid(WorldObject* pSource, bool ifneed = false); void SetLootThreshold(ItemQualities threshold) { m_lootThreshold = threshold; } void Disband(bool hideDestroy=false); @@ -347,11 +347,11 @@ class MANGOS_DLL_SPEC Group void SendLootRoll(ObjectGuid const& targetGuid, uint8 rollNumber, uint8 rollType, const Roll &r); void SendLootRollWon(ObjectGuid const& targetGuid, uint8 rollNumber, RollVote rollType, const Roll &r); void SendLootAllPassed(const Roll &r); - void GroupLoot(Creature *creature, Loot *loot); - void NeedBeforeGreed(Creature *creature, Loot *loot); - void MasterLoot(Creature *creature, Loot *loot); + void GroupLoot(WorldObject* pSource, Loot* loot); + void NeedBeforeGreed(WorldObject* pSource, Loot* loot); + void MasterLoot(WorldObject* pSource, Loot* loot); bool CountRollVote(Player* player, ObjectGuid const& lootedTarget, uint32 itemSlot, RollVote vote); - void StartLootRool(Creature* lootTarget, LootMethod method, Loot* loot, uint8 itemSlot, uint32 maxEnchantingSkill); + void StartLootRool(WorldObject* lootTarget, LootMethod method, Loot* loot, uint8 itemSlot, uint32 maxEnchantingSkill); void EndRoll(); void LinkMember(GroupReference *pRef) { m_memberMgr.insertFirst(pRef); } diff --git a/src/game/Object.h b/src/game/Object.h index dfdd35ae0..98e0738af 100644 --- a/src/game/Object.h +++ b/src/game/Object.h @@ -66,6 +66,7 @@ class WorldSession; class Creature; class Player; class Unit; +class Group; class Map; class UpdateMask; class InstanceData; @@ -356,6 +357,7 @@ class MANGOS_DLL_SPEC Object virtual bool HasQuest(uint32 /* quest_id */) const { return false; } virtual bool HasInvolvedQuest(uint32 /* quest_id */) const { return false; } + protected: Object ( ); @@ -586,6 +588,9 @@ class MANGOS_DLL_SPEC WorldObject : public Object // ASSERT print helper bool PrintCoordinatesError(float x, float y, float z, char const* descr) const; + + virtual void StartGroupLoot(Group* group, uint32 timer) {} + protected: explicit WorldObject(); @@ -595,6 +600,8 @@ class MANGOS_DLL_SPEC WorldObject : public Object void SetLocationMapId(uint32 _mapId) { m_mapId = _mapId; } void SetLocationInstanceId(uint32 _instanceId) { m_InstanceId = _instanceId; } + virtual void StopGroupLoot() {} + std::string m_name; private: diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 64e049cd6..bffcaadab 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -7976,7 +7976,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) if (ObjectGuid lootGuid = GetLootGuid()) m_session->DoLootRelease(lootGuid); - Loot *loot = 0; + Loot* loot = NULL; PermissionTypes permission = ALL_PERMISSION; DEBUG_LOG("Player::SendLoot"); @@ -7997,6 +7997,13 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) loot = &go->loot; + Player* recipient = go->GetLootRecipient(); + if (!recipient) + { + go->SetLootRecipient(this); + recipient = this; + } + // generate loot only if ready for open and spawned in world if (go->getLootState() == GO_READY && go->isSpawned()) { @@ -8020,12 +8027,65 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) loot->clear(); loot->FillLoot(lootid, LootTemplates_Gameobject, this, false); loot->generateMoneyLoot(go->GetGOInfo()->MinMoneyLoot, go->GetGOInfo()->MaxMoneyLoot); + + if (go->GetGoType() == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules) + { + if (Group* group = go->GetGroupLootRecipient()) + { + group->UpdateLooterGuid(go, true); + + switch (group->GetLootMethod()) + { + case GROUP_LOOT: + // GroupLoot delete items over threshold (threshold even not implemented), and roll them. Items with qualityGroupLoot(go, loot); + permission = GROUP_PERMISSION; + break; + case NEED_BEFORE_GREED: + group->NeedBeforeGreed(go, loot); + permission = GROUP_PERMISSION; + break; + case MASTER_LOOT: + group->MasterLoot(go, loot); + permission = MASTER_PERMISSION; + break; + default: + break; + } + } + } } else if (loot_type == LOOT_FISHING) go->getFishLoot(loot,this); go->SetLootState(GO_ACTIVATED); } + if (go->getLootState() == GO_ACTIVATED && go->GetGoType() == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules) + { + if (Group* group = go->GetGroupLootRecipient()) + { + if (group == GetGroup()) + { + if (group->GetLootMethod() == FREE_FOR_ALL) + permission = ALL_PERMISSION; + else if (group->GetLooterGuid() == GetObjectGuid()) + { + if (group->GetLootMethod() == MASTER_LOOT) + permission = MASTER_PERMISSION; + else + permission = ALL_PERMISSION; + } + else + permission = GROUP_PERMISSION; + } + else + permission = NONE_PERMISSION; + } + else if (recipient == this) + permission = ALL_PERMISSION; + else + permission = NONE_PERMISSION; + } break; } case HIGHGUID_ITEM: diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 5f9741d1c..a8f3feabf 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "11877" + #define REVISION_NR "11878" #endif // __REVISION_NR_H__