diff options
author | Shauren <shauren.trinity@gmail.com> | 2020-05-01 15:41:32 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-01 15:41:32 +0200 |
commit | 57a5969c2672b36160fea1b7c38c424de562a57b (patch) | |
tree | ee52f8ddaa8d37297142e59029df461d3000ce31 /src/server/game | |
parent | fbd74eb5d8b5aa3a6874ee99044054e097b5ef21 (diff) | |
parent | 05ba662d5daaa3428cc01cdaa3794bf5a073ef17 (diff) |
Merge pull request #24500 from funjoker/cherry-picks
Diffstat (limited to 'src/server/game')
89 files changed, 2554 insertions, 1910 deletions
diff --git a/src/server/game/AI/CoreAI/TotemAI.cpp b/src/server/game/AI/CoreAI/TotemAI.cpp index 70ed58d853d..a2f0f8f664b 100644 --- a/src/server/game/AI/CoreAI/TotemAI.cpp +++ b/src/server/game/AI/CoreAI/TotemAI.cpp @@ -83,8 +83,7 @@ void TotemAI::UpdateAI(uint32 /*diff*/) i_victimGuid = victim->GetGUID(); // attack - me->SetInFront(victim); // client change orientation by self - me->CastSpell(victim, me->ToTotem()->GetSpell(), false); + me->CastSpell(victim, me->ToTotem()->GetSpell()); } else i_victimGuid.Clear(); diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index 21d15731b11..124361f0c07 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -65,14 +65,20 @@ void UnitAI::DoMeleeAttackIfReady() //Make sure our attack is ready and we aren't currently casting before checking distance if (me->isAttackReady()) { - me->AttackerStateUpdate(victim); + if (ShouldSparWith(victim)) + me->FakeAttackerStateUpdate(victim); + else + me->AttackerStateUpdate(victim); me->resetAttackTimer(); } if (me->haveOffhandWeapon() && me->isAttackReady(OFF_ATTACK)) { - me->AttackerStateUpdate(victim, OFF_ATTACK); + if (ShouldSparWith(victim)) + me->FakeAttackerStateUpdate(victim, OFF_ATTACK); + else + me->AttackerStateUpdate(victim, OFF_ATTACK); me->resetAttackTimer(OFF_ATTACK); } diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h index 0145233e3c9..73dc7afc2ec 100644 --- a/src/server/game/AI/CoreAI/UnitAI.h +++ b/src/server/game/AI/CoreAI/UnitAI.h @@ -265,6 +265,8 @@ class TC_GAME_API UnitAI void DoCastVictim(uint32 spellId, bool triggered = false); void DoCastAOE(uint32 spellId, bool triggered = false); + virtual bool ShouldSparWith(Unit const* /*target*/) const { return false; } + void DoMeleeAttackIfReady(); bool DoSpellAttackIfReady(uint32 spellId); @@ -277,7 +279,6 @@ class TC_GAME_API UnitAI virtual void sQuestAccept(Player* /*player*/, Quest const* /*quest*/) { } virtual void sQuestSelect(Player* /*player*/, Quest const* /*quest*/) { } virtual void sQuestReward(Player* /*player*/, Quest const* /*quest*/, uint32 /*opt*/) { } - virtual bool sOnDummyEffect(Unit* /*caster*/, uint32 /*spellId*/, SpellEffIndex /*effIndex*/) { return false; } virtual void sOnGameEvent(bool /*start*/, uint16 /*eventId*/) { } private: diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index b9d052cb528..3c65160aaf6 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -44,7 +44,7 @@ void CreatureAI::OnCharmed(bool apply) AISpellInfoType* UnitAI::AISpellInfo; AISpellInfoType* GetAISpellInfo(uint32 i) { return &UnitAI::AISpellInfo[i]; } -CreatureAI::CreatureAI(Creature* creature) : UnitAI(creature), me(creature), _boundary(nullptr), m_MoveInLineOfSight_locked(false) +CreatureAI::CreatureAI(Creature* creature) : UnitAI(creature), me(creature), _boundary(nullptr), _negateBoundary(false), m_MoveInLineOfSight_locked(false) { } @@ -173,9 +173,9 @@ void CreatureAI::TriggerAlert(Unit const* who) const me->SendAIReaction(AI_REACTION_ALERT); // Face the unit (stealthed player) and set distracted state for 5 seconds - me->SetFacingTo(me->GetAngle(who->GetPositionX(), who->GetPositionY()), true); - me->StopMoving(); me->GetMotionMaster()->MoveDistract(5 * IN_MILLISECONDS); + me->StopMoving(); + me->SetFacingTo(me->GetAngle(who)); } void CreatureAI::EnterEvadeMode(EvadeReason why) @@ -217,7 +217,7 @@ void CreatureAI::SetGazeOn(Unit* target) { if (me->IsValidAttackTarget(target)) { - if (!me->IsFocusing(nullptr, true)) + if (!me->IsFocusing(nullptr, true) && target != me->GetVictim()) AttackStart(target); me->SetReactState(REACT_PASSIVE); } @@ -237,7 +237,7 @@ bool CreatureAI::UpdateVictimWithGaze() } if (Unit* victim = me->SelectVictim()) - if (!me->IsFocusing(nullptr, true)) + if (!me->IsFocusing(nullptr, true) && victim != me->GetVictim()) AttackStart(victim); return me->GetVictim() != nullptr; @@ -251,7 +251,7 @@ bool CreatureAI::UpdateVictim() if (!me->HasReactState(REACT_PASSIVE)) { if (Unit* victim = me->SelectVictim()) - if (!me->IsFocusing(nullptr, true)) + if (!me->IsFocusing(nullptr, true) && victim != me->GetVictim()) AttackStart(victim); return me->GetVictim() != nullptr; @@ -364,32 +364,28 @@ int32 CreatureAI::VisualizeBoundary(uint32 duration, Unit* owner, bool fill) con bool CreatureAI::CheckBoundary(Position const* who) const { + if (!_boundary) + return true; + if (!who) who = me; - if (_boundary) - for (AreaBoundary const* areaBoundary : *_boundary) - if (!areaBoundary->IsWithinBoundary(who)) - return false; - - return true; + return (CreatureAI::IsInBounds(*_boundary, who) != _negateBoundary); } -bool CreatureAI::IsInBounds(CreatureBoundary const* boundary, Position const* pos) +bool CreatureAI::IsInBounds(CreatureBoundary const& boundary, Position const* pos) { - if (!boundary) - return true; - - for (AreaBoundary const* areaBoundary : *boundary) + for (AreaBoundary const* areaBoundary : boundary) if (!areaBoundary->IsWithinBoundary(pos)) return false; return true; } -void CreatureAI::SetBoundary(CreatureBoundary const* boundary) +void CreatureAI::SetBoundary(CreatureBoundary const* boundary, bool negateBoundaries /*= false*/) { _boundary = boundary; + _negateBoundary = negateBoundaries; me->DoImmediateBoundaryCheck(); } diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index 5fe93e7675a..edc09a650c7 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -83,7 +83,6 @@ class TC_GAME_API CreatureAI : public UnitAI bool CheckBoundary(Position const* who = nullptr) const; - void SetBoundary(CreatureBoundary const* boundary); public: enum EvadeReason { @@ -211,11 +210,12 @@ class TC_GAME_API CreatureAI : public UnitAI virtual PlayerAI* GetAIForCharmedPlayer(Player* /*who*/) { return nullptr; } // intended for encounter design/debugging. do not use for other purposes. expensive. - int32 VisualizeBoundary(uint32 duration, Unit* owner=nullptr, bool fill=false) const; + int32 VisualizeBoundary(uint32 duration, Unit* owner = nullptr, bool fill = false) const; virtual bool CheckInRoom(); CreatureBoundary const* GetBoundary() const { return _boundary; } + void SetBoundary(CreatureBoundary const* boundary, bool negativeBoundaries = false); - static bool IsInBounds(CreatureBoundary const* boundary, Position const* who); + static bool IsInBounds(CreatureBoundary const& boundary, Position const* who); protected: virtual void MoveInLineOfSight(Unit* /*who*/); @@ -223,6 +223,7 @@ class TC_GAME_API CreatureAI : public UnitAI bool _EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER); CreatureBoundary const* _boundary; + bool _negateBoundary; private: bool m_MoveInLineOfSight_locked; diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index 20a1c895dae..46e4159bb4c 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -176,7 +176,7 @@ void npc_escortAI::JustRespawned() //add a small delay before going to first waypoint, normal in near all cases m_uiWPWaitTimer = 2500; - if (me->getFaction() != me->GetCreatureTemplate()->faction) + if (me->GetFaction() != me->GetCreatureTemplate()->faction) me->RestoreFaction(); Reset(); diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp index 961700d43b1..6c38e34df97 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp @@ -166,8 +166,8 @@ void FollowerAI::JustRespawned() if (!IsCombatMovementAllowed()) SetCombatMovement(true); - if (me->getFaction() != me->GetCreatureTemplate()->faction) - me->setFaction(me->GetCreatureTemplate()->faction); + if (me->GetFaction() != me->GetCreatureTemplate()->faction) + me->SetFaction(me->GetCreatureTemplate()->faction); Reset(); } @@ -303,7 +303,7 @@ void FollowerAI::StartFollow(Player* player, uint32 factionForFollower, const Qu m_uiLeaderGUID = player->GetGUID(); if (factionForFollower) - me->setFaction(factionForFollower); + me->SetFaction(factionForFollower); m_pQuestForFollow = quest; diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index a215153b550..1d4c2150585 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -580,7 +580,7 @@ void SmartAI::JustRespawned() mDespawnState = 0; mEscortState = SMART_ESCORT_NONE; me->SetVisible(true); - if (me->getFaction() != me->GetCreatureTemplate()->faction) + if (me->GetFaction() != me->GetCreatureTemplate()->faction) me->RestoreFaction(); mJustReset = true; JustReachedHome(); @@ -830,12 +830,6 @@ void SmartAI::sQuestReward(Player* player, Quest const* quest, uint32 opt) GetScript()->ProcessEventsFor(SMART_EVENT_REWARD_QUEST, player, quest->GetQuestId(), opt); } -bool SmartAI::sOnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effIndex) -{ - GetScript()->ProcessEventsFor(SMART_EVENT_DUMMY_EFFECT, caster, spellId, (uint32)effIndex); - return true; -} - void SmartAI::SetCombatMove(bool on) { if (mCanCombatMove == on) diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index 02cb4529a38..cce8ffd57ad 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -182,7 +182,6 @@ class TC_GAME_API SmartAI : public CreatureAI void sQuestAccept(Player* player, Quest const* quest) override; //void sQuestSelect(Player* player, Quest const* quest) override; void sQuestReward(Player* player, Quest const* quest, uint32 opt) override; - bool sOnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effIndex) override; void sOnGameEvent(bool start, uint16 eventId) override; uint32 mEscortQuestID; diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 6224533846e..abf80f95794 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -365,7 +365,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { if (e.action.faction.factionID) { - (*itr)->ToCreature()->setFaction(e.action.faction.factionID); + (*itr)->ToCreature()->SetFaction(e.action.faction.factionID); TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SET_FACTION: Creature entry %u, %s set faction to %u", (*itr)->GetEntry(), (*itr)->GetGUID().ToString().c_str(), e.action.faction.factionID); } @@ -373,9 +373,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate((*itr)->ToCreature()->GetEntry())) { - if ((*itr)->ToCreature()->getFaction() != ci->faction) + if ((*itr)->ToCreature()->GetFaction() != ci->faction) { - (*itr)->ToCreature()->setFaction(ci->faction); + (*itr)->ToCreature()->SetFaction(ci->faction); TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SET_FACTION: Creature entry %u, %s set faction to %u", (*itr)->GetEntry(), (*itr)->GetGUID().ToString().c_str(), ci->faction); } @@ -3831,13 +3831,6 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui ProcessAction(e, unit, var0, var1); break; } - case SMART_EVENT_DUMMY_EFFECT: - { - if (e.event.dummy.spell != var0 || e.event.dummy.effIndex != var1) - return; - ProcessAction(e, unit, var0, var1); - break; - } case SMART_EVENT_GAME_EVENT_START: case SMART_EVENT_GAME_EVENT_END: { diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 9faaab903e6..42f0a4b4f2c 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -857,15 +857,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) if (!IsTextValid(e, e.event.textOver.textGroupID)) return false; break; - case SMART_EVENT_DUMMY_EFFECT: - { - if (!IsSpellValid(e, e.event.dummy.spell)) - return false; - - if (e.event.dummy.effIndex > EFFECT_2) - return false; - break; - } case SMART_EVENT_IS_BEHIND_TARGET: { if (!IsMinMaxValid(e, e.event.behindTarget.cooldownMin, e.event.behindTarget.cooldownMax)) diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 1ff452f4182..392cdc7cc22 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -177,7 +177,7 @@ enum SMART_EVENT SMART_EVENT_JUST_CREATED = 63, // none SMART_EVENT_GOSSIP_HELLO = 64, // noReportUse (for GOs) SMART_EVENT_FOLLOW_COMPLETED = 65, // none - SMART_EVENT_DUMMY_EFFECT = 66, // spellId, effectIndex + // 66 unused SMART_EVENT_IS_BEHIND_TARGET = 67, // cooldownMin, CooldownMax SMART_EVENT_GAME_EVENT_START = 68, // game_event.Entry SMART_EVENT_GAME_EVENT_END = 69, // game_event.Entry @@ -1492,7 +1492,7 @@ const uint32 SmartAIEventMask[SMART_EVENT_END][2] = {SMART_EVENT_JUST_CREATED, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT }, {SMART_EVENT_GOSSIP_HELLO, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT }, {SMART_EVENT_FOLLOW_COMPLETED, SMART_SCRIPT_TYPE_MASK_CREATURE }, - {SMART_EVENT_DUMMY_EFFECT, SMART_SCRIPT_TYPE_MASK_SPELL }, + {66, 0 }, // unused {SMART_EVENT_IS_BEHIND_TARGET, SMART_SCRIPT_TYPE_MASK_CREATURE }, {SMART_EVENT_GAME_EVENT_START, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT }, {SMART_EVENT_GAME_EVENT_END, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT }, diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index b86ec0a29f3..ca785cea3da 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -653,7 +653,7 @@ void AuctionHouseMgr::AddAItem(Item* item) _itemsByGuid[item->GetGUID()] = item; } -bool AuctionHouseMgr::RemoveAItem(ObjectGuid id, bool deleteItem) +bool AuctionHouseMgr::RemoveAItem(ObjectGuid id, bool deleteItem /*= false*/, CharacterDatabaseTransaction* trans /*= nullptr*/) { auto i = _itemsByGuid.find(id); if (i == _itemsByGuid.end()) @@ -661,9 +661,9 @@ bool AuctionHouseMgr::RemoveAItem(ObjectGuid id, bool deleteItem) if (deleteItem) { - CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); + ASSERT(trans); i->second->FSetState(ITEM_REMOVED); - i->second->SaveToDB(trans); + i->second->SaveToDB(*trans); } _itemsByGuid.erase(i); @@ -1850,7 +1850,7 @@ void AuctionHouseObject::SendAuctionWon(AuctionPosting const* auction, Player* b { // bidder doesn't exist, delete the item for (Item* item : auction->Items) - sAuctionMgr->RemoveAItem(item->GetGUID(), true); + sAuctionMgr->RemoveAItem(item->GetGUID(), true, &trans); } } @@ -1902,7 +1902,7 @@ void AuctionHouseObject::SendAuctionExpired(AuctionPosting const* auction, Chara { // owner doesn't exist, delete the item for (Item* item : auction->Items) - sAuctionMgr->RemoveAItem(item->GetGUID(), true); + sAuctionMgr->RemoveAItem(item->GetGUID(), true, &trans); } } diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 308df2083a4..33a902b6d32 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -373,7 +373,7 @@ class TC_GAME_API AuctionHouseMgr void LoadAuctions(); void AddAItem(Item* item); - bool RemoveAItem(ObjectGuid itemGuid, bool deleteItem = false); + bool RemoveAItem(ObjectGuid itemGuid, bool deleteItem = false, CharacterDatabaseTransaction* trans = nullptr); bool PendingAuctionAdd(Player* player, uint32 auctionHouseId, uint32 auctionId, uint64 deposit); std::size_t PendingAuctionCount(Player const* player) const; void PendingAuctionProcess(Player* player); diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp index f4c8aa5c320..b93a979033c 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp @@ -179,21 +179,21 @@ void AuctionBotConfig::GetConfigFromFile() SetConfig(CONFIG_AHBOT_ITEM_ORANGE_AMOUNT, "AuctionHouseBot.Items.Amount.Orange", 0); SetConfig(CONFIG_AHBOT_ITEM_YELLOW_AMOUNT, "AuctionHouseBot.Items.Amount.Yellow", 0); - SetConfigMax(CONFIG_AHBOT_CLASS_CONSUMABLE_AMOUNT, "AuctionHouseBot.Class.Consumable", 6, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_CONTAINER_AMOUNT, "AuctionHouseBot.Class.Container", 4, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_WEAPON_AMOUNT, "AuctionHouseBot.Class.Weapon", 8, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_GEM_AMOUNT, "AuctionHouseBot.Class.Gem", 3, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_ARMOR_AMOUNT, "AuctionHouseBot.Class.Armor", 8, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_REAGENT_AMOUNT, "AuctionHouseBot.Class.Reagent", 1, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_PROJECTILE_AMOUNT, "AuctionHouseBot.Class.Projectile", 2, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_TRADEGOOD_AMOUNT, "AuctionHouseBot.Class.TradeGood", 10, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_GENERIC_AMOUNT, "AuctionHouseBot.Class.Generic", 1, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_RECIPE_AMOUNT, "AuctionHouseBot.Class.Recipe", 6, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_QUIVER_AMOUNT, "AuctionHouseBot.Class.Quiver", 1, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_QUEST_AMOUNT, "AuctionHouseBot.Class.Quest", 1, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_KEY_AMOUNT, "AuctionHouseBot.Class.Key", 1, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_MISC_AMOUNT, "AuctionHouseBot.Class.Misc", 5, 10); - SetConfigMax(CONFIG_AHBOT_CLASS_GLYPH_AMOUNT, "AuctionHouseBot.Class.Glyph", 3, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_CONSUMABLE_PRIORITY, "AuctionHouseBot.Class.Consumable", 6, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_CONTAINER_PRIORITY, "AuctionHouseBot.Class.Container", 4, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_WEAPON_PRIORITY, "AuctionHouseBot.Class.Weapon", 8, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_GEM_PRIORITY, "AuctionHouseBot.Class.Gem", 3, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_ARMOR_PRIORITY, "AuctionHouseBot.Class.Armor", 8, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_REAGENT_PRIORITY, "AuctionHouseBot.Class.Reagent", 1, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_PROJECTILE_PRIORITY, "AuctionHouseBot.Class.Projectile", 2, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_TRADEGOOD_PRIORITY, "AuctionHouseBot.Class.TradeGood", 10, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_GENERIC_PRIORITY, "AuctionHouseBot.Class.Generic", 1, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_RECIPE_PRIORITY, "AuctionHouseBot.Class.Recipe", 6, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_QUIVER_PRIORITY, "AuctionHouseBot.Class.Quiver", 1, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_QUEST_PRIORITY, "AuctionHouseBot.Class.Quest", 1, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_KEY_PRIORITY, "AuctionHouseBot.Class.Key", 1, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_MISC_PRIORITY, "AuctionHouseBot.Class.Misc", 5, 10); + SetConfigMax(CONFIG_AHBOT_CLASS_GLYPH_PRIORITY, "AuctionHouseBot.Class.Glyph", 3, 10); SetConfig(CONFIG_AHBOT_ALLIANCE_PRICE_RATIO, "AuctionHouseBot.Alliance.Price.Ratio", 100); SetConfig(CONFIG_AHBOT_HORDE_PRICE_RATIO, "AuctionHouseBot.Horde.Price.Ratio", 100); @@ -351,6 +351,19 @@ uint32 AuctionBotConfig::GetConfigItemAmountRatio(AuctionHouseType houseType) co } } +uint32 AuctionBotConfig::GetConfigPriceRatio(AuctionHouseType houseType) const +{ + switch (houseType) + { + case AUCTION_HOUSE_ALLIANCE: + return GetConfig(CONFIG_AHBOT_ALLIANCE_PRICE_RATIO); + case AUCTION_HOUSE_HORDE: + return GetConfig(CONFIG_AHBOT_HORDE_PRICE_RATIO); + default: + return GetConfig(CONFIG_AHBOT_NEUTRAL_PRICE_RATIO); + } +} + bool AuctionBotConfig::GetConfigBuyerEnabled(AuctionHouseType houseType) const { switch (houseType) diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBot.h b/src/server/game/AuctionHouseBot/AuctionHouseBot.h index 75145c498e6..3bf9caef9f8 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBot.h +++ b/src/server/game/AuctionHouseBot/AuctionHouseBot.h @@ -71,21 +71,21 @@ enum AuctionBotConfigUInt32Values CONFIG_AHBOT_ITEM_PURPLE_AMOUNT, CONFIG_AHBOT_ITEM_ORANGE_AMOUNT, CONFIG_AHBOT_ITEM_YELLOW_AMOUNT, - CONFIG_AHBOT_CLASS_CONSUMABLE_AMOUNT, - CONFIG_AHBOT_CLASS_CONTAINER_AMOUNT, - CONFIG_AHBOT_CLASS_WEAPON_AMOUNT, - CONFIG_AHBOT_CLASS_GEM_AMOUNT, - CONFIG_AHBOT_CLASS_ARMOR_AMOUNT, - CONFIG_AHBOT_CLASS_REAGENT_AMOUNT, - CONFIG_AHBOT_CLASS_PROJECTILE_AMOUNT, - CONFIG_AHBOT_CLASS_TRADEGOOD_AMOUNT, - CONFIG_AHBOT_CLASS_GENERIC_AMOUNT, - CONFIG_AHBOT_CLASS_RECIPE_AMOUNT, - CONFIG_AHBOT_CLASS_QUIVER_AMOUNT, - CONFIG_AHBOT_CLASS_QUEST_AMOUNT, - CONFIG_AHBOT_CLASS_KEY_AMOUNT, - CONFIG_AHBOT_CLASS_MISC_AMOUNT, - CONFIG_AHBOT_CLASS_GLYPH_AMOUNT, + CONFIG_AHBOT_CLASS_CONSUMABLE_PRIORITY, + CONFIG_AHBOT_CLASS_CONTAINER_PRIORITY, + CONFIG_AHBOT_CLASS_WEAPON_PRIORITY, + CONFIG_AHBOT_CLASS_GEM_PRIORITY, + CONFIG_AHBOT_CLASS_ARMOR_PRIORITY, + CONFIG_AHBOT_CLASS_REAGENT_PRIORITY, + CONFIG_AHBOT_CLASS_PROJECTILE_PRIORITY, + CONFIG_AHBOT_CLASS_TRADEGOOD_PRIORITY, + CONFIG_AHBOT_CLASS_GENERIC_PRIORITY, + CONFIG_AHBOT_CLASS_RECIPE_PRIORITY, + CONFIG_AHBOT_CLASS_QUIVER_PRIORITY, + CONFIG_AHBOT_CLASS_QUEST_PRIORITY, + CONFIG_AHBOT_CLASS_KEY_PRIORITY, + CONFIG_AHBOT_CLASS_MISC_PRIORITY, + CONFIG_AHBOT_CLASS_GLYPH_PRIORITY, CONFIG_AHBOT_ALLIANCE_PRICE_RATIO, CONFIG_AHBOT_HORDE_PRICE_RATIO, CONFIG_AHBOT_NEUTRAL_PRICE_RATIO, @@ -205,8 +205,8 @@ enum AuctionBotConfigFloatValues class TC_GAME_API AuctionBotConfig { private: - AuctionBotConfig(): _itemsPerCycleBoost(1000), _itemsPerCycleNormal(20) { } - ~AuctionBotConfig() { } + AuctionBotConfig(): _itemsPerCycleBoost(1000), _itemsPerCycleNormal(20) {} + ~AuctionBotConfig() {} AuctionBotConfig(AuctionBotConfig const&) = delete; AuctionBotConfig& operator=(AuctionBotConfig const&) = delete; @@ -225,6 +225,7 @@ public: void SetConfig(AuctionBotConfigFloatValues index, float value) { _configFloatValues[index] = value; } uint32 GetConfigItemAmountRatio(AuctionHouseType houseType) const; + uint32 GetConfigPriceRatio(AuctionHouseType houseType) const; bool GetConfigBuyerEnabled(AuctionHouseType houseType) const; uint32 GetConfigItemQualityAmount(AuctionQuality quality) const; @@ -286,8 +287,8 @@ class TC_GAME_API AuctionHouseBot private: AuctionHouseBot(); ~AuctionHouseBot(); - AuctionHouseBot(const AuctionHouseBot&); - AuctionHouseBot& operator=(const AuctionHouseBot&); + AuctionHouseBot(AuctionHouseBot const&) = delete; + AuctionHouseBot& operator=(AuctionHouseBot const&) = delete; public: static AuctionHouseBot* instance(); diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp index 44ee9fcc2a4..da14231ec3c 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp @@ -22,6 +22,7 @@ #include "GameTime.h" #include "Item.h" #include "Log.h" +#include "Containers.h" #include "ObjectMgr.h" #include "Random.h" #include <sstream> @@ -29,7 +30,7 @@ AuctionBotSeller::AuctionBotSeller() { // Define faction for our main data class. - for (int i = 0; i < MAX_AUCTION_HOUSE_TYPE; ++i) + for (uint8 i = 0; i < MAX_AUCTION_HOUSE_TYPE; ++i) _houseConfig[i].Initialize(AuctionHouseType(i)); } @@ -117,11 +118,11 @@ bool AuctionBotSeller::Initialize() continue; // forced exclude filter - if (excludeItems.find(itemId) != excludeItems.end()) + if (excludeItems.count(itemId)) continue; // forced include filter - if (includeItems.find(itemId) != includeItems.end()) + if (includeItems.count(itemId)) { _itemPool[prototype->GetQuality()][prototype->GetClass()].push_back(itemId); ++itemsAdded; @@ -207,19 +208,20 @@ bool AuctionBotSeller::Initialize() // vendor filter if (!sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEMS_VENDOR)) - if (npcItems.find(itemId) != npcItems.end()) + if (npcItems.count(itemId)) continue; // loot filter if (!sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEMS_LOOT)) - if (lootItems.find(itemId) != lootItems.end()) + if (lootItems.count(itemId)) continue; // not vendor/loot filter if (!sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEMS_MISC)) { - bool isVendorItem = npcItems.find(itemId) != npcItems.end(); - bool isLootItem = lootItems.find(itemId) != lootItems.end(); + bool const isVendorItem = npcItems.count(itemId) > 0; + bool const isLootItem = lootItems.count(itemId) > 0; + if (!isLootItem && !isVendorItem) continue; } @@ -368,7 +370,7 @@ bool AuctionBotSeller::Initialize() void AuctionBotSeller::LoadConfig() { - for (int i = 0; i < MAX_AUCTION_HOUSE_TYPE; ++i) + for (uint8 i = 0; i < MAX_AUCTION_HOUSE_TYPE; ++i) if (sAuctionBotConfig->GetConfigItemAmountRatio(AuctionHouseType(i))) LoadSellerValues(_houseConfig[i]); } @@ -377,129 +379,11 @@ void AuctionBotSeller::LoadItemsQuantity(SellerConfiguration& config) { uint32 ratio = sAuctionBotConfig->GetConfigItemAmountRatio(config.GetHouseType()); - config.SetItemsAmountPerQuality(AUCTION_QUALITY_GRAY, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GRAY_AMOUNT) * ratio / 100); - config.SetItemsAmountPerQuality(AUCTION_QUALITY_WHITE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_WHITE_AMOUNT) * ratio / 100); - config.SetItemsAmountPerQuality(AUCTION_QUALITY_GREEN, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GREEN_AMOUNT) * ratio / 100); - config.SetItemsAmountPerQuality(AUCTION_QUALITY_BLUE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_BLUE_AMOUNT) * ratio / 100); - config.SetItemsAmountPerQuality(AUCTION_QUALITY_PURPLE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_PURPLE_AMOUNT) * ratio / 100); - config.SetItemsAmountPerQuality(AUCTION_QUALITY_ORANGE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_ORANGE_AMOUNT) * ratio / 100); - config.SetItemsAmountPerQuality(AUCTION_QUALITY_YELLOW, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_YELLOW_AMOUNT) * ratio / 100); - - // Set quantity wanted but only on possible item color - // This avoid any no-exist class-color items selection by random items create function - // ============================================================================================ - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_CONSUMABLE, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_CONTAINER, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_WEAPON, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_WEAPON_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_GEM, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_ARMOR, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_ARMOR_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_REAGENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_PROJECTILE, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_TRADE_GOODS, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_TRADEGOOD_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_ITEM_ENHANCEMENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_RECIPE, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_QUIVER, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_QUEST, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_QUEST_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_KEY, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_MISCELLANEOUS, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_MISC_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GRAY, ITEM_CLASS_GLYPH, 0); - - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_CONSUMABLE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONSUMABLE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_CONTAINER, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONTAINER_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_WEAPON, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_WEAPON_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_GEM, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_GEM_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_ARMOR, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_ARMOR_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_REAGENT, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_REAGENT_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_PROJECTILE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_PROJECTILE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_TRADE_GOODS, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_TRADEGOOD_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_ITEM_ENHANCEMENT, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_GENERIC_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_RECIPE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_RECIPE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_QUIVER, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_QUIVER_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_QUEST, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_QUEST_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_KEY, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_KEY_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_MISCELLANEOUS, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_MISC_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_WHITE, ITEM_CLASS_GLYPH, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_GLYPH_AMOUNT)); - - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_CONSUMABLE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONSUMABLE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_CONTAINER, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONTAINER_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_WEAPON, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_WEAPON_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_GEM, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_GEM_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_ARMOR, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_ARMOR_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_REAGENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_PROJECTILE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_PROJECTILE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_TRADE_GOODS, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_TRADEGOOD_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_ITEM_ENHANCEMENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_RECIPE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_RECIPE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_QUIVER, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_QUIVER_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_QUEST, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_QUEST_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_KEY, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_KEY_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_MISCELLANEOUS, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_MISC_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_GREEN, ITEM_CLASS_GLYPH, 0); - - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_CONSUMABLE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONSUMABLE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_CONTAINER, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONTAINER_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_WEAPON, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_WEAPON_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_GEM, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_GEM_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_ARMOR, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_ARMOR_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_REAGENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_PROJECTILE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_PROJECTILE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_TRADE_GOODS, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_TRADEGOOD_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_ITEM_ENHANCEMENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_RECIPE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_RECIPE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_QUIVER, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_QUIVER_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_QUEST, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_QUEST_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_KEY, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_MISCELLANEOUS, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_MISC_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_BLUE, ITEM_CLASS_GLYPH, 0); - - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_CONSUMABLE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONSUMABLE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_CONTAINER, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONTAINER_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_WEAPON, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_WEAPON_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_GEM, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_GEM_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_ARMOR, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_ARMOR_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_REAGENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_PROJECTILE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_PROJECTILE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_TRADE_GOODS, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_TRADEGOOD_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_ITEM_ENHANCEMENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_RECIPE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_RECIPE_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_QUIVER, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_QUEST, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_QUEST_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_KEY, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_MISCELLANEOUS, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_MISC_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_PURPLE, ITEM_CLASS_GLYPH, 0); - - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_CONSUMABLE, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_CONTAINER, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_WEAPON, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_WEAPON_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_GEM, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_ARMOR, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_ARMOR_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_REAGENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_PROJECTILE, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_TRADE_GOODS, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_TRADEGOOD_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_ITEM_ENHANCEMENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_RECIPE, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_QUIVER, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_QUEST, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_KEY, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_MISCELLANEOUS, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_ORANGE, ITEM_CLASS_GLYPH, 0); - - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_CONSUMABLE, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_CONTAINER, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_WEAPON, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_WEAPON_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_GEM, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_ARMOR, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_ARMOR_AMOUNT)); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_REAGENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_PROJECTILE, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_TRADE_GOODS, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_ITEM_ENHANCEMENT, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_RECIPE, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_QUIVER, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_QUEST, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_KEY, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_MISCELLANEOUS, 0); - config.SetItemsQuantityPerClass(AUCTION_QUALITY_YELLOW, ITEM_CLASS_GLYPH, 0); - // ============================================================================================ + for (uint32 i = 0; i < MAX_AUCTION_QUALITY; ++i) + { + uint32 amount = sAuctionBotConfig->GetConfig(AuctionBotConfigUInt32Values(CONFIG_AHBOT_ITEM_GRAY_AMOUNT + i)); + config.SetItemsAmountPerQuality(AuctionQuality(i), std::lroundf(amount * ratio / 100.f)); + } // Set Stack Quantities config.SetRandomStackRatioPerClass(ITEM_CLASS_CONSUMABLE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_RANDOMSTACKRATIO_CONSUMABLE)); @@ -519,45 +403,100 @@ void AuctionBotSeller::LoadItemsQuantity(SellerConfiguration& config) config.SetRandomStackRatioPerClass(ITEM_CLASS_GLYPH, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_RANDOMSTACKRATIO_GLYPH)); // Set the best value to get nearest amount of items wanted + auto getPriorityForClass = [](uint32 itemClass) -> uint32 + { + AuctionBotConfigUInt32Values index; + switch (itemClass) + { + case ITEM_CLASS_CONSUMABLE: + index = CONFIG_AHBOT_CLASS_CONSUMABLE_PRIORITY; break; + case ITEM_CLASS_CONTAINER: + index = CONFIG_AHBOT_CLASS_CONTAINER_PRIORITY; break; + case ITEM_CLASS_WEAPON: + index = CONFIG_AHBOT_CLASS_WEAPON_PRIORITY; break; + case ITEM_CLASS_GEM: + index = CONFIG_AHBOT_CLASS_GEM_PRIORITY; break; + case ITEM_CLASS_ARMOR: + index = CONFIG_AHBOT_CLASS_ARMOR_PRIORITY; break; + case ITEM_CLASS_REAGENT: + index = CONFIG_AHBOT_CLASS_REAGENT_PRIORITY; break; + case ITEM_CLASS_PROJECTILE: + index = CONFIG_AHBOT_CLASS_PROJECTILE_PRIORITY; break; + case ITEM_CLASS_TRADE_GOODS: + index = CONFIG_AHBOT_CLASS_TRADEGOOD_PRIORITY; break; + case ITEM_CLASS_ITEM_ENHANCEMENT: + index = CONFIG_AHBOT_CLASS_GENERIC_PRIORITY; break; + case ITEM_CLASS_RECIPE: + index = CONFIG_AHBOT_CLASS_RECIPE_PRIORITY; break; + case ITEM_CLASS_QUIVER: + index = CONFIG_AHBOT_CLASS_QUIVER_PRIORITY; break; + case ITEM_CLASS_QUEST: + index = CONFIG_AHBOT_CLASS_QUEST_PRIORITY; break; + case ITEM_CLASS_KEY: + index = CONFIG_AHBOT_CLASS_KEY_PRIORITY; break; + case ITEM_CLASS_MISCELLANEOUS: + index = CONFIG_AHBOT_CLASS_MISC_PRIORITY; break; + case ITEM_CLASS_GLYPH: + index = CONFIG_AHBOT_CLASS_GLYPH_PRIORITY; break; + default: + return 0; + } + + return sAuctionBotConfig->GetConfig(index); + }; + + std::vector<uint32> totalPrioPerQuality(MAX_AUCTION_QUALITY); for (uint32 j = 0; j < MAX_AUCTION_QUALITY; ++j) { - uint32 index = config.GetItemsAmountPerQuality(AuctionQuality(j)) / - (sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONSUMABLE_AMOUNT) + sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONTAINER_AMOUNT) + sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_WEAPON_AMOUNT) + - sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_GEM_AMOUNT) + sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_ARMOR_AMOUNT) + sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_REAGENT_AMOUNT) + - sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_PROJECTILE_AMOUNT) + sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_TRADEGOOD_AMOUNT) + sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_GENERIC_AMOUNT) + - sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_RECIPE_AMOUNT) + sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_QUIVER_AMOUNT) + sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_QUEST_AMOUNT) + - sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_KEY_AMOUNT) + sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_MISC_AMOUNT) + sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_GLYPH_AMOUNT)); + for (uint32 i = 0; i < MAX_ITEM_CLASS; ++i) + { + // skip empty pools + if (_itemPool[j][i].empty()) + continue; + + totalPrioPerQuality[j] += getPriorityForClass(i); + } + } + + for (uint32 j = 0; j < MAX_AUCTION_QUALITY; ++j) + { + uint32 qualityAmount = config.GetItemsAmountPerQuality(AuctionQuality(j)); + if (!totalPrioPerQuality[j]) + continue; + + for (uint32 i = 0; i < MAX_ITEM_CLASS; ++i) + { + uint32 classPrio = getPriorityForClass(i); + if (_itemPool[j][i].empty()) + classPrio = 0; + + uint32 weightedAmount = std::lroundf(classPrio / float(totalPrioPerQuality[j]) * qualityAmount); + config.SetItemsAmountPerClass(AuctionQuality(j), ItemClass(i), weightedAmount); + } + } + // do some assert checking, GetItemAmount must always return 0 if selected _itemPool is empty + for (uint32 j = 0; j < MAX_AUCTION_QUALITY; ++j) + { for (uint32 i = 0; i < MAX_ITEM_CLASS; ++i) - config.SetItemsAmountPerClass(AuctionQuality(j), ItemClass(i), index); + { + if (_itemPool[j][i].empty()) + ASSERT(config.GetItemsAmountPerClass(AuctionQuality(j), ItemClass(i)) == 0); + } } } void AuctionBotSeller::LoadSellerValues(SellerConfiguration& config) { LoadItemsQuantity(config); - uint32 PriceRatio; - switch (config.GetHouseType()) + uint32 ratio = sAuctionBotConfig->GetConfigPriceRatio(config.GetHouseType()); + + for (uint32 i = 0; i < MAX_AUCTION_QUALITY; ++i) { - case AUCTION_HOUSE_ALLIANCE: - PriceRatio = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ALLIANCE_PRICE_RATIO); - break; - case AUCTION_HOUSE_HORDE: - PriceRatio = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_HORDE_PRICE_RATIO); - break; - default: - PriceRatio = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_NEUTRAL_PRICE_RATIO); - break; + uint32 amount = sAuctionBotConfig->GetConfig(AuctionBotConfigUInt32Values(CONFIG_AHBOT_ITEM_GRAY_PRICE_RATIO + i)); + config.SetPriceRatioPerQuality(AuctionQuality(i), std::lroundf(amount * ratio / 100.f)); } - config.SetPriceRatioPerQuality(AUCTION_QUALITY_GRAY, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GRAY_PRICE_RATIO)); - config.SetPriceRatioPerQuality(AUCTION_QUALITY_WHITE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_WHITE_PRICE_RATIO)); - config.SetPriceRatioPerQuality(AUCTION_QUALITY_GREEN, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GREEN_PRICE_RATIO)); - config.SetPriceRatioPerQuality(AUCTION_QUALITY_BLUE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_BLUE_PRICE_RATIO)); - config.SetPriceRatioPerQuality(AUCTION_QUALITY_PURPLE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_PURPLE_PRICE_RATIO)); - config.SetPriceRatioPerQuality(AUCTION_QUALITY_ORANGE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_ORANGE_PRICE_RATIO)); - config.SetPriceRatioPerQuality(AUCTION_QUALITY_YELLOW, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_YELLOW_PRICE_RATIO)); - config.SetPriceRatioPerClass(ITEM_CLASS_CONSUMABLE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONSUMABLE_PRICE_RATIO)); config.SetPriceRatioPerClass(ITEM_CLASS_CONTAINER, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONTAINER_PRICE_RATIO)); config.SetPriceRatioPerClass(ITEM_CLASS_WEAPON, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_WEAPON_PRICE_RATIO)); @@ -579,18 +518,6 @@ void AuctionBotSeller::LoadSellerValues(SellerConfiguration& config) //load min and max auction times config.SetMinTime(sAuctionBotConfig->GetConfig(CONFIG_AHBOT_MINTIME)); config.SetMaxTime(sAuctionBotConfig->GetConfig(CONFIG_AHBOT_MAXTIME)); - - TC_LOG_DEBUG("ahbot", "AHBot: minTime = %u", config.GetMinTime()); - TC_LOG_DEBUG("ahbot", "AHBot: maxTime = %u", config.GetMaxTime()); - - TC_LOG_DEBUG("ahbot", "AHBot: For AH type %u", config.GetHouseType()); - TC_LOG_DEBUG("ahbot", "AHBot: GrayItems = %u", config.GetItemsAmountPerQuality(AUCTION_QUALITY_GRAY)); - TC_LOG_DEBUG("ahbot", "AHBot: WhiteItems = %u", config.GetItemsAmountPerQuality(AUCTION_QUALITY_WHITE)); - TC_LOG_DEBUG("ahbot", "AHBot: GreenItems = %u", config.GetItemsAmountPerQuality(AUCTION_QUALITY_GREEN)); - TC_LOG_DEBUG("ahbot", "AHBot: BlueItems = %u", config.GetItemsAmountPerQuality(AUCTION_QUALITY_BLUE)); - TC_LOG_DEBUG("ahbot", "AHBot: PurpleItems = %u", config.GetItemsAmountPerQuality(AUCTION_QUALITY_PURPLE)); - TC_LOG_DEBUG("ahbot", "AHBot: OrangeItems = %u", config.GetItemsAmountPerQuality(AUCTION_QUALITY_ORANGE)); - TC_LOG_DEBUG("ahbot", "AHBot: YellowItems = %u", config.GetItemsAmountPerQuality(AUCTION_QUALITY_YELLOW)); } // Set static of items on one AH faction. @@ -644,12 +571,13 @@ bool AuctionBotSeller::GetItemsToSell(SellerConfiguration& config, ItemsToSellAr { for (uint32 i = 0; i < MAX_ITEM_CLASS; ++i) { - if (config.GetMissedItemsPerClass(AuctionQuality(j), ItemClass(i)) > addedItem[j][i] && !_itemPool[j][i].empty()) + // if _itemPool for chosen is empty, MissedItemsPerClass will return 0 here (checked at startup) + if (config.GetMissedItemsPerClass(AuctionQuality(j), ItemClass(i)) > addedItem[j][i]) { ItemToSell miss_item; miss_item.Color = j; miss_item.Itemclass = i; - itemsToSellArray.push_back(miss_item); + itemsToSellArray.emplace_back(std::move(miss_item)); found = true; } } @@ -688,7 +616,9 @@ void AuctionBotSeller::SetPricesOfItem(ItemTemplate const* itemProto, SellerConf if (sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYPRICE_SELLER)) buyPrice = sellPrice; - float basePriceFloat = (buyPrice * stackCount * priceRatio) / (itemProto->GetClass() == 6 ? 200.0f : static_cast<float>(itemProto->GetBuyCount())) / 100.0f; + float basePriceFloat = buyPrice * stackCount / (itemProto->GetClass() == 6 ? 200.0f : static_cast<float>(itemProto->GetBuyCount())); + basePriceFloat *= priceRatio; + float range = basePriceFloat * 0.04f; buyout = (static_cast<uint32>(frand(basePriceFloat - range, basePriceFloat + range) + 0.5f) / SILVER) * SILVER; @@ -908,11 +838,11 @@ void AuctionBotSeller::AddNewAuctions(SellerConfiguration& config) --items; // Select random position from missed items table - uint32 pos = urand(0, itemsToSell.size() - 1); + ItemToSell const& sellItem = Trinity::Containers::SelectRandomContainerElement(itemsToSell); // Set itemId with random item ID for selected categories and color, from _itemPool table - uint32 itemId = _itemPool[itemsToSell[pos].Color][itemsToSell[pos].Itemclass][urand(0, _itemPool[itemsToSell[pos].Color][itemsToSell[pos].Itemclass].size() - 1)]; - ++allItems[itemsToSell[pos].Color][itemsToSell[pos].Itemclass]; // Helper table to avoid rescan from DB in this loop. (has we add item in random orders) + uint32 itemId = Trinity::Containers::SelectRandomContainerElement(_itemPool[sellItem.Color][sellItem.Itemclass]); + ++allItems[sellItem.Color][sellItem.Itemclass]; // Helper table to avoid rescan from DB in this loop. (has we add item in random orders) if (!itemId) { @@ -951,16 +881,14 @@ void AuctionBotSeller::AddNewAuctions(SellerConfiguration& config) switch (etime) { case 1: - etime = 43200; - break; - case 2: - etime = 86400; + etime = DAY / 2; break; case 3: - etime = 172800; + etime = 2 *DAY; break; + case 2: default: - etime = 86400; + etime = DAY; break; } diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h index ea82dd991f4..29288c6fb3b 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h @@ -32,36 +32,28 @@ struct ItemToSell typedef std::vector<ItemToSell> ItemsToSellArray; typedef std::array<std::array<uint32, MAX_ITEM_CLASS>, MAX_ITEM_QUALITY> AllItemsArray; -struct SellerItemClassInfo +struct SellerItemInfo { - SellerItemClassInfo(): AmountOfItems(0), MissItems(0), Quantity(0), PriceRatio(0), RandomStackRatio(100) {} - - uint32 AmountOfItems; - uint32 MissItems; - uint32 Quantity; - uint32 PriceRatio; - uint32 RandomStackRatio; + uint32 AmountOfItems = 0; + uint32 MissItems = 0; }; -struct SellerItemInfo +struct SellerItemClassSharedInfo { - SellerItemInfo(): AmountOfItems(0), MissItems(0), PriceRatio(0) {} - - uint32 AmountOfItems; - uint32 MissItems; - uint32 PriceRatio; + uint32 PriceRatio = 0; + uint32 RandomStackRatio = 100; +}; - SellerItemClassInfo ItemClassInfos[MAX_ITEM_CLASS]; +struct SellerItemQualitySharedInfo +{ + uint32 AmountOfItems = 0; + uint32 PriceRatio = 0; }; class SellerConfiguration { public: - SellerConfiguration(): LastMissedItem(0), _houseType(AUCTION_HOUSE_NEUTRAL), _minTime(1), _maxTime(72) - { - } - - ~SellerConfiguration() {} + SellerConfiguration() : LastMissedItem(0), _houseType(AUCTION_HOUSE_NEUTRAL), _minTime(1), _maxTime(72), _itemInfo(), _itemSharedQualityInfo(), _itemSharedClassInfo() { } void Initialize(AuctionHouseType houseType) { @@ -83,35 +75,43 @@ public: void SetMaxTime(uint32 value) { _maxTime = value; } uint32 GetMaxTime() const { return _maxTime; } + // Data access classified by item class and item quality - void SetItemsAmountPerClass(AuctionQuality quality, ItemClass itemclass, uint32 amount) { _ItemInfo[quality].ItemClassInfos[itemclass].AmountOfItems = amount * _ItemInfo[quality].ItemClassInfos[itemclass].Quantity; } - uint32 GetItemsAmountPerClass(AuctionQuality quality, ItemClass itemclass) const { return _ItemInfo[quality].ItemClassInfos[itemclass].AmountOfItems; } - void SetItemsQuantityPerClass(AuctionQuality quality, ItemClass itemclass, uint32 qty) { _ItemInfo[quality].ItemClassInfos[itemclass].Quantity = qty; } - uint32 GetItemsQuantityPerClass(AuctionQuality quality, ItemClass itemclass) const { return _ItemInfo[quality].ItemClassInfos[itemclass].Quantity; } - void SetMissedItemsPerClass(AuctionQuality quality, ItemClass itemclass, uint32 found) + void SetItemsAmountPerClass(AuctionQuality quality, ItemClass itemClass, uint32 amount) { _itemInfo[quality][itemClass].AmountOfItems = amount; } + uint32 GetItemsAmountPerClass(AuctionQuality quality, ItemClass itemClass) const { return _itemInfo[quality][itemClass].AmountOfItems; } + + void SetMissedItemsPerClass(AuctionQuality quality, ItemClass itemClass, uint32 found) { - if (_ItemInfo[quality].ItemClassInfos[itemclass].AmountOfItems > found) - _ItemInfo[quality].ItemClassInfos[itemclass].MissItems = _ItemInfo[quality].ItemClassInfos[itemclass].AmountOfItems - found; + if (_itemInfo[quality][itemClass].AmountOfItems > found) + _itemInfo[quality][itemClass].MissItems = _itemInfo[quality][itemClass].AmountOfItems - found; else - _ItemInfo[quality].ItemClassInfos[itemclass].MissItems = 0; + _itemInfo[quality][itemClass].MissItems = 0; } - uint32 GetMissedItemsPerClass(AuctionQuality quality, ItemClass itemclass) const { return _ItemInfo[quality].ItemClassInfos[itemclass].MissItems; } + uint32 GetMissedItemsPerClass(AuctionQuality quality, ItemClass itemClass) const { return _itemInfo[quality][itemClass].MissItems; } // Data for every quality of item - void SetItemsAmountPerQuality(AuctionQuality quality, uint32 cnt) { _ItemInfo[quality].AmountOfItems = cnt; } - uint32 GetItemsAmountPerQuality(AuctionQuality quality) const { return _ItemInfo[quality].AmountOfItems; } - void SetPriceRatioPerQuality(AuctionQuality quality, uint32 value) { _ItemInfo[quality].PriceRatio = value; } - uint32 GetPriceRatioPerQuality(AuctionQuality quality) const { return _ItemInfo[quality].PriceRatio; } - void SetPriceRatioPerClass(ItemClass item, uint32 value) { _ItemInfo[0].ItemClassInfos[item].PriceRatio = value; } - uint32 GetPriceRatioPerClass(ItemClass item) const { return _ItemInfo[0].ItemClassInfos[item].PriceRatio; } - void SetRandomStackRatioPerClass(ItemClass item, uint32 value) { _ItemInfo[0].ItemClassInfos[item].RandomStackRatio = value; } - uint32 GetRandomStackRatioPerClass(ItemClass item) const { return _ItemInfo[0].ItemClassInfos[item].RandomStackRatio; } + void SetItemsAmountPerQuality(AuctionQuality quality, uint32 cnt) { _itemSharedQualityInfo[quality].AmountOfItems = cnt; } + uint32 GetItemsAmountPerQuality(AuctionQuality quality) const { return _itemSharedQualityInfo[quality].AmountOfItems; } + + void SetPriceRatioPerQuality(AuctionQuality quality, uint32 value) { _itemSharedQualityInfo[quality].PriceRatio = value; } + uint32 GetPriceRatioPerQuality(AuctionQuality quality) const { return _itemSharedQualityInfo[quality].PriceRatio; } + + // data for every class of item + void SetPriceRatioPerClass(ItemClass itemClass, uint32 value) { _itemSharedClassInfo[itemClass].PriceRatio = value; } + uint32 GetPriceRatioPerClass(ItemClass itemClass) const { return _itemSharedClassInfo[itemClass].PriceRatio; } + + void SetRandomStackRatioPerClass(ItemClass itemClass, uint32 value) { _itemSharedClassInfo[itemClass].RandomStackRatio = value; } + uint32 GetRandomStackRatioPerClass(ItemClass itemClass) const { return _itemSharedClassInfo[itemClass].RandomStackRatio; } private: AuctionHouseType _houseType; uint32 _minTime; uint32 _maxTime; - SellerItemInfo _ItemInfo[MAX_AUCTION_QUALITY]; + + SellerItemInfo _itemInfo[MAX_AUCTION_QUALITY][MAX_ITEM_CLASS]; + + SellerItemQualitySharedInfo _itemSharedQualityInfo[MAX_ITEM_QUALITY]; + SellerItemClassSharedInfo _itemSharedClassInfo[MAX_ITEM_CLASS]; }; // This class handle all Selling method diff --git a/src/server/game/Battlefield/Zones/BattlefieldTB.cpp b/src/server/game/Battlefield/Zones/BattlefieldTB.cpp index f2130d973db..1d610373c94 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldTB.cpp +++ b/src/server/game/Battlefield/Zones/BattlefieldTB.cpp @@ -609,7 +609,7 @@ void BattlefieldTB::OnCreatureCreate(Creature* creature) HideNpc(creature); break; case NPC_ABANDONED_SIEGE_ENGINE: - creature->setFaction(TBFactions[GetDefenderTeam()]); + creature->SetFaction(TBFactions[GetDefenderTeam()]); creature->CastSpell(creature, SPELL_THICK_LAYER_OF_RUST, true); break; case NPC_SIEGE_ENGINE_TURRET: diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp index 3589d7dd08c..6e1bba5f1a9 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp @@ -580,7 +580,7 @@ void BattlefieldWG::OnBattleStart() if (Creature* creature = GetCreature(*itr)) { ShowNpc(creature, true); - creature->setFaction(WintergraspFaction[GetDefenderTeam()]); + creature->SetFaction(WintergraspFaction[GetDefenderTeam()]); } } @@ -664,7 +664,7 @@ void BattlefieldWG::OnBattleEnd(bool endByTimer) if (Creature* creature = GetCreature(*itr)) { if (!endByTimer) - creature->setFaction(WintergraspFaction[GetDefenderTeam()]); + creature->SetFaction(WintergraspFaction[GetDefenderTeam()]); HideNpc(creature); } } @@ -891,9 +891,9 @@ void BattlefieldWG::OnCreatureRemove(Creature* /*creature*/) case NPC_WINTERGRASP_DEMOLISHER: { uint8 team; - if (creature->getFaction() == WintergraspFaction[TEAM_ALLIANCE]) + if (creature->GetFaction() == WintergraspFaction[TEAM_ALLIANCE]) team = TEAM_ALLIANCE; - else if (creature->getFaction() == WintergraspFaction[TEAM_HORDE]) + else if (creature->GetFaction() == WintergraspFaction[TEAM_HORDE]) team = TEAM_HORDE; else return; @@ -1604,12 +1604,12 @@ void BfWGGameObjectBuilding::Init(GameObject* go) case GO_WINTERGRASP_FORTRESS_TOWER_2: case GO_WINTERGRASP_FORTRESS_TOWER_3: case GO_WINTERGRASP_FORTRESS_TOWER_4: - turret->setFaction(WintergraspFaction[_wg->GetDefenderTeam()]); + turret->SetFaction(WintergraspFaction[_wg->GetDefenderTeam()]); break; case GO_WINTERGRASP_SHADOWSIGHT_TOWER: case GO_WINTERGRASP_WINTER_S_EDGE_TOWER: case GO_WINTERGRASP_FLAMEWATCH_TOWER: - turret->setFaction(WintergraspFaction[_wg->GetAttackerTeam()]); + turret->SetFaction(WintergraspFaction[_wg->GetAttackerTeam()]); break; } @@ -1629,12 +1629,12 @@ void BfWGGameObjectBuilding::Init(GameObject* go) case GO_WINTERGRASP_FORTRESS_TOWER_2: case GO_WINTERGRASP_FORTRESS_TOWER_3: case GO_WINTERGRASP_FORTRESS_TOWER_4: - turret->setFaction(WintergraspFaction[_wg->GetDefenderTeam()]); + turret->SetFaction(WintergraspFaction[_wg->GetDefenderTeam()]); break; case GO_WINTERGRASP_SHADOWSIGHT_TOWER: case GO_WINTERGRASP_WINTER_S_EDGE_TOWER: case GO_WINTERGRASP_FLAMEWATCH_TOWER: - turret->setFaction(WintergraspFaction[_wg->GetAttackerTeam()]); + turret->SetFaction(WintergraspFaction[_wg->GetAttackerTeam()]); break; } _wg->HideNpc(turret); @@ -1689,14 +1689,14 @@ void BfWGGameObjectBuilding::UpdateTurretAttack(bool disable) case GO_WINTERGRASP_FORTRESS_TOWER_3: case GO_WINTERGRASP_FORTRESS_TOWER_4: { - creature->setFaction(WintergraspFaction[_wg->GetDefenderTeam()]); + creature->SetFaction(WintergraspFaction[_wg->GetDefenderTeam()]); break; } case GO_WINTERGRASP_SHADOWSIGHT_TOWER: case GO_WINTERGRASP_WINTER_S_EDGE_TOWER: case GO_WINTERGRASP_FLAMEWATCH_TOWER: { - creature->setFaction(WintergraspFaction[_wg->GetAttackerTeam()]); + creature->SetFaction(WintergraspFaction[_wg->GetAttackerTeam()]); break; } } @@ -1719,14 +1719,14 @@ void BfWGGameObjectBuilding::UpdateTurretAttack(bool disable) case GO_WINTERGRASP_FORTRESS_TOWER_3: case GO_WINTERGRASP_FORTRESS_TOWER_4: { - creature->setFaction(WintergraspFaction[_wg->GetDefenderTeam()]); + creature->SetFaction(WintergraspFaction[_wg->GetDefenderTeam()]); break; } case GO_WINTERGRASP_SHADOWSIGHT_TOWER: case GO_WINTERGRASP_WINTER_S_EDGE_TOWER: case GO_WINTERGRASP_FLAMEWATCH_TOWER: { - creature->setFaction(WintergraspFaction[_wg->GetAttackerTeam()]); + creature->SetFaction(WintergraspFaction[_wg->GetAttackerTeam()]); break; } } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp index 6e89bc86b3f..ebe3f5462b6 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp @@ -384,7 +384,7 @@ void BattlegroundAB::_NodeOccupied(uint8 node, Team team) //aura should only apply to players who have accupied the node, set correct faction for trigger if (trigger) { - trigger->setFaction(team == ALLIANCE ? 84 : 83); + trigger->SetFaction(team == ALLIANCE ? 84 : 83); trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); } } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp index fd31810e4b8..1dabe1a572d 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp @@ -342,7 +342,7 @@ Creature* BattlegroundAV::AddAVCreature(uint16 cinfoid, uint16 type) { if (Creature* trigger = AddCreature(WORLD_TRIGGER, triggerSpawnID, BG_AV_CreaturePos[triggerSpawnID])) { - trigger->setFaction(newFaction); + trigger->SetFaction(newFaction); trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); } } @@ -756,7 +756,7 @@ void BattlegroundAV::PopulateNode(BG_AV_Nodes node) DelCreature(node + 302); return; } - trigger->setFaction(owner == ALLIANCE ? 84 : 83); + trigger->SetFaction(owner == ALLIANCE ? 84 : 83); trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); } } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp index 25400dd00b4..9cf74c56dd9 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp @@ -721,8 +721,7 @@ void BattlegroundEY::EventTeamLostPoint(Player* player, uint32 Point) UpdatePointsCount(Team); //remove bonus honor aura trigger creature when node is lost - if (Point < EY_POINTS_MAX) - DelCreature(Point + 6);//NULL checks are in DelCreature! 0-5 spirit guides + DelCreature(Point + 6);//NULL checks are in DelCreature! 0-5 spirit guides } void BattlegroundEY::EventTeamCapturedPoint(Player* player, uint32 Point) @@ -774,9 +773,6 @@ void BattlegroundEY::EventTeamCapturedPoint(Player* player, uint32 Point) UpdatePointsIcons(Team, Point); UpdatePointsCount(Team); - if (Point >= EY_POINTS_MAX) - return; - Creature* trigger = GetBGCreature(Point + 6, false);//0-5 spirit guides if (!trigger) trigger = AddCreature(WORLD_TRIGGER, Point+6, BG_EY_TriggerPositions[Point], GetTeamIndexByTeamId(Team)); @@ -786,7 +782,7 @@ void BattlegroundEY::EventTeamCapturedPoint(Player* player, uint32 Point) //aura should only apply to players who have accupied the node, set correct faction for trigger if (trigger) { - trigger->setFaction(Team == ALLIANCE ? 84 : 83); + trigger->SetFaction(Team == ALLIANCE ? 84 : 83); trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); } } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp index e93fc640d31..b8c28b586f8 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp @@ -363,9 +363,9 @@ bool BattlegroundIC::SetupBattleground() // setting correct factions for Keep Cannons for (uint8 i = BG_IC_NPC_KEEP_CANNON_1; i <= BG_IC_NPC_KEEP_CANNON_12; ++i) - GetBGCreature(i)->setFaction(BG_IC_Factions[0]); + GetBGCreature(i)->SetFaction(BG_IC_Factions[0]); for (uint8 i = BG_IC_NPC_KEEP_CANNON_13; i <= BG_IC_NPC_KEEP_CANNON_24; ++i) - GetBGCreature(i)->setFaction(BG_IC_Factions[1]); + GetBGCreature(i)->SetFaction(BG_IC_Factions[1]); // correcting spawn time for keeps bombs for (uint8 i = BG_IC_GO_HUGE_SEAFORIUM_BOMBS_A_1; i < BG_IC_GO_HUGE_SEAFORIUM_BOMBS_H_4; ++i) @@ -694,7 +694,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* node, bool recapture) continue; if (AddCreature(node->faction == TEAM_ALLIANCE ? NPC_GLAIVE_THROWER_A : NPC_GLAIVE_THROWER_H, type, BG_IC_DocksVehiclesGlaives[i], node->faction, RESPAWN_ONE_DAY)) - GetBGCreature(type)->setFaction(BG_IC_Factions[(node->faction == TEAM_ALLIANCE ? 0 : 1)]); + GetBGCreature(type)->SetFaction(BG_IC_Factions[(node->faction == TEAM_ALLIANCE ? 0 : 1)]); } // spawning catapults @@ -706,7 +706,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* node, bool recapture) continue; if (AddCreature(NPC_CATAPULT, type, BG_IC_DocksVehiclesCatapults[i], node->faction, RESPAWN_ONE_DAY)) - GetBGCreature(type)->setFaction(BG_IC_Factions[(node->faction == TEAM_ALLIANCE ? 0 : 1)]); + GetBGCreature(type)->SetFaction(BG_IC_Factions[(node->faction == TEAM_ALLIANCE ? 0 : 1)]); } break; case BG_IC_GO_WORKSHOP_BANNER: @@ -738,7 +738,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* node, bool recapture) continue; if (AddCreature(NPC_DEMOLISHER, type, BG_IC_WorkshopVehicles[i], node->faction, RESPAWN_ONE_DAY)) - GetBGCreature(type)->setFaction(BG_IC_Factions[(node->faction == TEAM_ALLIANCE ? 0 : 1)]); + GetBGCreature(type)->SetFaction(BG_IC_Factions[(node->faction == TEAM_ALLIANCE ? 0 : 1)]); } // we check if the opossing siege engine is in use @@ -763,7 +763,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* node, bool recapture) if (Creature* siegeEngine = GetBGCreature(siegeType)) { siegeEngine->AddUnitFlag(UnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_CANNOT_SWIM | UNIT_FLAG_IMMUNE_TO_PC)); - siegeEngine->setFaction(BG_IC_Factions[(node->faction == TEAM_ALLIANCE ? 0 : 1)]); + siegeEngine->SetFaction(BG_IC_Factions[(node->faction == TEAM_ALLIANCE ? 0 : 1)]); } } } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp index 52cb90f5e0c..0abb08afd0f 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp @@ -654,13 +654,13 @@ void BattlegroundSA::OverrideGunFaction() for (uint8 i = BG_SA_GUN_1; i <= BG_SA_GUN_10; i++) { if (Creature* gun = GetBGCreature(i)) - gun->setFaction(BG_SA_Factions[Attackers ? TEAM_ALLIANCE : TEAM_HORDE]); + gun->SetFaction(BG_SA_Factions[Attackers ? TEAM_ALLIANCE : TEAM_HORDE]); } for (uint8 i = BG_SA_DEMOLISHER_1; i <= BG_SA_DEMOLISHER_4; i++) { if (Creature* dem = GetBGCreature(i)) - dem->setFaction(BG_SA_Factions[Attackers]); + dem->SetFaction(BG_SA_Factions[Attackers]); } } @@ -831,7 +831,7 @@ void BattlegroundSA::CaptureGraveyard(BG_SA_Graveyards i, Player* Source) AddCreature(BG_SA_NpcEntries[j], j, BG_SA_NpcSpawnlocs[j], (Attackers == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE), 600); if (Creature* dem = GetBGCreature(j)) - dem->setFaction(BG_SA_Factions[Attackers]); + dem->SetFaction(BG_SA_Factions[Attackers]); } UpdateWorldState(BG_SA_LEFT_GY_ALLIANCE, GraveyardStatus[i] == TEAM_ALLIANCE); @@ -856,7 +856,7 @@ void BattlegroundSA::CaptureGraveyard(BG_SA_Graveyards i, Player* Source) AddCreature(BG_SA_NpcEntries[j], j, BG_SA_NpcSpawnlocs[j], Attackers == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE, 600); if (Creature* dem = GetBGCreature(j)) - dem->setFaction(BG_SA_Factions[Attackers]); + dem->SetFaction(BG_SA_Factions[Attackers]); } UpdateWorldState(BG_SA_RIGHT_GY_ALLIANCE, GraveyardStatus[i] == TEAM_ALLIANCE); diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 88670fa59fd..2d18d2d035c 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -892,7 +892,7 @@ GameTele const* ChatHandler::extractGameTeleFromLink(char* text) return nullptr; // id case (explicit or from shift link) - if (cId[0] >= '0' || cId[0] >= '9') + if (cId[0] >= '0' && cId[0] <= '9') if (uint32 id = atoi(cId)) return sObjectMgr->GetGameTele(id); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 4bbb29d43c1..f9f68237d0d 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -501,7 +501,7 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, if (!GetCreatureAddon()) SetSheath(SHEATH_STATE_MELEE); - setFaction(cInfo->faction); + SetFaction(cInfo->faction); uint64 npcFlags; uint32 unitFlags, unitFlags2, unitFlags3, dynamicFlags; @@ -555,7 +555,7 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, { if (Player* owner = Creature::GetCharmerOrOwnerPlayerOrPlayerItself()) // this check comes in case we don't have a player { - setFaction(owner->getFaction()); // vehicles should have same as owner faction + SetFaction(owner->GetFaction()); // vehicles should have same as owner faction owner->VehicleSpellInitialize(); } } @@ -622,7 +622,18 @@ void Creature::Update(uint32 diff) if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day) SetRespawnTime(DAY); else - m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little + { + // else copy time from master and add a little + time_t baseRespawnTime = std::max(linkedRespawntime, now); + time_t const offset = urand(5, MINUTE); + + // linked guid can be a boss, uses std::numeric_limits<time_t>::max to never respawn in that instance + // we shall inherit it instead of adding and causing an overflow + if (baseRespawnTime <= std::numeric_limits<time_t>::max() - offset) + m_respawnTime = baseRespawnTime + offset; + else + m_respawnTime = std::numeric_limits<time_t>::max(); + } SaveRespawnTime(); // also save to DB immediately } } @@ -670,10 +681,10 @@ void Creature::Update(uint32 diff) if (!m_suppressedTarget.IsEmpty()) { if (WorldObject const* objTarget = ObjectAccessor::GetWorldObject(*this, m_suppressedTarget)) - SetFacingToObject(objTarget); + SetFacingToObject(objTarget, false); } else - SetFacingTo(m_suppressedOrientation); + SetFacingTo(m_suppressedOrientation, false); m_shouldReacquireTarget = false; } @@ -2331,7 +2342,7 @@ bool Creature::CanAssistTo(const Unit* u, const Unit* enemy, bool checkfaction / // only from same creature faction if (checkfaction) { - if (getFaction() != u->getFaction()) + if (GetFaction() != u->GetFaction()) return false; } else @@ -3077,10 +3088,10 @@ void Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target) bool const canTurnDuringCast = !spellInfo->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST); // Face the target - we need to do this before the unit state is modified for no-turn spells if (target) - SetFacingToObject(target); + SetFacingToObject(target, false); else if (!canTurnDuringCast) if (Unit* victim = GetVictim()) - SetFacingToObject(victim); // ensure orientation is correct at beginning of cast + SetFacingToObject(victim, false); // ensure orientation is correct at beginning of cast if (!canTurnDuringCast) AddUnitState(UNIT_STATE_CANNOT_TURN); @@ -3126,10 +3137,10 @@ void Creature::ReleaseFocus(Spell const* focusSpell, bool withDelay) if (!m_suppressedTarget.IsEmpty()) { if (WorldObject const* objTarget = ObjectAccessor::GetWorldObject(*this, m_suppressedTarget)) - SetFacingToObject(objTarget); + SetFacingToObject(objTarget, false); } else - SetFacingTo(m_suppressedOrientation); + SetFacingTo(m_suppressedOrientation, false); } else // tell the creature that it should reacquire its actual target after the delay expires (this is handled in ::Update) @@ -3143,6 +3154,26 @@ void Creature::ReleaseFocus(Spell const* focusSpell, bool withDelay) m_focusDelay = (!IsPet() && withDelay) ? GameTime::GetGameTimeMS() : 0; // don't allow re-target right away to prevent visual bugs } +bool Creature::IsMovementPreventedByCasting() const +{ + // first check if currently a movement allowed channel is active and we're not casting + if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]) + { + if (spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive()) + if (spell->GetSpellInfo()->IsMoveAllowedChannel()) + if (HasUnitState(UNIT_STATE_CASTING)) + return true; + } + + if (const_cast<Creature*>(this)->IsFocusing(nullptr, true)) + return true; + + if (HasUnitState(UNIT_STATE_CASTING)) + return true; + + return false; +} + void Creature::StartPickPocketRefillTimer() { _pickpocketLootRestore = time(nullptr) + sWorld->getIntConfig(CONFIG_CREATURE_PICKPOCKET_REFILL); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 1be3e00e39a..cc5608bfc3d 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -328,9 +328,11 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma void MustReacquireTarget() { m_shouldReacquireTarget = true; } // flags the Creature for forced (client displayed) target reacquisition in the next ::Update call void DoNotReacquireTarget() { m_shouldReacquireTarget = false; m_suppressedTarget = ObjectGuid::Empty; m_suppressedOrientation = 0.0f; } void FocusTarget(Spell const* focusSpell, WorldObject const* target); - bool IsFocusing(Spell const* focusSpell = nullptr, bool withDelay = false); + bool IsFocusing(Spell const* focusSpell = nullptr, bool withDelay = false) override; void ReleaseFocus(Spell const* focusSpell = nullptr, bool withDelay = true); + bool IsMovementPreventedByCasting() const override; + // Part of Evade mechanics time_t GetLastDamagedTime() const { return _lastDamagedTime; } void SetLastDamagedTime(time_t val) { _lastDamagedTime = val; } diff --git a/src/server/game/Entities/Creature/CreatureGroups.cpp b/src/server/game/Entities/Creature/CreatureGroups.cpp index 1643a712f8e..07547ae9139 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.cpp +++ b/src/server/game/Entities/Creature/CreatureGroups.cpp @@ -224,13 +224,15 @@ void CreatureGroup::FormationReset(bool dismiss) m_Formed = !dismiss; } -void CreatureGroup::LeaderMoveTo(float x, float y, float z) +void CreatureGroup::LeaderMoveTo(Position destination, uint32 id /*= 0*/, uint32 moveType /*= 0*/, bool orientation /*= false*/) { //! To do: This should probably get its own movement generator or use WaypointMovementGenerator. //! If the leader's path is known, member's path can be plotted as well using formation offsets. if (!m_leader) return; + float x = destination.GetPositionX(), y = destination.GetPositionY(), z = destination.GetPositionZ(); + float pathangle = std::atan2(m_leader->GetPositionY() - y, m_leader->GetPositionX() - x); for (CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) @@ -256,12 +258,9 @@ void CreatureGroup::LeaderMoveTo(float x, float y, float z) if (!member->IsFlying()) member->UpdateGroundPositionZ(dx, dy, dz); - if (member->IsWithinDist(m_leader, dist + MAX_DESYNC)) - member->SetUnitMovementFlags(m_leader->GetUnitMovementFlags()); - else - member->SetWalk(false); + Position point(dx, dy, dz, destination.GetOrientation()); - member->GetMotionMaster()->MovePoint(0, dx, dy, dz); + member->GetMotionMaster()->MoveFormation(id, point, moveType, !member->IsWithinDist(m_leader, dist + MAX_DESYNC), orientation); member->SetHomePosition(dx, dy, dz, pathangle); } } diff --git a/src/server/game/Entities/Creature/CreatureGroups.h b/src/server/game/Entities/Creature/CreatureGroups.h index 38e86ea4c12..85eb7454432 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.h +++ b/src/server/game/Entities/Creature/CreatureGroups.h @@ -19,6 +19,7 @@ #define _FORMATIONS_H #include "Define.h" +#include "Position.h" #include "ObjectGuid.h" #include <unordered_map> #include <map> @@ -87,7 +88,7 @@ class TC_GAME_API CreatureGroup void RemoveMember(Creature* member); void FormationReset(bool dismiss); - void LeaderMoveTo(float x, float y, float z); + void LeaderMoveTo(Position destination, uint32 id = 0, uint32 moveType = 0, bool orientation = false); void MemberAttackStart(Creature* member, Unit* target); }; diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index 63578dfd872..18613f0fda2 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -187,7 +187,7 @@ void TempSummon::InitStats(uint32 duration) if (owner && IsTrigger() && m_spells[0]) { - setFaction(owner->getFaction()); + SetFaction(owner->GetFaction()); SetLevel(owner->getLevel()); if (owner->GetTypeId() == TYPEID_PLAYER) m_ControlledByPlayer = true; @@ -212,9 +212,9 @@ void TempSummon::InitStats(uint32 duration) } if (m_Properties->Faction) - setFaction(m_Properties->Faction); + SetFaction(m_Properties->Faction); else if (IsVehicle() && owner) // properties should be vehicle - setFaction(owner->getFaction()); + SetFaction(owner->GetFaction()); } void TempSummon::InitSummon() @@ -307,7 +307,7 @@ void Minion::InitStats(uint32 duration) SetReactState(REACT_PASSIVE); SetCreatorGUID(GetOwner()->GetGUID()); - setFaction(GetOwner()->getFaction()); + SetFaction(GetOwner()->GetFaction()); GetOwner()->SetMinion(this, true); } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 4e34c8b4785..1bc8383a51c 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -2112,7 +2112,7 @@ void GameObject::CastSpell(Unit* target, uint32 spellId, TriggerCastFlags trigge if (Unit* owner = GetOwner()) { - trigger->setFaction(owner->getFaction()); + trigger->SetFaction(owner->GetFaction()); if (owner->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) trigger->AddUnitFlag(UNIT_FLAG_PVP_ATTACKABLE); // copy pvp state flags from owner @@ -2123,7 +2123,7 @@ void GameObject::CastSpell(Unit* target, uint32 spellId, TriggerCastFlags trigge } else { - trigger->setFaction(spellInfo->IsPositive() ? 35 : 14); + trigger->SetFaction(spellInfo->IsPositive() ? 35 : 14); // Set owner guid for target if no owner available - needed by trigger auras // - trigger gets despawned and there's no caster avalible (see AuraEffect::TriggerSpell()) trigger->CastSpell(target ? target : trigger, spellInfo, triggered, nullptr, nullptr, target ? target->GetGUID() : ObjectGuid::Empty); diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 99235c38da3..56eabab98d1 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -30,6 +30,7 @@ #include "ItemEnchantmentMgr.h" #include "ItemPackets.h" #include "Log.h" +#include "LootItemStorage.h" #include "LootMgr.h" #include "Map.h" #include "ObjectAccessor.h" @@ -763,7 +764,7 @@ void Item::SaveToDB(CharacterDatabaseTransaction& trans) // Delete the items if this is a container if (!loot.isLooted()) - ItemContainerDeleteLootMoneyAndLootItemsFromDB(); + sLootItemStorage->RemoveStoredLootForContainer(GetGUID().GetCounter()); delete this; return; @@ -1058,7 +1059,7 @@ void Item::DeleteFromDB(CharacterDatabaseTransaction& trans) // Delete the items if this is a container if (!loot.isLooted()) - ItemContainerDeleteLootMoneyAndLootItemsFromDB(); + sLootItemStorage->RemoveStoredLootForContainer(GetGUID().GetCounter()); } /*static*/ @@ -2141,191 +2142,6 @@ uint32 Item::GetSellPrice(ItemTemplate const* proto, uint32 quality, uint32 item return 0; } -void Item::ItemContainerSaveLootToDB() -{ - // Saves the money and item loot associated with an openable item to the DB - if (loot.isLooted()) // no money and no loot - return; - - CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - - loot.containerID = GetGUID(); // Save this for when a LootItem is removed - - // Save money - if (loot.gold > 0) - { - CharacterDatabasePreparedStatement* stmt_money = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY); - stmt_money->setUInt64(0, loot.containerID.GetCounter()); - trans->Append(stmt_money); - - stmt_money = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_MONEY); - stmt_money->setUInt64(0, loot.containerID.GetCounter()); - stmt_money->setUInt32(1, loot.gold); - trans->Append(stmt_money); - } - - // Save items - if (!loot.isLooted()) - { - CharacterDatabasePreparedStatement* stmt_items = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEMS); - stmt_items->setUInt64(0, loot.containerID.GetCounter()); - trans->Append(stmt_items); - - // Now insert the items - for (LootItemList::const_iterator _li = loot.items.begin(); _li != loot.items.end(); ++_li) - { - // When an item is looted, it doesn't get removed from the items collection - // but we don't want to resave it. - if (!_li->canSave) - continue; - // Conditions are not checked when loot is generated, it is checked when loot is sent to a player. - // For items that are lootable, loot is saved to the DB immediately, that means that loot can be - // saved to the DB that the player never should have gotten. This check prevents that, so that only - // items that the player should get in loot are in the DB. - // IE: Horde items are not saved to the DB for Ally players. - Player* const guid = GetOwner(); - if (!_li->AllowedForPlayer(guid)) - continue; - - stmt_items = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_ITEMS); - - // container_id, item_id, item_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, context, bonus_list_ids - stmt_items->setUInt64(0, loot.containerID.GetCounter()); - stmt_items->setUInt32(1, _li->itemid); - stmt_items->setUInt32(2, _li->count); - stmt_items->setBool(3, _li->follow_loot_rules); - stmt_items->setBool(4, _li->freeforall); - stmt_items->setBool(5, _li->is_blocked); - stmt_items->setBool(6, _li->is_counted); - stmt_items->setBool(7, _li->is_underthreshold); - stmt_items->setBool(8, _li->needs_quest); - stmt_items->setUInt32(9, _li->randomBonusListId); - stmt_items->setUInt8(10, AsUnderlyingType(_li->context)); - std::ostringstream bonusListIDs; - for (int32 bonusListID : _li->BonusListIDs) - bonusListIDs << bonusListID << ' '; - stmt_items->setString(11, bonusListIDs.str()); - trans->Append(stmt_items); - } - } - - CharacterDatabase.CommitTransaction(trans); -} - -bool Item::ItemContainerLoadLootFromDB() -{ - // Loads the money and item loot associated with an openable item from the DB - // Default. If there are no records for this item then it will be rolled for in Player::SendLoot() - m_lootGenerated = false; - - // Save this for later use - loot.containerID = GetGUID(); - - // First, see if there was any money loot. This gets added directly to the container. - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_MONEY); - stmt->setUInt64(0, loot.containerID.GetCounter()); - PreparedQueryResult money_result = CharacterDatabase.Query(stmt); - - if (money_result) - { - Field* fields = money_result->Fetch(); - loot.gold = fields[0].GetUInt32(); - } - - // Next, load any items that were saved - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_ITEMS); - stmt->setUInt64(0, loot.containerID.GetCounter()); - PreparedQueryResult item_result = CharacterDatabase.Query(stmt); - - if (item_result) - { - // Get a LootTemplate for the container item. This is where - // the saved loot was originally rolled from, we will copy conditions from it - LootTemplate const* lt = LootTemplates_Item.GetLootFor(GetEntry()); - if (lt) - { - do - { - // Create an empty LootItem - LootItem loot_item = LootItem(); - - // Fill in the rest of the LootItem from the DB - Field* fields = item_result->Fetch(); - - // item_id, itm_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, context, bonus_list_ids - loot_item.itemid = fields[0].GetUInt32(); - loot_item.count = fields[1].GetUInt32(); - loot_item.follow_loot_rules = fields[2].GetBool(); - loot_item.freeforall = fields[3].GetBool(); - loot_item.is_blocked = fields[4].GetBool(); - loot_item.is_counted = fields[5].GetBool(); - loot_item.canSave = true; - loot_item.is_underthreshold = fields[6].GetBool(); - loot_item.needs_quest = fields[7].GetBool(); - loot_item.randomBonusListId = fields[8].GetUInt32(); - loot_item.context = ItemContext(fields[9].GetUInt8()); - Tokenizer bonusLists(fields[10].GetString(), ' '); - std::transform(bonusLists.begin(), bonusLists.end(), std::back_inserter(loot_item.BonusListIDs), [](char const* token) - { - return int32(strtol(token, NULL, 10)); - }); - - // Copy the extra loot conditions from the item in the loot template - lt->CopyConditions(&loot_item); - - // If container item is in a bag, add that player as an allowed looter - if (GetBagSlot()) - loot_item.AddAllowedLooter(GetOwner()); - - // Finally add the LootItem to the container - loot.items.push_back(loot_item); - - // Increment unlooted count - loot.unlootedCount++; - - } - while (item_result->NextRow()); - } - } - - // Mark the item if it has loot so it won't be generated again on open - m_lootGenerated = !loot.isLooted(); - - return m_lootGenerated; -} - -void Item::ItemContainerDeleteLootItemsFromDB() -{ - // Deletes items associated with an openable item from the DB - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEMS); - stmt->setUInt64(0, GetGUID().GetCounter()); - CharacterDatabase.Execute(stmt); -} - -void Item::ItemContainerDeleteLootItemFromDB(uint32 itemID) -{ - // Deletes a single item associated with an openable item from the DB - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEM); - stmt->setUInt64(0, GetGUID().GetCounter()); - stmt->setUInt32(1, itemID); - CharacterDatabase.Execute(stmt); -} - -void Item::ItemContainerDeleteLootMoneyFromDB() -{ - // Deletes the money loot associated with an openable item from the DB - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY); - stmt->setUInt64(0, GetGUID().GetCounter()); - CharacterDatabase.Execute(stmt); -} - -void Item::ItemContainerDeleteLootMoneyAndLootItemsFromDB() -{ - // Deletes money and items associated with an openable item from the DB - ItemContainerDeleteLootMoneyFromDB(); - ItemContainerDeleteLootItemsFromDB(); -} - uint32 Item::GetItemLevel(Player const* owner) const { ItemTemplate const* itemTemplate = GetTemplate(); diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index c4c5ee62d5a..007b9ef46c5 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -231,14 +231,6 @@ class TC_GAME_API Item : public Object virtual void DeleteFromDB(CharacterDatabaseTransaction& trans); static void DeleteFromInventoryDB(CharacterDatabaseTransaction& trans, ObjectGuid::LowType itemGuid); - // Lootable items and their contents - void ItemContainerSaveLootToDB(); - bool ItemContainerLoadLootFromDB(); - void ItemContainerDeleteLootItemsFromDB(); - void ItemContainerDeleteLootItemFromDB(uint32 itemID); - void ItemContainerDeleteLootMoneyFromDB(); - void ItemContainerDeleteLootMoneyAndLootItemsFromDB(); - void DeleteFromInventoryDB(CharacterDatabaseTransaction& trans); void SaveRefundDataToDB(); void DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 94fd3e932c8..5e79eb1316c 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1889,7 +1889,7 @@ Creature* WorldObject::SummonTrigger(float x, float y, float z, float ang, uint3 //summon->SetName(GetName()); if (GetTypeId() == TYPEID_PLAYER || GetTypeId() == TYPEID_UNIT) { - summon->setFaction(((Unit*)this)->getFaction()); + summon->SetFaction(((Unit*)this)->GetFaction()); summon->SetLevel(((Unit*)this)->getLevel()); } diff --git a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h index 52813dd4c82..1901e5e623d 100644 --- a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h +++ b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h @@ -147,7 +147,7 @@ public: FactionTemplateEntry const* ft2 = receiver->GetFactionTemplateEntry(); if (ft1 && ft2 && !ft1->IsFriendlyTo(ft2)) // pretend that all other HOSTILE players have own faction, to allow follow, heal, rezz (trade wont work) - factionTemplate = receiver->getFaction(); + factionTemplate = receiver->GetFaction(); } return factionTemplate; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 9c16502dd25..8852af1be8c 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -186,7 +186,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c PhasingHandler::InheritPhaseShift(this, owner); setPetType(petType); - setFaction(owner->getFaction()); + SetFaction(owner->GetFaction()); SetCreatedBySpell(summonSpellId); if (IsCritter()) @@ -1676,7 +1676,7 @@ void Pet::CastPetAuras(bool current) if (!IsPermanentPetFor(owner)) return; - for (PetAuraSet::const_iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end();) + for (auto itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end();) { PetAura const* pa = *itr; ++itr; @@ -1708,7 +1708,7 @@ bool Pet::IsPetAura(Aura const* aura) Player* owner = GetOwner(); // if the owner has that pet aura, return true - for (PetAuraSet::const_iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); ++itr) + for (auto itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); ++itr) { if ((*itr)->GetAura(GetEntry()) == aura->GetId()) return true; diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h index 2e17b976be6..09a6e0a8afe 100644 --- a/src/server/game/Entities/Pet/Pet.h +++ b/src/server/game/Entities/Pet/Pet.h @@ -40,6 +40,7 @@ typedef std::unordered_map<uint32, PetSpell> PetSpellMap; typedef std::vector<uint32> AutoSpellList; class Player; +class PetAura; class TC_GAME_API Pet : public Guardian { diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b932d9e35c4..3dd310f4b52 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -75,6 +75,7 @@ #include "LFGMgr.h" #include "Language.h" #include "Log.h" +#include "LootItemStorage.h" #include "LootMgr.h" #include "LootPackets.h" #include "Mail.h" @@ -90,6 +91,7 @@ #include "OutdoorPvPMgr.h" #include "Pet.h" #include "PetPackets.h" +#include "PoolMgr.h" #include "PhasingHandler.h" #include "QueryCallback.h" #include "QueryHolder.h" @@ -2198,13 +2200,13 @@ void Player::SetGameMaster(bool on) if (on) { m_ExtraFlags |= PLAYER_EXTRA_GM_ON; - setFaction(35); + SetFaction(35); AddPlayerFlag(PLAYER_FLAGS_GM); AddUnitFlag2(UNIT_FLAG2_ALLOW_CHEAT_SPELLS); if (Pet* pet = GetPet()) { - pet->setFaction(35); + pet->SetFaction(35); pet->getHostileRefManager().setOnlineOfflineState(false); } @@ -2228,7 +2230,7 @@ void Player::SetGameMaster(bool on) if (Pet* pet = GetPet()) { - pet->setFaction(getFaction()); + pet->SetFaction(GetFaction()); pet->getHostileRefManager().setOnlineOfflineState(true); } @@ -2936,7 +2938,7 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent if (active) { - if (spellInfo->IsPassive() && IsNeedCastPassiveSpellAtLearn(spellInfo)) + if (spellInfo->IsPassive() && HandlePassiveSpellLearn(spellInfo)) CastSpell(this, spellId, true); } else if (IsInWorld()) @@ -3063,7 +3065,7 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks else if (spellInfo->IsPassive()) { - if (IsNeedCastPassiveSpellAtLearn(spellInfo)) + if (HandlePassiveSpellLearn(spellInfo)) CastSpell(this, spellId, true); } else if (spellInfo->HasEffect(SPELL_EFFECT_SKILL_STEP)) @@ -3194,7 +3196,7 @@ void Player::RemoveTemporarySpell(uint32 spellId) m_spells.erase(itr); } -bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const +bool Player::HandlePassiveSpellLearn(SpellInfo const* spellInfo) { // note: form passives activated with shapeshift spells be implemented by HandleShapeshiftBoosts instead of spell_learn_spell // talent dependent passives activated at form apply have proper stance data @@ -3206,12 +3208,16 @@ bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const need_cast &= IsCurrentSpecMasterySpell(spellInfo); // Check EquippedItemClass - // passive spells which apply aura and have an item requirement are to be added in Player::ApplyItemDependentAuras - if (spellInfo->IsPassive() && spellInfo->EquippedItemClass >= 0) + // passive spells which apply aura and have an item requirement are to be added manually, instead of casted + if (spellInfo->EquippedItemClass >= 0) { for (SpellEffectInfo const* effectInfo : spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE)) if (effectInfo && effectInfo->IsAura()) + { + if (!HasAura(spellInfo->Id) && HasItemFitToSpellRequirements(spellInfo)) + AddAura(spellInfo->Id, this); return false; + } } //Check CasterAuraStates @@ -6292,7 +6298,7 @@ void Player::setFactionForRace(uint8 race) m_team = TeamForRace(race); ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race); - setFaction(rEntry ? rEntry->FactionID : 0); + SetFaction(rEntry ? rEntry->FactionID : 0); } ReputationRank Player::GetReputationRank(uint32 faction) const @@ -6415,8 +6421,8 @@ void Player::RewardReputation(Unit* victim, float rate) FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction1); uint32 current_reputation_rank1 = GetReputationMgr().GetRank(factionEntry1); - if (factionEntry1 && current_reputation_rank1 <= Rep->ReputationMaxCap1) - GetReputationMgr().ModifyReputation(factionEntry1, donerep1); + if (factionEntry1) + GetReputationMgr().ModifyReputation(factionEntry1, donerep1, current_reputation_rank1 > Rep->ReputationMaxCap1); } if (Rep->RepFaction2 && (!Rep->TeamDependent || team == HORDE)) @@ -6426,8 +6432,8 @@ void Player::RewardReputation(Unit* victim, float rate) FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction2); uint32 current_reputation_rank2 = GetReputationMgr().GetRank(factionEntry2); - if (factionEntry2 && current_reputation_rank2 <= Rep->ReputationMaxCap2) - GetReputationMgr().ModifyReputation(factionEntry2, donerep2); + if (factionEntry2) + GetReputationMgr().ModifyReputation(factionEntry2, donerep2, current_reputation_rank2 > Rep->ReputationMaxCap2); } } @@ -6479,7 +6485,7 @@ void Player::RewardReputation(Quest const* quest) rep = CalculateReputationGain(REPUTATION_SOURCE_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], noQuestBonus); bool noSpillover = (quest->GetRewardReputationMask() & (1 << i)) != 0; - GetReputationMgr().ModifyReputation(factionEntry, rep, noSpillover); + GetReputationMgr().ModifyReputation(factionEntry, rep, false, noSpillover); } } @@ -6937,7 +6943,7 @@ void Player::ModifyCurrency(uint32 id, int32 count, bool printLog/* = true*/, bo { if (currency->Flags & CURRENCY_FLAG_HIGH_PRECISION) count /= 100; - GetReputationMgr().ModifyReputation(factionEntry, count, true); + GetReputationMgr().ModifyReputation(factionEntry, count, false, true); return; } @@ -8834,9 +8840,12 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa loot = &item->loot; + // Store container id + loot->containerID = item->GetGUID(); + // 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 && !item->ItemContainerLoadLootFromDB()) + // fails then this is first time opening, generate loot + if (!item->m_lootGenerated && !sLootItemStorage->LoadStoredLoot(item, this)) { item->m_lootGenerated = true; loot->clear(); @@ -8859,7 +8868,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa // 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) - item->ItemContainerSaveLootToDB(); + sLootItemStorage->AddNewStoredLoot(loot, this); break; } @@ -12799,6 +12808,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount()); sScriptMgr->OnItemRemove(this, pItem); + ItemTemplate const* pProto = pItem->GetTemplate(); if (bag == INVENTORY_SLOT_BAG_0) { SetInvSlot(slot, ObjectGuid::Empty); @@ -12806,10 +12816,8 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) // equipment and equipped bags can have applied bonuses if (slot < INVENTORY_SLOT_BAG_END) { - ItemTemplate const* pProto = pItem->GetTemplate(); - // item set bonuses applied only at equip and removed at unequip, and still active for broken items - if (pProto && pProto->GetItemSet()) + if (pProto->GetItemSet()) RemoveItemsSetItem(this, pProto); _ApplyItemMods(pItem, slot, false); @@ -12843,9 +12851,8 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) // Delete rolled money / loot from db. // MUST be done before RemoveFromWorld() or GetTemplate() fails - if (ItemTemplate const* pTmp = pItem->GetTemplate()) - if (pTmp->GetFlags() & ITEM_FLAG_HAS_LOOT) - pItem->ItemContainerDeleteLootMoneyAndLootItemsFromDB(); + if (pProto->GetFlags() & ITEM_FLAG_HAS_LOOT) + sLootItemStorage->RemoveStoredLootForContainer(pItem->GetGUID().GetCounter()); if (IsInWorld() && update) { @@ -13762,7 +13769,7 @@ void Player::SwapItem(uint16 src, uint16 dst) { if (Item* bagItem = bag->GetItemByPos(i)) { - if (bagItem->m_lootGenerated) + if (bagItem->GetGUID() == GetLootGUID()) { m_session->DoLootRelease(GetLootGUID()); released = true; // so we don't need to look at dstBag @@ -13779,7 +13786,7 @@ void Player::SwapItem(uint16 src, uint16 dst) { if (Item* bagItem = bag->GetItemByPos(i)) { - if (bagItem->m_lootGenerated) + if (bagItem->GetGUID() == GetLootGUID()) { m_session->DoLootRelease(GetLootGUID()); break; @@ -13868,7 +13875,13 @@ void Player::RemoveItemFromBuyBackSlot(uint32 slot, bool del) { pItem->RemoveFromWorld(); if (del) + { + if (ItemTemplate const* itemTemplate = pItem->GetTemplate()) + if (itemTemplate->GetFlags() & ITEM_FLAG_HAS_LOOT) + sLootItemStorage->RemoveStoredLootForContainer(pItem->GetGUID().GetCounter()); + pItem->SetState(ITEM_REMOVED, this); + } } m_items[slot] = nullptr; @@ -15204,8 +15217,8 @@ bool Player::CanSeeStartQuest(Quest const* quest) { if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this) && SatisfyQuestClass(quest, false) && SatisfyQuestRace(quest, false) && SatisfyQuestSkill(quest, false) && SatisfyQuestExclusiveGroup(quest, false) && SatisfyQuestReputation(quest, false) && - SatisfyQuestDependentQuests(quest, false) && SatisfyQuestNextChain(quest, false) && - SatisfyQuestPrevChain(quest, false) && SatisfyQuestDay(quest, false) && SatisfyQuestWeek(quest, false) && + SatisfyQuestDependentQuests(quest, false) && + SatisfyQuestDay(quest, false) && SatisfyQuestWeek(quest, false) && SatisfyQuestMonth(quest, false) && SatisfyQuestSeasonal(quest, false)) { return int32(getLevel() + sWorld->getIntConfig(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF)) >= GetQuestMinLevel(quest); @@ -15221,7 +15234,6 @@ bool Player::CanTakeQuest(Quest const* quest, bool msg) && SatisfyQuestClass(quest, msg) && SatisfyQuestRace(quest, msg) && SatisfyQuestLevel(quest, msg) && SatisfyQuestSkill(quest, msg) && SatisfyQuestReputation(quest, msg) && SatisfyQuestDependentQuests(quest, msg) && SatisfyQuestTimed(quest, msg) - && SatisfyQuestNextChain(quest, msg) && SatisfyQuestPrevChain(quest, msg) && SatisfyQuestDay(quest, msg) && SatisfyQuestWeek(quest, msg) && SatisfyQuestMonth(quest, msg) && SatisfyQuestSeasonal(quest, msg) && SatisfyQuestConditions(quest, msg); @@ -15259,7 +15271,7 @@ bool Player::CanCompleteQuest(uint32 quest_id) if (!qInfo) return false; - if (!qInfo->IsRepeatable() && m_RewardedQuests.find(quest_id) != m_RewardedQuests.end()) + if (!qInfo->IsRepeatable() && GetQuestRewardStatus(quest_id)) return false; // not allow re-complete quest // auto complete quest @@ -15652,10 +15664,10 @@ uint32 Player::GetQuestMoneyReward(Quest const* quest) const uint32 Player::GetQuestXPReward(Quest const* quest) { - bool rewarded = (m_RewardedQuests.find(quest->GetQuestId()) != m_RewardedQuests.end()); + bool rewarded = IsQuestRewarded(quest->GetQuestId()) && !quest->IsDFQuest(); // Not give XP in case already completed once repeatable quest - if (rewarded && !quest->IsDFQuest()) + if (rewarded) return 0; uint32 XP = quest->XPValue(this) * sWorld->getRate(RATE_XP_QUEST); @@ -15807,7 +15819,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, SendNewItem(item, quest->RewardItemCount[i], true, false); } else if (quest->IsDFQuest()) - SendItemRetrievalMail(quest->RewardItemId[i], quest->RewardItemCount[i], ItemContext::Quest_Reward); + SendItemRetrievalMail(itemId, quest->RewardItemCount[i], ItemContext::Quest_Reward); } } } @@ -15886,12 +15898,6 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, if (quest->CanIncreaseRewardedQuestCounters()) SetRewardedQuest(quest_id); - // StoreNewItem, mail reward, etc. save data directly to the database - // to prevent exploitable data desynchronisation we save the quest status to the database too - // (to prevent rewarding this quest another time while rewards were already given out) - CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); - _SaveQuestStatus(trans); - SendQuestReward(quest, questGiver ? questGiver->ToCreature() : nullptr, XP, !announce); // cast spells after mark quest complete (some spells have quest completed state requirements in spell_area data) @@ -15927,6 +15933,9 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, UpdateCriteria(CRITERIA_TYPE_COMPLETE_QUEST_COUNT); UpdateCriteria(CRITERIA_TYPE_COMPLETE_QUEST, quest->GetQuestId()); + // make full db save + SaveToDB(false); + if (uint32 questBit = sDB2Manager.GetQuestUniqueBitFlag(quest_id)) SetQuestCompletedBit(questBit, true); @@ -15957,13 +15966,15 @@ void Player::FailQuest(uint32 questId) { if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId)) { - // Already complete quests shouldn't turn failed. - if (GetQuestStatus(questId) == QUEST_STATUS_COMPLETE && !quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED)) - return; + QuestStatus qStatus = GetQuestStatus(questId); - // You can't fail a quest if you don't have it, or if it's already rewarded. - if (GetQuestStatus(questId) == QUEST_STATUS_NONE || GetQuestStatus(questId) == QUEST_STATUS_REWARDED) - return; + // we can only fail incomplete quest or... + if (qStatus != QUEST_STATUS_INCOMPLETE) + { + // completed timed quest with no requirements + if (qStatus != QUEST_STATUS_COMPLETE || !quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED) || !quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_COMPLETED_AT_START)) + return; + } SetQuestStatus(questId, QUEST_STATUS_FAILED); @@ -16344,7 +16355,7 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) const } // alternative quest already started or completed - but don't check rewarded states if both are repeatable - if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE || (!(qInfo->IsRepeatable() && Nquest->IsRepeatable()) && (m_RewardedQuests.find(exclude_Id) != m_RewardedQuests.end()))) + if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE || (!(qInfo->IsRepeatable() && Nquest->IsRepeatable()) && GetQuestRewardStatus(exclude_Id))) { if (msg) { @@ -16358,62 +16369,6 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) const return true; } -bool Player::SatisfyQuestNextChain(Quest const* qInfo, bool msg) const -{ - uint32 nextQuest = qInfo->GetNextQuestInChain(); - if (!nextQuest) - return true; - - // next quest in chain already started or completed - if (GetQuestStatus(nextQuest) != QUEST_STATUS_NONE) // GetQuestStatus returns QUEST_STATUS_COMPLETED for rewarded quests - { - if (msg) - { - SendCanTakeQuestResponse(QUEST_ERR_NONE); - TC_LOG_DEBUG("misc", "Player::SatisfyQuestNextChain: Sent QUEST_ERR_NONE (QuestID: %u) because player '%s' (%s) already did or started next quest in chain.", - qInfo->GetQuestId(), GetName().c_str(), GetGUID().ToString().c_str()); - } - return false; - } - - // check for all quests further up the chain - // only necessary if there are quest chains with more than one quest that can be skipped - //return SatisfyQuestNextChain(qInfo->GetNextQuestInChain(), msg); - return true; -} - -bool Player::SatisfyQuestPrevChain(Quest const* qInfo, bool msg) -{ - // No previous quest in chain - if (qInfo->PrevChainQuests.empty()) - return true; - - for (uint32 prevQuestId : qInfo->PrevChainQuests) - { - auto itr = m_QuestStatus.find(prevQuestId); - - // If any of the previous quests in chain active, return false - if (itr != m_QuestStatus.end() && itr->second.Status != QUEST_STATUS_NONE) - { - if (msg) - { - SendCanTakeQuestResponse(QUEST_ERR_NONE); - TC_LOG_DEBUG("misc", "Player::SatisfyQuestNextChain: Sent QUEST_ERR_NONE (QuestID: %u) because player '%s' (%s) already did or started next quest in chain.", - qInfo->GetQuestId(), GetName().c_str(), GetGUID().ToString().c_str()); - } - return false; - } - - // check for all quests further down the chain - // only necessary if there are quest chains with more than one quest that can be skipped - //if (!SatisfyQuestPrevChain(prevId, msg)) - // return false; - } - - // No previous quest in chain active - return true; -} - bool Player::SatisfyQuestDay(Quest const* qInfo, bool /*msg*/) const { if (!qInfo->IsDaily() && !qInfo->IsDFQuest()) @@ -16541,7 +16496,7 @@ bool Player::GetQuestRewardStatus(uint32 quest_id) const // for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once if (!qInfo->IsRepeatable()) - return m_RewardedQuests.find(quest_id) != m_RewardedQuests.end(); + return IsQuestRewarded(quest_id); return false; } @@ -16556,14 +16511,8 @@ QuestStatus Player::GetQuestStatus(uint32 quest_id) const if (itr != m_QuestStatus.end()) return itr->second.Status; - if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id)) - { - if (qInfo->IsSeasonal() && !qInfo->IsRepeatable()) - return SatisfyQuestSeasonal(qInfo, false) ? QUEST_STATUS_NONE : QUEST_STATUS_REWARDED; - - if (!qInfo->IsRepeatable() && IsQuestRewarded(quest_id)) - return QUEST_STATUS_REWARDED; - } + if (GetQuestRewardStatus(quest_id)) + return QUEST_STATUS_REWARDED; } return QUEST_STATUS_NONE; } @@ -16571,7 +16520,22 @@ QuestStatus Player::GetQuestStatus(uint32 quest_id) const bool Player::CanShareQuest(uint32 quest_id) const { Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id); - return qInfo && qInfo->HasFlag(QUEST_FLAGS_SHARABLE) && IsActiveQuest(quest_id); + if (qInfo && qInfo->HasFlag(QUEST_FLAGS_SHARABLE)) + { + QuestStatusMap::const_iterator itr = m_QuestStatus.find(quest_id); + if (itr != m_QuestStatus.end()) + { + // in pool and not currently available (wintergrasp weekly, dalaran weekly) - can't share + if (sPoolMgr->IsPartOfAPool<Quest>(quest_id) && !sPoolMgr->IsSpawnedObject<Quest>(quest_id)) + { + SendPushToPartyResponse(this, QUEST_PUSH_NOT_DAILY); + return false; + } + + return true; + } + } + return false; } void Player::SetQuestStatus(uint32 questId, QuestStatus status, bool update /*= true*/) @@ -16617,6 +16581,18 @@ void Player::RemoveRewardedQuest(uint32 questId, bool update /*= true*/) if (uint32 questBit = sDB2Manager.GetQuestUniqueBitFlag(questId)) SetQuestCompletedBit(questBit, false); + // Remove seasonal quest also + Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId); + if (qInfo->IsSeasonal()) + { + uint16 eventId = qInfo->GetEventIdForQuest(); + if (m_seasonalquests.find(eventId) != m_seasonalquests.end()) + { + m_seasonalquests[eventId].erase(questId); + m_SeasonalQuestChanged = true; + } + } + if (update) SendQuestUpdate(questId); } @@ -16875,8 +16851,10 @@ void Player::AreaExploredOrEventHappens(uint32 questId) { q_status.Explored = true; m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE; - SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE); - SendQuestComplete(questId); + + // if we cannot complete quest send exploration succeded (to mark exploration on client) + if (!CanCompleteQuest(questId)) + SendQuestComplete(questId); }**/ } if (CanCompleteQuest(questId)) @@ -17651,7 +17629,7 @@ void Player::SendQuestConfirmAccept(Quest const* quest, Player* receiver) const receiver->GetSession()->SendPacket(packet.Write()); } -void Player::SendPushToPartyResponse(Player* player, QuestPushReason reason) const +void Player::SendPushToPartyResponse(Player const* player, QuestPushReason reason) const { if (player) { @@ -19840,7 +19818,8 @@ void Player::_LoadBoundInstances(PreparedQueryResult result) Group* group = GetGroup(); - //QueryResult* result = CharacterDatabase.PQuery("SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid)); + // 0 1 2 3 4 5 6 + // SELECT id, permanent, map, difficulty, extendState, resettime, entranceId FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = ? if (result) { do @@ -19853,7 +19832,7 @@ void Player::_LoadBoundInstances(PreparedQueryResult result) uint8 difficulty = fields[3].GetUInt8(); BindExtensionState extendState = BindExtensionState(fields[4].GetUInt8()); - time_t resetTime = time_t(fields[5].GetUInt32()); + time_t resetTime = time_t(fields[5].GetUInt64()); // the resettime for normal instances is only saved when the InstanceSave is unloaded // so the value read from the DB may be wrong here but only if the InstanceSave is loaded // and in that case it is not used @@ -20874,12 +20853,25 @@ void Player::_SaveInventory(CharacterDatabaseTransaction& trans) for (uint8 i = BUYBACK_SLOT_START; i < BUYBACK_SLOT_END; ++i) { Item* item = m_items[i]; - if (!item || item->GetState() == ITEM_NEW) + if (!item) + continue; + + if (item->GetState() == ITEM_NEW) + { + if (ItemTemplate const* itemTemplate = item->GetTemplate()) + if (itemTemplate->GetFlags() & ITEM_FLAG_HAS_LOOT) + sLootItemStorage->RemoveStoredLootForContainer(item->GetGUID().GetCounter()); + continue; + } item->DeleteFromInventoryDB(trans); item->DeleteFromDB(trans); m_items[i]->FSetState(ITEM_NEW); + + if (ItemTemplate const* itemTemplate = item->GetTemplate()) + if (itemTemplate->GetFlags() & ITEM_FLAG_HAS_LOOT) + sLootItemStorage->RemoveStoredLootForContainer(item->GetGUID().GetCounter()); } // Updated played time for refundable items. We don't do this in Player::Update because there's simply no need for it, @@ -21275,7 +21267,7 @@ void Player::_SaveWeeklyQuestStatus(CharacterDatabaseTransaction& trans) void Player::_SaveSeasonalQuestStatus(CharacterDatabaseTransaction& trans) { - if (!m_SeasonalQuestChanged || m_seasonalquests.empty()) + if (!m_SeasonalQuestChanged) return; // we don't need transactions here. @@ -21283,6 +21275,11 @@ void Player::_SaveSeasonalQuestStatus(CharacterDatabaseTransaction& trans) stmt->setUInt64(0, GetGUID().GetCounter()); trans->Append(stmt); + m_SeasonalQuestChanged = false; + + if (m_seasonalquests.empty()) + return; + for (SeasonalEventQuestMap::const_iterator iter = m_seasonalquests.begin(); iter != m_seasonalquests.end(); ++iter) { uint16 eventId = iter->first; @@ -21298,8 +21295,6 @@ void Player::_SaveSeasonalQuestStatus(CharacterDatabaseTransaction& trans) trans->Append(stmt); } } - - m_SeasonalQuestChanged = false; } void Player::_SaveMonthlyQuestStatus(CharacterDatabaseTransaction& trans) @@ -21401,13 +21396,14 @@ void Player::_SaveSpells(CharacterDatabaseTransaction& trans) if (itr->second->state == PLAYERSPELL_REMOVED) { delete itr->second; - m_spells.erase(itr++); + itr = m_spells.erase(itr); + continue; } - else - { + + if (itr->second->state != PLAYERSPELL_TEMPORARY) itr->second->state = PLAYERSPELL_UNCHANGED; - ++itr; - } + + ++itr; } } @@ -21838,6 +21834,20 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) } } +void Player::AddPetAura(PetAura const* petSpell) +{ + m_petAuras.insert(petSpell); + if (Pet* pet = GetPet()) + pet->CastPetAura(petSpell); +} + +void Player::RemovePetAura(PetAura const* petSpell) +{ + m_petAuras.erase(petSpell); + if (Pet* pet = GetPet()) + pet->RemoveAurasDueToSpell(petSpell->GetAura(pet->GetEntry())); +} + void Player::StopCastingCharm() { Unit* charm = GetCharm(); @@ -22622,7 +22632,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc float discount = GetReputationPriceDiscount(npc); totalcost = uint32(ceil(totalcost * discount)); firstcost = uint32(ceil(firstcost * discount)); - m_taxi.SetFlightMasterFactionTemplateId(npc->getFaction()); + m_taxi.SetFlightMasterFactionTemplateId(npc->GetFaction()); } else m_taxi.SetFlightMasterFactionTemplateId(0); @@ -26225,7 +26235,7 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot, AELootResult* aeResult/* // LootItem is being removed (looted) from the container, delete it from the DB. if (!loot->containerID.IsEmpty()) - loot->DeleteLootItemFromContainerItemDB(item->itemid); + sLootItemStorage->RemoveStoredLootItemForContainer(loot->containerID.GetCounter(), item->itemid, item->count); } else SendEquipError(msg, nullptr, nullptr, item->itemid); @@ -28217,7 +28227,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy PhasingHandler::InheritPhaseShift(pet, this); pet->SetCreatorGUID(GetGUID()); - pet->setFaction(getFaction()); + pet->SetFaction(GetFaction()); pet->SetNpcFlags(UNIT_NPC_FLAG_NONE); pet->SetNpcFlags2(UNIT_NPC_FLAG_2_NONE); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 0eacdb13fe1..dafedb22e77 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -78,6 +78,7 @@ class Item; class LootStore; class OutdoorPvP; class Pet; +class PetAura; class PlayerAI; class PlayerAchievementMgr; class PlayerMenu; @@ -1097,6 +1098,11 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> Pet* SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime); void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false); + // pet auras + std::unordered_set<PetAura const*> m_petAuras; + void AddPetAura(PetAura const* petSpell); + void RemovePetAura(PetAura const* petSpell); + /// Handles said message in regular chat based on declared language and in config pre-defined Range. void Say(std::string const& text, Language language, WorldObject const* = nullptr) override; void Say(uint32 textId, WorldObject const* target = nullptr) override; @@ -1352,8 +1358,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool SatisfyQuestConditions(Quest const* qInfo, bool msg); bool SatisfyQuestTimed(Quest const* qInfo, bool msg) const; bool SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) const; - bool SatisfyQuestNextChain(Quest const* qInfo, bool msg) const; - bool SatisfyQuestPrevChain(Quest const* qInfo, bool msg); bool SatisfyQuestDay(Quest const* qInfo, bool msg) const; bool SatisfyQuestWeek(Quest const* qInfo, bool msg) const; bool SatisfyQuestMonth(Quest const* qInfo, bool msg) const; @@ -1419,7 +1423,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void SendQuestTimerFailed(uint32 questID) const; void SendCanTakeQuestResponse(QuestFailedReason reason, bool sendErrorMessage = true, std::string reasonText = "") const; void SendQuestConfirmAccept(Quest const* quest, Player* receiver) const; - void SendPushToPartyResponse(Player* player, QuestPushReason reason) const; + void SendPushToPartyResponse(Player const* player, QuestPushReason reason) const; void SendQuestUpdateAddCredit(Quest const* quest, ObjectGuid guid, QuestObjective const& obj, uint16 count) const; void SendQuestUpdateAddCreditSimple(QuestObjective const& obj) const; void SendQuestUpdateAddPlayer(Quest const* quest, uint16 newCount) const; @@ -1556,7 +1560,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool HasActiveSpell(uint32 spell) const; // show in spellbook SpellInfo const* GetCastSpellInfo(SpellInfo const* spellInfo) const override; bool IsSpellFitByClassAndRace(uint32 spell_id) const; - bool IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const; + bool HandlePassiveSpellLearn(SpellInfo const* spellInfo); bool IsCurrentSpecMasterySpell(SpellInfo const* spellInfo) const; void SendProficiency(ItemClass itemClass, uint32 itemSubclassMask) const; diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 2170ba4d7c7..88fb805a71d 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -577,7 +577,7 @@ void Player::UpdateHealingDonePercentMod() SetUpdateFieldStatValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ModHealingDonePercent), value); } -const float m_diminishing_k[MAX_CLASSES] = +float const m_diminishing_k[MAX_CLASSES] = { 0.9560f, // Warrior 0.9560f, // Paladin @@ -593,27 +593,50 @@ const float m_diminishing_k[MAX_CLASSES] = 0.9830f // Demon Hunter }; -void Player::UpdateParryPercentage() +// helper function +float CalculateDiminishingReturns(float const (&capArray)[MAX_CLASSES], uint8 playerClass, float nonDiminishValue, float diminishValue) { - const float parry_cap[MAX_CLASSES] = - { - 65.631440f, // Warrior - 65.631440f, // Paladin - 145.560408f, // Hunter - 145.560408f, // Rogue - 0.0f, // Priest - 65.631440f, // DK - 145.560408f, // Shaman - 0.0f, // Mage - 0.0f, // Warlock - 90.6425f, // Monk - 0.0f, // Druid - 65.631440f // Demon Hunter - }; + // 1 1 k cx + // --- = --- + --- <=> x' = -------- + // x' c x x + ck + + // where: + // k is m_diminishing_k for that class + // c is capArray for that class + // x is chance before DR (diminishValue) + // x' is chance after DR (our result) + + uint32 const classIdx = playerClass - 1; + + float const k = m_diminishing_k[classIdx]; + float const c = capArray[classIdx]; + + float result = c * diminishValue / (diminishValue + c * k); + result += nonDiminishValue; + return result; +} + +float const parry_cap[MAX_CLASSES] = +{ + 65.631440f, // Warrior + 65.631440f, // Paladin + 145.560408f, // Hunter + 145.560408f, // Rogue + 0.0f, // Priest + 65.631440f, // DK + 145.560408f, // Shaman + 0.0f, // Mage + 0.0f, // Warlock + 90.6425f, // Monk + 0.0f, // Druid + 65.631440f // Demon Hunter +}; +void Player::UpdateParryPercentage() +{ // No parry float value = 0.0f; - uint32 pclass = getClass()-1; + uint32 pclass = getClass() - 1; if (CanParry() && parry_cap[pclass] > 0.0f) { float nondiminishing = 5.0f; @@ -621,8 +644,9 @@ void Player::UpdateParryPercentage() float diminishing = GetRatingBonusValue(CR_PARRY); // Parry from SPELL_AURA_MOD_PARRY_PERCENT aura nondiminishing += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT); + // apply diminishing formula to diminishing parry chance - value = nondiminishing + diminishing * parry_cap[pclass] / (diminishing + parry_cap[pclass] * m_diminishing_k[pclass]); + value = CalculateDiminishingReturns(parry_cap, getClass(), nondiminishing, diminishing); if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE)) value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_PARRY) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_PARRY) : value; @@ -631,33 +655,33 @@ void Player::UpdateParryPercentage() SetUpdateFieldStatValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ParryPercentage), value); } -void Player::UpdateDodgePercentage() +float const dodge_cap[MAX_CLASSES] = { - const float dodge_cap[MAX_CLASSES] = - { - 65.631440f, // Warrior - 65.631440f, // Paladin - 145.560408f, // Hunter - 145.560408f, // Rogue - 150.375940f, // Priest - 65.631440f, // DK - 145.560408f, // Shaman - 150.375940f, // Mage - 150.375940f, // Warlock - 145.560408f, // Monk - 116.890707f, // Druid - 145.560408f // Demon Hunter - }; + 65.631440f, // Warrior + 65.631440f, // Paladin + 145.560408f, // Hunter + 145.560408f, // Rogue + 150.375940f, // Priest + 65.631440f, // DK + 145.560408f, // Shaman + 150.375940f, // Mage + 150.375940f, // Warlock + 145.560408f, // Monk + 116.890707f, // Druid + 145.560408f // Demon Hunter +}; +void Player::UpdateDodgePercentage() +{ float diminishing = 0.0f, nondiminishing = 0.0f; GetDodgeFromAgility(diminishing, nondiminishing); // Dodge from SPELL_AURA_MOD_DODGE_PERCENT aura nondiminishing += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT); // Dodge from rating diminishing += GetRatingBonusValue(CR_DODGE); + // apply diminishing formula to diminishing dodge chance - uint32 pclass = getClass()-1; - float value = nondiminishing + (diminishing * dodge_cap[pclass] / (diminishing + dodge_cap[pclass] * m_diminishing_k[pclass])); + float value = CalculateDiminishingReturns(dodge_cap, getClass(), nondiminishing, diminishing); if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE)) value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_DODGE) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_DODGE) : value; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 0f809261bc0..ad69398d494 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -285,12 +285,12 @@ SpellNonMeleeDamage::SpellNonMeleeDamage(Unit* _attacker, Unit* _target, uint32 } Unit::Unit(bool isWorldObject) : - WorldObject(isWorldObject), m_playerMovingMe(NULL), m_lastSanctuaryTime(0), + WorldObject(isWorldObject), m_playerMovingMe(nullptr), m_lastSanctuaryTime(0), IsAIEnabled(false), NeedChangeAI(false), LastCharmerGUID(), m_ControlledByPlayer(false), movespline(new Movement::MoveSpline()), - i_AI(NULL), i_disabledAI(NULL), m_AutoRepeatFirstCast(false), m_procDeep(0), + i_AI(nullptr), i_disabledAI(nullptr), m_AutoRepeatFirstCast(false), m_procDeep(0), m_removedAurasCount(0), i_motionMaster(new MotionMaster(this)), m_regenTimer(0), m_ThreatManager(this), - m_vehicle(NULL), m_vehicleKit(NULL), m_unitTypeMask(UNIT_MASK_NONE), + m_vehicle(nullptr), m_vehicleKit(nullptr), m_unitTypeMask(UNIT_MASK_NONE), m_HostileRefManager(this), _aiAnimKitId(0), _movementAnimKitId(0), _meleeAnimKitId(0), _spellHistory(new SpellHistory(this)) { @@ -389,10 +389,11 @@ Unit::Unit(bool isWorldObject) : m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE); - _lastLiquid = NULL; + _lastLiquid = nullptr; _oldFactionId = 0; _isWalkingBeforeCharm = false; + _instantCast = false; } //////////////////////////////////////////////////////////// @@ -1474,27 +1475,22 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) GetTypeId() != TYPEID_PLAYER && !ToCreature()->IsControlledByPlayer() && !victim->HasInArc(float(M_PI), this) && (victim->GetTypeId() == TYPEID_PLAYER || !victim->ToCreature()->isWorldBoss())&& !victim->IsVehicle()) { - // -probability is between 0% and 40% // 20% base chance - float Probability = 20.0f; + float chance = 20.0f; // there is a newbie protection, at level 10 just 7% base chance; assuming linear function if (victim->getLevel() < 30) - Probability = 0.65f * victim->GetLevelForTarget(this) + 0.5f; - - uint32 VictimDefense = victim->GetMaxSkillValueForLevel(this); - uint32 AttackerMeleeSkill = GetMaxSkillValueForLevel(); - - Probability *= AttackerMeleeSkill/(float)VictimDefense*0.16f; + chance = 0.65f * victim->GetLevelForTarget(this) + 0.5f; - if (Probability < 0) - Probability = 0; + uint32 const victimDefense = victim->GetMaxSkillValueForLevel(this); + uint32 const attackerMeleeSkill = GetMaxSkillValueForLevel(); - if (Probability > 40.0f) - Probability = 40.0f; + chance *= attackerMeleeSkill / float(victimDefense) * 0.16f; - if (roll_chance_f(Probability)) - CastSpell(victim, 1604, true); + // -probability is between 0% and 40% + RoundToInterval(chance, 0.0f, 40.0f); + if (roll_chance_f(chance)) + CastSpell(victim, 1604 /*SPELL_DAZED*/, true); } if (GetTypeId() == TYPEID_PLAYER) @@ -1676,31 +1672,30 @@ uint32 Unit::CalcSpellResistedDamage(Unit* attacker, Unit* victim, uint32 damage return 0; } - float averageResist = GetEffectiveResistChance(this, schoolMask, victim, spellInfo); - - float discreteResistProbability[11]; - for (uint32 i = 0; i < 11; ++i) - { - discreteResistProbability[i] = 0.5f - 2.5f * std::fabs(0.1f * i - averageResist); - if (discreteResistProbability[i] < 0.0f) - discreteResistProbability[i] = 0.0f; - } + float const averageResist = CalculateAverageResistReduction(schoolMask, victim, spellInfo); + float discreteResistProbability[11] = { }; if (averageResist <= 0.1f) { discreteResistProbability[0] = 1.0f - 7.5f * averageResist; discreteResistProbability[1] = 5.0f * averageResist; discreteResistProbability[2] = 2.5f * averageResist; } + else + { + for (uint32 i = 0; i < 11; ++i) + discreteResistProbability[i] = std::max(0.5f - 2.5f * std::fabs(0.1f * i - averageResist), 0.0f); + } - uint32 resistance = 0; - float r = float(rand_norm()); - float probabilitySum = discreteResistProbability[0]; + float roll = float(rand_norm()); + float probabilitySum = 0.0f; - while (r >= probabilitySum && resistance < 10) - probabilitySum += discreteResistProbability[++resistance]; + uint32 resistance = 0; + for (; resistance < 11; ++resistance) + if (roll < (probabilitySum += discreteResistProbability[resistance])) + break; - float damageResisted = float(damage * resistance / 10); + float damageResisted = damage * resistance / 10.f; if (damageResisted > 0.0f) // if any damage was resisted { int32 ignoredResistance = 0; @@ -1714,31 +1709,31 @@ uint32 Unit::CalcSpellResistedDamage(Unit* attacker, Unit* victim, uint32 damage if (spellInfo && spellInfo->HasAttribute(SPELL_ATTR0_CU_SCHOOLMASK_NORMAL_WITH_MAGIC)) { uint32 damageAfterArmor = CalcArmorReducedDamage(attacker, victim, damage, spellInfo, BASE_ATTACK); - uint32 armorReduction = damage - damageAfterArmor; - if (armorReduction < damageResisted) // pick the lower one, the weakest resistance counts - damageResisted = armorReduction; + float armorReduction = damage - damageAfterArmor; + + // pick the lower one, the weakest resistance counts + damageResisted = std::min(damageResisted, armorReduction); } } - return damageResisted; + damageResisted = std::max(damageResisted, 0.f); + return uint32(damageResisted); } -float Unit::GetEffectiveResistChance(Unit const* owner, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo) +float Unit::CalculateAverageResistReduction(SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo) const { float victimResistance = float(victim->GetResistance(schoolMask)); - if (owner) + + // pets inherit 100% of masters penetration + // excluding traps + Player const* player = GetSpellModOwner(); + if (player && GetEntry() != WORLD_TRIGGER) { - // pets inherit 100% of masters penetration - // excluding traps - Player const* player = owner->GetSpellModOwner(); - if (player && owner->GetEntry() != WORLD_TRIGGER) - { - victimResistance += float(player->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask)); - victimResistance -= float(player->GetSpellPenetrationItemMod()); - } - else - victimResistance += float(owner->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask)); + victimResistance += float(player->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask)); + victimResistance -= float(player->GetSpellPenetrationItemMod()); } + else + victimResistance += float(GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask)); // holy resistance exists in pve and comes from level difference, ignore template values if (schoolMask & SPELL_SCHOOL_MASK_HOLY) @@ -1749,8 +1744,10 @@ float Unit::GetEffectiveResistChance(Unit const* owner, SpellSchoolMask schoolMa victimResistance = 0.0f; victimResistance = std::max(victimResistance, 0.0f); - if (owner) - victimResistance += std::max((float(victim->GetLevelForTarget(owner)) - float(owner->GetLevelForTarget(victim))) * 5.0f, 0.0f); + + // level-based resistance does not apply to binary spells, and cannot be overcome by spell penetration + if (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL)) + victimResistance += std::max((float(victim->GetLevelForTarget(this)) - float(GetLevelForTarget(victim))) * 5.0f, 0.0f); static uint32 const bossLevel = 83; static float const bossResistanceConstant = 510.0f; @@ -2030,7 +2027,7 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extr return; // ignore ranged case if (GetTypeId() == TYPEID_UNIT && !HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) - SetFacingToObject(victim); // update client side facing to face the target (prevents visual glitches when casting untargeted spells) + SetFacingToObject(victim, false); // update client side facing to face the target (prevents visual glitches when casting untargeted spells) // melee attack spell cast at main hand attack only - no normal melee dmg dealt if (attType == BASE_ATTACK && m_currentSpells[CURRENT_MELEE_SPELL] && !extra) @@ -2111,7 +2108,7 @@ void Unit::FakeAttackerStateUpdate(Unit* victim, WeaponAttackType attType /*= BA return; // ignore ranged case if (GetTypeId() == TYPEID_UNIT && !HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) - SetFacingToObject(victim); // update client side facing to face the target (prevents visual glitches when casting untargeted spells) + SetFacingToObject(victim, false); // update client side facing to face the target (prevents visual glitches when casting untargeted spells) CalcDamageInfo damageInfo; damageInfo.attacker = this; @@ -2534,7 +2531,6 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo return SPELL_MISS_NONE; } -/// @todo need use unit spell resistances in calculations SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const { // Can`t miss on dead target (on skinning for example) @@ -2590,23 +2586,21 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo int32 tmp = 10000 - HitChance; int32 rand = irand(0, 9999); - if (rand < tmp) + if (tmp > 0 && rand < tmp) return SPELL_MISS_MISS; // Chance resist mechanic (select max value from every mechanic spell effect) int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100; - tmp += resist_chance; // Roll chance - if (rand < tmp) + if (resist_chance > 0 && rand < (tmp += resist_chance)) return SPELL_MISS_RESIST; // cast by caster in front of victim if (!victim->HasUnitState(UNIT_STATE_CONTROLLED) && (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))) { int32 deflect_chance = victim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS) * 100; - tmp += deflect_chance; - if (rand < tmp) + if (deflect_chance > 0 && rand < (tmp += deflect_chance)) return SPELL_MISS_DEFLECT; } @@ -2623,18 +2617,6 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo // Resist SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect /*= false*/) { - if (spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) - return SPELL_MISS_NONE; - - // Check for immune - if (victim->IsImmunedToSpell(spellInfo, this)) - return SPELL_MISS_IMMUNE; - - // Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply - // returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit - if (spellInfo->HasOnlyDamageEffects() && victim->IsImmunedToDamage(spellInfo)) - return SPELL_MISS_IMMUNE; - // All positive spells can`t miss /// @todo client not show miss log for this spells - so need find info for this in dbc and use it! if (spellInfo->IsPositive() && !IsHostileTo(victim)) // prevent from affecting enemy by "positive" spell @@ -2657,6 +2639,18 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, boo return SPELL_MISS_REFLECT; } + if (spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) + return SPELL_MISS_NONE; + + // Check for immune + if (victim->IsImmunedToSpell(spellInfo, this)) + return SPELL_MISS_IMMUNE; + + // Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply + // returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit + if (spellInfo->HasOnlyDamageEffects() && victim->IsImmunedToDamage(spellInfo)) + return SPELL_MISS_IMMUNE; + switch (spellInfo->DmgClass) { case SPELL_DAMAGE_CLASS_RANGED: @@ -2748,7 +2742,7 @@ float Unit::GetUnitParryChance(WeaponAttackType attType, Unit const* victim) con float Unit::GetUnitMissChance(WeaponAttackType attType) const { - float miss_chance = 5.00f; + float miss_chance = 5.0f; if (attType == RANGED_ATTACK) miss_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE); @@ -4671,7 +4665,7 @@ int32 Unit::GetTotalAuraModifier(AuraType auratype, std::function<bool(AuraEffec { // Check if the Aura Effect has a the Same Effect Stack Rule and if so, use the highest amount of that SpellGroup // If the Aura Effect does not have this Stack Rule, it returns false so we can add to the multiplier as usual - if (!sSpellMgr->AddSameEffectStackRuleSpellGroups(aurEff->GetSpellInfo(), aurEff->GetAmount(), sameEffectSpellGroup)) + if (!sSpellMgr->AddSameEffectStackRuleSpellGroups(aurEff->GetSpellInfo(), static_cast<uint32>(auratype), aurEff->GetAmount(), sameEffectSpellGroup)) modifier += aurEff->GetAmount(); } } @@ -4698,7 +4692,7 @@ float Unit::GetTotalAuraMultiplier(AuraType auratype, std::function<bool(AuraEff { // Check if the Aura Effect has a the Same Effect Stack Rule and if so, use the highest amount of that SpellGroup // If the Aura Effect does not have this Stack Rule, it returns false so we can add to the multiplier as usual - if (!sSpellMgr->AddSameEffectStackRuleSpellGroups(aurEff->GetSpellInfo(), aurEff->GetAmount(), sameEffectSpellGroup)) + if (!sSpellMgr->AddSameEffectStackRuleSpellGroups(aurEff->GetSpellInfo(), static_cast<uint32>(auratype), aurEff->GetAmount(), sameEffectSpellGroup)) AddPct(multiplier, aurEff->GetAmount()); } } @@ -5435,15 +5429,15 @@ void Unit::UpdateDisplayPower() FactionTemplateEntry const* Unit::GetFactionTemplateEntry() const { - FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(getFaction()); + FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(GetFaction()); if (!entry) { if (Player const* player = ToPlayer()) - TC_LOG_ERROR("entities.unit", "Player %s has invalid faction (faction template id) #%u", player->GetName().c_str(), getFaction()); + TC_LOG_ERROR("entities.unit", "Player %s has invalid faction (faction template id) #%u", player->GetName().c_str(), GetFaction()); else if (Creature const* creature = ToCreature()) - TC_LOG_ERROR("entities.unit", "Creature (template id: %u) has invalid faction (faction template id) #%u", creature->GetCreatureTemplate()->Entry, getFaction()); + TC_LOG_ERROR("entities.unit", "Creature (template id: %u) has invalid faction (faction template id) #%u", creature->GetCreatureTemplate()->Entry, GetFaction()); else - TC_LOG_ERROR("entities.unit", "Unit (name=%s, type=%u) has invalid faction (faction template id) #%u", GetName().c_str(), uint32(GetTypeId()), getFaction()); + TC_LOG_ERROR("entities.unit", "Unit (name=%s, type=%u) has invalid faction (faction template id) #%u", GetName().c_str(), uint32(GetTypeId()), GetFaction()); ABORT(); } @@ -8658,7 +8652,7 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate) m_speed_rate[mtype] = rate; - propagateSpeedChange(); + PropagateSpeedChange(); // Spline packets are for creatures and move_update are for players static OpcodeServer const moveTypeToOpcode[MAX_MOVE_TYPE][3] = @@ -8845,7 +8839,8 @@ void Unit::TauntApply(Unit* taunter) if (target && target == taunter) return; - SetInFront(taunter); + if (!IsFocusing(nullptr, true)) + SetInFront(taunter); if (creature->IsAIEnabled) creature->AI()->AttackStart(taunter); @@ -8884,7 +8879,8 @@ void Unit::TauntFadeOut(Unit* taunter) if (target && target != taunter) { - SetInFront(target); + if (!IsFocusing(nullptr, true)) + SetInFront(target); if (creature->IsAIEnabled) creature->AI()->AttackStart(target); } @@ -8966,7 +8962,7 @@ Unit* Creature::SelectVictim() if (target && _IsTargetAcceptable(target) && CanCreatureAttack(target)) { - if (!IsFocusing()) + if (!IsFocusing(nullptr, true)) SetInFront(target); return target; } @@ -9162,7 +9158,7 @@ void Unit::ModSpellCastTime(SpellInfo const* spellInfo, int32 & castTime, Spell* if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) - castTime = int32(float(castTime) * m_unitData->ModCastingSpeed); + castTime = CanInstantCast() ? 0 : int32(float(castTime) * m_unitData->ModCastingSpeed); else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) castTime = int32(float(castTime) * m_modAttackSpeedPct[RANGED_ATTACK]); else if (IsPartOfSkillLine(SKILL_COOKING, spellInfo->Id) && HasAura(67556)) // cooking with Chef Hat. @@ -9215,11 +9211,11 @@ void Unit::IncrDiminishing(SpellInfo const* auraSpellInfo) ++diminish.hitCount; } -float Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, Unit* caster, DiminishingLevels previousLevel) +bool Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, Unit* caster, DiminishingLevels previousLevel) const { DiminishingGroup const group = auraSpellInfo->GetDiminishingReturnsGroupForSpell(); if (duration == -1 || group == DIMINISHING_NONE) - return 1.0f; + return true; int32 const limitDuration = auraSpellInfo->GetDiminishingReturnsLimitDuration(); @@ -9239,7 +9235,6 @@ float Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& du } float mod = 1.0f; - switch (group) { case DIMINISHING_TAUNT: @@ -9296,7 +9291,7 @@ float Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& du } duration = int32(duration * mod); - return mod; + return (duration != 0); } void Unit::ApplyDiminishingAura(DiminishingGroup group, bool apply) @@ -9587,7 +9582,13 @@ void Unit::UpdateDamageDoneMods(WeaponAttackType attackType) break; } - float amount = GetTotalAuraModifier(SPELL_AURA_MOD_DAMAGE_DONE, std::bind(&Unit::CheckAttackFitToAuraRequirement, this, attackType, std::placeholders::_1)); + float amount = GetTotalAuraModifier(SPELL_AURA_MOD_DAMAGE_DONE, [&](AuraEffect const* aurEff) -> bool + { + if (!(aurEff->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)) + return false; + + return CheckAttackFitToAuraRequirement(attackType, aurEff); + }); SetStatFlatModifier(unitMod, TOTAL_VALUE, amount); } @@ -10661,9 +10662,9 @@ void Unit::SendPetAIReaction(ObjectGuid guid) owner->ToPlayer()->SendDirectMessage(packet.Write()); } -void Unit::propagateSpeedChange() +void Unit::PropagateSpeedChange() { - GetMotionMaster()->propagateSpeedChange(); + GetMotionMaster()->PropagateSpeedChange(); } ///----------End of Pet responses methods---------- @@ -11161,26 +11162,6 @@ void Unit::SetContestedPvP(Player* attackedPlayer) } } -void Unit::AddPetAura(PetAura const* petSpell) -{ - if (GetTypeId() != TYPEID_PLAYER) - return; - - m_petAuras.insert(petSpell); - if (Pet* pet = ToPlayer()->GetPet()) - pet->CastPetAura(petSpell); -} - -void Unit::RemovePetAura(PetAura const* petSpell) -{ - if (GetTypeId() != TYPEID_PLAYER) - return; - - m_petAuras.erase(petSpell); - if (Pet* pet = ToPlayer()->GetPet()) - pet->RemoveAurasDueToSpell(petSpell->GetAura(pet->GetEntry())); -} - Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spell_id) { if (GetTypeId() != TYPEID_PLAYER) @@ -11224,7 +11205,7 @@ Pet* Unit::CreateTamedPetFrom(uint32 creatureEntry, uint32 spell_id) bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) { pet->SetCreatorGUID(GetGUID()); - pet->setFaction(getFaction()); + pet->SetFaction(GetFaction()); pet->SetCreatedBySpell(spell_id); if (GetTypeId() == TYPEID_PLAYER) @@ -11520,7 +11501,7 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) { // the reset time is set but not added to the scheduler // until the players leave the instance - time_t resettime = creature->GetRespawnTimeEx() + 2 * HOUR; + time_t resettime = GameTime::GetGameTime() + 2 * HOUR; if (InstanceSave* save = sInstanceSaveMgr->GetInstanceSave(creature->GetInstanceId())) if (save->GetResetTime() < resettime) save->SetResetTime(resettime); @@ -11880,8 +11861,8 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au if (aurApp && aurApp->GetRemoveMode()) return false; - _oldFactionId = getFaction(); - setFaction(charmer->getFaction()); + _oldFactionId = GetFaction(); + SetFaction(charmer->GetFaction()); // Set charmed charmer->SetCharm(this, true); @@ -11997,7 +11978,7 @@ void Unit::RemoveCharmedBy(Unit* charmer) if (_oldFactionId) { - setFaction(_oldFactionId); + SetFaction(_oldFactionId); _oldFactionId = 0; } else @@ -12089,13 +12070,13 @@ void Unit::RestoreFaction() { if (Unit* owner = GetOwner()) { - setFaction(owner->getFaction()); + SetFaction(owner->GetFaction()); return; } } if (CreatureTemplate const* cinfo = ToCreature()->GetCreatureTemplate()) // normal creature - setFaction(cinfo->faction); + SetFaction(cinfo->faction); } } @@ -12190,7 +12171,7 @@ bool Unit::IsInPartyWith(Unit const* unit) const (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return true; - return u1->GetTypeId() == TYPEID_UNIT && u2->GetTypeId() == TYPEID_UNIT && u1->getFaction() == u2->getFaction(); + return u1->GetTypeId() == TYPEID_UNIT && u2->GetTypeId() == TYPEID_UNIT && u1->GetFaction() == u2->GetFaction(); } bool Unit::IsInRaidWith(Unit const* unit) const @@ -12209,7 +12190,7 @@ bool Unit::IsInRaidWith(Unit const* unit) const (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return true; - return u1->GetTypeId() == TYPEID_UNIT && u2->GetTypeId() == TYPEID_UNIT && u1->getFaction() == u2->getFaction(); + return u1->GetTypeId() == TYPEID_UNIT && u2->GetTypeId() == TYPEID_UNIT && u1->GetFaction() == u2->GetFaction(); } void Unit::GetPartyMembers(std::list<Unit*> &TagUnitMap) @@ -12426,25 +12407,23 @@ void Unit::ApplyResilience(Unit const* victim, int32* damage) const // Melee based spells can be miss, parry or dodge on this step // Crit or block - determined on damage calculation phase! (and can be both in some time) -float Unit::MeleeSpellMissChance(const Unit* victim, WeaponAttackType attType, uint32 spellId) const +float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, uint32 spellId) const { //calculate miss chance float missChance = victim->GetUnitMissChance(attType); + // melee attacks while dual wielding have +19% chance to miss if (!spellId && haveOffhandWeapon() && !IsInFeralForm()) - missChance += 19; - - // Calculate hit chance - float hitChance = 100.0f; + missChance += 19.0f; // Spellmod from SPELLMOD_RESIST_MISS_CHANCE + float resistMissChance = 100.0f; if (spellId) { if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, hitChance); + modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, resistMissChance); } - - missChance += hitChance - 100.0f; + missChance -= resistMissChance - 100.0f; if (attType == RANGED_ATTACK) missChance -= m_modRangedHitChance; @@ -12452,10 +12431,7 @@ float Unit::MeleeSpellMissChance(const Unit* victim, WeaponAttackType attType, u missChance -= m_modMeleeHitChance; // Limit miss chance from 0 to 60% - if (missChance < 0.0f) - return 0.0f; - if (missChance > 77.0f) - return 77.0f; + RoundToInterval(missChance, 0.f, 60.f); return missChance; } @@ -13562,7 +13538,7 @@ void Unit::SendClearTarget() int32 Unit::GetResistance(SpellSchoolMask mask) const { Optional<int32> resist; - for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i) + for (int32 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i) { int32 schoolResistance = GetResistance(SpellSchools(i)) + GetBonusResistanceMod(SpellSchools(i)); if (mask & (1 << i) && (!resist || *resist > schoolResistance)) @@ -13651,7 +13627,8 @@ void Unit::SetInFront(WorldObject const* target) void Unit::SetFacingTo(float ori, bool force) { - if (!force && !IsStopped()) + // do not face when already moving + if (!force && (!IsStopped() || !movespline->Finalized())) return; Movement::MoveSplineInit init(this); @@ -13665,7 +13642,7 @@ void Unit::SetFacingTo(float ori, bool force) void Unit::SetFacingToObject(WorldObject const* object, bool force) { // do not face when already moving - if (!force && !IsStopped()) + if (!force && (!IsStopped() || !movespline->Finalized())) return; /// @todo figure out under what conditions creature will move towards object instead of facing it where it currently is. diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 7db8583f69f..48ab77e3856 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -200,7 +200,6 @@ class Item; class Minion; class MotionMaster; class Pet; -class PetAura; class Spell; class SpellCastTargets; class SpellEffectInfo; @@ -948,7 +947,7 @@ class TC_GAME_API Unit : public WorldObject DiminishingLevels GetDiminishing(DiminishingGroup group); void IncrDiminishing(SpellInfo const* auraSpellInfo); - float ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, Unit* caster, DiminishingLevels previousLevel); + bool ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, Unit* caster, DiminishingLevels previousLevel) const; void ApplyDiminishingAura(DiminishingGroup group, bool apply); void ClearDiminishings(); @@ -1042,7 +1041,7 @@ class TC_GAME_API Unit : public WorldObject int32 GetResistance(SpellSchoolMask mask) const; void SetResistance(SpellSchools school, int32 val) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::Resistances, school), val); } void SetBonusResistanceMod(SpellSchools school, int32 val) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::BonusResistanceMods, school), val); } - float GetEffectiveResistChance(Unit const* owner, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo = nullptr); + float CalculateAverageResistReduction(SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo = nullptr) const; uint64 GetHealth() const { return m_unitData->Health; } uint64 GetMaxHealth() const { return m_unitData->MaxHealth; } @@ -1119,8 +1118,8 @@ class TC_GAME_API Unit : public WorldObject void SetSheath(SheathState sheathed) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::SheatheState), sheathed); } // faction template id - uint32 getFaction() const { return m_unitData->FactionTemplate; } - void setFaction(uint32 faction) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::FactionTemplate), faction); } + uint32 GetFaction() const { return m_unitData->FactionTemplate; } + void SetFaction(uint32 faction) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::FactionTemplate), faction); } FactionTemplateEntry const* GetFactionTemplateEntry() const; ReputationRank GetReactionTo(Unit const* target) const; @@ -1383,8 +1382,8 @@ class TC_GAME_API Unit : public WorldObject void UpdateMovementForcesModMagnitude(); void SetInFront(WorldObject const* target); - void SetFacingTo(float ori, bool force = false); - void SetFacingToObject(WorldObject const* object, bool force = false); + void SetFacingTo(float const ori, bool force = true); + void SetFacingToObject(WorldObject const* object, bool force = true); void SendChangeCurrentVictimOpcode(HostileReference* pHostileReference); void SendClearThreatListOpcode(); @@ -1659,8 +1658,8 @@ class TC_GAME_API Unit : public WorldObject virtual SpellInfo const* GetCastSpellInfo(SpellInfo const* spellInfo) const; uint32 GetCastSpellXSpellVisualId(SpellInfo const* spellInfo) const; - // Check if our current channel spell has attribute SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING - bool IsMovementPreventedByCasting() const; + virtual bool IsFocusing(Spell const* /*focusSpell*/ = nullptr, bool /*withDelay*/ = false) { return false; } + virtual bool IsMovementPreventedByCasting() const; SpellHistory* GetSpellHistory() { return _spellHistory; } SpellHistory const* GetSpellHistory() const { return _spellHistory; } @@ -1915,7 +1914,7 @@ class TC_GAME_API Unit : public WorldObject void SendPetAIReaction(ObjectGuid guid); ///----------End of Pet responses methods---------- - void propagateSpeedChange(); + void PropagateSpeedChange(); // reactive attacks void ClearAllReactives(); @@ -1929,12 +1928,6 @@ class TC_GAME_API Unit : public WorldObject bool CanProc() const { return !m_procDeep; } void SetCantProc(bool apply); - // pet auras - typedef std::set<PetAura const*> PetAuraSet; - PetAuraSet m_petAuras; - void AddPetAura(PetAura const* petSpell); - void RemovePetAura(PetAura const* petSpell); - uint32 GetModelForForm(ShapeshiftForm form, uint32 spellId) const; // Redirect Threat @@ -1997,6 +1990,9 @@ class TC_GAME_API Unit : public WorldObject ObjectGuid GetTarget() const { return m_unitData->Target; } virtual void SetTarget(ObjectGuid const& /*guid*/) = 0; + void SetInstantCast(bool set) { _instantCast = set; } + bool CanInstantCast() const { return _instantCast; } + // Movement info Movement::MoveSpline * movespline; @@ -2149,6 +2145,7 @@ class TC_GAME_API Unit : public WorldObject bool m_cleanupDone; // lock made to not add stuff after cleanup before delete bool m_duringRemoveFromWorld; // lock made to not add stuff after begining removing from world + bool _instantCast; uint32 _oldFactionId; ///< faction before charm bool _isWalkingBeforeCharm; ///< Are we walking before we were charmed? diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 4ab04447914..f519c69948f 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -4529,8 +4529,6 @@ void ObjectMgr::LoadQuests() qinfo->GetQuestId(), qinfo->_nextQuestInChain, qinfo->_nextQuestInChain); qinfo->_nextQuestInChain = 0; } - else - qNextItr->second->PrevChainQuests.push_back(qinfo->GetQuestId()); } for (uint8 j = 0; j < QUEST_REWARD_CURRENCY_COUNT; ++j) @@ -4617,9 +4615,9 @@ void ObjectMgr::LoadQuests() if (qinfo->_nextQuestID) { - auto qNextItr = _questTemplates.find(qinfo->GetNextQuestId()); + auto qNextItr = _questTemplates.find(qinfo->_nextQuestID); if (qNextItr == _questTemplates.end()) - TC_LOG_ERROR("sql.sql", "Quest %d has NextQuestId %u, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId()); + TC_LOG_ERROR("sql.sql", "Quest %d has NextQuestId %u, but no such quest", qinfo->GetQuestId(), qinfo->_nextQuestID); else qNextItr->second->DependentPreviousQuests.push_back(qinfo->GetQuestId()); } @@ -4628,6 +4626,27 @@ void ObjectMgr::LoadQuests() _exclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->_exclusiveGroup, qinfo->GetQuestId())); if (qinfo->_limitTime) qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED); + + // Special flag to determine if quest is completed from the start, used to determine if we can fail timed quest if it is completed + if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO | QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT)) + { + bool addFlag = true; + if (qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER)) + { + for (QuestObjective const& obj : qinfo->GetObjectives()) + { + if (obj.Type == QUEST_OBJECTIVE_ITEM) + if (static_cast<uint32>(obj.ObjectID) != qinfo->GetSrcItemId() || static_cast<uint32>(obj.Amount) > qinfo->GetSrcItemCount()) + { + addFlag = false; + break; + } + } + } + + if (addFlag) + qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_COMPLETED_AT_START); + } } // check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index e4ccc62f5dc..95454d49f90 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -52,7 +52,7 @@ void WorldSession::HandleAuctionBrowseQuery(WorldPackets::AuctionHouse::AuctionB if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); TC_LOG_DEBUG("auctionHouse", "Auctionhouse search (%s), searchedname: %s, levelmin: %u, levelmax: %u, filters: %u", browseQuery.Auctioneer.ToString().c_str(), browseQuery.Name.c_str(), browseQuery.MinLevel, browseQuery.MaxLevel, AsUnderlyingType(browseQuery.Filters)); @@ -115,7 +115,7 @@ void WorldSession::HandleAuctionCancelCommoditiesPurchase(WorldPackets::AuctionH if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); auctionHouse->CancelCommodityQuote(_player->GetGUID()); } @@ -136,7 +136,7 @@ void WorldSession::HandleAuctionConfirmCommoditiesPurchase(WorldPackets::Auction if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (auctionHouse->BuyCommodity(trans, _player, confirmCommoditiesPurchase.ItemID, confirmCommoditiesPurchase.Quantity, throttle.DelayUntilNext)) @@ -190,7 +190,7 @@ void WorldSession::HandleAuctionListBidderItems(WorldPackets::AuctionHouse::Auct if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); WorldPackets::AuctionHouse::AuctionListBidderItemsResult result; @@ -218,7 +218,7 @@ void WorldSession::HandleAuctionListBucketsByBucketKeys(WorldPackets::AuctionHou if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); WorldPackets::AuctionHouse::AuctionListBucketsResult listBucketsResult; @@ -248,7 +248,7 @@ void WorldSession::HandleAuctionListItemsByBucketKey(WorldPackets::AuctionHouse: if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); WorldPackets::AuctionHouse::AuctionListItemsResult listItemsResult; listItemsResult.DesiredDelay = uint32(throttle.DelayUntilNext.count()); @@ -279,7 +279,7 @@ void WorldSession::HandleAuctionListItemsByItemID(WorldPackets::AuctionHouse::Au if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); WorldPackets::AuctionHouse::AuctionListItemsResult listItemsResult; listItemsResult.DesiredDelay = uint32(throttle.DelayUntilNext.count()); @@ -311,7 +311,7 @@ void WorldSession::HandleAuctionListOwnerItems(WorldPackets::AuctionHouse::Aucti if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); WorldPackets::AuctionHouse::AuctionListOwnerItemsResult result; @@ -345,7 +345,7 @@ void WorldSession::HandleAuctionPlaceBid(WorldPackets::AuctionHouse::AuctionPlac if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); AuctionPosting* auction = auctionHouse->GetAuction(placeBid.AuctionID); if (!auction || auction->IsCommodity()) @@ -467,7 +467,7 @@ void WorldSession::HandleAuctionRemoveItem(WorldPackets::AuctionHouse::AuctionRe if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); AuctionPosting* auction = auctionHouse->GetAuction(removeItem.AuctionID); Player* player = GetPlayer(); @@ -529,7 +529,7 @@ void WorldSession::HandleAuctionReplicateItems(WorldPackets::AuctionHouse::Aucti if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); WorldPackets::AuctionHouse::AuctionReplicateResponse response; @@ -569,7 +569,7 @@ void WorldSession::HandleAuctionSellCommodity(WorldPackets::AuctionHouse::Auctio } uint32 houseId = 0; - AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(creature->getFaction(), &houseId); + AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(creature->GetFaction(), &houseId); if (!auctionHouseEntry) { TC_LOG_DEBUG("network", "WORLD: HandleAuctionSellItem - Unit (%s) has wrong faction.", sellCommodity.Auctioneer.ToString().c_str()); @@ -645,7 +645,7 @@ void WorldSession::HandleAuctionSellCommodity(WorldPackets::AuctionHouse::Auctio } Seconds auctionTime = Seconds(int64(std::chrono::duration_cast<Seconds>(Minutes(sellCommodity.RunTime)).count() * double(sWorld->getRate(RATE_AUCTION_TIME)))); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); uint64 deposit = AuctionHouseMgr::GetCommodityAuctionDeposit(items2.begin()->second.first->GetTemplate(), Minutes(sellCommodity.RunTime), totalCount); if (!_player->HasEnoughMoney(deposit)) @@ -793,7 +793,7 @@ void WorldSession::HandleAuctionSellItem(WorldPackets::AuctionHouse::AuctionSell } uint32 houseId = 0; - AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(creature->getFaction(), &houseId); + AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(creature->GetFaction(), &houseId); if (!auctionHouseEntry) { TC_LOG_DEBUG("network", "WORLD: HandleAuctionSellItem - Unit (%s) has wrong faction.", sellItem.Auctioneer.ToString().c_str()); @@ -837,7 +837,7 @@ void WorldSession::HandleAuctionSellItem(WorldPackets::AuctionHouse::AuctionSell } Seconds auctionTime = Seconds(int64(std::chrono::duration_cast<Seconds>(Minutes(sellItem.RunTime)).count() * double(sWorld->getRate(RATE_AUCTION_TIME)))); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); uint64 deposit = AuctionHouseMgr::GetItemAuctionDeposit(_player, item, Minutes(sellItem.RunTime)); if (!_player->HasEnoughMoney(deposit)) @@ -947,7 +947,7 @@ void WorldSession::HandleAuctionStartCommoditiesPurchase(WorldPackets::AuctionHo if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); WorldPackets::AuctionHouse::AuctionCommodityQuote auctionCommodityQuote; @@ -972,7 +972,7 @@ void WorldSession::SendAuctionHello(ObjectGuid guid, Creature* unit) return; } - AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntry(unit->getFaction(), nullptr); + AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntry(unit->GetFaction(), nullptr); if (!ahEntry) return; diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 7335b5f3676..68911435766 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -28,6 +28,7 @@ #include "GuildMgr.h" #include "Item.h" #include "Log.h" +#include "LootItemStorage.h" #include "LootMgr.h" #include "LootPackets.h" #include "Object.h" @@ -255,7 +256,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPackets::Loot::LootMoney& /*packet // Delete the money loot record from the DB if (!loot->containerID.IsEmpty()) - loot->DeleteLootMoneyFromContainerItemDB(); + sLootItemStorage->RemoveStoredMoneyForContainer(loot->containerID.GetCounter()); // Delete container if empty if (loot->isLooted() && guid.IsItem()) diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 08981180970..1cda9ba2f1c 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -159,7 +159,7 @@ void WorldSession::HandleGossipHelloOpcode(WorldPackets::NPC::Hello& packet) } // set faction visible if needed - if (FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(unit->getFaction())) + if (FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(unit->GetFaction())) _player->GetReputationMgr().SetVisible(factionTemplateEntry); GetPlayer()->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK); diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 8dc2523afcc..73bc95f2caf 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -331,13 +331,15 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe { if (unit_target) { - pet->SetInFront(unit_target); + if (!pet->IsFocusing()) + pet->SetInFront(unit_target); if (Player* player = unit_target->ToPlayer()) pet->SendUpdateToPlayer(player); } else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget()) { - pet->SetInFront(unit_target2); + if (!pet->IsFocusing()) + pet->SetInFront(unit_target2); if (Player* player = unit_target2->ToPlayer()) pet->SendUpdateToPlayer(player); } diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index b56e0ec130c..aeafff6d1cc 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -350,46 +350,41 @@ void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPackets::Quest::Quest { //For AutoSubmition was added plr case there as it almost same exclute AI script cases. Creature* creatureQGiver = object->ToCreature(); - if (!creatureQGiver || !sScriptMgr->OnQuestReward(_player, creatureQGiver, quest, packet.ItemChoiceID)) + // Send next quest + if (Quest const* nextQuest = _player->GetNextQuest(packet.QuestGiverGUID, quest)) { - // Send next quest - if (Quest const* nextQuest = _player->GetNextQuest(packet.QuestGiverGUID, quest)) + // Only send the quest to the player if the conditions are met + if (_player->CanTakeQuest(nextQuest, false)) { - // Only send the quest to the player if the conditions are met - if (_player->CanTakeQuest(nextQuest, false)) - { - if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true)) - _player->AddQuestAndCheckCompletion(nextQuest, object); + if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true)) + _player->AddQuestAndCheckCompletion(nextQuest, object); - _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, packet.QuestGiverGUID, true, false); - } + _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, packet.QuestGiverGUID, true, false); } - - if (creatureQGiver) - creatureQGiver->GetAI()->sQuestReward(_player, quest, packet.ItemChoiceID); } + + if (creatureQGiver && !sScriptMgr->OnQuestReward(_player, creatureQGiver, quest, packet.ItemChoiceID)) + creatureQGiver->GetAI()->sQuestReward(_player, quest, packet.ItemChoiceID); break; } case TYPEID_GAMEOBJECT: { GameObject* questGiver = object->ToGameObject(); - if (!sScriptMgr->OnQuestReward(_player, questGiver, quest, packet.ItemChoiceID)) + // Send next quest + if (Quest const* nextQuest = _player->GetNextQuest(packet.QuestGiverGUID, quest)) { - // Send next quest - if (Quest const* nextQuest = _player->GetNextQuest(packet.QuestGiverGUID, quest)) + // Only send the quest to the player if the conditions are met + if (_player->CanTakeQuest(nextQuest, false)) { - // Only send the quest to the player if the conditions are met - if (_player->CanTakeQuest(nextQuest, false)) - { - if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true)) - _player->AddQuestAndCheckCompletion(nextQuest, object); + if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true)) + _player->AddQuestAndCheckCompletion(nextQuest, object); - _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, packet.QuestGiverGUID, true, false); - } + _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, packet.QuestGiverGUID, true, false); } + } + if (!sScriptMgr->OnQuestReward(_player, questGiver, quest, packet.ItemChoiceID)) questGiver->AI()->QuestReward(_player, quest, packet.ItemChoiceID); - } break; } default: @@ -590,7 +585,10 @@ void WorldSession::HandlePushQuestToParty(WorldPackets::Quest::PushQuestToParty& Group* group = sender->GetGroup(); if (!group) + { + sender->SendPushToPartyResponse(sender, QUEST_PUSH_NOT_IN_PARTY); return; + } for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) { @@ -611,6 +609,12 @@ void WorldSession::HandlePushQuestToParty(WorldPackets::Quest::PushQuestToParty& continue; } + if (!receiver->SatisfyQuestDay(quest, false)) + { + sender->SendPushToPartyResponse(receiver, QUEST_PUSH_DIFFERENT_SERVER_DAILY); + continue; + } + if (!receiver->CanTakeQuest(quest, false)) { sender->SendPushToPartyResponse(receiver, QUEST_PUSH_INVALID); diff --git a/src/server/game/Instances/InstanceSaveMgr.cpp b/src/server/game/Instances/InstanceSaveMgr.cpp index 903e4f084d6..fdeee7535a1 100644 --- a/src/server/game/Instances/InstanceSaveMgr.cpp +++ b/src/server/game/Instances/InstanceSaveMgr.cpp @@ -168,7 +168,7 @@ void InstanceSaveManager::RemoveInstanceSave(uint32 InstanceId) { CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_INSTANCE_RESETTIME); - stmt->setUInt32(0, uint32(resettime)); + stmt->setUInt64(0, uint64(resettime)); stmt->setUInt32(1, InstanceId); CharacterDatabase.Execute(stmt); @@ -222,7 +222,7 @@ void InstanceSave::SaveToDB() CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_INSTANCE_SAVE); stmt->setUInt32(0, m_instanceid); stmt->setUInt16(1, GetMapId()); - stmt->setUInt32(2, uint32(GetResetTimeForDB())); + stmt->setUInt64(2, uint64(GetResetTimeForDB())); stmt->setUInt8(3, uint8(GetDifficultyID())); stmt->setUInt32(4, completedEncounters); stmt->setString(5, data); @@ -329,8 +329,7 @@ void InstanceSaveManager::LoadResetTimes() typedef std::pair<ResetTimeMapDiffInstances::const_iterator, ResetTimeMapDiffInstances::const_iterator> ResetTimeMapDiffInstancesBounds; ResetTimeMapDiffInstances mapDiffResetInstances; - QueryResult result = CharacterDatabase.Query("SELECT id, map, difficulty, resettime FROM instance ORDER BY id ASC"); - if (result) + if (QueryResult result = CharacterDatabase.Query("SELECT id, map, difficulty, resettime FROM instance ORDER BY id ASC")) { do { @@ -346,7 +345,7 @@ void InstanceSaveManager::LoadResetTimes() // Mark instance id as being used sMapMgr->RegisterInstanceId(instanceId); - if (time_t resettime = time_t(fields[3].GetUInt32())) + if (time_t resettime = time_t(fields[3].GetUInt64())) { uint32 mapid = fields[1].GetUInt16(); uint32 difficulty = fields[2].GetUInt8(); @@ -357,24 +356,6 @@ void InstanceSaveManager::LoadResetTimes() } while (result->NextRow()); - // update reset time for normal instances with the max creature respawn time + X hours - if (PreparedQueryResult result2 = CharacterDatabase.Query(CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAX_CREATURE_RESPAWNS))) - { - do - { - Field* fields = result2->Fetch(); - uint32 instance = fields[1].GetUInt32(); - time_t resettime = time_t(fields[0].GetUInt32() + 2 * HOUR); - InstResetTimeMapDiffType::iterator itr = instResetTime.find(instance); - if (itr != instResetTime.end() && itr->second.second != resettime) - { - CharacterDatabase.DirectPExecute("UPDATE instance SET resettime = '" UI64FMTD "' WHERE id = '%u'", uint64(resettime), instance); - itr->second.second = resettime; - } - } - while (result2->NextRow()); - } - // schedule the reset times for (InstResetTimeMapDiffType::iterator itr = instResetTime.begin(); itr != instResetTime.end(); ++itr) if (itr->second.second > now) @@ -383,28 +364,37 @@ void InstanceSaveManager::LoadResetTimes() // load the global respawn times for raid/heroic instances uint32 diff = sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR; - result = CharacterDatabase.Query("SELECT mapid, difficulty, resettime FROM instance_reset"); - if (result) + if (QueryResult result = CharacterDatabase.Query("SELECT mapid, difficulty, resettime FROM instance_reset")) { do { Field* fields = result->Fetch(); uint32 mapid = fields[0].GetUInt16(); Difficulty difficulty = Difficulty(fields[1].GetUInt8()); - uint64 oldresettime = fields[2].GetUInt32(); + uint64 oldresettime = fields[2].GetUInt64(); MapDifficultyEntry const* mapDiff = sDB2Manager.GetMapDifficultyData(mapid, difficulty); if (!mapDiff) { TC_LOG_ERROR("misc", "InstanceSaveManager::LoadResetTimes: invalid mapid(%u)/difficulty(%u) pair in instance_reset!", mapid, uint32(difficulty)); - CharacterDatabase.DirectPExecute("DELETE FROM instance_reset WHERE mapid = '%u' AND difficulty = '%u'", mapid, uint32(difficulty)); + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GLOBAL_INSTANCE_RESETTIME); + stmt->setUInt16(0, uint16(mapid)); + stmt->setUInt8(1, uint8(difficulty)); + CharacterDatabase.DirectExecute(stmt); continue; } // update the reset time if the hour in the configs changes uint64 newresettime = (oldresettime / DAY) * DAY + diff; if (oldresettime != newresettime) - CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '%u' WHERE mapid = '%u' AND difficulty = '%u'", uint32(newresettime), mapid, uint32(difficulty)); + { + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GLOBAL_INSTANCE_RESETTIME); + stmt->setUInt64(0, uint64(newresettime)); + stmt->setUInt16(1, uint16(mapid)); + stmt->setUInt8(2, uint8(difficulty)); + CharacterDatabase.DirectExecute(stmt); + } InitializeResetTimeFor(mapid, difficulty, newresettime); } while (result->NextRow()); @@ -433,7 +423,12 @@ void InstanceSaveManager::LoadResetTimes() { // initialize the reset time t = today + period + diff; - CharacterDatabase.DirectPExecute("INSERT INTO instance_reset VALUES ('%u', '%u', '%u')", mapid, uint32(difficulty), (uint32)t); + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GLOBAL_INSTANCE_RESETTIME); + stmt->setUInt16(0, uint16(mapid)); + stmt->setUInt8(1, uint8(difficulty)); + stmt->setUInt64(2, uint64(t)); + CharacterDatabase.DirectExecute(stmt); } if (t < now) @@ -442,7 +437,12 @@ void InstanceSaveManager::LoadResetTimes() // calculate the next reset time t = (t / DAY) * DAY; t += ((today - t) / period + 1) * period + diff; - CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '" UI64FMTD "' WHERE mapid = '%u' AND difficulty= '%u'", (uint64)t, mapid, uint32(difficulty)); + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GLOBAL_INSTANCE_RESETTIME); + stmt->setUInt64(0, uint64(t)); + stmt->setUInt16(1, uint16(mapid)); + stmt->setUInt8(2, uint8(difficulty)); + CharacterDatabase.DirectExecute(stmt); } InitializeResetTimeFor(mapid, difficulty, t); @@ -702,7 +702,7 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b // Update it in the DB stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GLOBAL_INSTANCE_RESETTIME); - stmt->setUInt32(0, next_reset); + stmt->setUInt64(0, uint64(next_reset)); stmt->setUInt16(1, uint16(mapid)); stmt->setUInt8(2, uint8(difficulty)); diff --git a/src/server/game/Loot/Loot.cpp b/src/server/game/Loot/Loot.cpp index 88365d6641f..9e4dc90cab9 100644 --- a/src/server/game/Loot/Loot.cpp +++ b/src/server/game/Loot/Loot.cpp @@ -54,7 +54,6 @@ LootItem::LootItem(LootStoreItem const& li) is_underthreshold = 0; is_counted = 0; rollWinnerGUID = ObjectGuid::Empty; - canSave = true; } // Basic checks for player/item compatibility - if false no chance to see the item in the loot @@ -104,33 +103,6 @@ Loot::~Loot() clear(); } -void Loot::DeleteLootItemFromContainerItemDB(uint32 itemID) -{ - // Deletes a single item associated with an openable item from the DB - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEM); - stmt->setUInt64(0, containerID.GetCounter()); - stmt->setUInt32(1, itemID); - CharacterDatabase.Execute(stmt); - - // Mark the item looted to prevent resaving - for (LootItemList::iterator _itr = items.begin(); _itr != items.end(); ++_itr) - { - if (_itr->itemid != itemID) - continue; - - _itr->canSave = false; - break; - } -} - -void Loot::DeleteLootMoneyFromContainerItemDB() -{ - // Deletes money loot associated with an openable item from the DB - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY); - stmt->setUInt64(0, containerID.GetCounter()); - CharacterDatabase.Execute(stmt); -} - void Loot::clear() { for (NotNormalLootItemMap::const_iterator itr = PlayerQuestItems.begin(); itr != PlayerQuestItems.end(); ++itr) diff --git a/src/server/game/Loot/Loot.h b/src/server/game/Loot/Loot.h index 27f9c89f040..1aaed3f3cf7 100644 --- a/src/server/game/Loot/Loot.h +++ b/src/server/game/Loot/Loot.h @@ -148,7 +148,6 @@ struct TC_GAME_API LootItem bool is_counted : 1; bool needs_quest : 1; // quest drop bool follow_loot_rules : 1; - bool canSave; // Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties // Should be called for non-reference LootStoreItem entries only (reference = 0) @@ -156,8 +155,7 @@ struct TC_GAME_API LootItem // Empty constructor for creating an empty LootItem to be filled in with DB data LootItem() : itemid(0), randomBonusListId(0), context(ItemContext::NONE), count(0), is_looted(false), is_blocked(false), - freeforall(false), is_underthreshold(false), is_counted(false), needs_quest(false), follow_loot_rules(false), - canSave(true){ }; + freeforall(false), is_underthreshold(false), is_counted(false), needs_quest(false), follow_loot_rules(false) { }; // Basic checks for player/item compatibility - if false no chance to see the item in the loot bool AllowedForPlayer(Player const* player) const; @@ -230,10 +228,6 @@ struct TC_GAME_API Loot ObjectGuid const& GetGUID() const { return _GUID; } void SetGUID(ObjectGuid const& guid) { _GUID = guid; } - // For deleting items at loot removal since there is no backward interface to the Item() - void DeleteLootItemFromContainerItemDB(uint32 itemID); - void DeleteLootMoneyFromContainerItemDB(); - // if loot becomes invalid this reference is used to inform the listener void addLootValidatorRef(LootValidatorRef* pLootValidatorRef) { diff --git a/src/server/game/Loot/LootItemStorage.cpp b/src/server/game/Loot/LootItemStorage.cpp new file mode 100644 index 00000000000..4e3464eb31c --- /dev/null +++ b/src/server/game/Loot/LootItemStorage.cpp @@ -0,0 +1,365 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "DatabaseEnv.h" +#include "Item.h" +#include "ItemTemplate.h" +#include "Log.h" +#include "LootItemStorage.h" +#include "LootMgr.h" +#include "ObjectMgr.h" +#include "Player.h" + +#include <boost/thread/shared_mutex.hpp> +#include <boost/thread/locks.hpp> + +#include <unordered_map> + +namespace +{ + std::unordered_map<uint64, StoredLootContainer> _lootItemStore; +} + +StoredLootItem::StoredLootItem(LootItem const& lootItem) : ItemId(lootItem.itemid), Count(lootItem.count), FollowRules(lootItem.follow_loot_rules), +FFA(lootItem.freeforall), Blocked(lootItem.is_blocked), Counted(lootItem.is_counted), UnderThreshold(lootItem.is_underthreshold), +NeedsQuest(lootItem.needs_quest), RandomBonusListId(lootItem.randomBonusListId), Context(lootItem.context), BonusListIDs(lootItem.BonusListIDs) +{ +} + +LootItemStorage* LootItemStorage::instance() +{ + static LootItemStorage instance; + return &instance; +} + +boost::shared_mutex* LootItemStorage::GetLock() +{ + static boost::shared_mutex _lock; + return &_lock; +} + +void LootItemStorage::LoadStorageFromDB() +{ + uint32 oldMSTime = getMSTime(); + _lootItemStore.clear(); + uint32 count = 0; + + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_ITEMS); + PreparedQueryResult result = CharacterDatabase.Query(stmt); + if (result) + { + do + { + Field* fields = result->Fetch(); + + uint64 key = fields[0].GetUInt64(); + auto itr = _lootItemStore.find(key); + if (itr == _lootItemStore.end()) + { + bool added; + std::tie(itr, added) = _lootItemStore.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(key)); + + ASSERT(added); + } + + StoredLootContainer& storedContainer = itr->second; + + LootItem lootItem; + lootItem.itemid = fields[1].GetUInt32(); + lootItem.count = fields[2].GetUInt32(); + lootItem.follow_loot_rules = fields[3].GetBool(); + lootItem.freeforall = fields[4].GetBool(); + lootItem.is_blocked = fields[5].GetBool(); + lootItem.is_counted = fields[6].GetBool(); + lootItem.is_underthreshold = fields[7].GetBool(); + lootItem.needs_quest = fields[8].GetBool(); + lootItem.randomBonusListId = fields[9].GetUInt32(); + lootItem.context = ItemContext(fields[10].GetUInt8()); + Tokenizer bonusLists(fields[11].GetString(), ' '); + std::transform(bonusLists.begin(), bonusLists.end(), std::back_inserter(lootItem.BonusListIDs), [](char const* token) + { + return int32(strtol(token, NULL, 10)); + }); + + storedContainer.AddLootItem(lootItem, trans); + + ++count; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u stored item loots in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + } + else + TC_LOG_INFO("server.loading", ">> Loaded 0 stored item loots"); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_MONEY); + result = CharacterDatabase.Query(stmt); + if (result) + { + count = 0; + do + { + Field* fields = result->Fetch(); + + uint64 key = fields[0].GetUInt64(); + auto itr = _lootItemStore.find(key); + if (itr == _lootItemStore.end()) + { + bool added; + std::tie(itr, added) = _lootItemStore.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(key)); + + ASSERT(added); + } + + StoredLootContainer& storedContainer = itr->second; + storedContainer.AddMoney(fields[1].GetUInt32(), trans); + + ++count; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u stored item money in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + } + else + TC_LOG_INFO("server.loading", ">> Loaded 0 stored item money"); +} + +bool LootItemStorage::LoadStoredLoot(Item* item, Player* player) +{ + Loot* loot = &item->loot; + StoredLootContainer const* container = nullptr; + + // read + { + boost::shared_lock<boost::shared_mutex> lock(*GetLock()); + + auto itr = _lootItemStore.find(loot->containerID.GetCounter()); + if (itr == _lootItemStore.end()) + return false; + + container = &itr->second; + } + + // container is never null at this point + loot->gold = container->GetMoney(); + + if (LootTemplate const* lt = LootTemplates_Item.GetLootFor(item->GetEntry())) + { + for (auto const& storedItemPair : container->GetLootItems()) + { + LootItem li; + li.itemid = storedItemPair.first; + li.count = storedItemPair.second.Count; + li.follow_loot_rules = storedItemPair.second.FollowRules; + li.freeforall = storedItemPair.second.FFA; + li.is_blocked = storedItemPair.second.Blocked; + li.is_counted = storedItemPair.second.Counted; + li.is_underthreshold = storedItemPair.second.UnderThreshold; + li.needs_quest = storedItemPair.second.NeedsQuest; + li.randomBonusListId = storedItemPair.second.RandomBonusListId; + li.context = storedItemPair.second.Context; + li.BonusListIDs = storedItemPair.second.BonusListIDs; + + // Copy the extra loot conditions from the item in the loot template + lt->CopyConditions(&li); + + // If container item is in a bag, add that player as an allowed looter + if (item->GetBagSlot()) + li.AddAllowedLooter(player); + + // Finally add the LootItem to the container + loot->items.push_back(li); + + // Increment unlooted count + ++loot->unlootedCount; + } + } + + // Mark the item if it has loot so it won't be generated again on open + item->m_lootGenerated = true; + return true; +} + +void LootItemStorage::RemoveStoredMoneyForContainer(uint64 containerId) +{ + // write + boost::unique_lock<boost::shared_mutex> lock(*GetLock()); + + auto itr = _lootItemStore.find(containerId); + if (itr == _lootItemStore.end()) + return; + + itr->second.RemoveMoney(); +} + +void LootItemStorage::RemoveStoredLootForContainer(uint64 containerId) +{ + // write + { + boost::unique_lock<boost::shared_mutex> lock(*GetLock()); + _lootItemStore.erase(containerId); + } + + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEMS); + stmt->setUInt64(0, containerId); + trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY); + stmt->setUInt64(0, containerId); + trans->Append(stmt); + + CharacterDatabase.CommitTransaction(trans); +} + +void LootItemStorage::RemoveStoredLootItemForContainer(uint64 containerId, uint32 itemId, uint32 count) +{ + // write + boost::unique_lock<boost::shared_mutex> lock(*GetLock()); + + auto itr = _lootItemStore.find(containerId); + if (itr == _lootItemStore.end()) + return; + + itr->second.RemoveItem(itemId, count); +} + +void LootItemStorage::AddNewStoredLoot(Loot* loot, Player* player) +{ + // Saves the money and item loot associated with an openable item to the DB + if (loot->isLooted()) // no money and no loot + return; + + // read + { + boost::shared_lock<boost::shared_mutex> lock(*GetLock()); + + auto itr = _lootItemStore.find(loot->containerID.GetCounter()); + if (itr != _lootItemStore.end()) + { + TC_LOG_ERROR("misc", "Trying to store item loot by player: %s for container id: %lu that is already in storage!", player->GetGUID().ToString().c_str(), loot->containerID.GetCounter()); + return; + } + } + + StoredLootContainer container(loot->containerID.GetCounter()); + + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + if (loot->gold) + container.AddMoney(loot->gold, trans); + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEMS); + stmt->setUInt64(0, loot->containerID.GetCounter()); + trans->Append(stmt); + + for (LootItem const& li : loot->items) + { + // Conditions are not checked when loot is generated, it is checked when loot is sent to a player. + // For items that are lootable, loot is saved to the DB immediately, that means that loot can be + // saved to the DB that the player never should have gotten. This check prevents that, so that only + // items that the player should get in loot are in the DB. + // IE: Horde items are not saved to the DB for Ally players. + if (!li.AllowedForPlayer(player)) + continue; + + // Don't save currency tokens + ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(li.itemid); + if (!itemTemplate || itemTemplate->IsCurrencyToken()) + continue; + + container.AddLootItem(li, trans); + } + + CharacterDatabase.CommitTransaction(trans); + + // write + { + boost::unique_lock<boost::shared_mutex> lock(*GetLock()); + _lootItemStore.emplace(loot->containerID.GetCounter(), std::move(container)); + } +} + +void StoredLootContainer::AddLootItem(LootItem const& lootItem, CharacterDatabaseTransaction& trans) +{ + _lootItems.emplace(std::piecewise_construct, std::forward_as_tuple(lootItem.itemid), std::forward_as_tuple(lootItem)); + if (!trans) + return; + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_ITEMS); + + // container_id, item_id, item_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix + stmt->setUInt64(0, _containerId); + stmt->setUInt32(1, lootItem.itemid); + stmt->setUInt32(2, lootItem.count); + stmt->setBool(3, lootItem.follow_loot_rules); + stmt->setBool(4, lootItem.freeforall); + stmt->setBool(5, lootItem.is_blocked); + stmt->setBool(6, lootItem.is_counted); + stmt->setBool(7, lootItem.is_underthreshold); + stmt->setBool(8, lootItem.needs_quest); + stmt->setInt32(9, lootItem.randomBonusListId); + stmt->setUInt8(10, AsUnderlyingType(lootItem.context)); + std::ostringstream bonusListIDs; + for (int32 bonusListID : lootItem.BonusListIDs) + bonusListIDs << bonusListID << ' '; + stmt->setString(11, bonusListIDs.str()); + trans->Append(stmt); +} + +void StoredLootContainer::AddMoney(uint32 money, CharacterDatabaseTransaction& trans) +{ + _money = money; + if (!trans) + return; + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY); + stmt->setUInt64(0, _containerId); + trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_MONEY); + stmt->setUInt64(0, _containerId); + stmt->setUInt32(1, _money); + trans->Append(stmt); +} + +void StoredLootContainer::RemoveMoney() +{ + _money = 0; + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY); + stmt->setUInt64(0, _containerId); + CharacterDatabase.Execute(stmt); +} + +void StoredLootContainer::RemoveItem(uint32 itemId, uint32 count) +{ + auto bounds = _lootItems.equal_range(itemId); + for (auto itr = bounds.first; itr != bounds.second; ++itr) + { + if (itr->second.Count == count) + { + _lootItems.erase(itr); + break; + } + } + + // Deletes a single item associated with an openable item from the DB + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEM); + stmt->setUInt64(0, _containerId); + stmt->setUInt32(1, itemId); + stmt->setUInt32(2, count); + CharacterDatabase.Execute(stmt); +} diff --git a/src/server/game/Loot/LootItemStorage.h b/src/server/game/Loot/LootItemStorage.h new file mode 100644 index 00000000000..7855559ce1e --- /dev/null +++ b/src/server/game/Loot/LootItemStorage.h @@ -0,0 +1,99 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LOOTITEMSTORAGE_H +#define __LOOTITEMSTORAGE_H + +#include "DatabaseEnvFwd.h" +#include "Define.h" +#include "DBCEnums.h" +#include "ItemEnchantmentMgr.h" + +#include <unordered_map> +#include <vector> + +class Item; +class Player; +struct Loot; +struct LootItem; + +namespace boost +{ + class shared_mutex; +} + +struct StoredLootItem +{ + explicit StoredLootItem(LootItem const& lootItem); + + uint32 ItemId; + uint32 Count; + bool FollowRules; + bool FFA; + bool Blocked; + bool Counted; + bool UnderThreshold; + bool NeedsQuest; + ItemRandomBonusListId RandomBonusListId; + ItemContext Context; + std::vector<int32> BonusListIDs; +}; + +class StoredLootContainer +{ + public: + typedef std::unordered_multimap<uint32 /*itemId*/, StoredLootItem> StoredLootItemContainer; + + explicit StoredLootContainer(uint64 containerId) : _containerId(containerId), _money(0) { } + + void AddLootItem(LootItem const& lootItem, CharacterDatabaseTransaction& trans); + void AddMoney(uint32 money, CharacterDatabaseTransaction& trans); + + void RemoveMoney(); + void RemoveItem(uint32 itemId, uint32 count); + + uint32 GetContainer() const { return _containerId; } + uint32 GetMoney() const { return _money; } + StoredLootItemContainer const& GetLootItems() const { return _lootItems; } + + private: + StoredLootItemContainer _lootItems; + uint64 const _containerId; + uint32 _money; +}; + +class LootItemStorage +{ + public: + static LootItemStorage* instance(); + static boost::shared_mutex* GetLock(); + + void LoadStorageFromDB(); + bool LoadStoredLoot(Item* item, Player* player); + void RemoveStoredMoneyForContainer(uint64 containerId); + void RemoveStoredLootForContainer(uint64 containerId); + void RemoveStoredLootItemForContainer(uint64 containerId, uint32 itemId, uint32 count); + void AddNewStoredLoot(Loot* loot, Player* player); + + private: + LootItemStorage() { } + ~LootItemStorage() { } +}; + +#define sLootItemStorage LootItemStorage::instance() + +#endif diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 7b83e3412d5..24392412710 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3982,7 +3982,7 @@ void Map::SaveCreatureRespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CREATURE_RESPAWN); stmt->setUInt64(0, dbGuid); - stmt->setUInt32(1, uint32(respawnTime)); + stmt->setUInt64(1, uint64(respawnTime)); stmt->setUInt16(2, GetId()); stmt->setUInt32(3, GetInstanceId()); CharacterDatabase.Execute(stmt); @@ -4012,7 +4012,7 @@ void Map::SaveGORespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime) CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GO_RESPAWN); stmt->setUInt64(0, dbGuid); - stmt->setUInt32(1, uint32(respawnTime)); + stmt->setUInt64(1, uint64(respawnTime)); stmt->setUInt16(2, GetId()); stmt->setUInt32(3, GetInstanceId()); CharacterDatabase.Execute(stmt); @@ -4040,7 +4040,7 @@ void Map::LoadRespawnTimes() { Field* fields = result->Fetch(); ObjectGuid::LowType loguid = fields[0].GetUInt64(); - uint32 respawnTime = fields[1].GetUInt32(); + uint64 respawnTime = fields[1].GetUInt64(); _creatureRespawnTimes[loguid] = time_t(respawnTime); } while (result->NextRow()); @@ -4055,7 +4055,7 @@ void Map::LoadRespawnTimes() { Field* fields = result->Fetch(); ObjectGuid::LowType loguid = fields[0].GetUInt64(); - uint32 respawnTime = fields[1].GetUInt32(); + uint64 respawnTime = fields[1].GetUInt64(); _goRespawnTimes[loguid] = time_t(respawnTime); } while (result->NextRow()); diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 3e775d238a4..3b446733bbb 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -30,8 +30,10 @@ #include "WaypointMovementGenerator.h" #include "RandomMovementGenerator.h" #include "SplineChainMovementGenerator.h" +#include "FormationMovementGenerator.h" #include "MoveSpline.h" #include "MoveSplineInit.h" +#include "PathGenerator.h" inline bool IsStatic(MovementGenerator* movement) { @@ -76,7 +78,7 @@ void MotionMaster::InitDefault() if (_owner->GetTypeId() == TYPEID_UNIT) { MovementGenerator* movement = FactorySelector::selectMovementGenerator(_owner->ToCreature()); - Mutate(movement == NULL ? &si_idleMovement : movement, MOTION_SLOT_IDLE); + Mutate(movement == nullptr ? &si_idleMovement : movement, MOTION_SLOT_IDLE); } else { @@ -89,9 +91,6 @@ void MotionMaster::UpdateMotion(uint32 diff) if (!_owner) return; - if (_owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) // what about UNIT_STATE_DISTRACTED? Why is this not included? - return; - ASSERT(!empty()); _cleanFlag |= MMCF_UPDATE; @@ -174,12 +173,12 @@ MovementGenerator* MotionMaster::GetMotionSlot(int slot) const return _slot[slot]; } -void MotionMaster::propagateSpeedChange() +void MotionMaster::PropagateSpeedChange() { for (int i = 0; i <= _top; ++i) { if (_slot[i]) - _slot[i]->unitSpeedChanged(); + _slot[i]->UnitSpeedChanged(); } } @@ -713,6 +712,15 @@ void MotionMaster::MoveRotate(uint32 time, RotateDirection direction) Mutate(new RotateMovementGenerator(time, direction), MOTION_SLOT_ACTIVE); } +void MotionMaster::MoveFormation(uint32 id, Position destination, uint32 moveType, bool forceRun /*= false*/, bool forceOrientation /*= false*/) +{ + if (_owner->GetTypeId() == TYPEID_UNIT) + { + TC_LOG_DEBUG("misc", "MotionMaster::MoveFormation: Creature (Entry: %u %s) targeted point (Id: %u X: %f Y: %f Z: %f).", _owner->GetEntry(), _owner->GetGUID().ToString().c_str(), id, destination.GetPositionX(), destination.GetPositionY(), destination.GetPositionZ()); + Mutate(new FormationMovementGenerator(id, destination, moveType, forceRun, forceOrientation), MOTION_SLOT_ACTIVE); + } +} + /******************** Private methods ********************/ void MotionMaster::pop() diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index c496a0ec4ec..7cb72c353f7 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -66,6 +66,7 @@ enum MovementGeneratorType : uint8 ROTATE_MOTION_TYPE = 15, EFFECT_MOTION_TYPE = 16, SPLINE_CHAIN_MOTION_TYPE = 17, // SplineChainMovementGenerator.h + FORMATION_MOTION_TYPE = 18, // FormationMovementGenerator.h MAX_MOTION_TYPE // limit }; @@ -125,7 +126,7 @@ class TC_GAME_API MotionMaster MovementGeneratorType GetMotionSlotType(int slot) const; MovementGenerator* GetMotionSlot(int slot) const; - void propagateSpeedChange(); + void PropagateSpeedChange(); bool GetDestination(float &x, float &y, float &z); @@ -176,6 +177,8 @@ class TC_GAME_API MotionMaster void MovePath(uint32 path_id, bool repeatable); void MoveRotate(uint32 time, RotateDirection direction); + void MoveFormation(uint32 id, Position destination, uint32 moveType, bool forceRun = false, bool forceOrientation = false); + private: typedef std::vector<MovementGenerator*> MovementList; diff --git a/src/server/game/Movement/MovementGenerator.h b/src/server/game/Movement/MovementGenerator.h index a28ffca0172..fdf5506dd15 100755 --- a/src/server/game/Movement/MovementGenerator.h +++ b/src/server/game/Movement/MovementGenerator.h @@ -33,14 +33,12 @@ class TC_GAME_API MovementGenerator virtual void Initialize(Unit*) = 0; virtual void Finalize(Unit*) = 0; - virtual void Reset(Unit*) = 0; - - virtual bool Update(Unit*, uint32 time_diff) = 0; + virtual bool Update(Unit*, uint32 diff) = 0; virtual MovementGeneratorType GetMovementGeneratorType() const = 0; - virtual void unitSpeedChanged() { } + virtual void UnitSpeedChanged() { } // used by Evade code for select point to evade with expected restart default movement virtual bool GetResetPosition(Unit*, float& /*x*/, float& /*y*/, float& /*z*/) { return false; } @@ -52,42 +50,39 @@ class MovementGeneratorMedium : public MovementGenerator public: void Initialize(Unit* u) override { - //u->AssertIsType<T>(); (static_cast<D*>(this))->DoInitialize(static_cast<T*>(u)); } void Finalize(Unit* u) override { - //u->AssertIsType<T>(); (static_cast<D*>(this))->DoFinalize(static_cast<T*>(u)); } void Reset(Unit* u) override { - //u->AssertIsType<T>(); (static_cast<D*>(this))->DoReset(static_cast<T*>(u)); } bool Update(Unit* u, uint32 time_diff) override { - //u->AssertIsType<T>(); return (static_cast<D*>(this))->DoUpdate(static_cast<T*>(u), time_diff); } }; struct SelectableMovement : public FactoryHolder<MovementGenerator, MovementGeneratorType> { - SelectableMovement(MovementGeneratorType mgt) : FactoryHolder<MovementGenerator, MovementGeneratorType>(mgt) { } + SelectableMovement(MovementGeneratorType movementGeneratorType) : FactoryHolder<MovementGenerator, MovementGeneratorType>(movementGeneratorType) { } }; -template<class REAL_MOVEMENT> +template<class Movement> struct MovementGeneratorFactory : public SelectableMovement { - MovementGeneratorFactory(MovementGeneratorType mgt) : SelectableMovement(mgt) { } + MovementGeneratorFactory(MovementGeneratorType movementGeneratorType) : SelectableMovement(movementGeneratorType) { } MovementGenerator* Create(void *) const override; }; typedef FactoryHolder<MovementGenerator, MovementGeneratorType> MovementGeneratorCreator; typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRegistry MovementGeneratorRegistry; + #endif diff --git a/src/server/game/Movement/MovementGeneratorImpl.h b/src/server/game/Movement/MovementGeneratorImpl.h index 346fae28d1f..0b5474b22b3 100644 --- a/src/server/game/Movement/MovementGeneratorImpl.h +++ b/src/server/game/Movement/MovementGeneratorImpl.h @@ -20,10 +20,10 @@ #include "MovementGenerator.h" -template<class MOVEMENT_GEN> -inline MovementGenerator* -MovementGeneratorFactory<MOVEMENT_GEN>::Create(void * /*data*/) const +template<class Movement> +inline MovementGenerator* MovementGeneratorFactory<Movement>::Create(void * /*data*/) const { - return (new MOVEMENT_GEN()); + return (new Movement()); } + #endif diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp index 37b0cf912ad..0432241432d 100755 --- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp @@ -16,92 +16,95 @@ */ #include "Creature.h" -#include "ConfusedMovementGenerator.h" +#include "Player.h" #include "PathGenerator.h" #include "MoveSplineInit.h" #include "MoveSpline.h" -#include "Player.h" #include "Random.h" +#include "ConfusedMovementGenerator.h" template<class T> -void ConfusedMovementGenerator<T>::DoInitialize(T* unit) +ConfusedMovementGenerator<T>::~ConfusedMovementGenerator() { - unit->AddUnitState(UNIT_STATE_CONFUSED); - unit->AddUnitFlag(UNIT_FLAG_CONFUSED); - unit->GetPosition(i_x, i_y, i_z); + delete _path; +} - if (!unit->IsAlive() || unit->IsStopped()) +template<class T> +void ConfusedMovementGenerator<T>::DoInitialize(T* owner) +{ + if (!owner || !owner->IsAlive()) return; - unit->StopMoving(); - unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE); + owner->AddUnitState(UNIT_STATE_CONFUSED); + owner->AddUnitFlag(UNIT_FLAG_CONFUSED); + owner->StopMoving(); + + _timer.Reset(0); + owner->GetPosition(_reference.m_positionX, _reference.m_positionY, _reference.m_positionZ); } template<class T> -void ConfusedMovementGenerator<T>::DoReset(T* unit) +void ConfusedMovementGenerator<T>::DoReset(T* owner) { - i_nextMoveTime.Reset(0); - - if (!unit->IsAlive() || unit->IsStopped()) - return; - - unit->StopMoving(); - unit->AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE); + DoInitialize(owner); } template<class T> -bool ConfusedMovementGenerator<T>::DoUpdate(T* unit, uint32 diff) +bool ConfusedMovementGenerator<T>::DoUpdate(T* owner, uint32 diff) { - if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED)) - return true; + if (!owner || !owner->IsAlive()) + return false; - if (i_nextMoveTime.Passed()) + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) { - // currently moving, update location - unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE); - - if (unit->movespline->Finalized()) - i_nextMoveTime.Reset(urand(800, 1500)); + _interrupt = true; + owner->StopMoving(); + return true; } else + _interrupt = false; + + // waiting for next move + _timer.Update(diff); + if (!_interrupt && _timer.Passed() && owner->movespline->Finalized()) { - // waiting for next move - i_nextMoveTime.Update(diff); - if (i_nextMoveTime.Passed()) + // start moving + owner->AddUnitState(UNIT_STATE_CONFUSED_MOVE); + + Position destination(_reference); + float distance = 4.0f * frand(0.0f, 1.0f) - 2.0f; + float angle = frand(0.0f, 1.0f) * float(M_PI) * 2.0f; + owner->MovePositionToFirstCollision(destination, distance, angle); + + if (!_path) + _path = new PathGenerator(owner); + + _path->SetPathLengthLimit(30.0f); + bool result = _path->CalculatePath(destination.GetPositionX(), destination.GetPositionY(), destination.GetPositionZ()); + if (!result || (_path->GetPathType() & PATHFIND_NOPATH)) { - // start moving - unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE); - - float dest = 4.0f * (float)rand_norm() - 2.0f; - - Position pos; - pos.Relocate(i_x, i_y, i_z); - unit->MovePositionToFirstCollision(pos, dest, 0.0f); - - PathGenerator path(unit); - path.SetPathLengthLimit(30.0f); - bool result = path.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ); - if (!result || (path.GetPathType() & PATHFIND_NOPATH)) - { - i_nextMoveTime.Reset(100); - return true; - } - - Movement::MoveSplineInit init(unit); - init.MovebyPath(path.GetPath()); - init.SetWalk(true); - init.Launch(); + _timer.Reset(100); + return true; } + + Movement::MoveSplineInit init(owner); + init.MovebyPath(_path->GetPath()); + init.SetWalk(true); + int32 traveltime = init.Launch(); + _timer.Reset(traveltime + urand(800, 1500)); } return true; } +template<class T> +void ConfusedMovementGenerator<T>::DoFinalize(T*) { } + template<> void ConfusedMovementGenerator<Player>::DoFinalize(Player* unit) { unit->RemoveUnitFlag(UNIT_FLAG_CONFUSED); - unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE); + unit->ClearUnitState(UNIT_STATE_CONFUSED); unit->StopMoving(); } @@ -114,6 +117,8 @@ void ConfusedMovementGenerator<Creature>::DoFinalize(Creature* unit) unit->SetTarget(unit->EnsureVictim()->GetGUID()); } +template ConfusedMovementGenerator<Player>::~ConfusedMovementGenerator(); +template ConfusedMovementGenerator<Creature>::~ConfusedMovementGenerator(); template void ConfusedMovementGenerator<Player>::DoInitialize(Player*); template void ConfusedMovementGenerator<Creature>::DoInitialize(Creature*); template void ConfusedMovementGenerator<Player>::DoReset(Player*); diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h index a67afb72024..7d4cd073457 100755 --- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h @@ -25,16 +25,21 @@ template<class T> class ConfusedMovementGenerator : public MovementGeneratorMedium< T, ConfusedMovementGenerator<T> > { public: - explicit ConfusedMovementGenerator() : i_nextMoveTime(0), i_x(0), i_y(0), i_z(0) { } + explicit ConfusedMovementGenerator() : _path(nullptr), _timer(0), _reference(0.f, 0.f, 0.f), _interrupt(false) { } + ~ConfusedMovementGenerator(); + + MovementGeneratorType GetMovementGeneratorType() const override { return CONFUSED_MOTION_TYPE; } void DoInitialize(T*); void DoFinalize(T*); void DoReset(T*); bool DoUpdate(T*, uint32); - MovementGeneratorType GetMovementGeneratorType() const override { return CONFUSED_MOTION_TYPE; } private: - TimeTracker i_nextMoveTime; - float i_x, i_y, i_z; + PathGenerator* _path; + TimeTracker _timer; + Position _reference; + bool _interrupt; }; + #endif diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp index bfe3f4399d0..a1d179c23ce 100644 --- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp @@ -15,176 +15,203 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "Creature.h" +#include "VMapFactory.h" #include "CreatureAI.h" -#include "FleeingMovementGenerator.h" -#include "PathGenerator.h" #include "ObjectAccessor.h" +#include "Creature.h" +#include "Player.h" +#include "PathGenerator.h" #include "MoveSplineInit.h" #include "MoveSpline.h" #include "PhasingHandler.h" -#include "Player.h" -#include "VMapFactory.h" +#include "FleeingMovementGenerator.h" #define MIN_QUIET_DISTANCE 28.0f #define MAX_QUIET_DISTANCE 43.0f template<class T> -void FleeingMovementGenerator<T>::_setTargetLocation(T* owner) +FleeingMovementGenerator<T>::~FleeingMovementGenerator() +{ + delete _path; +} + +template<class T> +void FleeingMovementGenerator<T>::DoInitialize(T* owner) { if (!owner) return; - if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) + owner->AddUnitFlag(UNIT_FLAG_FLEEING); + owner->AddUnitState(UNIT_STATE_FLEEING); + SetTargetLocation(owner); +} + +template<class T> +void FleeingMovementGenerator<T>::DoFinalize(T *) +{ +} + +template<> +void FleeingMovementGenerator<Player>::DoFinalize(Player* owner) +{ + owner->RemoveUnitFlag(UNIT_FLAG_FLEEING); + owner->ClearUnitState(UNIT_STATE_FLEEING); + owner->StopMoving(); +} + +template<> +void FleeingMovementGenerator<Creature>::DoFinalize(Creature* owner) +{ + owner->RemoveUnitFlag(UNIT_FLAG_FLEEING); + owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); + if (owner->GetVictim()) + owner->SetTarget(owner->EnsureVictim()->GetGUID()); +} + +template<class T> +void FleeingMovementGenerator<T>::DoReset(T* owner) +{ + DoInitialize(owner); +} + +template<class T> +bool FleeingMovementGenerator<T>::DoUpdate(T* owner, uint32 diff) +{ + if (!owner || !owner->IsAlive()) + return false; + + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) + { + _interrupt = true; + owner->StopMoving(); + return true; + } + else + _interrupt = false; + + _timer.Update(diff); + if (!_interrupt && _timer.Passed() && owner->movespline->Finalized()) + SetTargetLocation(owner); + + return true; +} + +template<class T> +void FleeingMovementGenerator<T>::SetTargetLocation(T* owner) +{ + if (!owner) return; - if (owner->IsMovementPreventedByCasting()) + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) { - owner->CastStop(); + _interrupt = true; + owner->StopMoving(); return; } owner->AddUnitState(UNIT_STATE_FLEEING_MOVE); - float x, y, z; - _getPoint(owner, x, y, z); + Position destination = owner->GetPosition(); + GetPoint(owner, destination); // Add LOS check for target point - Position mypos = owner->GetPosition(); - bool isInLOS = VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight( - PhasingHandler::GetTerrainMapId(owner->GetPhaseShift(), owner->GetMap(), mypos.m_positionX, mypos.m_positionY), - mypos.m_positionX, mypos.m_positionY, mypos.m_positionZ + 2.0f, x, y, z + 2.0f, VMAP::ModelIgnoreFlags::Nothing); - if (!isInLOS) + Position currentPosition = owner->GetPosition(); + if (!VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(PhasingHandler::GetTerrainMapId(owner->GetPhaseShift(), owner->GetMap(), currentPosition.m_positionX, currentPosition.m_positionY), currentPosition.m_positionX, currentPosition.m_positionY, currentPosition.m_positionZ + 2.0f, destination.GetPositionX(), destination.GetPositionY(), destination.GetPositionZ() + 2.0f, VMAP::ModelIgnoreFlags::Nothing)) { - i_nextCheckTime.Reset(200); + _timer.Reset(200); return; } - PathGenerator path(owner); - path.SetPathLengthLimit(30.0f); - bool result = path.CalculatePath(x, y, z); - if (!result || (path.GetPathType() & PATHFIND_NOPATH)) + if (!_path) + _path = new PathGenerator(owner); + + _path->SetPathLengthLimit(30.0f); + bool result = _path->CalculatePath(destination.GetPositionX(), destination.GetPositionY(), destination.GetPositionZ()); + if (!result || (_path->GetPathType() & PATHFIND_NOPATH)) { - i_nextCheckTime.Reset(100); + _timer.Reset(100); return; } Movement::MoveSplineInit init(owner); - init.MovebyPath(path.GetPath()); + init.MovebyPath(_path->GetPath()); init.SetWalk(false); int32 traveltime = init.Launch(); - i_nextCheckTime.Reset(traveltime + urand(800, 1500)); + _timer.Reset(traveltime + urand(800, 1500)); } template<class T> -void FleeingMovementGenerator<T>::_getPoint(T* owner, float &x, float &y, float &z) +void FleeingMovementGenerator<T>::GetPoint(T* owner, Position &position) { - float dist_from_caster, angle_to_caster; - if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID)) + float casterDistance, casterAngle; + if (Unit* fleeTarget = ObjectAccessor::GetUnit(*owner, _fleeTargetGUID)) { - dist_from_caster = fright->GetDistance(owner); - if (dist_from_caster > 0.2f) - angle_to_caster = fright->GetAngle(owner); + casterDistance = fleeTarget->GetDistance(owner); + if (casterDistance > 0.2f) + casterAngle = fleeTarget->GetAngle(owner); else - angle_to_caster = frand(0, 2 * static_cast<float>(M_PI)); + casterAngle = frand(0.0f, 2.0f * float(M_PI)); } else { - dist_from_caster = 0.0f; - angle_to_caster = frand(0, 2 * static_cast<float>(M_PI)); + casterDistance = 0.0f; + casterAngle = frand(0.0f, 2.0f * float(M_PI)); } - float dist, angle; - if (dist_from_caster < MIN_QUIET_DISTANCE) + float distance, angle; + if (casterDistance < MIN_QUIET_DISTANCE) { - dist = frand(0.4f, 1.3f)*(MIN_QUIET_DISTANCE - dist_from_caster); - angle = angle_to_caster + frand(-static_cast<float>(M_PI)/8, static_cast<float>(M_PI)/8); + distance = frand(0.4f, 1.3f) * (MIN_QUIET_DISTANCE - casterDistance); + angle = casterAngle + frand(-float(M_PI) / 8.0f, float(M_PI) / 8.0f); } - else if (dist_from_caster > MAX_QUIET_DISTANCE) + else if (casterDistance > MAX_QUIET_DISTANCE) { - dist = frand(0.4f, 1.0f)*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE); - angle = -angle_to_caster + frand(-static_cast<float>(M_PI)/4, static_cast<float>(M_PI)/4); + distance = frand(0.4f, 1.0f) * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE); + angle = -casterAngle + frand(-float(M_PI) / 4.0f, float(M_PI) / 4.0f); } else // we are inside quiet range { - dist = frand(0.6f, 1.2f)*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE); - angle = frand(0, 2*static_cast<float>(M_PI)); + distance = frand(0.6f, 1.2f) * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE); + angle = frand(0.0f, 2.0f * float(M_PI)); } - Position pos = owner->GetFirstCollisionPosition(dist, angle); - x = pos.m_positionX; - y = pos.m_positionY; - z = pos.m_positionZ; -} - -template<class T> -void FleeingMovementGenerator<T>::DoInitialize(T* owner) -{ - if (!owner) - return; - - owner->AddUnitFlag(UNIT_FLAG_FLEEING); - owner->AddUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); - _setTargetLocation(owner); + owner->MovePositionToFirstCollision(position, distance, angle); } -template<> -void FleeingMovementGenerator<Player>::DoFinalize(Player* owner) -{ - owner->RemoveUnitFlag(UNIT_FLAG_FLEEING); - owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); - owner->StopMoving(); -} - -template<> -void FleeingMovementGenerator<Creature>::DoFinalize(Creature* owner) -{ - owner->RemoveUnitFlag(UNIT_FLAG_FLEEING); - owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE); - if (owner->GetVictim()) - owner->SetTarget(owner->EnsureVictim()->GetGUID()); -} +template FleeingMovementGenerator<Player>::~FleeingMovementGenerator(); +template FleeingMovementGenerator<Creature>::~FleeingMovementGenerator(); +template void FleeingMovementGenerator<Player>::DoInitialize(Player*); +template void FleeingMovementGenerator<Creature>::DoInitialize(Creature*); +template void FleeingMovementGenerator<Player>::DoReset(Player*); +template void FleeingMovementGenerator<Creature>::DoReset(Creature*); +template bool FleeingMovementGenerator<Player>::DoUpdate(Player*, uint32); +template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature*, uint32); +template void FleeingMovementGenerator<Player>::SetTargetLocation(Player*); +template void FleeingMovementGenerator<Creature>::SetTargetLocation(Creature*); +template void FleeingMovementGenerator<Player>::GetPoint(Player*, Position &); +template void FleeingMovementGenerator<Creature>::GetPoint(Creature*, Position &); -template<class T> -void FleeingMovementGenerator<T>::DoReset(T* owner) -{ - DoInitialize(owner); -} +//---- TimedFleeingMovementGenerator -template<class T> -bool FleeingMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff) +bool TimedFleeingMovementGenerator::Update(Unit* owner, uint32 time_diff) { - if (!owner || !owner->IsAlive()) + if (!owner->IsAlive()) return false; - if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) - { - owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE); - return true; - } - - i_nextCheckTime.Update(time_diff); - if (i_nextCheckTime.Passed() && owner->movespline->Finalized()) - _setTargetLocation(owner); + _totalFleeTime.Update(time_diff); + if (_totalFleeTime.Passed()) + return false; - return true; + // This calls grant-parent Update method hiden by FleeingMovementGenerator::Update(Creature &, uint32) version + // This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly + return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff); } -template void FleeingMovementGenerator<Player>::DoInitialize(Player*); -template void FleeingMovementGenerator<Creature>::DoInitialize(Creature*); -template void FleeingMovementGenerator<Player>::_getPoint(Player*, float&, float&, float&); -template void FleeingMovementGenerator<Creature>::_getPoint(Creature*, float&, float&, float&); -template void FleeingMovementGenerator<Player>::_setTargetLocation(Player*); -template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature*); -template void FleeingMovementGenerator<Player>::DoReset(Player*); -template void FleeingMovementGenerator<Creature>::DoReset(Creature*); -template bool FleeingMovementGenerator<Player>::DoUpdate(Player*, uint32); -template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature*, uint32); - void TimedFleeingMovementGenerator::Finalize(Unit* owner) { owner->RemoveUnitFlag(UNIT_FLAG_FLEEING); - owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE); + owner->ClearUnitState(UNIT_STATE_FLEEING); + owner->StopMoving(); if (Unit* victim = owner->GetVictim()) { if (owner->IsAlive()) @@ -194,23 +221,3 @@ void TimedFleeingMovementGenerator::Finalize(Unit* owner) } } } - -bool TimedFleeingMovementGenerator::Update(Unit* owner, uint32 time_diff) -{ - if (!owner->IsAlive()) - return false; - - if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) - { - owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE); - return true; - } - - i_totalFleeTime.Update(time_diff); - if (i_totalFleeTime.Passed()) - return false; - - // This calls grant-parent Update method hiden by FleeingMovementGenerator::Update(Creature &, uint32) version - // This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly - return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff); -} diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h index 025e783be5b..7481e340154 100755 --- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h @@ -19,41 +19,43 @@ #define TRINITY_FLEEINGMOVEMENTGENERATOR_H #include "MovementGenerator.h" +#include "Timer.h" template<class T> class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovementGenerator<T> > { public: - FleeingMovementGenerator(ObjectGuid fright) : i_frightGUID(fright), i_nextCheckTime(0) { } + explicit FleeingMovementGenerator(ObjectGuid fleeTargetGUID) : _path(nullptr), _fleeTargetGUID(fleeTargetGUID), _timer(0), _interrupt(false) { } + ~FleeingMovementGenerator(); + + MovementGeneratorType GetMovementGeneratorType() const override { return FLEEING_MOTION_TYPE; } void DoInitialize(T*); void DoFinalize(T*); void DoReset(T*); bool DoUpdate(T*, uint32); - MovementGeneratorType GetMovementGeneratorType() const override { return FLEEING_MOTION_TYPE; } - private: - void _setTargetLocation(T*); - void _getPoint(T*, float &x, float &y, float &z); + void SetTargetLocation(T*); + void GetPoint(T*, Position &position); - ObjectGuid i_frightGUID; - TimeTracker i_nextCheckTime; + PathGenerator* _path; + ObjectGuid _fleeTargetGUID; + TimeTracker _timer; + bool _interrupt; }; class TimedFleeingMovementGenerator : public FleeingMovementGenerator<Creature> { public: - TimedFleeingMovementGenerator(ObjectGuid fright, uint32 time) : - FleeingMovementGenerator<Creature>(fright), - i_totalFleeTime(time) { } + explicit TimedFleeingMovementGenerator(ObjectGuid fleeTargetGUID, uint32 time) : FleeingMovementGenerator<Creature>(fleeTargetGUID), _totalFleeTime(time) { } MovementGeneratorType GetMovementGeneratorType() const override { return TIMED_FLEEING_MOTION_TYPE; } bool Update(Unit*, uint32) override; void Finalize(Unit*) override; private: - TimeTracker i_totalFleeTime; + TimeTracker _totalFleeTime; }; #endif diff --git a/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.cpp new file mode 100644 index 00000000000..a1382ec673e --- /dev/null +++ b/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.cpp @@ -0,0 +1,130 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "Creature.h" +#include "CreatureAI.h" +#include "MoveSplineInit.h" +#include "MoveSpline.h" +#include "FormationMovementGenerator.h" + +void FormationMovementGenerator::DoInitialize(Creature* owner) +{ + owner->AddUnitState(UNIT_STATE_ROAMING); + + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) + { + _interrupt = true; + owner->StopMoving(); + return; + } + + owner->AddUnitState(UNIT_STATE_ROAMING_MOVE); + + Movement::MoveSplineInit init(owner); + init.MoveTo(_destination.GetPositionX(), _destination.GetPositionY(), _destination.GetPositionZ()); + if (_orientation) + init.SetFacing(_destination.GetOrientation()); + + switch (_moveType) + { + case 2: // WAYPOINT_MOVE_TYPE_LAND + init.SetAnimation(Movement::ToGround); + break; + case 3: // WAYPOINT_MOVE_TYPE_TAKEOFF + init.SetAnimation(Movement::ToFly); + break; + case 1: // WAYPOINT_MOVE_TYPE_RUN + init.SetWalk(false); + break; + case 0: // WAYPOINT_MOVE_TYPE_WALK + init.SetWalk(true); + break; + } + + if (_run) + init.SetWalk(false); + + init.Launch(); +} + +bool FormationMovementGenerator::DoUpdate(Creature* owner, uint32 /*diff*/) +{ + if (!owner) + return false; + + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) + { + _interrupt = true; + owner->StopMoving(); + return true; + } + + if ((_interrupt && owner->movespline->Finalized()) || (_recalculateSpeed && !owner->movespline->Finalized())) + { + _recalculateSpeed = false; + _interrupt = false; + + owner->AddUnitState(UNIT_STATE_ROAMING_MOVE); + + Movement::MoveSplineInit init(owner); + init.MoveTo(_destination.GetPositionX(), _destination.GetPositionY(), _destination.GetPositionZ()); + if (_orientation) + init.SetFacing(_destination.GetOrientation()); + + switch (_moveType) + { + case 2: // WAYPOINT_MOVE_TYPE_LAND + init.SetAnimation(Movement::ToGround); + break; + case 3: // WAYPOINT_MOVE_TYPE_TAKEOFF + init.SetAnimation(Movement::ToFly); + break; + case 1: // WAYPOINT_MOVE_TYPE_RUN + init.SetWalk(false); + break; + case 0: // WAYPOINT_MOVE_TYPE_WALK + init.SetWalk(true); + break; + } + + if (_run) + init.SetWalk(false); + init.Launch(); + } + + return !owner->movespline->Finalized(); +} + +void FormationMovementGenerator::DoFinalize(Creature* owner) +{ + owner->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); + + if (owner->movespline->Finalized()) + MovementInform(owner); +} + +void FormationMovementGenerator::DoReset(Creature* owner) +{ + owner->StopMoving(); + DoInitialize(owner); +} + +void FormationMovementGenerator::MovementInform(Creature* owner) +{ + if (owner->AI()) + owner->AI()->MovementInform(FORMATION_MOTION_TYPE, _movementId); +} diff --git a/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.h new file mode 100644 index 00000000000..27ab7d50da5 --- /dev/null +++ b/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.h @@ -0,0 +1,49 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_FORMATIONMOVEMENTGENERATOR_H +#define TRINITY_FORMATIONMOVEMENTGENERATOR_H + +#include "MovementGenerator.h" + +class FormationMovementGenerator : public MovementGeneratorMedium< Creature, FormationMovementGenerator > +{ + public: + explicit FormationMovementGenerator(uint32 id, Position destination, uint32 moveType, bool run, bool orientation) : _movementId(id), _destination(destination), _moveType(moveType), _run(run), _orientation(orientation), _recalculateSpeed(false), _interrupt(false) { } + + MovementGeneratorType GetMovementGeneratorType() const override { return FORMATION_MOTION_TYPE; } + + void DoInitialize(Creature*); + void DoFinalize(Creature*); + void DoReset(Creature*); + bool DoUpdate(Creature*, uint32); + + void UnitSpeedChanged() override { _recalculateSpeed = true; } + + private: + void MovementInform(Creature*); + + uint32 _movementId; + Position _destination; + uint32 _moveType; + bool _run; + bool _orientation; + bool _recalculateSpeed; + bool _interrupt; +}; + +#endif // TRINITY_FORMATIONMOVEMENTGENERATOR_H diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp index e8fa2ba6c53..041961ce91c 100644 --- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp @@ -15,36 +15,31 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "HomeMovementGenerator.h" #include "Creature.h" #include "CreatureAI.h" #include "MoveSplineInit.h" #include "MoveSpline.h" +#include "PathGenerator.h" +#include "HomeMovementGenerator.h" -void HomeMovementGenerator<Creature>::DoInitialize(Creature* owner) -{ - _setTargetLocation(owner); -} +template<class T> +HomeMovementGenerator<T>::~HomeMovementGenerator() { } -void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner) +template<> +HomeMovementGenerator<Creature>::~HomeMovementGenerator() { - if (arrived) - { - owner->ClearUnitState(UNIT_STATE_EVADE); - owner->SetWalk(true); - owner->LoadCreaturesAddon(); - owner->AI()->JustReachedHome(); - owner->SetSpawnHealth(); - } + delete _path; } -void HomeMovementGenerator<Creature>::DoReset(Creature*) { } +template<class T> +void HomeMovementGenerator<T>::SetTargetLocation(T*) { } -void HomeMovementGenerator<Creature>::_setTargetLocation(Creature* owner) +template<> +void HomeMovementGenerator<Creature>::SetTargetLocation(Creature* owner) { if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED)) { // if we are ROOT/STUNNED/DISTRACTED even after aura clear, finalize on next update - otherwise we would get stuck in evade - skipToHome = true; + _skipToHome = true; return; } @@ -60,14 +55,55 @@ void HomeMovementGenerator<Creature>::_setTargetLocation(Creature* owner) init.SetWalk(false); init.Launch(); - skipToHome = false; - arrived = false; + _skipToHome = false; + _arrived = false; owner->ClearUnitState(UNIT_STATE_ALL_ERASABLE & ~UNIT_STATE_EVADE); } -bool HomeMovementGenerator<Creature>::DoUpdate(Creature* owner, const uint32 /*time_diff*/) +template<class T> +void HomeMovementGenerator<T>::DoInitialize(T*) { } + +template<> +void HomeMovementGenerator<Creature>::DoInitialize(Creature* owner) +{ + SetTargetLocation(owner); +} + +template<class T> +void HomeMovementGenerator<T>::DoFinalize(T*) { } + +template<> +void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner) +{ + if (_arrived) + { + owner->ClearUnitState(UNIT_STATE_EVADE); + owner->SetWalk(true); + owner->LoadCreaturesAddon(); + owner->AI()->JustReachedHome(); + owner->SetSpawnHealth(); + } +} + +template<class T> +void HomeMovementGenerator<T>::DoReset(T*) { } + +template<> +void HomeMovementGenerator<Creature>::DoReset(Creature* owner) +{ + DoInitialize(owner); +} + +template<class T> +bool HomeMovementGenerator<T>::DoUpdate(T*, uint32) +{ + return false; +} + +template<> +bool HomeMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 /*diff*/) { - arrived = skipToHome || owner->movespline->Finalized(); - return !arrived; + _arrived = _skipToHome || owner->movespline->Finalized(); + return !_arrived; } diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h index d3489a249f3..5e1c25d9361 100644 --- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h @@ -20,28 +20,26 @@ #include "MovementGenerator.h" -class Creature; - -template < class T > -class HomeMovementGenerator; - -template <> -class HomeMovementGenerator<Creature> : public MovementGeneratorMedium< Creature, HomeMovementGenerator<Creature> > +template <class T> +class HomeMovementGenerator : public MovementGeneratorMedium< T, HomeMovementGenerator<T> > { public: + explicit HomeMovementGenerator() : _path(nullptr), _arrived(false), _skipToHome(false) { } + ~HomeMovementGenerator(); - HomeMovementGenerator() : arrived(false), skipToHome(false) { } - ~HomeMovementGenerator() { } - - void DoInitialize(Creature*); - void DoFinalize(Creature*); - void DoReset(Creature*); - bool DoUpdate(Creature*, const uint32); MovementGeneratorType GetMovementGeneratorType() const override { return HOME_MOTION_TYPE; } + void DoInitialize(T*); + void DoFinalize(T*); + void DoReset(T*); + bool DoUpdate(T*, uint32); + private: - void _setTargetLocation(Creature*); - bool arrived; - bool skipToHome; + void SetTargetLocation(T*); + + PathGenerator* _path; + bool _arrived; + bool _skipToHome; }; + #endif diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp index 811dc54fa79..76b1aca3cc2 100644 --- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp @@ -35,6 +35,8 @@ void IdleMovementGenerator::Reset(Unit* owner) owner->StopMoving(); } +//----------------------------------------------------// + void RotateMovementGenerator::Initialize(Unit* owner) { if (!owner->IsStopped()) @@ -50,27 +52,29 @@ void RotateMovementGenerator::Initialize(Unit* owner) bool RotateMovementGenerator::Update(Unit* owner, uint32 diff) { float angle = owner->GetOrientation(); - angle += (float(diff) * static_cast<float>(M_PI * 2) / m_maxDuration) * (m_direction == ROTATE_DIRECTION_LEFT ? 1.0f : -1.0f); + angle += (float(diff) * static_cast<float>(M_PI * 2) / _maxDuration) * (_direction == ROTATE_DIRECTION_LEFT ? 1.0f : -1.0f); angle = G3D::wrap(angle, 0.0f, float(G3D::twoPi())); owner->SetOrientation(angle); // UpdateSplinePosition does not set orientation with UNIT_STATE_ROTATING owner->SetFacingTo(angle); // Send spline movement to clients - if (m_duration > diff) - m_duration -= diff; + if (_duration > diff) + _duration -= diff; else return false; return true; } -void RotateMovementGenerator::Finalize(Unit* unit) +void RotateMovementGenerator::Finalize(Unit* owner) { - unit->ClearUnitState(UNIT_STATE_ROTATING); - if (unit->GetTypeId() == TYPEID_UNIT) - unit->ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0); + owner->ClearUnitState(UNIT_STATE_ROTATING); + if (owner->GetTypeId() == TYPEID_UNIT) + owner->ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0); } +//----------------------------------------------------// + void DistractMovementGenerator::Initialize(Unit* owner) { // Distracted creatures stand up if not standing @@ -92,17 +96,19 @@ void DistractMovementGenerator::Finalize(Unit* owner) } } -bool DistractMovementGenerator::Update(Unit* /*owner*/, uint32 time_diff) +bool DistractMovementGenerator::Update(Unit* /*owner*/, uint32 diff) { - if (time_diff > m_timer) + if (diff > _timer) return false; - m_timer -= time_diff; + _timer -= diff; return true; } -void AssistanceDistractMovementGenerator::Finalize(Unit* unit) +//----------------------------------------------------// + +void AssistanceDistractMovementGenerator::Finalize(Unit* owner) { - unit->ClearUnitState(UNIT_STATE_DISTRACTED); - unit->ToCreature()->SetReactState(REACT_AGGRESSIVE); + owner->ClearUnitState(UNIT_STATE_DISTRACTED); + owner->ToCreature()->SetReactState(REACT_AGGRESSIVE); } diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h index 022bda5a394..9bb58419433 100755 --- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h @@ -19,11 +19,11 @@ #define TRINITY_IDLEMOVEMENTGENERATOR_H #include "MovementGenerator.h" +#include "Timer.h" class IdleMovementGenerator : public MovementGenerator { public: - void Initialize(Unit*) override; void Finalize(Unit*) override { } void Reset(Unit*) override; @@ -36,7 +36,7 @@ TC_GAME_API extern IdleMovementGenerator si_idleMovement; class RotateMovementGenerator : public MovementGenerator { public: - explicit RotateMovementGenerator(uint32 time, RotateDirection direction) : m_duration(time), m_maxDuration(time), m_direction(direction) { } + explicit RotateMovementGenerator(uint32 time, RotateDirection direction) : _duration(time), _maxDuration(time), _direction(direction) { } void Initialize(Unit*) override; void Finalize(Unit*) override; @@ -45,14 +45,14 @@ class RotateMovementGenerator : public MovementGenerator MovementGeneratorType GetMovementGeneratorType() const override { return ROTATE_MOTION_TYPE; } private: - uint32 m_duration, m_maxDuration; - RotateDirection m_direction; + uint32 _duration, _maxDuration; + RotateDirection _direction; }; class DistractMovementGenerator : public MovementGenerator { public: - explicit DistractMovementGenerator(uint32 timer) : m_timer(timer) { } + explicit DistractMovementGenerator(uint32 timer) : _timer(timer) { } void Initialize(Unit*) override; void Finalize(Unit*) override; @@ -61,14 +61,13 @@ class DistractMovementGenerator : public MovementGenerator MovementGeneratorType GetMovementGeneratorType() const override { return DISTRACT_MOTION_TYPE; } private: - uint32 m_timer; + uint32 _timer; }; class AssistanceDistractMovementGenerator : public DistractMovementGenerator { public: - AssistanceDistractMovementGenerator(uint32 timer) : - DistractMovementGenerator(timer) { } + explicit AssistanceDistractMovementGenerator(uint32 timer) : DistractMovementGenerator(timer) { } MovementGeneratorType GetMovementGeneratorType() const override { return ASSISTANCE_DISTRACT_MOTION_TYPE; } void Finalize(Unit*) override; diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp index 3cf0711dc41..ff3a2699128 100755 --- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp @@ -15,33 +15,42 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "PointMovementGenerator.h" -#include "Errors.h" -#include "Creature.h" #include "CreatureAI.h" -#include "World.h" +#include "Creature.h" +#include "CreatureGroups.h" +#include "Player.h" #include "MoveSplineInit.h" #include "MoveSpline.h" -#include "Player.h" -#include "CreatureGroups.h" #include "ObjectAccessor.h" +#include "World.h" +#include "PointMovementGenerator.h" //----- Point Movement Generator + template<class T> -void PointMovementGenerator<T>::DoInitialize(T* unit) +void PointMovementGenerator<T>::DoInitialize(T* owner) { - if (!unit->IsStopped()) - unit->StopMoving(); + if (_movementId == EVENT_CHARGE_PREPATH) + { + owner->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); + return; + } - unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); + owner->AddUnitState(UNIT_STATE_ROAMING); - if (id == EVENT_CHARGE_PREPATH) + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) + { + _interrupt = true; + owner->StopMoving(); return; + } - Movement::MoveSplineInit init(unit); - init.MoveTo(i_x, i_y, i_z, m_generatePath); - if (speed > 0.0f) - init.SetVelocity(speed); + owner->AddUnitState(UNIT_STATE_ROAMING_MOVE); + + Movement::MoveSplineInit init(owner); + init.MoveTo(_destination.GetPositionX(), _destination.GetPositionY(), _destination.GetPositionZ(), _generatePath); + if (_speed > 0.0f) + init.SetVelocity(_speed); if (i_faceTarget) init.SetFacing(i_faceTarget); if (i_spellEffectExtra) @@ -49,69 +58,73 @@ void PointMovementGenerator<T>::DoInitialize(T* unit) init.Launch(); // Call for creature group update - if (Creature* creature = unit->ToCreature()) + if (Creature* creature = owner->ToCreature()) if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - creature->GetFormation()->LeaderMoveTo(i_x, i_y, i_z); + creature->GetFormation()->LeaderMoveTo(_destination, _movementId); } template<class T> -bool PointMovementGenerator<T>::DoUpdate(T* unit, uint32 /*diff*/) +bool PointMovementGenerator<T>::DoUpdate(T* owner, uint32 /*diff*/) { - if (!unit) + if (!owner) return false; - if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) + if (_movementId == EVENT_CHARGE_PREPATH) + return !owner->movespline->Finalized(); + + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) { - unit->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + _interrupt = true; + owner->StopMoving(); return true; } - unit->AddUnitState(UNIT_STATE_ROAMING_MOVE); - - if (id != EVENT_CHARGE_PREPATH && i_recalculateSpeed && !unit->movespline->Finalized()) + if ((_interrupt && owner->movespline->Finalized()) || (_recalculateSpeed && !owner->movespline->Finalized())) { - i_recalculateSpeed = false; - Movement::MoveSplineInit init(unit); - init.MoveTo(i_x, i_y, i_z, m_generatePath); - if (speed > 0.0f) // Default value for point motion type is 0.0, if 0.0 spline will use GetSpeed on unit - init.SetVelocity(speed); + _recalculateSpeed = false; + _interrupt = false; + + owner->AddUnitState(UNIT_STATE_ROAMING_MOVE); + + Movement::MoveSplineInit init(owner); + init.MoveTo(_destination.GetPositionX(), _destination.GetPositionY(), _destination.GetPositionZ(), _generatePath); + if (_speed > 0.0f) // Default value for point motion type is 0.0, if 0.0 spline will use GetSpeed on unit + init.SetVelocity(_speed); init.Launch(); // Call for creature group update - if (Creature* creature = unit->ToCreature()) + if (Creature* creature = owner->ToCreature()) if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - creature->GetFormation()->LeaderMoveTo(i_x, i_y, i_z); + creature->GetFormation()->LeaderMoveTo(_destination, _movementId); } - return !unit->movespline->Finalized(); + return !owner->movespline->Finalized(); } template<class T> -void PointMovementGenerator<T>::DoFinalize(T* unit) +void PointMovementGenerator<T>::DoFinalize(T* owner) { - if (unit->HasUnitState(UNIT_STATE_CHARGING)) - unit->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); + owner->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); - if (unit->movespline->Finalized()) - MovementInform(unit); + if (owner->movespline->Finalized()) + MovementInform(owner); } template<class T> -void PointMovementGenerator<T>::DoReset(T* unit) +void PointMovementGenerator<T>::DoReset(T* owner) { - if (!unit->IsStopped()) - unit->StopMoving(); - - unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); + owner->StopMoving(); + DoInitialize(owner); } template<class T> -void PointMovementGenerator<T>::MovementInform(T* /*unit*/) { } +void PointMovementGenerator<T>::MovementInform(T*) { } -template <> void PointMovementGenerator<Creature>::MovementInform(Creature* unit) +template <> +void PointMovementGenerator<Creature>::MovementInform(Creature* owner) { - if (unit->AI()) - unit->AI()->MovementInform(POINT_MOTION_TYPE, id); + if (owner->AI()) + owner->AI()->MovementInform(POINT_MOTION_TYPE, _movementId); } template void PointMovementGenerator<Player>::DoInitialize(Player*); @@ -123,34 +136,36 @@ template void PointMovementGenerator<Creature>::DoReset(Creature*); template bool PointMovementGenerator<Player>::DoUpdate(Player*, uint32); template bool PointMovementGenerator<Creature>::DoUpdate(Creature*, uint32); -void AssistanceMovementGenerator::Finalize(Unit* unit) +//---- AssistanceMovementGenerator + +void AssistanceMovementGenerator::Finalize(Unit* owner) +{ + owner->ClearUnitState(UNIT_STATE_ROAMING); + owner->StopMoving(); + owner->ToCreature()->SetNoCallAssistance(false); + owner->ToCreature()->CallAssistance(); + if (owner->IsAlive()) + owner->GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY)); +} + +//---- EffectMovementGenerator + +bool EffectMovementGenerator::Update(Unit* owner, uint32 /*diff*/) { - unit->ToCreature()->SetNoCallAssistance(false); - unit->ToCreature()->CallAssistance(); - if (unit->IsAlive()) - unit->GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY)); + return !owner->movespline->Finalized(); } -bool EffectMovementGenerator::Update(Unit* unit, uint32) +void EffectMovementGenerator::Finalize(Unit* owner) { - return !unit->movespline->Finalized(); + MovementInform(owner); } -void EffectMovementGenerator::Finalize(Unit* unit) +void EffectMovementGenerator::MovementInform(Unit* owner) { if (_arrivalSpellId) - unit->CastSpell(ObjectAccessor::GetUnit(*unit, _arrivalSpellTargetGuid), _arrivalSpellId, true); - - if (unit->GetTypeId() != TYPEID_UNIT) - return; - - // Need restore previous movement since we have no proper states system - if (unit->IsAlive() && !unit->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING)) - { - if (Unit* victim = unit->GetVictim()) - unit->GetMotionMaster()->MoveChase(victim); - } + owner->CastSpell(ObjectAccessor::GetUnit(*owner, _arrivalSpellTargetGuid), _arrivalSpellId, true); - if (unit->ToCreature()->AI()) - unit->ToCreature()->AI()->MovementInform(EFFECT_MOTION_TYPE, _id); + if (Creature* creature = owner->ToCreature()) + if (creature->AI()) + creature->AI()->MovementInform(EFFECT_MOTION_TYPE, _pointId); } diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h index 7b5451daa9e..18f552cc1cd 100644 --- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h @@ -19,7 +19,6 @@ #define TRINITY_POINTMOVEMENTGENERATOR_H #include "MovementGenerator.h" -#include "FollowerReference.h" class Creature; namespace Movement @@ -31,56 +30,54 @@ template<class T> class PointMovementGenerator : public MovementGeneratorMedium< T, PointMovementGenerator<T> > { public: - PointMovementGenerator(uint32 _id, float _x, float _y, float _z, bool _generatePath, float _speed = 0.0f, Unit const* faceTarget = nullptr, - Movement::SpellEffectExtraData const* spellEffectExtraData = nullptr) : id(_id), - i_x(_x), i_y(_y), i_z(_z), speed(_speed), i_faceTarget(faceTarget), i_spellEffectExtra(spellEffectExtraData), - m_generatePath(_generatePath), i_recalculateSpeed(false) { } +explicit PointMovementGenerator(uint32 id, float x, float y, float z, bool generatePath, float speed = 0.0f, Unit const* faceTarget = nullptr, Movement::SpellEffectExtraData const* spellEffectExtraData = nullptr) : _movementId(id), _destination(x, y, z), _speed(speed), i_faceTarget(faceTarget), i_spellEffectExtra(spellEffectExtraData), _generatePath(generatePath), _recalculateSpeed(false), _interrupt(false) { } + + MovementGeneratorType GetMovementGeneratorType() const override { return POINT_MOTION_TYPE; } void DoInitialize(T*); void DoFinalize(T*); void DoReset(T*); bool DoUpdate(T*, uint32); - void MovementInform(T*); - - void unitSpeedChanged() override { i_recalculateSpeed = true; } - - MovementGeneratorType GetMovementGeneratorType() const override { return POINT_MOTION_TYPE; } + void UnitSpeedChanged() override { _recalculateSpeed = true; } - void GetDestination(float& x, float& y, float& z) const { x = i_x; y = i_y; z = i_z; } private: - uint32 id; - float i_x, i_y, i_z; - float speed; + void MovementInform(T*); + + uint32 _movementId; + Position _destination; + float _speed; Unit const* i_faceTarget; Movement::SpellEffectExtraData const* i_spellEffectExtra; - bool m_generatePath; - bool i_recalculateSpeed; + bool _generatePath; + bool _recalculateSpeed; + bool _interrupt; }; class AssistanceMovementGenerator : public PointMovementGenerator<Creature> { public: - AssistanceMovementGenerator(float _x, float _y, float _z) : - PointMovementGenerator<Creature>(0, _x, _y, _z, true) { } + explicit AssistanceMovementGenerator(float _x, float _y, float _z) : PointMovementGenerator<Creature>(0, _x, _y, _z, true) { } MovementGeneratorType GetMovementGeneratorType() const override { return ASSISTANCE_MOTION_TYPE; } void Finalize(Unit*) override; }; -// Does almost nothing - just doesn't allows previous movegen interrupt current effect. class EffectMovementGenerator : public MovementGenerator { public: - EffectMovementGenerator(uint32 id, uint32 arrivalSpellId = 0, ObjectGuid const& arrivalSpellTargetGuid = ObjectGuid::Empty) - : _id(id), _arrivalSpellId(arrivalSpellId), _arrivalSpellTargetGuid(arrivalSpellTargetGuid) { } + explicit EffectMovementGenerator(uint32 id, uint32 arrivalSpellId = 0, ObjectGuid const& arrivalSpellTargetGuid = ObjectGuid::Empty) : _pointId(id), _arrivalSpellId(arrivalSpellId), _arrivalSpellTargetGuid(arrivalSpellTargetGuid) { } + void Initialize(Unit*) override { } void Finalize(Unit*) override; void Reset(Unit*) override { } bool Update(Unit*, uint32) override; MovementGeneratorType GetMovementGeneratorType() const override { return EFFECT_MOTION_TYPE; } + private: - uint32 _id; + void MovementInform(Unit*); + + uint32 _pointId; uint32 _arrivalSpellId; ObjectGuid _arrivalSpellTargetGuid; }; diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp index 473e63447ed..7b0025d644f 100644 --- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp @@ -16,165 +16,133 @@ */ #include "Creature.h" -#include "RandomMovementGenerator.h" -#include "Map.h" -#include "Util.h" #include "CreatureGroups.h" +#include "Map.h" #include "MoveSplineInit.h" #include "MoveSpline.h" +#include "PathGenerator.h" #include "Random.h" +#include "RandomMovementGenerator.h" -#define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV" +template<class T> +RandomMovementGenerator<T>::~RandomMovementGenerator() { } template<> -void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature) +RandomMovementGenerator<Creature>::~RandomMovementGenerator() { - if (creature->IsMovementPreventedByCasting()) - { - creature->CastStop(); + delete _path; +} + +template<class T> +void RandomMovementGenerator<T>::DoInitialize(T*) { } + +template<> +void RandomMovementGenerator<Creature>::DoInitialize(Creature* owner) +{ + if (!owner || !owner->IsAlive()) return; - } - float respX, respY, respZ, respO, destX, destY, destZ, travelDistZ; - creature->GetHomePosition(respX, respY, respZ, respO); - Map const* map = creature->GetMap(); + owner->AddUnitState(UNIT_STATE_ROAMING); + owner->GetPosition(_reference.m_positionX, _reference.m_positionY, _reference.m_positionZ); + owner->StopMoving(); + + if (!_wanderDistance) + _wanderDistance = owner->GetRespawnRadius(); - // For 2D/3D system selection - //bool is_land_ok = creature.CanWalk(); // not used? - //bool is_water_ok = creature.CanSwim(); // not used? - bool is_air_ok = creature->CanFly(); + _timer.Reset(0); +} - const float angle = float(rand_norm()) * static_cast<float>(M_PI*2.0f); - const float range = float(rand_norm()) * wander_distance; - const float distanceX = range * std::cos(angle); - const float distanceY = range * std::sin(angle); +template<class T> +void RandomMovementGenerator<T>::DoFinalize(T*) { } - destX = respX + distanceX; - destY = respY + distanceY; +template<> +void RandomMovementGenerator<Creature>::DoFinalize(Creature* owner) +{ + owner->ClearUnitState(UNIT_STATE_ROAMING); + owner->StopMoving(); + owner->SetWalk(false); +} - // prevent invalid coordinates generation - Trinity::NormalizeMapCoord(destX); - Trinity::NormalizeMapCoord(destY); +template<class T> +void RandomMovementGenerator<T>::DoReset(T*) { } - travelDistZ = range; // sin^2+cos^2=1, so travelDistZ=range^2; no need for sqrt below +template<> +void RandomMovementGenerator<Creature>::DoReset(Creature* owner) +{ + DoInitialize(owner); +} - if (is_air_ok) // 3D system above ground and above water (flying mode) - { - // Limit height change - const float distanceZ = float(rand_norm()) * travelDistZ/2.0f; - destZ = respZ + distanceZ; - float levelZ = map->GetWaterOrGroundLevel(creature->GetPhaseShift(), destX, destY, destZ-2.5f); - - // Problem here, we must fly above the ground and water, not under. Let's try on next tick - if (levelZ >= destZ) - return; - } - //else if (is_water_ok) // 3D system under water and above ground (swimming mode) - else // 2D only - { - // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE) - travelDistZ = travelDistZ >= 10.0f ? 10.0f : travelDistZ; - - // The fastest way to get an accurate result 90% of the time. - // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. - destZ = map->GetHeight(creature->GetPhaseShift(), destX, destY, respZ+travelDistZ-2.0f, false); - - if (std::fabs(destZ - respZ) > travelDistZ) // Map check - { - // Vmap Horizontal or above - destZ = map->GetHeight(creature->GetPhaseShift(), destX, destY, respZ - 2.0f, true); - - if (std::fabs(destZ - respZ) > travelDistZ) - { - // Vmap Higher - destZ = map->GetHeight(creature->GetPhaseShift(), destX, destY, respZ+travelDistZ-2.0f, true); - - // let's forget this bad coords where a z cannot be find and retry at next tick - if (std::fabs(destZ - respZ) > travelDistZ) - return; - } - } - } +template<class T> +void RandomMovementGenerator<T>::SetRandomLocation(T*) { } - if (is_air_ok) - i_nextMoveTime.Reset(0); - else +template<> +void RandomMovementGenerator<Creature>::SetRandomLocation(Creature* owner) +{ + if (!owner) + return; + + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) { - if (roll_chance_i(50)) - i_nextMoveTime.Reset(urand(5000, 10000)); - else - i_nextMoveTime.Reset(urand(50, 400)); + _interrupt = true; + owner->StopMoving(); + return; } - creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); + owner->AddUnitState(UNIT_STATE_ROAMING_MOVE); - Movement::MoveSplineInit init(creature); - init.MoveTo(destX, destY, destZ); - init.SetWalk(true); - init.Launch(); + Position position(_reference); + float distance = frand(0.f, 1.f) * _wanderDistance; + float angle = frand(0.f, 1.f) * float(M_PI) * 2.f; + owner->MovePositionToFirstCollision(position, distance, angle); - //Call for creature group update - if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - creature->GetFormation()->LeaderMoveTo(destX, destY, destZ); -} + uint32 resetTimer = roll_chance_i(50) ? urand(5000, 10000) : urand(1000, 2000); -template<> -void RandomMovementGenerator<Creature>::DoInitialize(Creature* creature) -{ - if (!creature->IsAlive()) + if (!_path) + _path = new PathGenerator(owner); + + _path->SetPathLengthLimit(30.0f); + bool result = _path->CalculatePath(position.GetPositionX(), position.GetPositionY(), position.GetPositionZ()); + if (!result || (_path->GetPathType() & PATHFIND_NOPATH)) + { + _timer.Reset(100); return; + } - if (!wander_distance) - wander_distance = creature->GetRespawnRadius(); + Movement::MoveSplineInit init(owner); + init.MovebyPath(_path->GetPath()); + init.SetWalk(true); + int32 traveltime = init.Launch(); + _timer.Reset(traveltime + resetTimer); - creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); - _setRandomLocation(creature); + // Call for creature group update + if (owner->GetFormation() && owner->GetFormation()->getLeader() == owner) + owner->GetFormation()->LeaderMoveTo(position); } -template<> -void RandomMovementGenerator<Creature>::DoReset(Creature* creature) +template<class T> +bool RandomMovementGenerator<T>::DoUpdate(T*, uint32) { - DoInitialize(creature); + return false; } template<> -void RandomMovementGenerator<Creature>::DoFinalize(Creature* creature) +bool RandomMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff) { - creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); - creature->SetWalk(false); -} - -template<> -bool RandomMovementGenerator<Creature>::DoUpdate(Creature* creature, const uint32 diff) -{ - if (!creature || !creature->IsAlive()) + if (!owner || !owner->IsAlive()) return false; - if (creature->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED)) + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) { - i_nextMoveTime.Reset(0); // Expire the timer - creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + _interrupt = true; + owner->StopMoving(); return true; } + else + _interrupt = false; - if (creature->movespline->Finalized()) - { - i_nextMoveTime.Update(diff); - if (i_nextMoveTime.Passed()) - _setRandomLocation(creature); - } - return true; -} - -template<> -bool RandomMovementGenerator<Creature>::GetResetPos(Creature* creature, float& x, float& y, float& z) -{ - float radius; - creature->GetRespawnPosition(x, y, z, NULL, &radius); - - // use current if in range - if (creature->IsWithinDist2d(x, y, radius)) - creature->GetPosition(x, y, z); + _timer.Update(diff); + if (!_interrupt && _timer.Passed() && owner->movespline->Finalized()) + SetRandomLocation(owner); return true; } diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h index 080c594ea01..a615bec49bc 100644 --- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h @@ -19,23 +19,30 @@ #define TRINITY_RANDOMMOTIONGENERATOR_H #include "MovementGenerator.h" +#include "Timer.h" template<class T> class RandomMovementGenerator : public MovementGeneratorMedium< T, RandomMovementGenerator<T> > { public: - RandomMovementGenerator(float spawn_dist = 0.0f) : i_nextMoveTime(0), wander_distance(spawn_dist) { } + explicit RandomMovementGenerator(float distance = 0.0f) : _path(nullptr), _timer(0), _reference(0.f, 0.f, 0.f), _wanderDistance(distance), _interrupt(false) { } + ~RandomMovementGenerator(); + + MovementGeneratorType GetMovementGeneratorType() const override { return RANDOM_MOTION_TYPE; } - void _setRandomLocation(T*); void DoInitialize(T*); void DoFinalize(T*); void DoReset(T*); - bool DoUpdate(T*, const uint32); - bool GetResetPos(T*, float& x, float& y, float& z); - MovementGeneratorType GetMovementGeneratorType() const override { return RANDOM_MOTION_TYPE; } + bool DoUpdate(T*, uint32); + private: - TimeTrackerSmall i_nextMoveTime; + void SetRandomLocation(T*); - float wander_distance; + PathGenerator* _path; + TimeTracker _timer; + Position _reference; + float _wanderDistance; + bool _interrupt; }; + #endif diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index 54cc97ab418..5c8eb721e18 100755 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -15,226 +15,210 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ByteBuffer.h" -#include "TargetedMovementGenerator.h" -#include "Errors.h" -#include "Creature.h" #include "CreatureAI.h" -#include "World.h" -#include "MoveSplineInit.h" -#include "MoveSpline.h" +#include "Creature.h" #include "Player.h" #include "VehicleDefines.h" +#include "MoveSplineInit.h" +#include "MoveSpline.h" +#include "PathGenerator.h" +#include "World.h" +#include "TargetedMovementGenerator.h" template<class T, typename D> -void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool updateDestination) +TargetedMovementGenerator<T, D>::~TargetedMovementGenerator() { - if (!i_target.isValid() || !i_target->IsInWorld()) - return; + delete _path; +} + +template<class T, typename D> +bool TargetedMovementGenerator<T, D>::DoUpdate(T* owner, uint32 diff) +{ + if (!IsTargetValid() || !GetTarget()->IsInWorld()) + return false; + + if (!owner || !owner->IsAlive()) + return false; + + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting() || HasLostTarget(owner)) + { + _interrupt = true; + owner->StopMoving(); + return true; + } + + if (_interrupt || _recalculateTravel) + { + _interrupt = false; + SetTargetLocation(owner, true); + return true; + } + + bool targetMoved = false; + _timer.Update(diff); + if (!_interrupt && _timer.Passed()) + { + _timer.Reset(100); + + float distance = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE); + if (owner->IsPet() && (owner->GetCharmerOrOwnerGUID() == GetTarget()->GetGUID())) + distance = 1.f; // pet following owner + + G3D::Vector3 destination = owner->movespline->FinalDestination(); + if (owner->movespline->onTransport) + if (TransportBase* transport = owner->GetDirectTransport()) + transport->CalculatePassengerPosition(destination.x, destination.y, destination.z); + + // First check distance + if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->CanFly()) + targetMoved = !GetTarget()->IsWithinDist3d(destination.x, destination.y, destination.z, distance); + else + targetMoved = !GetTarget()->IsWithinDist2d(destination.x, destination.y, distance); + + // then, if the target is in range, check also Line of Sight. + if (!targetMoved) + targetMoved = !GetTarget()->IsWithinLOSInMap(owner); + } + + if (targetMoved) + SetTargetLocation(owner, true); + else if (_speedChanged) + SetTargetLocation(owner, false); + + if (!_targetReached && owner->movespline->Finalized()) + { + MovementInform(owner); + if (_angle == 0.f && !owner->HasInArc(0.01f, GetTarget())) + owner->SetInFront(GetTarget()); + + if (!_targetReached) + { + _targetReached = true; + ReachTarget(owner); + } + } - if (owner->HasUnitState(UNIT_STATE_NOT_MOVE)) + return true; +} + +template<class T, typename D> +void TargetedMovementGenerator<T, D>::SetTargetLocation(T* owner, bool updateDestination) +{ + if (!IsTargetValid() || !GetTarget()->IsInWorld()) return; - if (owner->IsMovementPreventedByCasting()) + if (!owner || !owner->IsAlive()) return; - if (owner->GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner->ToCreature())) + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting() || HasLostTarget(owner)) { - owner->ToCreature()->SetCannotReachTarget(true); + _interrupt = true; + owner->StopMoving(); return; } - if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsFocusing(nullptr, true)) + if (owner->GetTypeId() == TYPEID_UNIT && !GetTarget()->isInAccessiblePlaceFor(owner->ToCreature())) + { + owner->ToCreature()->SetCannotReachTarget(true); return; + } float x, y, z; - - if (updateDestination || !i_path) + if (updateDestination || !_path) { - if (!i_offset) + if (!_offset) { - if (i_target->IsWithinDistInMap(owner, CONTACT_DISTANCE)) + if (GetTarget()->IsWithinDistInMap(owner, CONTACT_DISTANCE)) return; - // to nearest contact position - i_target->GetContactPoint(owner, x, y, z); + GetTarget()->GetContactPoint(owner, x, y, z); } else { - float dist; - float size; - - // Pets need special handling. - // We need to subtract GetCombatReach() because it gets added back further down the chain - // and that makes pets too far away. Subtracting it allows pets to properly - // be (GetCombatReach() + i_offset) away. - // Only applies when i_target is pet's owner otherwise pets and mobs end up - // doing a "dance" while fighting - if (owner->IsPet() && i_target->GetTypeId() == TYPEID_PLAYER) - { - dist = 1.0f; //i_target->GetCombatReach(); - size = 1.0f; //i_target->GetCombatReach() - i_target->GetCombatReach(); - } - else + float distance = _offset + 1.0f; + float size = owner->GetCombatReach(); + + if (owner->IsPet() && GetTarget()->GetTypeId() == TYPEID_PLAYER) { - dist = i_offset + 1.0f; - size = owner->GetCombatReach(); + distance = 1.0f; + size = 1.0f; } - if (i_target->IsWithinDistInMap(owner, dist)) + if (GetTarget()->IsWithinDistInMap(owner, distance)) return; - // to at i_offset distance from target and i_angle from target facing - i_target->GetClosePoint(x, y, z, size, i_offset, i_angle); + GetTarget()->GetClosePoint(x, y, z, size, _offset, _angle); } } else { // the destination has not changed, we just need to refresh the path (usually speed change) - G3D::Vector3 end = i_path->GetEndPosition(); + G3D::Vector3 end = _path->GetEndPosition(); x = end.x; y = end.y; z = end.z; } - if (!i_path) - i_path = new PathGenerator(owner); + if (!_path) + _path = new PathGenerator(owner); // allow pets to use shortcut if no path found when following their master - bool forceDest = (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsPet() - && owner->HasUnitState(UNIT_STATE_FOLLOW)); + bool forceDest = (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsPet() && owner->HasUnitState(UNIT_STATE_FOLLOW)); - bool result = i_path->CalculatePath(x, y, z, forceDest); - if (!result || (i_path->GetPathType() & PATHFIND_NOPATH)) + bool result = _path->CalculatePath(x, y, z, forceDest); + if (!result || (_path->GetPathType() & PATHFIND_NOPATH)) { // can't reach target - i_recalculateTravel = true; + _recalculateTravel = true; if (owner->GetTypeId() == TYPEID_UNIT) owner->ToCreature()->SetCannotReachTarget(true); return; } - D::_addUnitStateMove(owner); - i_targetReached = false; - i_recalculateTravel = false; - owner->AddUnitState(UNIT_STATE_CHASE); + _targetReached = false; + _recalculateTravel = false; + _speedChanged = false; + + AddUnitStateMove(owner); + if (owner->GetTypeId() == TYPEID_UNIT) owner->ToCreature()->SetCannotReachTarget(false); Movement::MoveSplineInit init(owner); - init.MovebyPath(i_path->GetPath()); - init.SetWalk(((D*)this)->EnableWalking()); + init.MovebyPath(_path->GetPath()); + init.SetWalk(EnableWalking()); // Using the same condition for facing target as the one that is used for SetInFront on movement end // - applies to ChaseMovementGenerator mostly - if (i_angle == 0.f) - init.SetFacing(i_target.getTarget()); + if (_angle == 0.f) + init.SetFacing(GetTarget()); init.Launch(); } template<class T, typename D> -bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T* owner, uint32 time_diff) +bool TargetedMovementGenerator<T, D>::IsReachable() const { - if (!i_target.isValid() || !i_target->IsInWorld()) - return false; - - if (!owner || !owner->IsAlive()) - return false; - - if (owner->HasUnitState(UNIT_STATE_NOT_MOVE)) - { - D::_clearUnitStateMove(owner); - return true; - } - - // prevent movement while casting spells with cast time or channel time - if (owner->IsMovementPreventedByCasting()) - { - if (!owner->IsStopped()) - owner->StopMoving(); - return true; - } - - // prevent crash after creature killed pet - if (static_cast<D*>(this)->_lostTarget(owner)) - { - D::_clearUnitStateMove(owner); - return true; - } - - bool targetMoved = false; - i_recheckDistance.Update(time_diff); - if (i_recheckDistance.Passed()) - { - i_recheckDistance.Reset(100); - - //More distance let have better performance, less distance let have more sensitive reaction at target move. - float allowed_dist = 0.0f; - - if (owner->IsPet() && (owner->GetCharmerOrOwnerGUID() == i_target->GetGUID())) - allowed_dist = 1.0f; // pet following owner - else - allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE); - - G3D::Vector3 dest = owner->movespline->FinalDestination(); - if (owner->movespline->onTransport) - if (TransportBase* transport = owner->GetDirectTransport()) - transport->CalculatePassengerPosition(dest.x, dest.y, dest.z); - - // First check distance - if (owner->GetTypeId() == TYPEID_UNIT && (owner->ToCreature()->CanFly() || owner->ToCreature()->CanSwim())) - targetMoved = !i_target->IsWithinDist3d(dest.x, dest.y, dest.z, allowed_dist); - else - targetMoved = !i_target->IsWithinDist2d(dest.x, dest.y, allowed_dist); - - // then, if the target is in range, check also Line of Sight. - if (!targetMoved) - targetMoved = !i_target->IsWithinLOSInMap(owner); - } - - if (i_recalculateTravel || targetMoved) - _setTargetLocation(owner, targetMoved); - - if (owner->movespline->Finalized()) - { - static_cast<D*>(this)->MovementInform(owner); - if (i_angle == 0.f && !owner->HasInArc(0.01f, i_target.getTarget())) - owner->SetInFront(i_target.getTarget()); - - if (!i_targetReached) - { - i_targetReached = true; - static_cast<D*>(this)->_reachTarget(owner); - } - } - - return true; + return (_path) ? (_path->GetPathType() & PATHFIND_NORMAL) : true; } -//-----------------------------------------------// +//---- ChaseMovementGenerator + template<class T> -void ChaseMovementGenerator<T>::_reachTarget(T* owner) -{ - _clearUnitStateMove(owner); - if (owner->IsWithinMeleeRange(this->i_target.getTarget())) - owner->Attack(this->i_target.getTarget(), true); - if (owner->GetTypeId() == TYPEID_UNIT) - owner->ToCreature()->SetCannotReachTarget(false); -} +void ChaseMovementGenerator<T>::DoInitialize(T*) { } template<> void ChaseMovementGenerator<Player>::DoInitialize(Player* owner) { - owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE); - _setTargetLocation(owner, true); + owner->AddUnitState(UNIT_STATE_CHASE); + SetTargetLocation(owner, true); } template<> void ChaseMovementGenerator<Creature>::DoInitialize(Creature* owner) { owner->SetWalk(false); - owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE); - _setTargetLocation(owner, true); + owner->AddUnitState(UNIT_STATE_CHASE); + SetTargetLocation(owner, true); } template<class T> @@ -250,41 +234,60 @@ void ChaseMovementGenerator<T>::DoReset(T* owner) } template<class T> -void ChaseMovementGenerator<T>::MovementInform(T* /*unit*/) { } +void ChaseMovementGenerator<T>::ClearUnitStateMove(T* owner) +{ + owner->ClearUnitState(UNIT_STATE_CHASE_MOVE); +} -template<> -void ChaseMovementGenerator<Creature>::MovementInform(Creature* unit) +template<class T> +void ChaseMovementGenerator<T>::AddUnitStateMove(T* owner) { - // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle - if (unit->AI()) - unit->AI()->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUID().GetCounter()); + owner->AddUnitState(UNIT_STATE_CHASE_MOVE); } -//-----------------------------------------------// -template<> -bool FollowMovementGenerator<Creature>::EnableWalking() const +template<class T> +bool ChaseMovementGenerator<T>::HasLostTarget(T* owner) const { - return i_target.isValid() && i_target->IsWalking(); + return owner->GetVictim() != TargetedMovementGeneratorBase::GetTarget(); } -template<> -bool FollowMovementGenerator<Player>::EnableWalking() const +template<class T> +void ChaseMovementGenerator<T>::ReachTarget(T* owner) { - return false; + ClearUnitStateMove(owner); + + if (owner->IsWithinMeleeRange(TargetedMovementGeneratorBase::GetTarget())) + owner->Attack(TargetedMovementGeneratorBase::GetTarget(), true); + + if (owner->GetTypeId() == TYPEID_UNIT) + owner->ToCreature()->SetCannotReachTarget(false); } +template<class T> +void ChaseMovementGenerator<T>::MovementInform(T*) { } + template<> -void FollowMovementGenerator<Player>::_updateSpeed(Player* /*owner*/) +void ChaseMovementGenerator<Creature>::MovementInform(Creature* owner) { - // nothing to do for Player + // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle + if (owner->AI()) + owner->AI()->MovementInform(CHASE_MOTION_TYPE, GetTarget()->GetGUID().GetCounter()); } +//---- FollowMovementGenerator + +template<class T> +void FollowMovementGenerator<T>::UpdateSpeed(T*) { } + +template<> +void FollowMovementGenerator<Player>::UpdateSpeed(Player* /*owner*/) { } + template<> -void FollowMovementGenerator<Creature>::_updateSpeed(Creature* owner) +void FollowMovementGenerator<Creature>::UpdateSpeed(Creature* owner) { - // pet only sync speed with owner - /// Make sure we are not in the process of a map change (IsInWorld) - if (!owner->IsPet() || !owner->IsInWorld() || !i_target.isValid() || i_target->GetGUID() != owner->GetOwnerGUID()) + // Pet only sync speed with owner + // Make sure we are not in the process of a map change (IsInWorld) + if (!owner->IsPet() || !owner->IsInWorld() || !IsTargetValid() || GetTarget()->GetGUID() != owner->GetOwnerGUID()) return; owner->UpdateSpeed(MOVE_RUN); @@ -292,27 +295,19 @@ void FollowMovementGenerator<Creature>::_updateSpeed(Creature* owner) owner->UpdateSpeed(MOVE_SWIM); } -template<> -void FollowMovementGenerator<Player>::DoInitialize(Player* owner) -{ - owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE); - _updateSpeed(owner); - _setTargetLocation(owner, true); -} - -template<> -void FollowMovementGenerator<Creature>::DoInitialize(Creature* owner) +template<class T> +void FollowMovementGenerator<T>::DoInitialize(T* owner) { - owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE); - _updateSpeed(owner); - _setTargetLocation(owner, true); + owner->AddUnitState(UNIT_STATE_FOLLOW); + UpdateSpeed(owner); + TargetedMovementGenerator<T, FollowMovementGenerator<T>>::SetTargetLocation(owner, true); } template<class T> void FollowMovementGenerator<T>::DoFinalize(T* owner) { owner->ClearUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE); - _updateSpeed(owner); + UpdateSpeed(owner); } template<class T> @@ -322,36 +317,85 @@ void FollowMovementGenerator<T>::DoReset(T* owner) } template<class T> -void FollowMovementGenerator<T>::MovementInform(T* /*unit*/) { } +void FollowMovementGenerator<T>::ClearUnitStateMove(T* owner) +{ + owner->ClearUnitState(UNIT_STATE_FOLLOW_MOVE); +} + +template<class T> +void FollowMovementGenerator<T>::AddUnitStateMove(T* owner) +{ + owner->AddUnitState(UNIT_STATE_FOLLOW_MOVE); +} + +template<class T> +void FollowMovementGenerator<T>::ReachTarget(T* owner) +{ + ClearUnitStateMove(owner); +} + +template<> +bool FollowMovementGenerator<Creature>::EnableWalking() const +{ + return IsTargetValid() && GetTarget()->IsWalking(); +} + +template<> +bool FollowMovementGenerator<Player>::EnableWalking() const +{ + return false; +} + +template<class T> +void FollowMovementGenerator<T>::MovementInform(T*) { } template<> void FollowMovementGenerator<Creature>::MovementInform(Creature* unit) { // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle if (unit->AI()) - unit->AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUID().GetCounter()); + unit->AI()->MovementInform(FOLLOW_MOTION_TYPE, GetTarget()->GetGUID().GetCounter()); } //-----------------------------------------------// -template void TargetedMovementGeneratorMedium<Player, ChaseMovementGenerator<Player> >::_setTargetLocation(Player*, bool); -template void TargetedMovementGeneratorMedium<Player, FollowMovementGenerator<Player> >::_setTargetLocation(Player*, bool); -template void TargetedMovementGeneratorMedium<Creature, ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool); -template void TargetedMovementGeneratorMedium<Creature, FollowMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool); -template bool TargetedMovementGeneratorMedium<Player, ChaseMovementGenerator<Player> >::DoUpdate(Player*, uint32); -template bool TargetedMovementGeneratorMedium<Player, FollowMovementGenerator<Player> >::DoUpdate(Player*, uint32); -template bool TargetedMovementGeneratorMedium<Creature, ChaseMovementGenerator<Creature> >::DoUpdate(Creature*, uint32); -template bool TargetedMovementGeneratorMedium<Creature, FollowMovementGenerator<Creature> >::DoUpdate(Creature*, uint32); - -template void ChaseMovementGenerator<Player>::_reachTarget(Player*); -template void ChaseMovementGenerator<Creature>::_reachTarget(Creature*); + +template TargetedMovementGenerator<Player, ChaseMovementGenerator<Player> >::~TargetedMovementGenerator(); +template TargetedMovementGenerator<Player, FollowMovementGenerator<Player> >::~TargetedMovementGenerator(); +template TargetedMovementGenerator<Creature, ChaseMovementGenerator<Creature> >::~TargetedMovementGenerator(); +template TargetedMovementGenerator<Creature, FollowMovementGenerator<Creature> >::~TargetedMovementGenerator(); +template bool TargetedMovementGenerator<Player, ChaseMovementGenerator<Player> >::DoUpdate(Player*, uint32); +template bool TargetedMovementGenerator<Player, FollowMovementGenerator<Player> >::DoUpdate(Player*, uint32); +template bool TargetedMovementGenerator<Creature, ChaseMovementGenerator<Creature> >::DoUpdate(Creature*, uint32); +template bool TargetedMovementGenerator<Creature, FollowMovementGenerator<Creature> >::DoUpdate(Creature*, uint32); +template void TargetedMovementGenerator<Player, ChaseMovementGenerator<Player> >::SetTargetLocation(Player*, bool); +template void TargetedMovementGenerator<Player, FollowMovementGenerator<Player> >::SetTargetLocation(Player*, bool); +template void TargetedMovementGenerator<Creature, ChaseMovementGenerator<Creature> >::SetTargetLocation(Creature*, bool); +template void TargetedMovementGenerator<Creature, FollowMovementGenerator<Creature> >::SetTargetLocation(Creature*, bool); + template void ChaseMovementGenerator<Player>::DoFinalize(Player*); template void ChaseMovementGenerator<Creature>::DoFinalize(Creature*); template void ChaseMovementGenerator<Player>::DoReset(Player*); template void ChaseMovementGenerator<Creature>::DoReset(Creature*); +template void ChaseMovementGenerator<Player>::ClearUnitStateMove(Player*); +template void ChaseMovementGenerator<Creature>::ClearUnitStateMove(Creature*); +template void ChaseMovementGenerator<Player>::AddUnitStateMove(Player*); +template void ChaseMovementGenerator<Creature>::AddUnitStateMove(Creature*); +template bool ChaseMovementGenerator<Player>::HasLostTarget(Player*) const; +template bool ChaseMovementGenerator<Creature>::HasLostTarget(Creature*) const; +template void ChaseMovementGenerator<Player>::ReachTarget(Player*); +template void ChaseMovementGenerator<Creature>::ReachTarget(Creature*); template void ChaseMovementGenerator<Player>::MovementInform(Player*); +template void FollowMovementGenerator<Player>::DoInitialize(Player*); +template void FollowMovementGenerator<Creature>::DoInitialize(Creature*); template void FollowMovementGenerator<Player>::DoFinalize(Player*); template void FollowMovementGenerator<Creature>::DoFinalize(Creature*); template void FollowMovementGenerator<Player>::DoReset(Player*); template void FollowMovementGenerator<Creature>::DoReset(Creature*); +template void FollowMovementGenerator<Player>::ClearUnitStateMove(Player*); +template void FollowMovementGenerator<Creature>::ClearUnitStateMove(Creature*); +template void FollowMovementGenerator<Player>::AddUnitStateMove(Player*); +template void FollowMovementGenerator<Creature>::AddUnitStateMove(Creature*); +template void FollowMovementGenerator<Player>::ReachTarget(Player*); +template void FollowMovementGenerator<Creature>::ReachTarget(Creature*); template void FollowMovementGenerator<Player>::MovementInform(Player*); diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h index 79a1c7952db..9f4e3712b5c 100755 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h @@ -21,95 +21,94 @@ #include "MovementGenerator.h" #include "FollowerReference.h" #include "Timer.h" -#include "Unit.h" -#include "PathGenerator.h" class TargetedMovementGeneratorBase { public: - TargetedMovementGeneratorBase(Unit* target) { i_target.link(target, this); } + TargetedMovementGeneratorBase(Unit* target) + { + _target.link(target, this); + } + + bool IsTargetValid() const { return _target.isValid(); } + Unit* GetTarget() const { return _target.getTarget(); } void stopFollowing() { } - protected: - FollowerReference i_target; + + private: + FollowerReference _target; }; template<class T, typename D> -class TargetedMovementGeneratorMedium : public MovementGeneratorMedium< T, D >, public TargetedMovementGeneratorBase +class TargetedMovementGenerator : public MovementGeneratorMedium< T, D >, public TargetedMovementGeneratorBase { - protected: - TargetedMovementGeneratorMedium(Unit* target, float offset, float angle) : - TargetedMovementGeneratorBase(target), i_path(NULL), - i_recheckDistance(0), i_offset(offset), i_angle(angle), - i_recalculateTravel(false), i_targetReached(false) - { - } - ~TargetedMovementGeneratorMedium() { delete i_path; } - public: + explicit TargetedMovementGenerator(Unit* target, float offset, float angle) : TargetedMovementGeneratorBase(target), _path(nullptr), _timer(0), _offset(offset), _angle(angle), _recalculateTravel(false), _speedChanged(false), _targetReached(false), _interrupt(false) { } + ~TargetedMovementGenerator(); + bool DoUpdate(T*, uint32); - Unit* GetTarget() const { return i_target.getTarget(); } - - void unitSpeedChanged() override { i_recalculateTravel = true; } - bool IsReachable() const { return (i_path) ? (i_path->GetPathType() & PATHFIND_NORMAL) : true; } - protected: - void _setTargetLocation(T* owner, bool updateDestination); - - PathGenerator* i_path; - TimeTrackerSmall i_recheckDistance; - float i_offset; - float i_angle; - bool i_recalculateTravel : 1; - bool i_targetReached : 1; + + void UnitSpeedChanged() override { _speedChanged = true; } + + virtual void ClearUnitStateMove(T*) { } + virtual void AddUnitStateMove(T*) { } + virtual bool HasLostTarget(T*) const { return false; } + virtual void ReachTarget(T*) { } + virtual bool EnableWalking() const { return false; } + virtual void MovementInform(T*) { } + + bool IsReachable() const; + void SetTargetLocation(T* owner, bool updateDestination); + + private: + PathGenerator* _path; + TimeTrackerSmall _timer; + float _offset; + float _angle; + bool _recalculateTravel; + bool _speedChanged; + bool _targetReached; + bool _interrupt; }; template<class T> -class ChaseMovementGenerator : public TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> > +class ChaseMovementGenerator : public TargetedMovementGenerator<T, ChaseMovementGenerator<T> > { public: - ChaseMovementGenerator(Unit* target) - : TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target) { } - ChaseMovementGenerator(Unit* target, float offset, float angle) - : TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target, offset, angle) { } - ~ChaseMovementGenerator() { } + explicit ChaseMovementGenerator(Unit* target, float offset, float angle) : TargetedMovementGenerator<T, ChaseMovementGenerator<T> >(target, offset, angle) { } MovementGeneratorType GetMovementGeneratorType() const override { return CHASE_MOTION_TYPE; } void DoInitialize(T*); void DoFinalize(T*); void DoReset(T*); - void MovementInform(T*); - static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_CHASE_MOVE); } - static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_CHASE_MOVE); } - bool EnableWalking() const { return false;} - bool _lostTarget(T* u) const { return u->GetVictim() != this->GetTarget(); } - void _reachTarget(T*); + void ClearUnitStateMove(T*) override; + void AddUnitStateMove(T*) override; + bool HasLostTarget(T*) const override; + void ReachTarget(T*) override; + void MovementInform(T*) override; }; template<class T> -class FollowMovementGenerator : public TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> > +class FollowMovementGenerator : public TargetedMovementGenerator<T, FollowMovementGenerator<T> > { public: - FollowMovementGenerator(Unit* target) - : TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target){ } - FollowMovementGenerator(Unit* target, float offset, float angle) - : TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target, offset, angle) { } - ~FollowMovementGenerator() { } + explicit FollowMovementGenerator(Unit* target, float offset, float angle) : TargetedMovementGenerator<T, FollowMovementGenerator<T> >(target, offset, angle) { } MovementGeneratorType GetMovementGeneratorType() const override { return FOLLOW_MOTION_TYPE; } void DoInitialize(T*); void DoFinalize(T*); void DoReset(T*); - void MovementInform(T*); - static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_FOLLOW_MOVE); } - static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_FOLLOW_MOVE); } - bool EnableWalking() const; - bool _lostTarget(T*) const { return false; } - void _reachTarget(T*) { } + void ClearUnitStateMove(T*) override; + void AddUnitStateMove(T*) override; + bool HasLostTarget(T*) const override { return false; } + void ReachTarget(T*) override; + bool EnableWalking() const override; + void MovementInform(T*) override; private: - void _updateSpeed(T* owner); + void UpdateSpeed(T* owner); }; #endif diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 9f07641d74d..0a01d7f3a96 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -139,7 +139,7 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); - Movement::Location formationDest(node->x, node->y, node->z, 0.0f); + Position formationDest(node->x, node->y, node->z, (node->orientation && node->delay) ? node->orientation : 0.0f); Movement::MoveSplineInit init(creature); //! If creature is on transport, we assume waypoints set in DB are already transport offsets @@ -147,7 +147,11 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) { init.DisableTransportPathTransformations(); if (TransportBase* trans = creature->GetDirectTransport()) - trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation); + { + float orientation = formationDest.GetOrientation(); + trans->CalculatePassengerPosition(formationDest.m_positionX, formationDest.m_positionY, formationDest.m_positionZ, &orientation); + formationDest.SetOrientation(orientation); + } } //! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call @@ -176,12 +180,9 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) init.Launch(); - //Call for creature group update + // Call for creature group update if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - { - creature->SetWalk(node->move_type != WAYPOINT_MOVE_TYPE_RUN); - creature->GetFormation()->LeaderMoveTo(formationDest.x, formationDest.y, formationDest.z); - } + creature->GetFormation()->LeaderMoveTo(formationDest, node->id, node->move_type, (node->orientation && node->delay) ? true : false); return true; } diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h index 2023a4de8e8..da9d80dad63 100644 --- a/src/server/game/Quests/QuestDef.h +++ b/src/server/game/Quests/QuestDef.h @@ -224,7 +224,8 @@ enum QuestSpecialFlags QUEST_SPECIAL_FLAGS_SPEAKTO = 0x100, // Internal flag computed only QUEST_SPECIAL_FLAGS_KILL = 0x200, // Internal flag computed only QUEST_SPECIAL_FLAGS_TIMED = 0x400, // Internal flag computed only - QUEST_SPECIAL_FLAGS_PLAYER_KILL = 0x800 // Internal flag computed only + QUEST_SPECIAL_FLAGS_PLAYER_KILL = 0x800, // Internal flag computed only + QUEST_SPECIAL_FLAGS_COMPLETED_AT_START = 0x1000 // Internal flag computed only }; enum QuestObjectiveType @@ -497,7 +498,6 @@ class TC_GAME_API Quest void BuildQuestRewards(WorldPackets::Quest::QuestRewards& rewards, Player* player) const; std::vector<uint32> DependentPreviousQuests; - std::vector<uint32> PrevChainQuests; WorldPacket QueryData[TOTAL_LOCALES]; private: diff --git a/src/server/game/Reputation/ReputationMgr.cpp b/src/server/game/Reputation/ReputationMgr.cpp index 1c362984ecc..b017947d56c 100644 --- a/src/server/game/Reputation/ReputationMgr.cpp +++ b/src/server/game/Reputation/ReputationMgr.cpp @@ -267,7 +267,7 @@ void ReputationMgr::Initialize() } } -bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standing, bool incremental, bool noSpillover) +bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standing, bool incremental, bool spillOverOnly, bool noSpillover) { sScriptMgr->OnPlayerReputationChange(_player, factionEntry->ID, standing, incremental); bool res = false; @@ -334,7 +334,10 @@ bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standi FactionStateList::iterator faction = _factions.find(factionEntry->ReputationIndex); if (faction != _factions.end()) { - res = SetOneFactionReputation(factionEntry, standing, incremental); + // if we update spillover only, do not update main reputation (rank exceeds creature reward rate) + if (!spillOverOnly) + res = SetOneFactionReputation(factionEntry, standing, incremental); + // only this faction gets reported to client, even if it has no own visible standing SendState(&faction->second); } diff --git a/src/server/game/Reputation/ReputationMgr.h b/src/server/game/Reputation/ReputationMgr.h index 3ad0958107c..ffc9ad826b5 100644 --- a/src/server/game/Reputation/ReputationMgr.h +++ b/src/server/game/Reputation/ReputationMgr.h @@ -118,11 +118,11 @@ class TC_GAME_API ReputationMgr public: // modifiers bool SetReputation(FactionEntry const* factionEntry, int32 standing) { - return SetReputation(factionEntry, standing, false, false); + return SetReputation(factionEntry, standing, false, false, false); } - bool ModifyReputation(FactionEntry const* factionEntry, int32 standing, bool noSpillover = false) + bool ModifyReputation(FactionEntry const* factionEntry, int32 standing, bool spillOverOnly = false, bool noSpillover = false) { - return SetReputation(factionEntry, standing, true, noSpillover); + return SetReputation(factionEntry, standing, true, spillOverOnly, noSpillover); } void SetVisible(FactionTemplateEntry const* factionTemplateEntry); @@ -144,7 +144,7 @@ class TC_GAME_API ReputationMgr private: // internal helper functions void Initialize(); uint32 GetDefaultStateFlags(FactionEntry const* factionEntry) const; - bool SetReputation(FactionEntry const* factionEntry, int32 standing, bool incremental, bool noSpillover); + bool SetReputation(FactionEntry const* factionEntry, int32 standing, bool incremental, bool spillOverOnly, bool noSpillover); void SetVisible(FactionState* faction); void SetAtWar(FactionState* faction, bool atWar) const; void SetInactive(FactionState* faction, bool inactive) const; diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index e9596c4fdd3..fea767fa9a5 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -1637,15 +1637,6 @@ InstanceScript* ScriptMgr::CreateInstanceData(InstanceMap* map) return tmpscript->GetInstanceScript(map); } -bool ScriptMgr::OnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effIndex, Item* target) -{ - ASSERT(caster); - ASSERT(target); - - GET_SCRIPT_RET(ItemScript, target->GetScriptId(), tmpscript, false); - return tmpscript->OnDummyEffect(caster, spellId, effIndex, target); -} - bool ScriptMgr::OnQuestAccept(Player* player, Item* item, Quest const* quest) { ASSERT(player); @@ -1695,15 +1686,6 @@ bool ScriptMgr::OnCastItemCombatSpell(Player* player, Unit* victim, SpellInfo co return tmpscript->OnCastItemCombatSpell(player, victim, spellInfo, item); } -bool ScriptMgr::OnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effIndex, Creature* target) -{ - ASSERT(caster); - ASSERT(target); - - GET_SCRIPT_RET(CreatureScript, target->GetScriptId(), tmpscript, false); - return tmpscript->OnDummyEffect(caster, spellId, effIndex, target); -} - bool ScriptMgr::OnGossipHello(Player* player, Creature* creature) { ASSERT(player); @@ -1920,15 +1902,6 @@ void ScriptMgr::OnGameObjectUpdate(GameObject* go, uint32 diff) tmpscript->OnUpdate(go, diff); } -bool ScriptMgr::OnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effIndex, GameObject* target) -{ - ASSERT(caster); - ASSERT(target); - - GET_SCRIPT_RET(GameObjectScript, target->GetScriptId(), tmpscript, false); - return tmpscript->OnDummyEffect(caster, spellId, effIndex, target); -} - bool ScriptMgr::OnAreaTrigger(Player* player, AreaTriggerEntry const* trigger, bool entered) { ASSERT(player); diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 53465b1dca2..518cc5f4a70 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -374,9 +374,6 @@ class TC_GAME_API ItemScript : public ScriptObject public: - // Called when a dummy spell effect is triggered on the item. - virtual bool OnDummyEffect(Unit* /*caster*/, uint32 /*spellId*/, SpellEffIndex /*effIndex*/, Item* /*target*/) { return false; } - // Called when a player accepts a quest from the item. virtual bool OnQuestAccept(Player* /*player*/, Item* /*item*/, Quest const* /*quest*/) { return false; } @@ -424,9 +421,6 @@ class TC_GAME_API CreatureScript : public UnitScript, public UpdatableScript<Cre public: - // Called when a dummy spell effect is triggered on the creature. - virtual bool OnDummyEffect(Unit* /*caster*/, uint32 /*spellId*/, SpellEffIndex /*effIndex*/, Creature* /*target*/) { return false; } - // Called when a player opens a gossip dialog with the creature. virtual bool OnGossipHello(Player* /*player*/, Creature* /*creature*/) { return false; } @@ -463,9 +457,6 @@ class TC_GAME_API GameObjectScript : public ScriptObject, public UpdatableScript public: - // Called when a dummy spell effect is triggered on the gameobject. - virtual bool OnDummyEffect(Unit* /*caster*/, uint32 /*spellId*/, SpellEffIndex /*effIndex*/, GameObject* /*target*/) { return false; } - // Called when a player opens a gossip dialog with the gameobject. virtual bool OnGossipHello(Player* /*player*/, GameObject* /*go*/) { return false; } @@ -1026,7 +1017,6 @@ class TC_GAME_API ScriptMgr public: /* ItemScript */ - bool OnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effIndex, Item* target); bool OnQuestAccept(Player* player, Item* item, Quest const* quest); bool OnItemUse(Player* player, Item* item, SpellCastTargets const& targets, ObjectGuid castId); bool OnItemExpire(Player* player, ItemTemplate const* proto); @@ -1035,7 +1025,6 @@ class TC_GAME_API ScriptMgr public: /* CreatureScript */ - bool OnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effIndex, Creature* target); bool OnGossipHello(Player* player, Creature* creature); bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action); bool OnGossipSelectCode(Player* player, Creature* creature, uint32 sender, uint32 action, const char* code); @@ -1049,7 +1038,6 @@ class TC_GAME_API ScriptMgr public: /* GameObjectScript */ - bool OnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effIndex, GameObject* target); bool OnGossipHello(Player* player, GameObject* go); bool OnGossipSelect(Player* player, GameObject* go, uint32 sender, uint32 action); bool OnGossipSelectCode(Player* player, GameObject* go, uint32 sender, uint32 action, const char* code); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 566ecfd9df8..9281d012f55 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -4072,6 +4072,34 @@ void AuraEffect::HandleModCastingSpeed(AuraApplication const* aurApp, uint8 mode Unit* target = aurApp->GetTarget(); + // Do not apply such auras in normal way + if (GetAmount() >= 1000) + { + if (apply) + target->SetInstantCast(true); + else + { + // only SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK can have this high amount + // it's some rare case that you have 2 auras like that, but just in case ;) + + bool remove = true; + Unit::AuraEffectList const& castingSpeedNotStack = target->GetAuraEffectsByType(SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK); + for (AuraEffect const* aurEff : castingSpeedNotStack) + { + if (aurEff != this && aurEff->GetAmount() >= 1000) + { + remove = false; + break; + } + } + + if (remove) + target->SetInstantCast(false); + } + + return; + } + target->ApplyCastTimePercentMod((float)GetAmount(), apply); } @@ -4457,15 +4485,15 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool Unit* caster = GetCaster(); - if (mode & AURA_EFFECT_HANDLE_REAL) + // pet auras + if (target->GetTypeId() == TYPEID_PLAYER && (mode & AURA_EFFECT_HANDLE_REAL)) { - // pet auras if (PetAura const* petSpell = sSpellMgr->GetPetAura(GetId(), m_effIndex)) { if (apply) - target->AddPetAura(petSpell); + target->ToPlayer()->AddPetAura(petSpell); else - target->RemovePetAura(petSpell); + target->ToPlayer()->RemovePetAura(petSpell); } } @@ -4900,7 +4928,7 @@ void AuraEffect::HandleAuraModFaction(AuraApplication const* aurApp, uint8 mode, if (apply) { - target->setFaction(GetMiscValue()); + target->SetFaction(GetMiscValue()); if (target->GetTypeId() == TYPEID_PLAYER) target->RemoveUnitFlag(UNIT_FLAG_PVP_ATTACKABLE); } @@ -5501,12 +5529,7 @@ void AuraEffect::HandlePeriodicTriggerSpellAuraTick(Unit* target, Unit* caster) } } else - { - Creature* c = target->ToCreature(); - if (!c || !caster || !sScriptMgr->OnDummyEffect(caster, GetId(), SpellEffIndex(GetEffIndex()), target->ToCreature()) || - !c->AI()->sOnDummyEffect(caster, GetId(), SpellEffIndex(GetEffIndex()))) - TC_LOG_DEBUG("spells", "AuraEffect::HandlePeriodicTriggerSpellAuraTick: Spell %u has non-existent spell %u in EffectTriggered[%d] and is therefor not triggered.", GetId(), triggerSpellId, GetEffIndex()); - } + TC_LOG_DEBUG("spells", "AuraEffect::HandlePeriodicTriggerSpellAuraTick: Spell %u has non-existent spell %u in EffectTriggered[%d] and is therefor not triggered.", GetId(), triggerSpellId, GetEffIndex()); } void AuraEffect::HandlePeriodicTriggerSpellWithValueAuraTick(Unit* target, Unit* caster) const diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index a03788cdda5..85388dc5821 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2626,11 +2626,24 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) } // Now Reduce spell duration using data received at spell hit + // check whatever effects we're going to apply, diminishing returns only apply to negative aura effects + bool positive = true; + if (m_originalCaster == unit || !m_originalCaster->IsFriendlyTo(unit)) + { + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if ((effectMask & (1 << i)) && !m_spellInfo->IsPositiveEffect(i)) + { + positive = false; + break; + } + } + } + int32 duration = m_spellAura->GetMaxDuration(); - float diminishMod = unit->ApplyDiminishingToDuration(m_spellInfo, duration, m_originalCaster, diminishLevel); // unit is immune to aura if it was diminished to 0 duration - if (diminishMod == 0.0f) + if (!positive && !unit->ApplyDiminishingToDuration(m_spellInfo, duration, m_originalCaster, diminishLevel)) { m_spellAura->Remove(); bool found = false; @@ -2642,11 +2655,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) } else { - ((UnitAura*)m_spellAura)->SetDiminishGroup(diminishGroup); - - bool positive = m_spellAura->GetSpellInfo()->IsPositive(); - if (AuraApplication* aurApp = m_spellAura->GetApplicationOfTarget(m_originalCaster->GetGUID())) - positive = aurApp->IsPositive(); + static_cast<UnitAura*>(m_spellAura)->SetDiminishGroup(diminishGroup); duration = m_originalCaster->ModSpellDuration(m_spellInfo, unit, duration, positive, effectMask); @@ -2944,15 +2953,6 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered else m_casttime = m_spellInfo->CalcCastTime(m_caster->getLevel(), this); - if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) // _UNIT actually means creature. for some reason. - if (!(m_spellInfo->IsNextMeleeSwingSpell() || IsAutoRepeat() || (_triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING))) - { - if (m_targets.GetObjectTarget() && m_caster != m_targets.GetObjectTarget()) - m_caster->ToCreature()->FocusTarget(this, m_targets.GetObjectTarget()); - else if (m_spellInfo->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST)) - m_caster->ToCreature()->FocusTarget(this, nullptr); - } - // don't allow channeled spells / spells with cast time to be cast while moving // exception are only channeled spells that have no casttime and SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING // (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in) @@ -2969,6 +2969,18 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered } } + // focus if not controlled creature + if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) + { + if (!(m_spellInfo->IsNextMeleeSwingSpell() || IsAutoRepeat() || (_triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING))) + { + if (m_targets.GetObjectTarget() && m_caster != m_targets.GetObjectTarget()) + m_caster->ToCreature()->FocusTarget(this, m_targets.GetObjectTarget()); + else if (m_spellInfo->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST)) + m_caster->ToCreature()->FocusTarget(this, nullptr); + } + } + // set timer base at cast time ReSetTimer(); @@ -3116,11 +3128,12 @@ void Spell::_cast(bool skipCheck) m_caster->SetInFront(m_targets.GetObjectTarget()); // Should this be done for original caster? - if (m_caster->GetTypeId() == TYPEID_PLAYER) + Player* modOwner = m_caster->GetSpellModOwner(); + if (modOwner) { // Set spell which will drop charges for triggered cast spells // if not successfully cast, will be remove in finish(false) - m_caster->ToPlayer()->SetSpellModTakingSpell(this, true); + modOwner->SetSpellModTakingSpell(this, true); } CallScriptBeforeCastHandlers(); @@ -3135,8 +3148,8 @@ void Spell::_cast(bool skipCheck) SendCastResult(castResult, ¶m1, ¶m2); SendInterrupted(0); - if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); + if (modOwner) + modOwner->SetSpellModTakingSpell(this, false); finish(false); SetExecutedCurrently(false); @@ -3147,9 +3160,9 @@ void Spell::_cast(bool skipCheck) // if trade not complete then remember it in trade data if (m_targets.GetTargetMask() & TARGET_FLAG_TRADE_ITEM) { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (modOwner) { - if (TradeData* my_trade = m_caster->ToPlayer()->GetTradeData()) + if (TradeData* my_trade = modOwner->GetTradeData()) { if (!my_trade->IsInAcceptProcess()) { @@ -3158,7 +3171,7 @@ void Spell::_cast(bool skipCheck) SendCastResult(SPELL_FAILED_DONT_REPORT); SendInterrupted(0); - m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); + modOwner->SetSpellModTakingSpell(this, false); finish(false); SetExecutedCurrently(false); @@ -3275,12 +3288,12 @@ void Spell::_cast(bool skipCheck) m_caster->CastSpell(m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : m_caster, *i, true); } - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (modOwner) { - m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); + modOwner->SetSpellModTakingSpell(this, false); //Clear spell cooldowns after every spell is cast if .cheat cooldown is enabled. - if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_COOLDOWN)) + if (modOwner->GetCommandStatus(CHEAT_COOLDOWN)) { m_caster->GetSpellHistory()->ResetCooldown(m_spellInfo->Id, true); m_caster->GetSpellHistory()->RestoreCharge(m_spellInfo->ChargeCategoryId); @@ -3289,9 +3302,6 @@ void Spell::_cast(bool skipCheck) SetExecutedCurrently(false); - if (Creature* creatureCaster = m_caster->ToCreature()) - creatureCaster->ReleaseFocus(this); - if (!m_originalCaster) return; @@ -3411,8 +3421,9 @@ uint64 Spell::handle_delayed(uint64 t_offset) if (single_missile && !t_offset) return m_delayMoment; - if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->SetSpellModTakingSpell(this, true); + Player* modOwner = m_caster->GetSpellModOwner(); + if (modOwner) + modOwner->SetSpellModTakingSpell(this, true); PrepareTargetProcessing(); @@ -3451,8 +3462,8 @@ uint64 Spell::handle_delayed(uint64 t_offset) FinishTargetProcessing(); - if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); + if (modOwner) + modOwner->SetSpellModTakingSpell(this, false); // All targets passed - need finish phase if (next_time == 0) @@ -4449,8 +4460,16 @@ void Spell::SendChannelStart(uint32 duration) m_timer = duration; for (TargetInfo const& target : m_UniqueTargetInfo) + { m_caster->AddChannelObject(target.targetGUID); + if (m_UniqueTargetInfo.size() == 1 && m_UniqueGOTargetInfo.empty()) + if(target.targetGUID != m_caster->GetGUID()) + if (Creature* creatureCaster = m_caster->ToCreature()) + if (!creatureCaster->IsFocusing(this)) + creatureCaster->FocusTarget(this, ObjectAccessor::GetWorldObject(*creatureCaster, target.targetGUID)); + } + for (GOTargetInfo const& target : m_UniqueGOTargetInfo) m_caster->AddChannelObject(target.targetGUID); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 56a050eea65..53e9c98b0db 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -607,24 +607,18 @@ void Spell::EffectDummy(SpellEffIndex effIndex) } // pet auras - if (PetAura const* petSpell = sSpellMgr->GetPetAura(m_spellInfo->Id, effIndex)) + if (m_caster->GetTypeId() == TYPEID_PLAYER) { - m_caster->AddPetAura(petSpell); - return; + if (PetAura const* petSpell = sSpellMgr->GetPetAura(m_spellInfo->Id, effIndex)) + { + m_caster->ToPlayer()->AddPetAura(petSpell); + return; + } } // normal DB scripted effect TC_LOG_DEBUG("spells", "Spell ScriptStart spellid %u in EffectDummy(%u)", m_spellInfo->Id, effIndex); m_caster->GetMap()->ScriptsStart(sSpellScripts, uint32(m_spellInfo->Id | (effIndex << 24)), m_caster, unitTarget); - - // Script based implementation. Must be used only for not good for implementation in core spell effects - // So called only for not proccessed cases - if (gameObjTarget) - sScriptMgr->OnDummyEffect(m_caster, m_spellInfo->Id, effIndex, gameObjTarget); - else if (unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT) - sScriptMgr->OnDummyEffect(m_caster, m_spellInfo->Id, effIndex, unitTarget->ToCreature()); - else if (itemTarget) - sScriptMgr->OnDummyEffect(m_caster, m_spellInfo->Id, effIndex, itemTarget); } void Spell::EffectTriggerSpell(SpellEffIndex /*effIndex*/) @@ -2031,7 +2025,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) if (properties->Control == SUMMON_CATEGORY_ALLY) { summon->SetOwnerGUID(m_originalCaster->GetGUID()); - summon->setFaction(m_originalCaster->getFaction()); + summon->SetFaction(m_originalCaster->GetFaction()); summon->SetCreatedBySpell(m_spellInfo->Id); } @@ -2072,9 +2066,9 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) uint32 faction = properties->Faction; if (!faction) - faction = m_originalCaster->getFaction(); + faction = m_originalCaster->GetFaction(); - summon->setFaction(faction); + summon->SetFaction(faction); break; } @@ -2232,11 +2226,11 @@ void Spell::EffectDistract(SpellEffIndex /*effIndex*/) if (unitTarget->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING)) return; - unitTarget->SetFacingTo(unitTarget->GetAngle(destTarget)); - unitTarget->ClearUnitState(UNIT_STATE_MOVING); - if (unitTarget->GetTypeId() == TYPEID_UNIT) unitTarget->GetMotionMaster()->MoveDistract(damage * IN_MILLISECONDS); + + unitTarget->StopMoving(); + unitTarget->SetFacingTo(unitTarget->GetAngle(destTarget)); } void Spell::EffectPickPocket(SpellEffIndex /*effIndex*/) @@ -3604,7 +3598,7 @@ void Spell::EffectDuel(SpellEffIndex effIndex) PhasingHandler::InheritPhaseShift(go, m_caster); - go->SetFaction(m_caster->getFaction()); + go->SetFaction(m_caster->GetFaction()); go->SetLevel(m_caster->getLevel()+1); int32 duration = m_spellInfo->CalcDuration(m_caster); go->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); @@ -4688,7 +4682,7 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) { case GAMEOBJECT_TYPE_FISHINGNODE: { - go->SetFaction(m_caster->getFaction()); + go->SetFaction(m_caster->GetFaction()); ObjectGuid bobberGuid = go->GetGUID(); // client requires fishing bobber guid in channel object slot 0 to be usable m_caster->SetChannelObject(0, bobberGuid); @@ -5206,7 +5200,7 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* ((Guardian*)summon)->InitStatsForLevel(level); if (properties && properties->Control == SUMMON_CATEGORY_ALLY) - summon->setFaction(caster->getFaction()); + summon->SetFaction(caster->GetFaction()); if (summon->HasUnitTypeMask(UNIT_MASK_MINION) && m_targets.HasDst()) ((Minion*)summon)->SetFollowAngle(m_caster->GetAngle(summon)); diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 12eb37b6861..b9f26ed4e08 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -510,14 +510,16 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster /*= nullptr*/, int32 const* if (Scaling.ResourceCoefficient) comboDamage = Scaling.ResourceCoefficient * value; } - else + else if (GetScalingExpectedStat() == ExpectedStatType::None) { - if (GetScalingExpectedStat() == ExpectedStatType::None) + if (caster && basePointsPerLevel != 0.0f) { - int32 level = caster ? int32(caster->getLevel()) : 0; + int32 level = int32(caster->getLevel()); if (level > int32(_spellInfo->MaxLevel) && _spellInfo->MaxLevel > 0) level = int32(_spellInfo->MaxLevel); - level -= int32(_spellInfo->BaseLevel); + + // if base level is greater than spell level, reduce by base level (eg. pilgrims foods) + level -= int32(std::max(_spellInfo->BaseLevel, _spellInfo->SpellLevel)); if (level < 0) level = 0; value += level * basePointsPerLevel; @@ -1673,7 +1675,7 @@ bool SpellInfo::IsChanneled() const bool SpellInfo::IsMoveAllowedChannel() const { - return IsChanneled() && HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING); + return IsChanneled() && (HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING) || (!(ChannelInterruptFlags[0] & (AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING)))); } bool SpellInfo::NeedsComboPoints() const @@ -3382,11 +3384,14 @@ void SpellInfo::_LoadImmunityInfo() { switch (Id) { + case 42292: // PvP trinket + case 59752: // Every Man for Himself + mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED); + break; case 34471: // The Beast Within case 19574: // Bestial Wrath - case 42292: // PvP trinket case 46227: // Medallion of Immunity - case 59752: // Every Man for Himself case 53490: // Bullheaded case 65547: // PvP Trinket case 134946: // Supremacy of the Alliance @@ -4507,6 +4512,7 @@ bool SpellInfo::_IsPositiveEffect(uint32 effIndex, bool deep) const case SPELL_AURA_MOD_STALKED: case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: case SPELL_AURA_PREVENT_RESURRECTION: + case SPELL_AURA_EMPATHY: return false; case SPELL_AURA_PERIODIC_DAMAGE: // used in positive spells also. // part of negative spell if cast at self (prevent cancel) diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 1dcc18773e1..eb2621954cf 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -335,32 +335,34 @@ void SpellMgr::GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& } } -bool SpellMgr::AddSameEffectStackRuleSpellGroups(SpellInfo const* spellInfo, int32 amount, std::map<SpellGroup, int32>& groups) const +bool SpellMgr::AddSameEffectStackRuleSpellGroups(SpellInfo const* spellInfo, uint32 auraType, int32 amount, std::map<SpellGroup, int32>& groups) const { uint32 spellId = spellInfo->GetFirstRankSpell()->Id; - SpellSpellGroupMapBounds spellGroup = GetSpellSpellGroupMapBounds(spellId); + auto spellGroupBounds = GetSpellSpellGroupMapBounds(spellId); // Find group with SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT if it belongs to one - for (SpellSpellGroupMap::const_iterator itr = spellGroup.first; itr != spellGroup.second; ++itr) + for (auto itr = spellGroupBounds.first; itr != spellGroupBounds.second; ++itr) { SpellGroup group = itr->second; - SpellGroupStackMap::const_iterator found = mSpellGroupStack.find(group); - if (found != mSpellGroupStack.end()) + auto found = mSpellSameEffectStack.find(group); + if (found != mSpellSameEffectStack.end()) { - if (found->second == SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT) + // check auraTypes + if (!found->second.count(auraType)) + continue; + + // Put the highest amount in the map + auto groupItr = groups.find(group); + if (groupItr == groups.end()) + groups.emplace(group, amount); + else { - // Put the highest amount in the map - if (groups.find(group) == groups.end()) - groups[group] = amount; - else - { - int32 curr_amount = groups[group]; - // Take absolute value because this also counts for the highest negative aura - if (abs(curr_amount) < abs(amount)) - groups[group] = amount; - } - // return because a spell should be in only one SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT group - return true; + int32 curr_amount = groups[group]; + // Take absolute value because this also counts for the highest negative aura + if (std::abs(curr_amount) < std::abs(amount)) + groupItr->second = amount; } + // return because a spell should be in only one SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT group per auraType + return true; } } // Not in a SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT group, so return false @@ -374,8 +376,6 @@ SpellGroupStackRule SpellMgr::CheckSpellGroupStackRules(SpellInfo const* spellIn uint32 spellid_1 = spellInfo1->GetFirstRankSpell()->Id; uint32 spellid_2 = spellInfo2->GetFirstRankSpell()->Id; - if (spellid_1 == spellid_2) - return SPELL_GROUP_STACK_RULE_DEFAULT; // find SpellGroups which are common for both spells SpellSpellGroupMapBounds spellGroup1 = GetSpellSpellGroupMapBounds(spellid_1); @@ -1181,19 +1181,19 @@ void SpellMgr::LoadSpellGroups() } int32 spell_id = fields[1].GetInt32(); - groups.insert(std::set<uint32>::value_type(group_id)); - mSpellGroupSpell.insert(SpellGroupSpellMap::value_type((SpellGroup)group_id, spell_id)); + groups.insert(group_id); + mSpellGroupSpell.emplace(SpellGroup(group_id), spell_id); } while (result->NextRow()); - for (SpellGroupSpellMap::iterator itr = mSpellGroupSpell.begin(); itr!= mSpellGroupSpell.end();) + for (auto itr = mSpellGroupSpell.begin(); itr!= mSpellGroupSpell.end();) { if (itr->second < 0) { if (groups.find(abs(itr->second)) == groups.end()) { TC_LOG_ERROR("sql.sql", "SpellGroup id %u listed in `spell_group` does not exist", abs(itr->second)); - mSpellGroupSpell.erase(itr++); + itr = mSpellGroupSpell.erase(itr); } else ++itr; @@ -1201,31 +1201,30 @@ void SpellMgr::LoadSpellGroups() else { SpellInfo const* spellInfo = GetSpellInfo(itr->second); - if (!spellInfo) { TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_group` does not exist", itr->second); - mSpellGroupSpell.erase(itr++); + itr = mSpellGroupSpell.erase(itr); } else if (spellInfo->GetRank() > 1) { TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_group` is not the first rank of the spell.", itr->second); - mSpellGroupSpell.erase(itr++); + itr = mSpellGroupSpell.erase(itr); } else ++itr; } } - for (std::set<uint32>::iterator groupItr = groups.begin(); groupItr != groups.end(); ++groupItr) + for (auto groupItr = groups.begin(); groupItr != groups.end(); ++groupItr) { std::set<uint32> spells; GetSetOfSpellsInSpellGroup(SpellGroup(*groupItr), spells); - for (std::set<uint32>::iterator spellItr = spells.begin(); spellItr != spells.end(); ++spellItr) + for (auto spellItr = spells.begin(); spellItr != spells.end(); ++spellItr) { ++count; - mSpellSpellGroup.insert(SpellSpellGroupMap::value_type(*spellItr, SpellGroup(*groupItr))); + mSpellSpellGroup.emplace(*spellItr, SpellGroup(*groupItr)); } } @@ -1237,6 +1236,9 @@ void SpellMgr::LoadSpellGroupStackRules() uint32 oldMSTime = getMSTime(); mSpellGroupStack.clear(); // need for reload case + mSpellSameEffectStack.clear(); + + std::vector<uint32> sameEffectGroups; // 0 1 QueryResult result = WorldDatabase.Query("SELECT group_id, stack_rule FROM spell_group_stack_rules"); @@ -1259,20 +1261,126 @@ void SpellMgr::LoadSpellGroupStackRules() continue; } - SpellGroupSpellMapBounds spellGroup = GetSpellGroupSpellMapBounds((SpellGroup)group_id); - - if (spellGroup.first == spellGroup.second) + auto bounds = GetSpellGroupSpellMapBounds((SpellGroup)group_id); + if (bounds.first == bounds.second) { TC_LOG_ERROR("sql.sql", "SpellGroup id %u listed in `spell_group_stack_rules` does not exist.", group_id); continue; } - mSpellGroupStack[(SpellGroup)group_id] = (SpellGroupStackRule)stack_rule; + mSpellGroupStack.emplace(SpellGroup(group_id), SpellGroupStackRule(stack_rule)); + + // different container for same effect stack rules, need to check effect types + if (stack_rule == SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT) + sameEffectGroups.push_back(group_id); ++count; } while (result->NextRow()); TC_LOG_INFO("server.loading", ">> Loaded %u spell group stack rules in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + + count = 0; + oldMSTime = getMSTime(); + TC_LOG_INFO("server.loading", ">> Parsing SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT stack rules..."); + + for (uint32 group_id : sameEffectGroups) + { + std::set<uint32> spellIds; + GetSetOfSpellsInSpellGroup(SpellGroup(group_id), spellIds); + + std::unordered_set<uint32> auraTypes; + + // we have to 'guess' what effect this group corresponds to + { + std::unordered_multiset<uint32 /*auraName*/> frequencyContainer; + + // only waylay for the moment (shared group) + std::vector<std::vector<uint32 /*auraName*/>> const SubGroups = + { + { SPELL_AURA_MOD_MELEE_HASTE, SPELL_AURA_MOD_MELEE_RANGED_HASTE, SPELL_AURA_MOD_RANGED_HASTE } + }; + + for (uint32 spellId : spellIds) + { + SpellInfo const* spellInfo = AssertSpellInfo(spellId); + for (SpellEffectInfo const* effectInfo : spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE)) + { + if (!effectInfo->IsAura()) + continue; + + int32 auraName = static_cast<int32>(effectInfo->ApplyAuraName); + for (std::vector<uint32> const& subGroup : SubGroups) + { + if (std::find(subGroup.begin(), subGroup.end(), auraName) != subGroup.end()) + { + // count as first aura + auraName = subGroup.front(); + break; + } + } + + frequencyContainer.insert(auraName); + } + } + + uint32 auraType = 0; + size_t auraTypeCount = 0; + for (uint32 auraName : frequencyContainer) + { + size_t currentCount = frequencyContainer.count(auraName); + if (currentCount > auraTypeCount) + { + auraType = auraName; + auraTypeCount = currentCount; + } + } + + for (std::vector<uint32> const& subGroup : SubGroups) + { + if (auraType == subGroup.front()) + { + auraTypes.insert(subGroup.begin(), subGroup.end()); + break; + } + } + + if (auraTypes.empty()) + auraTypes.insert(auraType); + } + + // re-check spells against guessed group + for (uint32 spellId : spellIds) + { + SpellInfo const* spellInfo = AssertSpellInfo(spellId); + + bool found = false; + while (spellInfo) + { + for (uint32 auraType : auraTypes) + { + if (spellInfo->HasAura(DIFFICULTY_NONE, AuraType(auraType))) + { + found = true; + break; + } + } + + if (found) + break; + + spellInfo = spellInfo->GetNextRankSpell(); + } + + // not found either, log error + if (!found) + TC_LOG_ERROR("sql.sql", "SpellId %u listed in `spell_group` with stack rule 3 does not share aura assigned for group %u", spellId, group_id); + } + + mSpellSameEffectStack[SpellGroup(group_id)] = auraTypes; + ++count; + } + + TC_LOG_INFO("server.loading", ">> Parsed %u SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT stack rules in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } void SpellMgr::LoadSpellProcs() @@ -3342,6 +3450,13 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->Speed = 0.0f; // This spell's summon happens instantly }); + // Chilled to the Bone + ApplySpellFix({ 70106 }, [](SpellInfo* spellInfo) + { + spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS; + spellInfo->AttributesEx6 |= SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS; + }); + // Ice Lock ApplySpellFix({ 71614 }, [](SpellInfo* spellInfo) { diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 6bf843cfff1..c121f1ec8f1 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -31,6 +31,7 @@ #include <set> #include <vector> #include <unordered_map> +#include <unordered_set> class SpellInfo; class Player; @@ -309,14 +310,26 @@ enum SpellGroup SPELL_GROUP_CORE_RANGE_MAX = 5 }; +namespace std +{ + template<> + struct hash<SpellGroup> + { + size_t operator()(SpellGroup const& group) const + { + return hash<uint32>()(uint32(group)); + } + }; +} + #define SPELL_GROUP_DB_RANGE_MIN 1000 // spell_id, group_id -typedef std::multimap<uint32, SpellGroup > SpellSpellGroupMap; +typedef std::unordered_multimap<uint32, SpellGroup> SpellSpellGroupMap; typedef std::pair<SpellSpellGroupMap::const_iterator, SpellSpellGroupMap::const_iterator> SpellSpellGroupMapBounds; // group_id, spell_id -typedef std::multimap<SpellGroup, int32> SpellGroupSpellMap; +typedef std::unordered_multimap<SpellGroup, int32> SpellGroupSpellMap; typedef std::pair<SpellGroupSpellMap::const_iterator, SpellGroupSpellMap::const_iterator> SpellGroupSpellMapBounds; enum SpellGroupStackRule @@ -329,7 +342,9 @@ enum SpellGroupStackRule SPELL_GROUP_STACK_RULE_MAX }; -typedef std::map<SpellGroup, SpellGroupStackRule> SpellGroupStackMap; +typedef std::unordered_map<SpellGroup, SpellGroupStackRule> SpellGroupStackMap; + +typedef std::unordered_map<SpellGroup, std::unordered_set<uint32 /*auraName*/>> SameEffectStackMap; struct SpellThreatEntry { @@ -655,7 +670,7 @@ class TC_GAME_API SpellMgr void GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& foundSpells, std::set<SpellGroup>& usedGroups) const; // Spell Group Stack Rules table - bool AddSameEffectStackRuleSpellGroups(SpellInfo const* spellInfo, int32 amount, std::map<SpellGroup, int32>& groups) const; + bool AddSameEffectStackRuleSpellGroups(SpellInfo const* spellInfo, uint32 auraType, int32 amount, std::map<SpellGroup, int32>& groups) const; SpellGroupStackRule CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2) const; SpellGroupStackRule GetSpellGroupStackRule(SpellGroup groupid) const; @@ -750,6 +765,7 @@ class TC_GAME_API SpellMgr SpellSpellGroupMap mSpellSpellGroup; SpellGroupSpellMap mSpellGroupSpell; SpellGroupStackMap mSpellGroupStack; + SameEffectStackMap mSpellSameEffectStack; SpellProcMap mSpellProcMap; SpellThreatMap mSpellThreatMap; SpellPetAuraMap mSpellPetAuraMap; diff --git a/src/server/game/Time/UpdateTime.cpp b/src/server/game/Time/UpdateTime.cpp index ffd985c2caf..6f3f5d6ff83 100644 --- a/src/server/game/Time/UpdateTime.cpp +++ b/src/server/game/Time/UpdateTime.cpp @@ -31,6 +31,7 @@ UpdateTime::UpdateTime() _maxUpdateTime = 0; _maxUpdateTimeOfLastTable = 0; _maxUpdateTimeOfCurrentTable = 0; + _recordedTime = 0; _updateTimeDataTable = { }; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index e1e71ce01e2..a94e06034ba 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -60,6 +60,7 @@ #include "IPLocation.h" #include "Language.h" #include "LFGMgr.h" +#include "LootItemStorage.h" #include "LootMgr.h" #include "M2Stores.h" #include "MapManager.h" @@ -2073,6 +2074,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Calendar data..."); sCalendarMgr->LoadFromDB(); + TC_LOG_INFO("server.loading", "Loading Item loot..."); + sLootItemStorage->LoadStorageFromDB(); + TC_LOG_INFO("server.loading", "Initialize query data..."); sObjectMgr->InitializeQueriesData(QUERY_DATA_ALL); |