From df9738aaa935f21f97381d8c0920058b8e81d237 Mon Sep 17 00:00:00 2001 From: xinef1 Date: Mon, 13 Feb 2017 20:42:06 +0100 Subject: [PATCH] Core/Misc: Fixed player corpse looting, added player corpse loot and some more (#19122) * Fixed corpse looting in wintergrasp Added corpse loot for wintergrasp quests and To the Looter Go the Spoils (1166) achievement Don't generate money for loot if no loot mode is available Simplified few things (cherry picked from commit 2412886ef69d305df1bd6e6422ca3134c0ab1449) --- ...022_01_29_01_world_2017_02_13_00_world.sql | 26 +++++++++ .../game/Battlefield/Zones/BattlefieldWG.cpp | 5 ++ src/server/game/Entities/Corpse/Corpse.cpp | 1 - src/server/game/Entities/Corpse/Corpse.h | 1 - .../game/Entities/Creature/Creature.cpp | 13 +++-- src/server/game/Entities/Creature/Creature.h | 6 +-- src/server/game/Entities/Player/Player.cpp | 54 ++++++++++++++----- src/server/game/Entities/Unit/Unit.cpp | 5 +- src/server/game/Loot/Loot.cpp | 2 +- src/server/game/Loot/LootMgr.cpp | 3 ++ src/server/game/Miscellaneous/SharedDefines.h | 2 + src/server/game/Spells/Spell.cpp | 25 +++++++++ src/server/game/Spells/SpellEffects.cpp | 2 +- 13 files changed, 119 insertions(+), 26 deletions(-) create mode 100644 sql/updates/world/4.3.4/2022_01_29_01_world_2017_02_13_00_world.sql diff --git a/sql/updates/world/4.3.4/2022_01_29_01_world_2017_02_13_00_world.sql b/sql/updates/world/4.3.4/2022_01_29_01_world_2017_02_13_00_world.sql new file mode 100644 index 0000000000..24eec51e4a --- /dev/null +++ b/sql/updates/world/4.3.4/2022_01_29_01_world_2017_02_13_00_world.sql @@ -0,0 +1,26 @@ + +-- To the Looter Go the Spoils (1166) +DELETE FROM `creature_loot_template` WHERE `entry`=1 AND `item`=18228; +INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `IsCurrency`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(1, 18228, 0, 1, 0, 0, 1, 0, 1, 1, ''); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=1 AND `SourceGroup`=1 AND `SourceEntry` IN(18228); +INSERT INTO `conditions` VALUES (1, 1, 18228, 0, 0, 22, 0, 30, 0, 0, 0, 0, 0, '', 'Requires map Alterac Valley'); + +-- Add Player Loot for WG quests +DELETE FROM `creature_loot_template` WHERE `entry`=1 AND `item` IN(43314, 43322, 43323, 43324, 44808, 44809); +INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `IsCurrency`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(1, 43314, 0, 100, 1, 0, 1, 0, 5, 5, ''), +(1, 43322, 0, 100, 1, 0, 1, 0, 5, 5, ''), +(1, 43323, 0, 100, 1, 0, 1, 0, 5, 5, ''), +(1, 43324, 0, 100, 1, 0, 1, 0, 5, 5, ''), +(1, 44808, 0, 100, 1, 0, 1, 0, 5, 5, ''), +(1, 44809, 0, 100, 1, 0, 1, 0, 5, 5, ''); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=1 AND `SourceGroup`=1 AND `SourceEntry` IN(43314, 43322, 43323, 43324, 44808, 44809); +INSERT INTO `conditions` VALUES (1, 1, 44808, 0, 0, 23, 0, 4585, 0, 0, 0, 0, 0, '', 'Requires Glacial Falls area'), +(1, 1, 43322, 0, 0, 23, 0, 4585, 0, 0, 0, 0, 0, '', 'Requires Glacial Falls area'), +(1, 1, 43314, 0, 0, 23, 0, 4584, 0, 0, 0, 0, 0, '', 'Requires Cauldron of Flames area'), +(1, 1, 43323, 0, 0, 23, 0, 4587, 0, 0, 0, 0, 0, '', 'Requires Forest of Shadows area'), +(1, 1, 43324, 0, 0, 23, 0, 4590, 0, 0, 0, 0, 0, '', 'Requires Steppe of Life area'), +(1, 1, 44809, 0, 0, 23, 0, 4590, 0, 0, 0, 0, 0, '', 'Requires Steppe of Life area'); diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp index eb78a33cc2..cbfd1ab9c6 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp @@ -952,8 +952,13 @@ void BattlefieldWG::HandleKill(Player* killer, Unit* victim) return; if (victim->GetTypeId() == TYPEID_PLAYER) + { HandlePromotion(killer, victim); + // Allow to Skin non-released corpse + victim->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); + } + /// @todoRecent PvP activity worldstate } diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index d09815ef4c..5ccbd0aa07 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -40,7 +40,6 @@ Corpse::Corpse(CorpseType type) : WorldObject(type != CORPSE_BONES), m_type(type m_time = GameTime::GetGameTime(); - lootForBody = false; lootRecipient = nullptr; } diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h index faabce06c2..87d073199a 100644 --- a/src/server/game/Entities/Corpse/Corpse.h +++ b/src/server/game/Entities/Corpse/Corpse.h @@ -74,7 +74,6 @@ class TC_GAME_API Corpse : public WorldObject, public GridObject Loot loot; // remove insignia ONLY at BG Player* lootRecipient; - bool lootForBody; bool IsExpired(time_t t) const; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index d63ee3a8c1..777dd34c7d 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -267,7 +267,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), -m_lootRecipient(), m_lootRecipientGroup(0), _skinner(), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), +m_lootRecipient(), m_lootRecipientGroup(0), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), @@ -1222,7 +1222,7 @@ Group* Creature::GetLootRecipientGroup() const return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup); } -void Creature::SetLootRecipient(Unit* unit) +void Creature::SetLootRecipient(Unit* unit, bool withGroup) { // set the player whose group should receive the right // to loot the creature after it dies @@ -1244,8 +1244,13 @@ void Creature::SetLootRecipient(Unit* unit) return; m_lootRecipient = player->GetGUID(); - if (Group* group = player->GetGroup()) - m_lootRecipientGroup = group->GetLowGUID(); + if (withGroup) + { + if (Group* group = player->GetGroup()) + m_lootRecipientGroup = group->GetLowGUID(); + } + else + m_lootRecipientGroup = ObjectGuid::Empty; SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED); } diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 9021c09c0e..0acd4959d6 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -200,14 +200,13 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void StartPickPocketRefillTimer(); void ResetPickPocketRefillTimer() { _pickpocketLootRestore = 0; } bool CanGeneratePickPocketLoot() const; - void SetSkinner(ObjectGuid guid) { _skinner = guid; } - ObjectGuid GetSkinner() const { return _skinner; } // Returns the player who skinned this creature + ObjectGuid GetLootRecipientGUID() const { return m_lootRecipient; } Player* GetLootRecipient() const; Group* GetLootRecipientGroup() const; bool hasLootRecipient() const { return !m_lootRecipient.IsEmpty() || m_lootRecipientGroup; } bool isTappedBy(Player const* player) const; // return true if the creature is tapped by the player or a member of his party. - void SetLootRecipient (Unit* unit); + void SetLootRecipient(Unit* unit, bool withGroup = true); void AllLootRemovedFromCorpse(); uint16 GetLootMode() const { return m_LootMode; } @@ -392,7 +391,6 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma ObjectGuid m_lootRecipient; uint32 m_lootRecipientGroup; - ObjectGuid _skinner; /// Timers time_t _pickpocketLootRestore; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 56c31d993a..2329ce16ac 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8243,7 +8243,8 @@ void Player::_ApplyAllLevelScaleItemMods(bool apply) Called by remove insignia spell effect */ void Player::RemovedInsignia(Player* looterPlr) { - if (!GetBattlegroundId()) + // If player is not in battleground and not in wintergrasp + if (!GetBattlegroundId() && GetZoneId() != AREA_WINTERGRASP && GetZoneId() != BATTLEFIELD_TB_ZONEID) return; // If not released spirit, do it ! @@ -8360,8 +8361,9 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) group->UpdateLooterGuid(go); } - if (GameObjectTemplateAddon const* addon = go->GetTemplateAddon()) - loot->generateMoneyLoot(addon->mingold, addon->maxgold); + 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); @@ -8477,14 +8479,24 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) loot = &bones->loot; - if (!bones->lootForBody) + if (loot->loot_type == LOOT_NONE) { - bones->lootForBody = true; uint32 pLevel = bones->loot.gold; bones->loot.clear(); + + // For AV Achievement if (Battleground* bg = GetBattleground()) + { if (bg->GetTypeID(true) == BATTLEGROUND_AV) - loot->FillLoot(1, LootTemplates_Creature, this, true); + loot->FillLoot(PLAYER_CORPSE_LOOT_ENTRY, LootTemplates_Creature, this, true); + } + // For wintergrasp Quests + else if (GetZoneId() == AREA_WINTERGRASP) + loot->FillLoot(PLAYER_CORPSE_LOOT_ENTRY, LootTemplates_Creature, this, true); + // For Tol Barad Quests + else if (GetZoneId() == BATTLEFIELD_TB_ZONEID) + loot->FillLoot(PLAYER_CORPSE_LOOT_ENTRY, LootTemplates_Creature, this, true); + // It may need a better formula // Now it works like this: lvl10: ~6copper, lvl70: ~9silver bones->loot.gold = uint32(urand(50, 150) * 0.016f * std::pow(float(pLevel) / 5.76f, 2.5f) * sWorld->getRate(RATE_DROP_MONEY)); @@ -8542,6 +8554,22 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) } else { + // exploit fix + if (!creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE)) + { + SendLootError(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(guid, LOOT_ERROR_DIDNT_KILL); + return; + } + if (loot->loot_type == LOOT_NONE) { // for creature, loot is filled when creature is killed. @@ -8569,14 +8597,16 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) if (loot->loot_type == LOOT_SKINNING) { loot_type = LOOT_SKINNING; - permission = creature->GetSkinner() == GetGUID() ? OWNER_PERMISSION : NONE_PERMISSION; + permission = creature->GetLootRecipientGUID() == GetGUID() ? OWNER_PERMISSION : NONE_PERMISSION; } else if (loot_type == LOOT_SKINNING) { loot->clear(); loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, this, true); - creature->SetSkinner(GetGUID()); permission = OWNER_PERMISSION; + + // Set new loot recipient + creature->SetLootRecipient(this, false); } // set group rights only for loot_type != LOOT_SKINNING else @@ -8637,12 +8667,12 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) // add 'this' player as one of the players that are looting 'loot' loot->AddLooter(GetGUID()); + + if (loot_type == LOOT_CORPSE && !guid.IsItem()) + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING); } else SendLootError(GetLootGUID(), LOOT_ERROR_DIDNT_KILL); - - if (loot_type == LOOT_CORPSE && !guid.IsItem()) - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING); } void Player::SendLootError(ObjectGuid guid, LootError error) const @@ -17856,7 +17886,7 @@ bool Player::isAllowedToLoot(Creature const* creature) return false; if (loot->loot_type == LOOT_SKINNING) - return creature->GetSkinner() == GetGUID(); + return creature->GetLootRecipientGUID() == GetGUID(); Group* thisGroup = GetGroup(); if (!thisGroup) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index e8bd9afcab..474e33b598 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -11836,12 +11836,13 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) if (creature) { Loot* loot = &creature->loot; - loot->clear(); + if (uint32 lootid = creature->GetCreatureTemplate()->lootid) loot->FillLoot(lootid, LootTemplates_Creature, looter, false, false, creature->GetLootMode()); - loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold); + if (creature->GetLootMode() > 0) + loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold); if (group) { diff --git a/src/server/game/Loot/Loot.cpp b/src/server/game/Loot/Loot.cpp index 1e1f045ac2..ca4584da03 100644 --- a/src/server/game/Loot/Loot.cpp +++ b/src/server/game/Loot/Loot.cpp @@ -105,7 +105,7 @@ void LootItem::AddAllowedLooter(Player const* player) // --------- Loot --------- // -Loot::Loot(uint32 _gold /*= 0*/) : gold(_gold), unlootedCount(0), roundRobinPlayer(), loot_type(LOOT_CORPSE), maxDuplicates(1), containerID(0) +Loot::Loot(uint32 _gold /*= 0*/) : gold(_gold), unlootedCount(0), roundRobinPlayer(), loot_type(LOOT_NONE), maxDuplicates(1), containerID(0) { } diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 6db682550f..5dc19288aa 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -822,6 +822,9 @@ void LoadLootTemplates_Creature() for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr) lootIdSet.erase(*itr); + // 1 means loot for player corpse + lootIdSet.erase(PLAYER_CORPSE_LOOT_ENTRY); + // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Creature.ReportUnusedIds(lootIdSet); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 5a67158973..20c394faf4 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -3589,6 +3589,8 @@ enum CorpseDynFlags CORPSE_DYNFLAG_LOOTABLE = 0x0001 }; +#define PLAYER_CORPSE_LOOT_ENTRY 1 + enum WeatherType { WEATHER_TYPE_FINE = 0, diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index c91c489ac0..822d22b8bd 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -7476,6 +7476,31 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* lo // all ok by some way or another, skip normal check break; + case SPELL_EFFECT_SKIN_PLAYER_CORPSE: + { + if (!m_targets.GetCorpseTargetGUID()) + { + if (target->IsWithinLOSInMap(m_caster, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE)) + return true; + + return false; + } + + Corpse* corpse = ObjectAccessor::GetCorpse(*m_caster, m_targets.GetCorpseTargetGUID()); + if (!corpse) + return false; + + if (target->GetGUID() != corpse->GetOwnerGUID()) + return false; + + if (!corpse->HasFlag(CORPSE_FIELD_FLAGS, CORPSE_FLAG_LOOTABLE)) + return false; + + if (!corpse->IsWithinLOSInMap(m_caster, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + return false; + + break; + } default: // normal case { if (losPosition) diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 87e4451f22..aa0e04f9b3 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -4292,9 +4292,9 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/) uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill(); - m_caster->ToPlayer()->SendLoot(creature->GetGUID(), LOOT_SKINNING); creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + m_caster->ToPlayer()->SendLoot(creature->GetGUID(), LOOT_SKINNING); int32 reqValue = 1; if (targetLevel <= 10)