diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Entities/GameObject/GameObject.cpp | 67 | ||||
-rw-r--r-- | src/server/game/Entities/GameObject/GameObject.h | 7 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 178 | ||||
-rw-r--r-- | src/server/game/Handlers/LootHandler.cpp | 54 | ||||
-rw-r--r-- | src/server/game/Handlers/SpellHandler.cpp | 23 | ||||
-rw-r--r-- | src/server/game/Loot/Loot.cpp | 6 | ||||
-rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 82 |
7 files changed, 186 insertions, 231 deletions
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 5e506dd8be0..ed518b5cf7a 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -956,6 +956,8 @@ void GameObject::Update(uint32 diff) // If there is no restock timer, or if the restock timer passed, the chest becomes ready to loot m_restockTime = 0; m_lootState = GO_READY; + m_loot = nullptr; + m_personalLoot.clear(); AddToObjectUpdateIfNeeded(); break; default: @@ -1176,11 +1178,16 @@ void GameObject::Update(uint32 diff) if (m_loot) m_loot->Update(); + for (auto&& [playerOwner, loot] : m_personalLoot) + loot->Update(); + // Non-consumable chest was partially looted and restock time passed, restock all loot now - if (GetGOInfo()->chest.consumable == 0 && GameTime::GetGameTime() >= m_restockTime) + if (GetGOInfo()->chest.consumable == 0 && GetGOInfo()->chest.chestRestockTime && GameTime::GetGameTime() >= m_restockTime) { m_restockTime = 0; m_lootState = GO_READY; + m_loot = nullptr; + m_personalLoot.clear(); AddToObjectUpdateIfNeeded(); } break; @@ -1255,6 +1262,7 @@ void GameObject::Update(uint32 diff) } m_loot = nullptr; + m_personalLoot.clear(); // Do not delete chests or goobers that are not consumed on loot, while still allowing them to despawn when they expire if summoned bool isSummonedAndExpired = (GetOwner() || GetSpellId()) && m_respawnTime == 0; @@ -1405,44 +1413,48 @@ void GameObject::SendGameObjectDespawn() SendMessageToSet(packet.Write(), true); } -void GameObject::getFishLoot(Loot* fishloot, Player* loot_owner) +Loot* GameObject::GetFishLoot(Player* lootOwner) { - fishloot->clear(); - uint32 zone, subzone; uint32 defaultzone = 1; GetZoneAndAreaId(zone, subzone); + Loot* fishLoot = new Loot(GetMap(), GetGUID(), LOOT_FISHING, nullptr); + // if subzone loot exist use it - fishloot->FillLoot(subzone, LootTemplates_Fishing, loot_owner, true, true); - if (fishloot->empty()) //use this becase if zone or subzone has set LOOT_MODE_JUNK_FISH,Even if no normal drop, fishloot->FillLoot return true. it wrong. + fishLoot->FillLoot(subzone, LootTemplates_Fishing, lootOwner, true, true); + if (fishLoot->empty()) //use this becase if zone or subzone has set LOOT_MODE_JUNK_FISH,Even if no normal drop, fishloot->FillLoot return true. it wrong. { //subzone no result,use zone loot - fishloot->FillLoot(zone, LootTemplates_Fishing, loot_owner, true, true); + fishLoot->FillLoot(zone, LootTemplates_Fishing, lootOwner, true, true); //use zone 1 as default, somewhere fishing got nothing,becase subzone and zone not set, like Off the coast of Storm Peaks. - if (fishloot->empty()) - fishloot->FillLoot(defaultzone, LootTemplates_Fishing, loot_owner, true, true); + if (fishLoot->empty()) + fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true); } + + return fishLoot; } -void GameObject::getFishLootJunk(Loot* fishloot, Player* loot_owner) +Loot* GameObject::GetFishLootJunk(Player* lootOwner) { - fishloot->clear(); - uint32 zone, subzone; uint32 defaultzone = 1; GetZoneAndAreaId(zone, subzone); + Loot* fishLoot = new Loot(GetMap(), GetGUID(), LOOT_FISHING_JUNK, nullptr); + // if subzone loot exist use it - fishloot->FillLoot(subzone, LootTemplates_Fishing, loot_owner, true, true, LOOT_MODE_JUNK_FISH); - if (fishloot->empty()) //use this becase if zone or subzone has normal mask drop, then fishloot->FillLoot return true. + fishLoot->FillLoot(subzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH); + if (fishLoot->empty()) //use this becase if zone or subzone has normal mask drop, then fishloot->FillLoot return true. { //use zone loot - fishloot->FillLoot(zone, LootTemplates_Fishing, loot_owner, true, true, LOOT_MODE_JUNK_FISH); - if (fishloot->empty()) + fishLoot->FillLoot(zone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH); + if (fishLoot->empty()) //use zone 1 as default - fishloot->FillLoot(defaultzone, LootTemplates_Fishing, loot_owner, true, true, LOOT_MODE_JUNK_FISH); + fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH); } + + return fishLoot; } void GameObject::SaveToDB() @@ -2402,10 +2414,16 @@ void GameObject::Use(Unit* user) SetLootState(GO_JUST_DEACTIVATED); } else + { + m_loot.reset(GetFishLoot(player)); player->SendLoot(GetGUID(), LOOT_FISHING); + } } else // If fishing skill is too low, send junk loot. + { + m_loot.reset(GetFishLootJunk(player)); player->SendLoot(GetGUID(), LOOT_FISHING_JUNK); + } break; } case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update @@ -2617,6 +2635,10 @@ void GameObject::Use(Unit* user) Player* player = user->ToPlayer(); + Loot* loot = new Loot(GetMap(), GetGUID(), LOOT_FISHINGHOLE, nullptr); + loot->FillLoot(GetGOInfo()->GetLootId(), LootTemplates_Gameobject, player, true); + m_personalLoot[player->GetGUID()].reset(loot); + player->SendLoot(GetGUID(), LOOT_FISHINGHOLE); player->UpdateCriteria(CriteriaType::CatchFishInFishingHole, GetGOInfo()->entry); return; @@ -3224,6 +3246,17 @@ bool GameObject::IsLootAllowedFor(Player const* player) const return true; } +Loot* GameObject::GetLootForPlayer(Player const* player) const +{ + if (m_personalLoot.empty()) + return m_loot.get(); + + if (std::unique_ptr<Loot> const* loot = Trinity::Containers::MapGetValuePtr(m_personalLoot, player->GetGUID())) + return loot->get(); + + return nullptr; +} + GameObject* GameObject::GetLinkedTrap() { return ObjectAccessor::GetGameObject(*this, m_linkedTrap); diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index fba6febb163..886b28b4cdb 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -225,8 +225,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void DespawnOrUnsummon(Milliseconds delay = 0ms, Seconds forceRespawnTime = 0s); void Delete(); void SendGameObjectDespawn(); - void getFishLoot(Loot* loot, Player* loot_owner); - void getFishLootJunk(Loot* loot, Player* loot_owner); + Loot* GetFishLoot(Player* lootOwner); + Loot* GetFishLootJunk(Player* lootOwner); bool HasFlag(GameObjectFlags flags) const { return (*m_gameObjectData->Flags & flags) != 0; } void SetFlag(GameObjectFlags flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::Flags), flags); } @@ -280,13 +280,14 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void SaveRespawnTime(uint32 forceDelay = 0); std::unique_ptr<Loot> m_loot; + std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> m_personalLoot; Player* GetLootRecipient() const; Group* GetLootRecipientGroup() const; void SetLootRecipient(Unit* unit, Group* group = nullptr); bool IsLootAllowedFor(Player const* player) const; bool HasLootRecipient() const { return !m_lootRecipient.IsEmpty() || !m_lootRecipientGroup.IsEmpty(); } - Loot* GetLootForPlayer(Player const* /*player*/) const override { return m_loot.get(); } + Loot* GetLootForPlayer(Player const* /*player*/) const override; GameObject* GetLinkedTrap(); void SetLinkedTrap(GameObject* linkedTrap) { m_linkedTrap = linkedTrap->GetGUID(); } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b56b414981d..e149b1a0bc3 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8800,89 +8800,14 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa if (guid.IsGameObject()) { GameObject* go = GetMap()->GetGameObject(guid); - auto shouldLootRelease = [this](GameObject* go, LootType lootType) -> bool - { - // not check distance for GO in case owned GO (fishing bobber case, for example) - // And permit out of range GO with no owner in case fishing hole - if (!go) - return true; - - switch (lootType) - { - case LOOT_FISHING: - case LOOT_FISHING_JUNK: - if (go->GetOwnerGUID() != GetGUID()) - return true; - break; - case LOOT_FISHINGHOLE: - break; - default: - if (!go->IsWithinDistInMap(this)) - return true; - break; - } - return false; - }; - - if (shouldLootRelease(go, loot_type)) + if (!go) { SendLootRelease(guid); return; } loot = go->GetLootForPlayer(this); - - // loot was generated and respawntime has passed since then, allow to recreate loot - // to avoid bugs, this rule covers spawned gameobjects only - // Don't allow to regenerate chest loot inside instances and raids, to avoid exploits with duplicate boss loot being given for some encounters - if (go->isSpawnedByDefault() && go->getLootState() == GO_ACTIVATED && (!loot || !loot->isLooted()) && !go->GetMap()->Instanceable() && go->GetLootGenerationTime() + go->GetRespawnDelay() < GameTime::GetGameTime()) - go->SetLootState(GO_READY); - - if (go->getLootState() == GO_READY) - { - uint32 lootid = go->GetGOInfo()->GetLootId(); - if (Battleground* bg = GetBattleground()) - if (!bg->CanActivateGO(go->GetEntry(), bg->GetPlayerTeam(GetGUID()))) - { - SendLootRelease(guid); - return; - } - - Group* group = GetGroup(); - bool groupRules = (group && go->GetGOInfo()->type == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.usegrouplootrules); - - loot = new Loot(GetMap(), guid, loot_type, groupRules ? group : nullptr); - if (go->GetMap()->Is25ManRaid()) - loot->maxDuplicates = 3; - - go->m_loot.reset(loot); - - if (lootid) - { - // 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(), GetMap()->GetDifficultyLootItemContext()); - go->SetLootGenerationTime(); - - // get next RR player (for next loot) - if (groupRules && !loot->empty()) - group->UpdateLooterGuid(go); - } - - if (go->GetLootMode() > 0) - if (GameObjectTemplateAddon const* addon = go->GetTemplateAddon()) - loot->generateMoneyLoot(addon->Mingold, addon->Maxgold); - - if (loot_type == LOOT_FISHING) - go->getFishLoot(loot, this); - else if (loot_type == LOOT_FISHING_JUNK) - go->getFishLootJunk(loot, this); - - go->SetLootState(GO_ACTIVATED, this); - } } else if (guid.IsItem()) { @@ -8895,44 +8820,12 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa } loot = item->GetLootForPlayer(this); - - // If item doesn't already have loot, attempt to load it. If that - // fails then this is first time opening, generate loot - if (!item->m_lootGenerated && !sLootItemStorage->LoadStoredLoot(item, this)) - { - item->m_lootGenerated = true; - loot = new Loot(GetMap(), guid, loot_type, nullptr); - item->m_loot.reset(loot); - - switch (loot_type) - { - case LOOT_DISENCHANTING: - loot->FillLoot(ASSERT_NOTNULL(item->GetDisenchantLoot(this))->ID, LootTemplates_Disenchant, this, true); - break; - case LOOT_PROSPECTING: - loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this, true); - break; - case LOOT_MILLING: - loot->FillLoot(item->GetEntry(), LootTemplates_Milling, this, true); - break; - default: - loot->generateMoneyLoot(item->GetTemplate()->MinMoneyLoot, item->GetTemplate()->MaxMoneyLoot); - loot->FillLoot(item->GetEntry(), LootTemplates_Item, this, true, loot->gold != 0); - - // Force save the loot and money items that were just rolled - // Also saves the container item ID in Loot struct (not to DB) - if (loot->gold > 0 || loot->unlootedCount > 0) - sLootItemStorage->AddNewStoredLoot(item->GetGUID().GetCounter(), loot, this); - - break; - } - } } else if (guid.IsCorpse()) // remove insignia { Corpse* bones = ObjectAccessor::GetCorpse(*this, guid); - if (!bones || !(loot_type == LOOT_CORPSE || loot_type == LOOT_INSIGNIA) || bones->GetType() != CORPSE_BONES) + if (!bones) { SendLootRelease(guid); return; @@ -8944,78 +8837,13 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa { Creature* creature = GetMap()->GetCreature(guid); - // must be in range and creature must be alive for pickpocket and must be dead for another loot - if (!creature || creature->IsAlive() != (loot_type == LOOT_PICKPOCKETING) || (!aeLooting && !creature->IsWithinDistInMap(this, INTERACTION_DISTANCE))) - { - SendLootRelease(guid); - return; - } - - if (loot_type == LOOT_PICKPOCKETING && IsFriendlyTo(creature)) + if (!creature) { SendLootRelease(guid); return; } loot = creature->GetLootForPlayer(this); - - if (loot_type == LOOT_PICKPOCKETING) - { - if (!loot || loot->loot_type != LOOT_PICKPOCKETING) - { - if (creature->CanGeneratePickPocketLoot()) - { - creature->StartPickPocketRefillTimer(); - - loot = new Loot(GetMap(), creature->GetGUID(), LOOT_PICKPOCKETING, nullptr); - creature->m_loot.reset(loot); - if (uint32 lootid = creature->GetCreatureTemplate()->pickpocketLootId) - loot->FillLoot(lootid, LootTemplates_Pickpocketing, this, true); - - // Generate extra money for pick pocket loot - const uint32 a = urand(0, creature->GetLevel() / 2); - const uint32 b = urand(0, GetLevel() / 2); - loot->gold = uint32(10 * (a + b) * sWorld->getRate(RATE_DROP_MONEY)); - } - else - { - SendLootError(loot->GetGUID(), guid, LOOT_ERROR_ALREADY_PICKPOCKETED); - return; - } - } // else - still has pickpocket loot generated & not fully taken - } - else - { - // exploit fix - if (!creature->HasDynamicFlag(UNIT_DYNFLAG_LOOTABLE)) - { - SendLootError(loot->GetGUID(), guid, LOOT_ERROR_DIDNT_KILL); - return; - } - - // the player whose group may loot the corpse - Player* recipient = creature->GetLootRecipient(); - Group* recipientGroup = creature->GetLootRecipientGroup(); - if (!recipient && !recipientGroup) - { - SendLootError(loot->GetGUID(), guid, LOOT_ERROR_DIDNT_KILL); - return; - } - - // if loot is already skinning loot then don't do anything else - if (loot->loot_type == LOOT_SKINNING) - { - loot_type = LOOT_SKINNING; - } - else if (loot_type == LOOT_SKINNING) - { - loot->clear(); - loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, this, true); - - // Set new loot recipient - creature->SetLootRecipient(this, false); - } - } } if (!guid.IsItem() && !aeLooting) diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index a367e18da8e..3d32e72e66e 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -44,12 +44,14 @@ public: AELootCreatureCheck(Player* looter, ObjectGuid mainLootTarget) : _looter(looter), _mainLootTarget(mainLootTarget) { } - bool operator()(Creature* creature) const + bool operator()(Creature const* creature) const { - if (creature->IsAlive()) - return false; + return IsValidAELootTarget(creature); + } - if (creature->GetGUID() == _mainLootTarget) + bool IsValidLootTarget(Creature const* creature) const + { + if (creature->IsAlive()) return false; if (!_looter->IsWithinDist(creature, LootDistance)) @@ -58,6 +60,15 @@ public: return _looter->isAllowedToLoot(creature); } + bool IsValidAELootTarget(Creature const* creature) const + { + if (creature->GetGUID() == _mainLootTarget) + return false; + + return IsValidLootTarget(creature); + } + +private: Player* _looter; ObjectGuid _mainLootTarget; }; @@ -201,14 +212,21 @@ void WorldSession::HandleLootOpcode(WorldPackets::Loot::LootUnit& packet) if (!GetPlayer()->IsAlive() || !packet.Unit.IsCreatureOrVehicle()) return; + Creature* lootTarget = ObjectAccessor::GetCreature(*GetPlayer(), packet.Unit); + if (!lootTarget) + return; + + AELootCreatureCheck check(_player, packet.Unit); + if (!check.IsValidLootTarget(lootTarget)) + return; + // interrupt cast if (GetPlayer()->IsNonMeleeSpellCast(false)) GetPlayer()->InterruptNonMeleeSpells(false); GetPlayer()->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Looting); - std::list<Creature*> corpses; - AELootCreatureCheck check(_player, packet.Unit); + std::vector<Creature*> corpses; Trinity::CreatureListSearcher<AELootCreatureCheck> searcher(_player, corpses, check); Cell::VisitGridObjects(_player, searcher, AELootCreatureCheck::LootDistance); @@ -246,6 +264,8 @@ void WorldSession::DoLootRelease(Loot* loot) if (player->GetLootGUID() == lguid) player->SetLootGUID(ObjectGuid::Empty); + //Player is not looking at loot list, he doesn't need to see updates on the loot list + loot->RemoveLooter(player->GetGUID()); player->SendLootRelease(lguid); player->m_AELootView.erase(loot->GetGUID()); @@ -263,12 +283,7 @@ void WorldSession::DoLootRelease(Loot* loot) if (!go || ((go->GetOwnerGUID() != player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(player))) return; - if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR) - { - // locked doors are opened with spelleffect openlock, prevent remove its as looted - go->UseDoorOrButton(); - } - else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE) + if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE) { if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE) { // The fishing hole used once more @@ -287,10 +302,6 @@ void WorldSession::DoLootRelease(Loot* loot) { // not fully looted object go->SetLootState(GO_ACTIVATED, player); - - // if the round robin player release, reset it. - if (player->GetGUID() == loot->roundRobinPlayer) - loot->roundRobinPlayer.Clear(); } } else if (lguid.IsCorpse()) // ONLY remove insignia at BG @@ -317,7 +328,7 @@ void WorldSession::DoLootRelease(Loot* loot) if (loot->loot_type == LOOT_PROSPECTING || loot->loot_type == LOOT_MILLING) { pItem->m_lootGenerated = false; - pItem->m_loot.reset(); + pItem->m_loot = nullptr; uint32 count = pItem->GetCount(); @@ -341,12 +352,6 @@ void WorldSession::DoLootRelease(Loot* loot) if (!creature) return; - if (!creature->IsWithinDistInMap(player, AELootCreatureCheck::LootDistance)) - return; - - if (creature->IsAlive() != (loot && loot->loot_type == LOOT_PICKPOCKETING)) - return; - if (loot->isLooted()) { creature->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); @@ -367,9 +372,6 @@ void WorldSession::DoLootRelease(Loot* loot) creature->ForceUpdateFieldChange(creature->m_values.ModifyValue(&Object::m_objectData).ModifyValue(&UF::ObjectData::DynamicFlags)); } } - - //Player is not looking at loot list, he doesn't need to see updates on the loot list - loot->RemoveLooter(player->GetGUID()); } void WorldSession::DoLootReleaseAll() diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index f52c02a32f9..4b1559fc2a4 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -27,6 +27,8 @@ #include "Item.h" #include "Log.h" #include "Loot.h" +#include "LootItemStorage.h" +#include "LootMgr.h" #include "Map.h" #include "Player.h" #include "ObjectAccessor.h" @@ -199,10 +201,29 @@ void WorldSession::HandleOpenItemOpcode(WorldPackets::Spells::OpenItem& packet) CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM); stmt->setUInt64(0, item->GetGUID().GetCounter()); _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt) - .WithPreparedCallback(std::bind(&WorldSession::HandleOpenWrappedItemCallback, this, item->GetPos(), item->GetGUID(), std::placeholders::_1))); + .WithPreparedCallback([this, pos = item->GetPos(), itemGuid = item->GetGUID()](PreparedQueryResult result) + { + HandleOpenWrappedItemCallback(pos, itemGuid, std::move(result)); + })); } else + { + // If item doesn't already have loot, attempt to load it. If that + // fails then this is first time opening, generate loot + if (!item->m_lootGenerated && !sLootItemStorage->LoadStoredLoot(item, player)) + { + Loot* loot = new Loot(player->GetMap(), item->GetGUID(), LOOT_ITEM, nullptr); + item->m_loot.reset(loot); + loot->generateMoneyLoot(item->GetTemplate()->MinMoneyLoot, item->GetTemplate()->MaxMoneyLoot); + loot->FillLoot(item->GetEntry(), LootTemplates_Item, player, true, loot->gold != 0); + + // Force save the loot and money items that were just rolled + // Also saves the container item ID in Loot struct (not to DB) + if (loot->gold > 0 || loot->unlootedCount > 0) + sLootItemStorage->AddNewStoredLoot(item->GetGUID().GetCounter(), loot, player); + } player->SendLoot(item->GetGUID(), LOOT_ITEM); + } } void WorldSession::HandleOpenWrappedItemCallback(uint16 pos, ObjectGuid itemGuid, PreparedQueryResult result) diff --git a/src/server/game/Loot/Loot.cpp b/src/server/game/Loot/Loot.cpp index 3c5e976245e..62d86b7ce0c 100644 --- a/src/server/game/Loot/Loot.cpp +++ b/src/server/game/Loot/Loot.cpp @@ -662,7 +662,10 @@ void Loot::NotifyItemRemoved(uint8 lootListId, Map const* map) { LootItem const& item = items[lootListId]; if (item.GetAllowedLooters().find(*itr) == item.GetAllowedLooters().end()) + { + ++itr; continue; + } if (Player* player = ObjectAccessor::GetPlayer(map, *itr)) { @@ -769,7 +772,8 @@ bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bo Group const* group = lootOwner->GetGroup(); if (!personal && group) { - roundRobinPlayer = lootOwner->GetGUID(); + if (loot_type == LOOT_CORPSE) + roundRobinPlayer = lootOwner->GetGUID(); for (GroupReference const* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) if (Player const* player = itr->GetSource()) // should actually be looted object instead of lootOwner but looter has to be really close so doesnt really matter diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 61a0e645188..af66302df7f 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1547,16 +1547,47 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype) return; case GAMEOBJECT_TYPE_CHEST: - /// @todo possible must be moved to loot release (in different from linked triggering) - if (gameObjTarget->GetGOInfo()->chest.triggeredEvent) + if (Battleground* bg = player->GetBattleground()) { - TC_LOG_DEBUG("spells", "Chest ScriptStart id %u for GO " UI64FMTD, gameObjTarget->GetGOInfo()->chest.triggeredEvent, gameObjTarget->GetSpawnId()); - GameEvents::Trigger(gameObjTarget->GetGOInfo()->chest.triggeredEvent, player, gameObjTarget); + if (!bg->CanActivateGO(gameObjTarget->GetEntry(), bg->GetPlayerTeam(player->GetGUID()))) + { + player->SendLootRelease(guid); + return; + } } - // triggering linked GO - if (uint32 trapEntry = gameObjTarget->GetGOInfo()->chest.linkedTrap) - gameObjTarget->TriggeringLinkedGameObject(trapEntry, player); + if (gameObjTarget->getLootState() == GO_READY) + { + if (uint32 lootId = gameObjTarget->GetGOInfo()->GetLootId()) + { + gameObjTarget->SetLootGenerationTime(); + + Group const* group = player->GetGroup(); + bool groupRules = group && gameObjTarget->GetGOInfo()->chest.usegrouplootrules; + + Loot* loot = new Loot(gameObjTarget->GetMap(), guid, loottype, groupRules ? group : nullptr); + gameObjTarget->m_loot.reset(loot); + + loot->FillLoot(lootId, LootTemplates_Gameobject, player, !groupRules, false, gameObjTarget->GetLootMode(), gameObjTarget->GetMap()->GetDifficultyLootItemContext()); + + if (gameObjTarget->GetLootMode() > 0) + if (GameObjectTemplateAddon const* addon = gameObjTarget->GetTemplateAddon()) + loot->generateMoneyLoot(addon->Mingold, addon->Maxgold); + } + + /// @todo possible must be moved to loot release (in different from linked triggering) + if (gameObjTarget->GetGOInfo()->chest.triggeredEvent) + { + TC_LOG_DEBUG("spells", "Chest ScriptStart id %u for GO " UI64FMTD, gameObjTarget->GetGOInfo()->chest.triggeredEvent, gameObjTarget->GetSpawnId()); + GameEvents::Trigger(gameObjTarget->GetGOInfo()->chest.triggeredEvent, player, gameObjTarget); + } + + // triggering linked GO + if (uint32 trapEntry = gameObjTarget->GetGOInfo()->chest.linkedTrap) + gameObjTarget->TriggeringLinkedGameObject(trapEntry, player); + + gameObjTarget->SetLootState(GO_ACTIVATED, player); + } // Don't return, let loots been taken break; @@ -2221,10 +2252,36 @@ void Spell::EffectPickPocket() if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; + Player* player = m_caster->ToPlayer(); if (m_caster->GetTypeId() != TYPEID_PLAYER) return; - m_caster->ToPlayer()->SendLoot(unitTarget->GetGUID(), LOOT_PICKPOCKETING); + Creature* creature = Object::ToCreature(unitTarget); + if (!creature) + return; + + if (creature->CanGeneratePickPocketLoot()) + { + creature->StartPickPocketRefillTimer(); + + creature->m_loot.reset(new Loot(creature->GetMap(), creature->GetGUID(), LOOT_PICKPOCKETING, nullptr)); + if (uint32 lootid = creature->GetCreatureTemplate()->pickpocketLootId) + creature->m_loot->FillLoot(lootid, LootTemplates_Pickpocketing, player, true); + + // Generate extra money for pick pocket loot + const uint32 a = urand(0, creature->GetLevel() / 2); + const uint32 b = urand(0, player->GetLevel() / 2); + creature->m_loot->gold = uint32(10 * (a + b) * sWorld->getRate(RATE_DROP_MONEY)); + } + else if (creature->m_loot) + { + if (creature->m_loot->loot_type == LOOT_PICKPOCKETING && creature->m_loot->isLooted()) + player->SendLootError(creature->m_loot->GetGUID(), creature->GetGUID(), LOOT_ERROR_ALREADY_PICKPOCKETED); + + return; + } + + player->SendLoot(unitTarget->GetGUID(), LOOT_PICKPOCKETING); } void Spell::EffectAddFarsight() @@ -3422,6 +3479,8 @@ void Spell::EffectDisEnchant() if (Player* caster = m_caster->ToPlayer()) { caster->UpdateCraftSkill(m_spellInfo); + itemTarget->m_loot.reset(new Loot(caster->GetMap(), itemTarget->GetGUID(), LOOT_DISENCHANTING, nullptr)); + itemTarget->m_loot->FillLoot(ASSERT_NOTNULL(itemTarget->GetDisenchantLoot(caster))->ID, LootTemplates_Disenchant, caster, true); caster->SendLoot(itemTarget->GetGUID(), LOOT_DISENCHANTING); } @@ -3781,6 +3840,9 @@ void Spell::EffectSkinning() creature->RemoveUnitFlag(UNIT_FLAG_SKINNABLE); creature->SetDynamicFlag(UNIT_DYNFLAG_LOOTABLE); + creature->m_loot.reset(new Loot(creature->GetMap(), creature->GetGUID(), LOOT_SKINNING, nullptr)); + creature->m_loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, player, true); + creature->SetLootRecipient(player, false); player->SendLoot(creature->GetGUID(), LOOT_SKINNING); if (skill == SKILL_SKINNING) @@ -4480,6 +4542,8 @@ void Spell::EffectProspecting() player->UpdateGatherSkill(SKILL_JEWELCRAFTING, SkillValue, reqSkillValue); } + itemTarget->m_loot.reset(new Loot(player->GetMap(), itemTarget->GetGUID(), LOOT_PROSPECTING, nullptr)); + itemTarget->m_loot->FillLoot(itemTarget->GetEntry(), LootTemplates_Prospecting, player, true); player->SendLoot(itemTarget->GetGUID(), LOOT_PROSPECTING); } @@ -4505,6 +4569,8 @@ void Spell::EffectMilling() player->UpdateGatherSkill(SKILL_INSCRIPTION, SkillValue, reqSkillValue); } + itemTarget->m_loot.reset(new Loot(player->GetMap(), itemTarget->GetGUID(), LOOT_MILLING, nullptr)); + itemTarget->m_loot->FillLoot(itemTarget->GetEntry(), LootTemplates_Milling, player, true); player->SendLoot(itemTarget->GetGUID(), LOOT_MILLING); } |