aboutsummaryrefslogtreecommitdiff
path: root/src/server/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game')
-rw-r--r--src/server/game/AI/CoreAI/PassiveAI.cpp4
-rw-r--r--src/server/game/AI/CoreAI/TotemAI.cpp2
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.cpp2
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.h5
-rw-r--r--src/server/game/AI/CreatureAI.h7
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.cpp37
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.h4
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp10
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h4
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp544
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.h135
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h4
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp647
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.h81
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp158
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp65
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h64
-rw-r--r--src/server/game/Accounts/RBAC.h16
-rw-r--r--src/server/game/Achievements/AchievementMgr.cpp4
-rw-r--r--src/server/game/Battlefield/Battlefield.cpp7
-rw-r--r--src/server/game/Battlegrounds/Battleground.cpp2
-rw-r--r--src/server/game/Battlegrounds/BattlegroundMgr.cpp3
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp6
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundAB.h10
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp1
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundAV.h20
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp6
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp3
-rw-r--r--src/server/game/Calendar/CalendarMgr.cpp6
-rw-r--r--src/server/game/Chat/Chat.cpp153
-rw-r--r--src/server/game/Chat/Chat.h25
-rw-r--r--src/server/game/Chat/ChatLink.cpp2
-rw-r--r--src/server/game/Combat/ThreatManager.h6
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp6
-rw-r--r--src/server/game/Conditions/ConditionMgr.h2
-rw-r--r--src/server/game/DataStores/DBCEnums.h2
-rw-r--r--src/server/game/DataStores/DBCStores.cpp10
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp4
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp469
-rw-r--r--src/server/game/Entities/Creature/Creature.h47
-rw-r--r--src/server/game/Entities/Creature/CreatureData.h39
-rw-r--r--src/server/game/Entities/Creature/CreatureGroups.cpp18
-rw-r--r--src/server/game/Entities/Creature/CreatureGroups.h2
-rw-r--r--src/server/game/Entities/Creature/GossipDef.cpp42
-rw-r--r--src/server/game/Entities/Creature/TemporarySummon.cpp22
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp262
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h14
-rw-r--r--src/server/game/Entities/GameObject/GameObjectData.h25
-rw-r--r--src/server/game/Entities/Item/ItemEnchantmentMgr.cpp2
-rw-r--r--src/server/game/Entities/Object/Object.cpp4
-rw-r--r--src/server/game/Entities/Object/Object.h4
-rw-r--r--src/server/game/Entities/Object/ObjectGuid.cpp8
-rw-r--r--src/server/game/Entities/Object/ObjectGuid.h5
-rw-r--r--src/server/game/Entities/Object/Position.h25
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp24
-rw-r--r--src/server/game/Entities/Player/Player.cpp135
-rw-r--r--src/server/game/Entities/Player/Player.h4
-rw-r--r--src/server/game/Entities/Player/PlayerTaxi.cpp14
-rw-r--r--src/server/game/Entities/Player/PlayerTaxi.h6
-rw-r--r--src/server/game/Entities/Transport/Transport.cpp20
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp45
-rw-r--r--src/server/game/Entities/Unit/Unit.h4
-rw-r--r--src/server/game/Entities/Unit/UnitDefines.h2
-rwxr-xr-xsrc/server/game/Entities/Vehicle/Vehicle.cpp12
-rw-r--r--src/server/game/Events/GameEventMgr.cpp170
-rw-r--r--src/server/game/Events/GameEventMgr.h6
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp572
-rw-r--r--src/server/game/Globals/ObjectMgr.h102
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.h42
-rw-r--r--src/server/game/Grids/ObjectGridLoader.cpp56
-rw-r--r--src/server/game/Groups/Group.cpp3
-rw-r--r--src/server/game/Handlers/BattleGroundHandler.cpp3
-rw-r--r--src/server/game/Handlers/CalendarHandler.cpp9
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp8
-rw-r--r--src/server/game/Handlers/ChatHandler.cpp14
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp4
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp1
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp7
-rw-r--r--src/server/game/Handlers/NPCHandler.cpp9
-rw-r--r--src/server/game/Handlers/PetitionsHandler.cpp12
-rw-r--r--src/server/game/Handlers/QuestHandler.cpp4
-rw-r--r--src/server/game/Handlers/TaxiHandler.cpp23
-rw-r--r--src/server/game/Instances/InstanceScript.cpp96
-rw-r--r--src/server/game/Instances/InstanceScript.h17
-rw-r--r--src/server/game/Loot/LootMgr.cpp22
-rw-r--r--src/server/game/Maps/Map.cpp645
-rw-r--r--src/server/game/Maps/Map.h168
-rw-r--r--src/server/game/Maps/MapManager.h8
-rw-r--r--src/server/game/Maps/MapScripts.cpp2
-rw-r--r--src/server/game/Maps/SpawnData.h76
-rw-r--r--src/server/game/Miscellaneous/Formulas.h7
-rw-r--r--src/server/game/Miscellaneous/Language.h27
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h2
-rw-r--r--src/server/game/Movement/MotionMaster.cpp150
-rw-r--r--src/server/game/Movement/MotionMaster.h17
-rwxr-xr-xsrc/server/game/Movement/MovementGenerator.h3
-rw-r--r--src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp6
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp7
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp4
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp309
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h97
-rw-r--r--src/server/game/Movement/Waypoints/WaypointDefines.h71
-rw-r--r--src/server/game/Movement/Waypoints/WaypointManager.cpp101
-rw-r--r--src/server/game/Movement/Waypoints/WaypointManager.h38
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvP.cpp10
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp2
-rw-r--r--src/server/game/Pools/PoolMgr.cpp147
-rw-r--r--src/server/game/Pools/PoolMgr.h3
-rw-r--r--src/server/game/Quests/QuestDef.cpp27
-rw-r--r--src/server/game/Quests/QuestDef.h3
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp53
-rw-r--r--src/server/game/Scripting/ScriptMgr.h70
-rw-r--r--src/server/game/Scripting/ScriptSystem.cpp56
-rw-r--r--src/server/game/Scripting/ScriptSystem.h46
-rw-r--r--src/server/game/Server/WorldSession.cpp2
-rw-r--r--src/server/game/Skills/SkillDiscovery.cpp2
-rw-r--r--src/server/game/Skills/SkillExtraItems.cpp11
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp6
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp3
-rw-r--r--src/server/game/Spells/Spell.cpp15
-rw-r--r--src/server/game/Spells/SpellEffects.cpp40
-rw-r--r--src/server/game/Spells/SpellInfo.cpp22
-rw-r--r--src/server/game/Spells/SpellMgr.cpp22
-rw-r--r--src/server/game/Spells/SpellScript.h4
-rw-r--r--src/server/game/Tools/PlayerDump.cpp2
-rw-r--r--src/server/game/Warden/WardenCheckMgr.cpp8
-rw-r--r--src/server/game/Weather/WeatherMgr.cpp2
-rw-r--r--src/server/game/World/World.cpp164
-rw-r--r--src/server/game/World/World.h27
130 files changed, 4643 insertions, 2345 deletions
diff --git a/src/server/game/AI/CoreAI/PassiveAI.cpp b/src/server/game/AI/CoreAI/PassiveAI.cpp
index 2eca6c64c9e..abbff7a870b 100644
--- a/src/server/game/AI/CoreAI/PassiveAI.cpp
+++ b/src/server/game/AI/CoreAI/PassiveAI.cpp
@@ -29,7 +29,7 @@ int32 NullCreatureAI::Permissible(Creature const* creature)
return PERMIT_BASE_PROACTIVE + 50;
if (creature->IsTrigger())
- return PERMIT_BASE_REACTIVE;
+ return PERMIT_BASE_PROACTIVE;
return PERMIT_BASE_IDLE;
}
@@ -105,7 +105,7 @@ void TriggerAI::IsSummonedBy(Unit* summoner)
int32 TriggerAI::Permissible(Creature const* creature)
{
if (creature->IsTrigger() && creature->m_spells[0])
- return PERMIT_BASE_PROACTIVE;
+ return PERMIT_BASE_SPECIAL;
return PERMIT_BASE_NO;
}
diff --git a/src/server/game/AI/CoreAI/TotemAI.cpp b/src/server/game/AI/CoreAI/TotemAI.cpp
index da484e20983..365f0ca5ce8 100644
--- a/src/server/game/AI/CoreAI/TotemAI.cpp
+++ b/src/server/game/AI/CoreAI/TotemAI.cpp
@@ -72,7 +72,7 @@ void TotemAI::UpdateAI(uint32 /*diff*/)
me->IsFriendlyTo(victim) || !me->CanSeeOrDetect(victim))
{
victim = nullptr;
- Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me, max_range);
+ Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me->GetCharmerOrOwnerOrSelf(), max_range);
Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> checker(me, victim, u_check);
Cell::VisitAllObjects(me, checker, max_range);
}
diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp
index 92c9c8e3fb6..244b0b5e047 100644
--- a/src/server/game/AI/CoreAI/UnitAI.cpp
+++ b/src/server/game/AI/CoreAI/UnitAI.cpp
@@ -190,7 +190,7 @@ void UnitAI::DoCastAOE(uint32 spellId, bool triggered)
if (!triggered && me->HasUnitState(UNIT_STATE_CASTING))
return;
- me->CastSpell((Unit*)nullptr, spellId, triggered);
+ me->CastSpell(nullptr, spellId, triggered);
}
uint32 UnitAI::GetDialogStatus(Player* /*player*/)
diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h
index 30a0575c3d2..1078c16f8de 100644
--- a/src/server/game/AI/CoreAI/UnitAI.h
+++ b/src/server/game/AI/CoreAI/UnitAI.h
@@ -330,6 +330,11 @@ class TC_GAME_API UnitAI
// Called when the dialog status between a player and the creature is requested.
virtual uint32 GetDialogStatus(Player* /*player*/);
+ virtual void WaypointPathStarted(uint32 /*nodeId*/, uint32 /*pathId*/) { }
+ virtual void WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/) { }
+ virtual void WaypointReached(uint32 /*nodeId*/, uint32 /*pathId*/) { }
+ virtual void WaypointPathEnded(uint32 /*nodeId*/, uint32 /*pathId*/) { }
+
private:
UnitAI(UnitAI const& right) = delete;
UnitAI& operator=(UnitAI const& right) = delete;
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index bd7a6efab22..015e46cccdd 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -134,8 +134,8 @@ class TC_GAME_API CreatureAI : public UnitAI
virtual bool IsEscorted() const { return false; }
- // Called when creature is spawned or respawned
- virtual void JustRespawned() { }
+ // Called when creature appears in the world (spawn, respawn, grid load etc...)
+ virtual void JustAppeared() { }
// Called at waypoint reached or point movement finished
virtual void MovementInform(uint32 /*type*/, uint32 /*id*/) { }
@@ -187,6 +187,9 @@ class TC_GAME_API CreatureAI : public UnitAI
// If a PlayerAI* is returned, that AI is placed on the player instead of the default charm AI
// Object destruction is handled by Unit::RemoveCharmedBy
virtual PlayerAI* GetAIForCharmedPlayer(Player* /*who*/) { return nullptr; }
+ // Should return true if the NPC is target of an escort quest
+ // If onlyIfActive is set, should return true only if the escort quest is currently active
+ virtual bool IsEscortNPC(bool /*onlyIfActive*/) const { return false; }
// intended for encounter design/debugging. do not use for other purposes. expensive.
int32 VisualizeBoundary(uint32 duration, Unit* owner = nullptr, bool fill = false) const;
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp
index 92f1c6f06dc..a103b34ff56 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.cpp
+++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp
@@ -769,18 +769,30 @@ Unit* PlayerAI::SelectAttackTarget() const
return me->GetCharmer() ? me->GetCharmer()->GetVictim() : nullptr;
}
-struct UncontrolledTargetSelectPredicate
+struct ValidTargetSelectPredicate
{
+ ValidTargetSelectPredicate(UnitAI const* ai) : _ai(ai) { }
+ UnitAI const* const _ai;
bool operator()(Unit const* target) const
{
- return !target->HasBreakableByDamageCrowdControlAura();
+ return _ai->CanAIAttack(target);
}
};
+bool SimpleCharmedPlayerAI::CanAIAttack(Unit const* who) const
+{
+ if (!me->IsValidAttackTarget(who) || who->HasBreakableByDamageCrowdControlAura())
+ return false;
+ if (Unit* charmer = me->GetCharmer())
+ if (!charmer->IsValidAttackTarget(who))
+ return false;
+ return UnitAI::CanAIAttack(who);
+}
+
Unit* SimpleCharmedPlayerAI::SelectAttackTarget() const
{
if (Unit* charmer = me->GetCharmer())
- return charmer->IsAIEnabled ? charmer->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0, UncontrolledTargetSelectPredicate()) : charmer->GetVictim();
+ return charmer->IsAIEnabled ? charmer->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0, ValidTargetSelectPredicate(this)) : charmer->GetVictim();
return nullptr;
}
@@ -1316,11 +1328,23 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 diff)
if (charmer->IsEngaged())
{
Unit* target = me->GetVictim();
- if (!target || !charmer->IsValidAttackTarget(target) || target->HasBreakableByDamageCrowdControlAura())
+ if (!target || !CanAIAttack(target))
{
target = SelectAttackTarget();
- if (!target)
+ if (!target || !CanAIAttack(target))
+ {
+ if (!_isFollowing)
+ {
+ _isFollowing = true;
+ me->AttackStop();
+ me->CastStop();
+ me->StopMoving();
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
+ }
return;
+ }
+ _isFollowing = false;
if (IsRangedAttacker())
{
@@ -1373,8 +1397,9 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 diff)
DoAutoAttackIfReady();
}
- else
+ else if (!_isFollowing)
{
+ _isFollowing = true;
me->AttackStop();
me->CastStop();
me->StopMoving();
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.h b/src/server/game/AI/PlayerAI/PlayerAI.h
index 8afc5ab3ce9..faff09b6b6e 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.h
+++ b/src/server/game/AI/PlayerAI/PlayerAI.h
@@ -97,11 +97,12 @@ class TC_GAME_API PlayerAI : public UnitAI
class TC_GAME_API SimpleCharmedPlayerAI : public PlayerAI
{
public:
- SimpleCharmedPlayerAI(Player* player) : PlayerAI(player), _castCheckTimer(500), _chaseCloser(false), _forceFacing(true) { }
+ SimpleCharmedPlayerAI(Player* player) : PlayerAI(player), _castCheckTimer(2500), _chaseCloser(false), _forceFacing(true), _isFollowing(false) { }
void UpdateAI(uint32 diff) override;
void OnCharmed(bool apply) override;
protected:
+ bool CanAIAttack(Unit const* who) const override;
Unit* SelectAttackTarget() const override;
private:
@@ -109,6 +110,7 @@ class TC_GAME_API SimpleCharmedPlayerAI : public PlayerAI
uint32 _castCheckTimer;
bool _chaseCloser;
bool _forceFacing;
+ bool _isFollowing;
};
#endif
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index e9e252bcf35..afa7e789b7a 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -478,7 +478,7 @@ void BossAI::_Reset()
events.Reset();
summons.DespawnAll();
scheduler.CancelAll();
- if (instance)
+ if (instance && instance->GetBossState(_bossId) != DONE)
instance->SetBossState(_bossId, NOT_STARTED);
}
@@ -564,12 +564,12 @@ bool BossAI::CanAIAttack(Unit const* target) const
return CheckBoundary(target);
}
-void BossAI::_DespawnAtEvade(uint32 delayToRespawn /*= 30*/, Creature* who /*= nullptr*/)
+void BossAI::_DespawnAtEvade(Seconds delayToRespawn, Creature* who)
{
- if (delayToRespawn < 2)
+ if (delayToRespawn < Seconds(2))
{
- TC_LOG_ERROR("scripts", "_DespawnAtEvade called with delay of %u seconds, defaulting to 2.", delayToRespawn);
- delayToRespawn = 2;
+ TC_LOG_ERROR("scripts", "_DespawnAtEvade called with delay of %ld seconds, defaulting to 2.", delayToRespawn.count());
+ delayToRespawn = Seconds(2);
}
if (!who)
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
index 6fc85b6a81a..9f2e49c1ffb 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -366,8 +366,8 @@ class TC_GAME_API BossAI : public ScriptedAI
void _EnterCombat();
void _JustDied();
void _JustReachedHome();
- void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr);
- void _DespawnAtEvade(Seconds const& time, Creature* who = nullptr) { _DespawnAtEvade(uint32(time.count()), who); }
+ void _DespawnAtEvade(Seconds delayToRespawn, Creature* who = nullptr);
+ void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr) { _DespawnAtEvade(Seconds(delayToRespawn), who); }
void TeleportCheaters();
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
index bcf060e2ad6..376aceba4ba 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -16,21 +16,17 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* ScriptData
-SDName: Npc_EscortAI
-SD%Complete: 100
-SDComment:
-SDCategory: Npc
-EndScriptData */
-
#include "ScriptedEscortAI.h"
#include "Creature.h"
#include "Group.h"
#include "Log.h"
#include "Map.h"
#include "MotionMaster.h"
+#include "MovementGenerator.h"
#include "ObjectAccessor.h"
#include "Player.h"
+#include "ScriptSystem.h"
+#include "World.h"
enum Points
{
@@ -38,101 +34,75 @@ enum Points
POINT_HOME = 0xFFFFFE
};
-npc_escortAI::npc_escortAI(Creature* creature) : ScriptedAI(creature),
- m_uiWPWaitTimer(2500),
- m_uiPlayerCheckTimer(1000),
- m_uiEscortState(STATE_ESCORT_NONE),
- MaxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE),
- m_pQuestForEscort(nullptr),
- m_bIsActiveAttacker(true),
- m_bIsRunning(false),
- m_bCanInstantRespawn(false),
- m_bCanReturnToStart(false),
- DespawnAtEnd(true),
- DespawnAtFar(true),
- ScriptWP(false),
- HasImmuneToNPCFlags(false)
-{ }
-
-void npc_escortAI::AttackStart(Unit* who)
+EscortAI::EscortAI(Creature* creature) : ScriptedAI(creature), _pauseTimer(2500), _playerCheckTimer(1000), _escortState(STATE_ESCORT_NONE), _maxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE),
+ _escortQuest(nullptr), _activeAttacker(true), _running(false), _instantRespawn(false), _returnToStart(false), _despawnAtEnd(true), _despawnAtFar(true), _manualPath(false),
+ _hasImmuneToNPCFlags(false), _started(false), _ended(false), _resume(false)
{
- if (!who)
- return;
-
- if (me->Attack(who, true))
- {
- if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
- me->GetMotionMaster()->MovementExpired();
-
- if (IsCombatMovementAllowed())
- me->GetMotionMaster()->MoveChase(who);
- }
}
-Player* npc_escortAI::GetPlayerForEscort()
+Player* EscortAI::GetPlayerForEscort()
{
- return ObjectAccessor::GetPlayer(*me, m_uiPlayerGUID);
+ return ObjectAccessor::GetPlayer(*me, _playerGUID);
}
-//see followerAI
-bool npc_escortAI::AssistPlayerInCombatAgainst(Unit* who)
+// see followerAI
+bool EscortAI::AssistPlayerInCombatAgainst(Unit* who)
{
if (!who || !who->GetVictim())
return false;
- //experimental (unknown) flag not present
+ if (me->HasReactState(REACT_PASSIVE))
+ return false;
+
+ // experimental (unknown) flag not present
if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST))
return false;
- //not a player
+ // not a player
if (!who->EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
return false;
- //never attack friendly
- if (me->IsFriendlyTo(who))
+ if (!who->isInAccessiblePlaceFor(me))
return false;
- //too far away and no free sight?
+ if (!CanAIAttack(who))
+ return false;
+
+ // we cannot attack in evade mode
+ if (me->IsInEvadeMode())
+ return false;
+
+ // or if enemy is in evade mode
+ if (who->GetTypeId() == TYPEID_UNIT && who->ToCreature()->IsInEvadeMode())
+ return false;
+
+ if (!me->IsValidAssistTarget(who->GetVictim()))
+ return false;
+
+ // too far away and no free sight
if (me->IsWithinDistInMap(who, GetMaxPlayerDistance()) && me->IsWithinLOSInMap(who))
{
- //already fighting someone?
- if (!me->GetVictim())
- {
- AttackStart(who);
- return true;
- }
- else
- {
- me->EngageWithTarget(who);
- return true;
- }
+ me->EngageWithTarget(who);
+ return true;
}
return false;
}
-void npc_escortAI::MoveInLineOfSight(Unit* who)
+void EscortAI::MoveInLineOfSight(Unit* who)
{
- if (me->HasReactState(REACT_AGGRESSIVE) && !me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me))
- {
- if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombatAgainst(who))
- return;
+ if (!who)
+ return;
- if (!me->CanFly() && me->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
- return;
+ if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombatAgainst(who))
+ return;
- if (me->IsHostileTo(who))
- {
- float fAttackRadius = me->GetAttackDistance(who);
- if (me->IsWithinDistInMap(who, fAttackRadius) && me->IsWithinLOSInMap(who))
- me->EngageWithTarget(who);
- }
- }
+ ScriptedAI::MoveInLineOfSight(who);
}
-void npc_escortAI::JustDied(Unit* /*killer*/)
+void EscortAI::JustDied(Unit* /*killer*/)
{
- if (!HasEscortState(STATE_ESCORT_ESCORTING) || !m_uiPlayerGUID || !m_pQuestForEscort)
+ if (!HasEscortState(STATE_ESCORT_ESCORTING) || !_playerGUID || !_escortQuest)
return;
if (Player* player = GetPlayerForEscort())
@@ -140,24 +110,26 @@ void npc_escortAI::JustDied(Unit* /*killer*/)
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
+ {
if (Player* member = groupRef->GetSource())
if (member->IsInMap(player))
- member->FailQuest(m_pQuestForEscort->GetQuestId());
+ member->FailQuest(_escortQuest->GetQuestId());
+ }
}
else
- player->FailQuest(m_pQuestForEscort->GetQuestId());
+ player->FailQuest(_escortQuest->GetQuestId());
}
}
-void npc_escortAI::JustRespawned()
+void EscortAI::JustAppeared()
{
- m_uiEscortState = STATE_ESCORT_NONE;
+ _escortState = STATE_ESCORT_NONE;
if (!IsCombatMovementAllowed())
SetCombatMovement(true);
- //add a small delay before going to first waypoint, normal in near all cases
- m_uiWPWaitTimer = 2500;
+ // add a small delay before going to first waypoint, normal in near all cases
+ _pauseTimer = 2000;
if (me->GetFaction() != me->GetCreatureTemplate()->faction)
me->RestoreFaction();
@@ -165,14 +137,12 @@ void npc_escortAI::JustRespawned()
Reset();
}
-void npc_escortAI::ReturnToLastPoint()
+void EscortAI::ReturnToLastPoint()
{
- float x, y, z, o;
- me->GetHomePosition(x, y, z, o);
- me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z);
+ me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, me->GetHomePosition());
}
-void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/)
+void EscortAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->RemoveAllAuras();
me->GetThreatManager().ClearAllThreat();
@@ -183,27 +153,29 @@ void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/)
{
AddEscortState(STATE_ESCORT_RETURNING);
ReturnToLastPoint();
- TC_LOG_DEBUG("scripts", "EscortAI has left combat and is now returning to last point");
+ TC_LOG_DEBUG("scripts", "EscortAI::EnterEvadeMode: left combat and is now returning to last point");
}
else
{
me->GetMotionMaster()->MoveTargetedHome();
- if (HasImmuneToNPCFlags)
+ if (_hasImmuneToNPCFlags)
me->SetImmuneToNPC(true);
Reset();
}
}
-bool npc_escortAI::IsPlayerOrGroupInRange()
+bool EscortAI::IsPlayerOrGroupInRange()
{
if (Player* player = GetPlayerForEscort())
{
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
+ {
if (Player* member = groupRef->GetSource())
if (me->IsWithinDistInMap(member, GetMaxPlayerDistance()))
return true;
+ }
}
else if (me->IsWithinDistInMap(player, GetMaxPlayerDistance()))
return true;
@@ -212,95 +184,94 @@ bool npc_escortAI::IsPlayerOrGroupInRange()
return false;
}
-void npc_escortAI::UpdateAI(uint32 diff)
+void EscortAI::UpdateAI(uint32 diff)
{
- //Waypoint Updating
- if (HasEscortState(STATE_ESCORT_ESCORTING) && !me->GetVictim() && m_uiWPWaitTimer && !HasEscortState(STATE_ESCORT_RETURNING))
+ // Waypoint Updating
+ if (HasEscortState(STATE_ESCORT_ESCORTING) && !me->IsEngaged() && !HasEscortState(STATE_ESCORT_RETURNING))
{
- if (m_uiWPWaitTimer <= diff)
+ if (_pauseTimer <= diff)
{
- //End of the line
- if (CurrentWP == WaypointList.end())
+ if (!HasEscortState(STATE_ESCORT_PAUSED))
{
- if (DespawnAtEnd)
- {
- TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints");
-
- if (m_bCanReturnToStart)
- {
- float fRetX, fRetY, fRetZ;
- me->GetRespawnPosition(fRetX, fRetY, fRetZ);
-
- me->GetMotionMaster()->MovePoint(POINT_HOME, fRetX, fRetY, fRetZ);
+ _pauseTimer = 0;
- m_uiWPWaitTimer = 0;
-
- TC_LOG_DEBUG("scripts", "EscortAI are returning home to spawn location: %u, %f, %f, %f", POINT_HOME, fRetX, fRetY, fRetZ);
- return;
- }
+ if (_ended)
+ {
+ _ended = false;
+ me->GetMotionMaster()->MoveIdle();
- if (m_bCanInstantRespawn)
+ if (_despawnAtEnd)
{
- me->setDeathState(JUST_DIED);
- me->Respawn();
+ TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: reached end of waypoints, despawning at end");
+ if (_returnToStart)
+ {
+ Position respawnPosition;
+ float orientation = 0.f;
+ me->GetRespawnPosition(respawnPosition.m_positionX, respawnPosition.m_positionY, respawnPosition.m_positionZ, &orientation);
+ respawnPosition.SetOrientation(orientation);
+ me->GetMotionMaster()->MovePoint(POINT_HOME, respawnPosition);
+ TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: returning to spawn location: %s", respawnPosition.ToString().c_str());
+ }
+ else if (_instantRespawn)
+ me->Respawn(true);
+ else
+ me->DespawnOrUnsummon();
}
- else
- me->DespawnOrUnsummon();
-
+ TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: reached end of waypoints");
+ RemoveEscortState(STATE_ESCORT_ESCORTING);
return;
}
- else
- {
- TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints with Despawn off");
- return;
+ if (!_started)
+ {
+ _started = true;
+ me->GetMotionMaster()->MovePath(_path, false);
+ }
+ else if (_resume)
+ {
+ _resume = false;
+ if (MovementGenerator* movementGenerator = me->GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE))
+ movementGenerator->Resume(0);
}
- }
-
- if (!HasEscortState(STATE_ESCORT_PAUSED))
- {
- me->GetMotionMaster()->MovePoint(CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z);
- TC_LOG_DEBUG("scripts", "EscortAI start waypoint %u (%f, %f, %f).", CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z);
-
- WaypointStart(CurrentWP->id);
-
- m_uiWPWaitTimer = 0;
}
}
else
- m_uiWPWaitTimer -= diff;
+ _pauseTimer -= diff;
}
- //Check if player or any member of his group is within range
- if (HasEscortState(STATE_ESCORT_ESCORTING) && m_uiPlayerGUID && !me->GetVictim() && !HasEscortState(STATE_ESCORT_RETURNING))
+ // Check if player or any member of his group is within range
+ if (_despawnAtFar && HasEscortState(STATE_ESCORT_ESCORTING) && _playerGUID && !me->GetVictim() && !HasEscortState(STATE_ESCORT_RETURNING))
{
- if (m_uiPlayerCheckTimer <= diff)
+ if (_playerCheckTimer <= diff)
{
- if (DespawnAtFar && !IsPlayerOrGroupInRange())
+ if (!IsPlayerOrGroupInRange())
{
- TC_LOG_DEBUG("scripts", "EscortAI failed because player/group was to far away or not found");
+ TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: failed because player/group was to far away or not found");
- if (m_bCanInstantRespawn)
- {
- me->setDeathState(JUST_DIED);
- me->Respawn();
- }
+ bool isEscort = false;
+ if (CreatureData const* creatureData = me->GetCreatureData())
+ isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (creatureData->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC));
+
+ if (_instantRespawn && !isEscort)
+ me->DespawnOrUnsummon(0, Seconds(1));
+ else if (_instantRespawn && isEscort)
+ me->GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, me->GetSpawnId(), true);
else
me->DespawnOrUnsummon();
return;
}
- m_uiPlayerCheckTimer = 1000;
+ _playerCheckTimer = 1000;
}
else
- m_uiPlayerCheckTimer -= diff;
+ _playerCheckTimer -= diff;
}
UpdateEscortAI(diff);
}
-void npc_escortAI::UpdateEscortAI(uint32 /*diff*/)
+void EscortAI::UpdateEscortAI(uint32 /*diff*/)
{
if (!UpdateVictim())
return;
@@ -308,262 +279,197 @@ void npc_escortAI::UpdateEscortAI(uint32 /*diff*/)
DoMeleeAttackIfReady();
}
-void npc_escortAI::MovementInform(uint32 moveType, uint32 pointId)
+void EscortAI::MovementInform(uint32 type, uint32 id)
{
- if (moveType != POINT_MOTION_TYPE || !HasEscortState(STATE_ESCORT_ESCORTING))
+ // no action allowed if there is no escort
+ if (!HasEscortState(STATE_ESCORT_ESCORTING))
return;
- //Combat start position reached, continue waypoint movement
- if (pointId == POINT_LAST_POINT)
+ if (type == POINT_MOTION_TYPE)
{
- TC_LOG_DEBUG("scripts", "EscortAI has returned to original position before combat");
+ if (!_pauseTimer)
+ _pauseTimer = 2000;
- me->SetWalk(!m_bIsRunning);
- RemoveEscortState(STATE_ESCORT_RETURNING);
-
- if (!m_uiWPWaitTimer)
- m_uiWPWaitTimer = 1;
+ // continue waypoint movement
+ if (id == POINT_LAST_POINT)
+ {
+ TC_LOG_DEBUG("scripts", "EscortAI::MovementInform: returned to before combat position");
+ me->SetWalk(!_running);
+ RemoveEscortState(STATE_ESCORT_RETURNING);
+ }
+ else if (id == POINT_HOME)
+ {
+ TC_LOG_DEBUG("scripts", "EscortAI::MovementInform: returned to home location and restarting waypoint path");
+ _started = false;
+ }
}
- else if (pointId == POINT_HOME)
+ else if (type == WAYPOINT_MOTION_TYPE)
{
- TC_LOG_DEBUG("scripts", "EscortAI has returned to original home location and will continue from beginning of waypoint list.");
+ ASSERT(id < _path.nodes.size(), "EscortAI::MovementInform: referenced movement id (%u) points to non-existing node in loaded path", id);
+ WaypointNode waypoint = _path.nodes[id];
- CurrentWP = WaypointList.begin();
- m_uiWPWaitTimer = 1;
- }
- else if (CurrentWP != WaypointList.end())
- {
- //Make sure that we are still on the right waypoint
- if (CurrentWP->id != pointId)
+ TC_LOG_DEBUG("scripts", "EscortAI::MovementInform: waypoint node %u reached", waypoint.id);
+
+ // last point
+ if (id == _path.nodes.size() - 1)
{
- TC_LOG_ERROR("misc", "TSCR ERROR: EscortAI reached waypoint out of order %u, expected %u, creature entry %u", pointId, CurrentWP->id, me->GetEntry());
- return;
+ _started = false;
+ _ended = true;
+ _pauseTimer = 1000;
}
-
- TC_LOG_DEBUG("scripts", "EscortAI Waypoint %u reached", CurrentWP->id);
-
- //Call WP function
- WaypointReached(CurrentWP->id);
-
- m_uiWPWaitTimer = CurrentWP->WaitTimeMs + 1;
-
- ++CurrentWP;
}
}
+///@todo investigate whether if its necessary to handle anything on charm
/*
-void npc_escortAI::OnPossess(bool apply)
+void EscortAI::OnCharmed(bool apply)
{
- // We got possessed in the middle of being escorted, store the point
- // where we left off to come back to when possess is removed
- if (HasEscortState(STATE_ESCORT_ESCORTING))
- {
- if (apply)
- me->GetPosition(LastPos.x, LastPos.y, LastPos.z);
- else
- {
- Returning = true;
- me->GetMotionMaster()->MovementExpired();
- me->GetMotionMaster()->MovePoint(WP_LAST_POINT, LastPos.x, LastPos.y, LastPos.z);
- }
- }
}
*/
-void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime)
+void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, float orientation/* = 0*/, uint32 waitTime/* = 0*/)
{
- Escort_Waypoint t(id, x, y, z, waitTime);
-
- WaypointList.push_back(t);
-
- // i think SD2 no longer uses this function
- ScriptWP = true;
- /*PointMovement wp;
- wp.m_uiCreatureEntry = me->GetEntry();
- wp.m_uiPointId = id;
- wp.m_fX = x;
- wp.m_fY = y;
- wp.m_fZ = z;
- wp.m_uiWaitTime = WaitTimeMs;
- PointMovementMap[wp.m_uiCreatureEntry].push_back(wp);*/
+ Trinity::NormalizeMapCoord(x);
+ Trinity::NormalizeMapCoord(y);
+
+ WaypointNode waypoint;
+ waypoint.id = id;
+ waypoint.x = x;
+ waypoint.y = y;
+ waypoint.z = z;
+ waypoint.orientation = orientation;
+ waypoint.moveType = _running ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK;
+ waypoint.delay = waitTime;
+ waypoint.eventId = 0;
+ waypoint.eventChance = 100;
+ _path.nodes.push_back(std::move(waypoint));
+
+ _manualPath = true;
}
-void npc_escortAI::FillPointMovementListForCreature()
+void EscortAI::FillPointMovementListForCreature()
{
- ScriptPointVector const* movePoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry());
- if (!movePoints)
+ WaypointPath const* path = sScriptSystemMgr->GetPath(me->GetEntry());
+ if (!path)
return;
- for (ScriptPointVector::const_iterator itr = movePoints->begin(); itr != movePoints->end(); ++itr)
+ for (WaypointNode const& value : path->nodes)
{
- Escort_Waypoint point(itr->uiPointId, itr->fX, itr->fY, itr->fZ, itr->uiWaitTime);
- WaypointList.push_back(point);
+ WaypointNode node = value;
+ Trinity::NormalizeMapCoord(node.x);
+ Trinity::NormalizeMapCoord(node.y);
+ node.moveType = _running ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK;
+
+ _path.nodes.push_back(std::move(node));
}
}
-void npc_escortAI::SetRun(bool on)
+void EscortAI::SetRun(bool on)
{
- if (on)
- {
- if (!m_bIsRunning)
- me->SetWalk(false);
- else
- TC_LOG_DEBUG("scripts", "EscortAI attempt to set run mode, but is already running.");
- }
- else
- {
- if (m_bIsRunning)
- me->SetWalk(true);
- else
- TC_LOG_DEBUG("scripts", "EscortAI attempt to set walk mode, but is already walking.");
- }
+ if (on && !_running)
+ me->SetWalk(false);
+ else if (!on && _running)
+ me->SetWalk(true);
- m_bIsRunning = on;
+ _running = on;
}
/// @todo get rid of this many variables passed in function.
-void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false */, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */)
+void EscortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false */, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */)
{
+ // Queue respawn from the point it starts
+ if (Map* map = me->GetMap())
+ {
+ if (CreatureData const* cdata = me->GetCreatureData())
+ {
+ if (SpawnGroupTemplateData const* groupdata = cdata->spawnGroupData)
+ {
+ if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (groupdata->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC) && !map->GetCreatureRespawnTime(me->GetSpawnId()))
+ {
+ me->SetRespawnTime(me->GetRespawnDelay());
+ me->SaveRespawnTime();
+ }
+ }
+ }
+ }
+
if (me->GetVictim())
{
- TC_LOG_ERROR("scripts.escortai", "TSCR ERROR: EscortAI (script: %s, creature entry: %u) attempts to Start while in combat", me->GetScriptName().c_str(), me->GetEntry());
+ TC_LOG_ERROR("scripts", "EscortAI::Start: (script: %s, creature entry: %u) attempts to Start while in combat", me->GetScriptName().c_str(), me->GetEntry());
return;
}
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
- TC_LOG_ERROR("scripts.escortai", "EscortAI (script: %s, creature entry: %u) attempts to Start while already escorting", me->GetScriptName().c_str(), me->GetEntry());
+ TC_LOG_ERROR("scripts", "EscortAI::Start: (script: %s, creature entry: %u) attempts to Start while already escorting", me->GetScriptName().c_str(), me->GetEntry());
return;
}
- if (!ScriptWP && resetWaypoints) // sd2 never adds wp in script, but tc does
- {
- if (!WaypointList.empty())
- WaypointList.clear();
+ if (!_manualPath && resetWaypoints)
FillPointMovementListForCreature();
- }
- if (WaypointList.empty())
+ if (_path.nodes.empty())
{
- TC_LOG_ERROR("scripts", "EscortAI (script: %s, creature entry: %u) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: %u).",
- me->GetScriptName().c_str(), me->GetEntry(), quest ? quest->GetQuestId() : 0);
+ TC_LOG_ERROR("scripts", "EscortAI::Start: (script: %s, creature entry: %u) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: %u).", me->GetScriptName().c_str(), me->GetEntry(), quest ? quest->GetQuestId() : 0);
return;
}
- //set variables
- m_bIsActiveAttacker = isActiveAttacker;
- m_bIsRunning = run;
-
- m_uiPlayerGUID = playerGUID;
- m_pQuestForEscort = quest;
+ // set variables
+ _activeAttacker = isActiveAttacker;
+ _running = run;
+ _playerGUID = playerGUID;
+ _escortQuest = quest;
+ _instantRespawn = instantRespawn;
+ _returnToStart = canLoopPath;
- m_bCanInstantRespawn = instantRespawn;
- m_bCanReturnToStart = canLoopPath;
+ if (_returnToStart && _instantRespawn)
+ TC_LOG_DEBUG("scripts", "EscortAI::Start: (script: %s, creature entry: %u) is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.", me->GetScriptName().c_str(), me->GetEntry());
- if (m_bCanReturnToStart && m_bCanInstantRespawn)
- TC_LOG_DEBUG("scripts", "EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.");
+ me->GetMotionMaster()->MoveIdle();
+ me->GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE);
- if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
- {
- me->GetMotionMaster()->MovementExpired();
- me->GetMotionMaster()->MoveIdle();
- TC_LOG_DEBUG("scripts", "EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle.");
- }
-
- //disable npcflags
+ // disable npcflags
me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
if (me->IsImmuneToNPC())
{
- HasImmuneToNPCFlags = true;
+ _hasImmuneToNPCFlags = true;
me->SetImmuneToNPC(false);
}
- TC_LOG_DEBUG("scripts", "EscortAI started with " UI64FMTD " waypoints. ActiveAttacker = %d, Run = %d, %s", uint64(WaypointList.size()), m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID.ToString().c_str());
+ TC_LOG_DEBUG("scripts", "EscortAI::Start: (script: %s, creature entry: %u) started with %u waypoints. ActiveAttacker = %d, Run = %d, Player = %s", me->GetScriptName().c_str(), me->GetEntry(), uint32(_path.nodes.size()), _activeAttacker, _running, _playerGUID.ToString().c_str());
- CurrentWP = WaypointList.begin();
-
- //Set initial speed
- if (m_bIsRunning)
- me->SetWalk(false);
- else
- me->SetWalk(true);
+ // set initial speed
+ me->SetWalk(!_running);
+ _started = false;
AddEscortState(STATE_ESCORT_ESCORTING);
}
-void npc_escortAI::SetEscortPaused(bool on)
+void EscortAI::SetEscortPaused(bool on)
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
return;
if (on)
- AddEscortState(STATE_ESCORT_PAUSED);
- else
- RemoveEscortState(STATE_ESCORT_PAUSED);
-}
-
-bool npc_escortAI::SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation)
-{
- me->UpdatePosition(x, y, z, orientation);
- return SetNextWaypoint(pointId, false, true);
-}
-
-bool npc_escortAI::SetNextWaypoint(uint32 pointId, bool setPosition, bool resetWaypointsOnFail)
-{
- if (!WaypointList.empty())
- WaypointList.clear();
-
- FillPointMovementListForCreature();
-
- if (WaypointList.empty())
- return false;
-
- size_t const size = WaypointList.size();
- Escort_Waypoint waypoint(0, 0, 0, 0, 0);
- do
{
- waypoint = WaypointList.front();
- WaypointList.pop_front();
- if (waypoint.id == pointId)
- {
- if (setPosition)
- me->UpdatePosition(waypoint.x, waypoint.y, waypoint.z, me->GetOrientation());
-
- CurrentWP = WaypointList.begin();
- return true;
- }
+ AddEscortState(STATE_ESCORT_PAUSED);
+ if (MovementGenerator* movementGenerator = me->GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE))
+ movementGenerator->Pause(0);
}
- while (!WaypointList.empty());
-
- // we failed.
- // we reset the waypoints in the start; if we pulled any, reset it again
- if (resetWaypointsOnFail && size != WaypointList.size())
+ else
{
- if (!WaypointList.empty())
- WaypointList.clear();
-
- FillPointMovementListForCreature();
+ RemoveEscortState(STATE_ESCORT_PAUSED);
+ _resume = true;
}
-
- return false;
}
-bool npc_escortAI::GetWaypointPosition(uint32 pointId, float& x, float& y, float& z)
+bool EscortAI::IsEscortNPC(bool onlyIfActive) const
{
- ScriptPointVector const* waypoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry());
- if (!waypoints)
- return false;
+ if (!onlyIfActive)
+ return true;
- for (ScriptPointVector::const_iterator itr = waypoints->begin(); itr != waypoints->end(); ++itr)
- {
- if (itr->uiPointId == pointId)
- {
- x = itr->fX;
- y = itr->fY;
- z = itr->fZ;
- return true;
- }
- }
+ if (GetEventStarterGUID())
+ return true;
return false;
}
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
index 754d96dced9..4a514af33cc 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
@@ -20,93 +20,60 @@
#define SC_ESCORTAI_H
#include "ScriptedCreature.h"
-#include "ScriptSystem.h"
+#include "WaypointDefines.h"
class Quest;
#define DEFAULT_MAX_PLAYER_DISTANCE 50
-struct Escort_Waypoint
+enum EscortState : uint32
{
- Escort_Waypoint(uint32 _id, float _x, float _y, float _z, uint32 _w)
- {
- id = _id;
- x = _x;
- y = _y;
- z = _z;
- WaitTimeMs = _w;
- }
-
- uint32 id;
- float x;
- float y;
- float z;
- uint32 WaitTimeMs;
+ STATE_ESCORT_NONE = 0x00, // nothing in progress
+ STATE_ESCORT_ESCORTING = 0x01, // escort is in progress
+ STATE_ESCORT_RETURNING = 0x02, // escort is returning after being in combat
+ STATE_ESCORT_PAUSED = 0x04 // escort is paused, wont continue with next waypoint
};
-enum eEscortState
-{
- STATE_ESCORT_NONE = 0x000, //nothing in progress
- STATE_ESCORT_ESCORTING = 0x001, //escort are in progress
- STATE_ESCORT_RETURNING = 0x002, //escort is returning after being in combat
- STATE_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed
-};
-
-struct TC_GAME_API npc_escortAI : public ScriptedAI
+struct TC_GAME_API EscortAI : public ScriptedAI
{
public:
- explicit npc_escortAI(Creature* creature);
- ~npc_escortAI() { }
-
- // CreatureAI functions
- void AttackStart(Unit* who) override;
+ explicit EscortAI(Creature* creature);
+ ~EscortAI() { }
+ void UpdateAI(uint32 diff) override; // the "internal" update, calls UpdateEscortAI()
void MoveInLineOfSight(Unit* who) override;
-
void JustDied(Unit*) override;
-
- void JustRespawned() override;
-
+ void JustAppeared() override;
void ReturnToLastPoint();
-
void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override;
-
- void UpdateAI(uint32 diff) override; // the "internal" update, calls UpdateEscortAI()
- virtual void UpdateEscortAI(uint32 diff); // used when it's needed to add code in update (abilities, scripted events, etc)
-
void MovementInform(uint32, uint32) override;
- // EscortAI functions
- void AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime = 0); // waitTime is in ms
-
- //this will set the current position to x/y/z/o, and the current WP to pointId.
- bool SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation);
+ virtual void UpdateEscortAI(uint32 diff); // used when it's needed to add code in update (abilities, scripted events, etc)
- //this will set the current position to WP start position (if setPosition == true),
- //and the current WP to pointId
- bool SetNextWaypoint(uint32 pointId, bool setPosition = true, bool resetWaypointsOnFail = true);
-
- bool GetWaypointPosition(uint32 pointId, float& x, float& y, float& z);
-
- virtual void WaypointReached(uint32 pointId) = 0;
- virtual void WaypointStart(uint32 /*pointId*/) { }
+ void AddWaypoint(uint32 id, float x, float y, float z, float orientation = 0.f, uint32 waitTime = 0); // waitTime is in ms
void Start(bool isActiveAttacker = true, bool run = false, ObjectGuid playerGUID = ObjectGuid::Empty, Quest const* quest = nullptr, bool instantRespawn = false, bool canLoopPath = false, bool resetWaypoints = true);
void SetRun(bool on = true);
+
void SetEscortPaused(bool on);
+ void SetPauseTimer(uint32 Timer) { _pauseTimer = Timer; }
+
+ bool HasEscortState(uint32 escortState) { return (_escortState & escortState) != 0; }
+ virtual bool IsEscorted() const override { return (_escortState & STATE_ESCORT_ESCORTING); }
+
+ void SetMaxPlayerDistance(float newMax) { _maxPlayerDistance = newMax; }
+ float GetMaxPlayerDistance() const { return _maxPlayerDistance; }
- bool HasEscortState(uint32 escortState) { return (m_uiEscortState & escortState) != 0; }
- virtual bool IsEscorted() const override { return (m_uiEscortState & STATE_ESCORT_ESCORTING); }
+ void SetDespawnAtEnd(bool despawn) { _despawnAtEnd = despawn; }
+ void SetDespawnAtFar(bool despawn) { _despawnAtFar = despawn; }
- void SetMaxPlayerDistance(float newMax) { MaxPlayerDistance = newMax; }
- float GetMaxPlayerDistance() const { return MaxPlayerDistance; }
+ bool GetAttack() const { return _activeAttacker; } // used in EnterEvadeMode override
+ void SetCanAttack(bool attack) { _activeAttacker = attack; }
- void SetDespawnAtEnd(bool despawn) { DespawnAtEnd = despawn; }
- void SetDespawnAtFar(bool despawn) { DespawnAtFar = despawn; }
- bool GetAttack() const { return m_bIsActiveAttacker; }//used in EnterEvadeMode override
- void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; }
- ObjectGuid GetEventStarterGUID() const { return m_uiPlayerGUID; }
+ ObjectGuid GetEventStarterGUID() const { return _playerGUID; }
+
+ virtual bool IsEscortNPC(bool isEscorting) const override;
protected:
Player* GetPlayerForEscort();
@@ -116,27 +83,29 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI
bool IsPlayerOrGroupInRange();
void FillPointMovementListForCreature();
- void AddEscortState(uint32 escortState) { m_uiEscortState |= escortState; }
- void RemoveEscortState(uint32 escortState) { m_uiEscortState &= ~escortState; }
-
- ObjectGuid m_uiPlayerGUID;
- uint32 m_uiWPWaitTimer;
- uint32 m_uiPlayerCheckTimer;
- uint32 m_uiEscortState;
- float MaxPlayerDistance;
-
- Quest const* m_pQuestForEscort; //generally passed in Start() when regular escort script.
-
- std::list<Escort_Waypoint> WaypointList;
- std::list<Escort_Waypoint>::iterator CurrentWP;
-
- bool m_bIsActiveAttacker; //obsolete, determined by faction.
- bool m_bIsRunning; //all creatures are walking by default (has flag MOVEMENTFLAG_WALK)
- bool m_bCanInstantRespawn; //if creature should respawn instantly after escort over (if not, database respawntime are used)
- bool m_bCanReturnToStart; //if creature can walk same path (loop) without despawn. Not for regular escort quests.
- bool DespawnAtEnd;
- bool DespawnAtFar;
- bool ScriptWP;
- bool HasImmuneToNPCFlags;
+ void AddEscortState(uint32 escortState) { _escortState |= escortState; }
+ void RemoveEscortState(uint32 escortState) { _escortState &= ~escortState; }
+
+ ObjectGuid _playerGUID;
+ uint32 _pauseTimer;
+ uint32 _playerCheckTimer;
+ uint32 _escortState;
+ float _maxPlayerDistance;
+
+ Quest const* _escortQuest; // generally passed in Start() when regular escort script.
+
+ WaypointPath _path;
+
+ bool _activeAttacker; // obsolete, determined by faction.
+ bool _running; // all creatures are walking by default (has flag MOVEMENTFLAG_WALK)
+ bool _instantRespawn; // if creature should respawn instantly after escort over (if not, database respawntime are used)
+ bool _returnToStart; // if creature can walk same path (loop) without despawn. Not for regular escort quests.
+ bool _despawnAtEnd;
+ bool _despawnAtFar;
+ bool _manualPath;
+ bool _hasImmuneToNPCFlags;
+ bool _started;
+ bool _ended;
+ bool _resume;
};
#endif
diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
index b0b332afecd..6f50cdaac10 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
@@ -145,7 +145,7 @@ void FollowerAI::JustDied(Unit* /*killer*/)
}
}
-void FollowerAI::JustRespawned()
+void FollowerAI::JustAppeared()
{
m_uiFollowState = STATE_FOLLOW_NONE;
diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h
index 7fe877a7589..1b13db8f7a3 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h
@@ -41,8 +41,6 @@ class TC_GAME_API FollowerAI : public ScriptedAI
explicit FollowerAI(Creature* creature);
~FollowerAI() { }
- //virtual void WaypointReached(uint32 uiPointId) = 0;
-
void MovementInform(uint32 motionType, uint32 pointId) override;
void AttackStart(Unit*) override;
@@ -53,7 +51,7 @@ class TC_GAME_API FollowerAI : public ScriptedAI
void JustDied(Unit*) override;
- void JustRespawned() override;
+ void JustAppeared() override;
void UpdateAI(uint32) override; //the "internal" update, calls UpdateFollowerAI()
virtual void UpdateFollowerAI(uint32); //used when it's needed to add code in update (abilities, scripted events, etc)
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 5aa2550a704..24719902659 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -17,6 +17,7 @@
#include "SmartAI.h"
#include "Creature.h"
+#include "CreatureGroups.h"
#include "DBCStructure.h"
#include "GameObject.h"
#include "Group.h"
@@ -28,54 +29,12 @@
#include "ScriptMgr.h"
#include "Vehicle.h"
-SmartAI::SmartAI(Creature* c) : CreatureAI(c)
+SmartAI::SmartAI(Creature* creature) : CreatureAI(creature), mIsCharmed(false), mFollowCreditType(0), mFollowArrivedTimer(0), mFollowCredit(0), mFollowArrivedEntry(0), mFollowDist(0.f), mFollowAngle(0.f),
+ _escortState(SMART_ESCORT_NONE), _escortNPCFlags(0), _escortInvokerCheckTimer(1000), _currentWaypointNode(0), _waypointReached(false), _waypointPauseTimer(0), _waypointPauseForced(false), _repeatWaypointPath(false),
+ _OOCReached(false), _waypointPathEnded(false), mRun(true), mEvadeDisabled(false), mCanAutoAttack(true), mCanCombatMove(true), mInvincibilityHpLevel(0), mDespawnTime(0), mDespawnState(0), mConditionsTimer(0),
+ _gossipReturn(false), mEscortQuestID(0)
{
- mIsCharmed = false;
- // copy script to local (protection for table reload)
-
- mWayPoints = nullptr;
- mEscortState = SMART_ESCORT_NONE;
- mCurrentWPID = 0;//first wp id is 1 !!
- mWPReached = false;
- mWPPauseTimer = 0;
- mEscortNPCFlags = 0;
- mLastWP = nullptr;
-
- mCanRepeatPath = false;
-
- // Spawn in run mode
- me->SetWalk(false);
- mRun = false;
- mEvadeDisabled = false;
-
- mLastOOCPos = me->GetPosition();
-
- mCanAutoAttack = true;
- mCanCombatMove = true;
-
- mForcedPaused = false;
- mLastWPIDReached = 0;
-
- mEscortQuestID = 0;
-
- mDespawnTime = 0;
- mDespawnState = 0;
-
- mEscortInvokerCheckTimer = 1000;
- mFollowGuid.Clear();
- mFollowDist = 0;
- mFollowAngle = 0;
- mFollowCredit = 0;
- mFollowArrivedEntry = 0;
- mFollowCreditType = 0;
- mFollowArrivedTimer = 0;
- mInvincibilityHpLevel = 0;
-
- mJustReset = false;
- mConditionsTimer = 0;
- mHasConditions = sConditionMgr->HasConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, c->GetEntry());
-
- _gossipReturn = false;
+ mHasConditions = sConditionMgr->HasConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, creature->GetEntry());
}
bool SmartAI::IsAIControlled() const
@@ -83,92 +42,68 @@ bool SmartAI::IsAIControlled() const
return !mIsCharmed;
}
-void SmartAI::UpdateDespawn(uint32 diff)
-{
- if (mDespawnState <= 1 || mDespawnState > 3)
- return;
-
- if (mDespawnTime < diff)
- {
- if (mDespawnState == 2)
- {
- me->SetVisible(false);
- mDespawnTime = 5000;
- mDespawnState++;
- }
- else
- me->DespawnOrUnsummon();
- } else mDespawnTime -= diff;
-}
-
-WayPoint* SmartAI::GetNextWayPoint()
-{
- if (!mWayPoints || mWayPoints->empty())
- return nullptr;
-
- mCurrentWPID++;
- WPPath::const_iterator itr = mWayPoints->find(mCurrentWPID);
- if (itr != mWayPoints->end())
- {
- mLastWP = (*itr).second;
- if (mLastWP->id != mCurrentWPID)
- {
- TC_LOG_ERROR("misc", "SmartAI::GetNextWayPoint: Got not expected waypoint id %u, expected %u", mLastWP->id, mCurrentWPID);
- }
- return (*itr).second;
- }
- return nullptr;
-}
-
-void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* invoker)
+void SmartAI::StartPath(bool run/* = false*/, uint32 pathId/* = 0*/, bool repeat/* = false*/, Unit* invoker/* = nullptr*/, uint32 nodeId/* = 1*/)
{
- if (me->IsInCombat())// no wp movement in combat
+ if (me->IsInCombat()) // no wp movement in combat
{
- TC_LOG_ERROR("misc", "SmartAI::StartPath: Creature entry %u wanted to start waypoint movement while in combat, ignoring.", me->GetEntry());
+ TC_LOG_ERROR("misc", "SmartAI::StartPath: Creature entry %u wanted to start waypoint movement (%u) while in combat, ignoring.", me->GetEntry(), pathId);
return;
}
if (HasEscortState(SMART_ESCORT_ESCORTING))
StopPath();
- if (path)
+ SetRun(run);
+
+ if (pathId)
{
- if (!LoadPath(path))
+ if (!LoadPath(pathId))
return;
}
- if (!mWayPoints || mWayPoints->empty())
+ if (_path.nodes.empty())
return;
- if (WayPoint* wp = GetNextWayPoint())
- {
- AddEscortState(SMART_ESCORT_ESCORTING);
- mCanRepeatPath = repeat;
+ _currentWaypointNode = nodeId;
+ _waypointPathEnded = false;
- SetRun(run);
+ _repeatWaypointPath = repeat;
- if (invoker && invoker->GetTypeId() == TYPEID_PLAYER)
- {
- mEscortNPCFlags = me->GetUInt32Value(UNIT_NPC_FLAGS);
- me->SetUInt32Value(UNIT_NPC_FLAGS, 0);
- }
+ // Do not use AddEscortState, removing everything from previous
+ _escortState = SMART_ESCORT_ESCORTING;
- mLastOOCPos = me->GetPosition();
- me->GetMotionMaster()->MovePoint(wp->id, wp->x, wp->y, wp->z);
- GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, wp->id, GetScript()->GetPathId());
+ if (invoker && invoker->GetTypeId() == TYPEID_PLAYER)
+ {
+ _escortNPCFlags = me->GetUInt32Value(UNIT_NPC_FLAGS);
+ me->SetFlag(UNIT_NPC_FLAGS, 0);
}
+
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, _currentWaypointNode, GetScript()->GetPathId());
+
+ me->GetMotionMaster()->MovePath(_path, _repeatWaypointPath);
}
bool SmartAI::LoadPath(uint32 entry)
{
if (HasEscortState(SMART_ESCORT_ESCORTING))
return false;
- mWayPoints = sSmartWaypointMgr->GetPath(entry);
- if (!mWayPoints)
+
+ WaypointPath const* path = sSmartWaypointMgr->GetPath(entry);
+ if (!path || path->nodes.empty())
{
GetScript()->SetPathId(0);
return false;
}
+
+ _path.id = path->id;
+ _path.nodes = path->nodes;
+ for (WaypointNode& waypoint : _path.nodes)
+ {
+ Trinity::NormalizeMapCoord(waypoint.x);
+ Trinity::NormalizeMapCoord(waypoint.y);
+ waypoint.moveType = mRun ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK;
+ }
+
GetScript()->SetPathId(entry);
return true;
}
@@ -176,65 +111,88 @@ bool SmartAI::LoadPath(uint32 entry)
void SmartAI::PausePath(uint32 delay, bool forced)
{
if (!HasEscortState(SMART_ESCORT_ESCORTING))
+ {
+ me->PauseMovement(delay, MOTION_SLOT_IDLE, forced);
+ if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
+ {
+ std::pair<uint32, uint32> waypointInfo = me->GetCurrentWaypointInfo();
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, waypointInfo.first, waypointInfo.second);
+ }
return;
+ }
+
if (HasEscortState(SMART_ESCORT_PAUSED))
{
- TC_LOG_ERROR("misc", "SmartAI::PausePath: Creature entry %u wanted to pause waypoint movement while already paused, ignoring.", me->GetEntry());
+ TC_LOG_ERROR("misc", "SmartAI::PausePath: Creature entry %u wanted to pause waypoint (current waypoint: %u) movement while already paused, ignoring.", me->GetEntry(), _currentWaypointNode);
return;
}
- mForcedPaused = forced;
- mLastOOCPos = me->GetPosition();
- AddEscortState(SMART_ESCORT_PAUSED);
- mWPPauseTimer = delay;
+
+ _waypointPauseTimer = delay;
+
if (forced)
{
+ _waypointPauseForced = forced;
SetRun(mRun);
- me->StopMoving();//force stop
- me->GetMotionMaster()->MoveIdle();//force stop
+ me->PauseMovement();
+ me->SetHomePosition(me->GetPosition());
}
- GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, mLastWP->id, GetScript()->GetPathId());
+ else
+ _waypointReached = false;
+
+ AddEscortState(SMART_ESCORT_PAUSED);
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, _currentWaypointNode, GetScript()->GetPathId());
}
void SmartAI::StopPath(uint32 DespawnTime, uint32 quest, bool fail)
{
if (!HasEscortState(SMART_ESCORT_ESCORTING))
+ {
+ std::pair<uint32, uint32> waypointInfo = { 0, 0 };
+ if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
+ waypointInfo = me->GetCurrentWaypointInfo();
+
+ if (mDespawnState != 2)
+ SetDespawnTime(DespawnTime);
+
+ me->GetMotionMaster()->MoveIdle();
+
+ if (waypointInfo.first)
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, waypointInfo.first, waypointInfo.second);
+
+ if (!fail)
+ {
+ if (waypointInfo.first)
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, waypointInfo.first, waypointInfo.second);
+ if (mDespawnState == 1)
+ StartDespawn();
+ }
return;
+ }
if (quest)
mEscortQuestID = quest;
- SetDespawnTime(DespawnTime);
- //mDespawnTime = DespawnTime;
- mLastOOCPos = me->GetPosition();
- me->StopMoving();//force stop
+ if (mDespawnState != 2)
+ SetDespawnTime(DespawnTime);
+
me->GetMotionMaster()->MoveIdle();
- GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, mLastWP->id, GetScript()->GetPathId());
+
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, _currentWaypointNode, GetScript()->GetPathId());
+
EndPath(fail);
}
void SmartAI::EndPath(bool fail)
{
- GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, mLastWP->id, GetScript()->GetPathId());
-
RemoveEscortState(SMART_ESCORT_ESCORTING | SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING);
- mWayPoints = nullptr;
- mCurrentWPID = 0;
- mWPPauseTimer = 0;
- mLastWP = nullptr;
-
- if (mEscortNPCFlags)
- {
- me->SetUInt32Value(UNIT_NPC_FLAGS, mEscortNPCFlags);
- mEscortNPCFlags = 0;
- }
+ _path.nodes.clear();
+ _waypointPauseTimer = 0;
- if (mCanRepeatPath)
+ if (_escortNPCFlags)
{
- if (IsAIControlled())
- StartPath(mRun, GetScript()->GetPathId(), true);
+ me->SetFlag(UNIT_NPC_FLAGS, _escortNPCFlags);
+ _escortNPCFlags = 0;
}
- else
- GetScript()->SetPathId(0);
ObjectVector const* targets = GetScript()->GetStoredTargetVector(SMART_ESCORT_TARGETS, *me);
if (targets && mEscortQuestID)
@@ -279,126 +237,57 @@ void SmartAI::EndPath(bool fail)
}
}
+ // End Path events should be only processed if it was SUCCESSFUL stop or stop called by SMART_ACTION_WAYPOINT_STOP
+ if (fail)
+ return;
+
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, _currentWaypointNode, GetScript()->GetPathId());
+
+ if (_repeatWaypointPath)
+ {
+ if (IsAIControlled())
+ StartPath(mRun, GetScript()->GetPathId(), _repeatWaypointPath);
+ }
+ else
+ GetScript()->SetPathId(0);
+
if (mDespawnState == 1)
StartDespawn();
}
void SmartAI::ResumePath()
{
- SetRun(mRun);
- if (mLastWP)
- me->GetMotionMaster()->MovePoint(mLastWP->id, mLastWP->x, mLastWP->y, mLastWP->z);
-}
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_RESUMED, nullptr, _currentWaypointNode, GetScript()->GetPathId());
-void SmartAI::ReturnToLastOOCPos()
-{
- if (!IsAIControlled())
- return;
+ RemoveEscortState(SMART_ESCORT_PAUSED);
+
+ _waypointPauseForced = false;
+ _waypointReached = false;
+ _waypointPauseTimer = 0;
SetRun(mRun);
- me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, mLastOOCPos);
+ me->ResumeMovement();
}
-void SmartAI::UpdatePath(const uint32 diff)
+void SmartAI::ReturnToLastOOCPos()
{
- if (!HasEscortState(SMART_ESCORT_ESCORTING))
- return;
- if (mEscortInvokerCheckTimer < diff)
- {
- // Escort failed, no players in range
- if (!IsEscortInvokerInRange())
- {
- StopPath(0, mEscortQuestID, true);
-
- // allow to properly hook out of range despawn action, which in most cases should perform the same operation as dying
- GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, me);
- me->DespawnOrUnsummon(1);
- return;
- }
- mEscortInvokerCheckTimer = 1000;
- }
- else
- mEscortInvokerCheckTimer -= diff;
-
- // handle pause
- if (HasEscortState(SMART_ESCORT_PAUSED))
- {
- if (mWPPauseTimer < diff)
- {
- if (!me->IsInCombat() && !HasEscortState(SMART_ESCORT_RETURNING) && (mWPReached || mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT || mForcedPaused))
- {
- GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_RESUMED, nullptr, mLastWP->id, GetScript()->GetPathId());
- RemoveEscortState(SMART_ESCORT_PAUSED);
- if (mForcedPaused)// if paused between 2 wps resend movement
- {
- ResumePath();
- mWPReached = false;
- mForcedPaused = false;
- }
- if (mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT)
- mWPReached = true;
- }
-
- mWPPauseTimer = 0;
- }
- else
- mWPPauseTimer -= diff;
- }
-
- if (HasEscortState(SMART_ESCORT_RETURNING))
- {
- if (mWPReached)//reached OOC WP
- {
- RemoveEscortState(SMART_ESCORT_RETURNING);
- if (!HasEscortState(SMART_ESCORT_PAUSED))
- ResumePath();
- mWPReached = false;
- }
- }
-
- if ((!me->HasReactState(REACT_PASSIVE) && me->IsInCombat()) || HasEscortState(SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING))
+ if (!IsAIControlled())
return;
- // handle next wp
- if (mWPReached)//reached WP
- {
- mWPReached = false;
- if (mCurrentWPID == GetWPCount())
- {
- EndPath();
- }
- else if (WayPoint* wp = GetNextWayPoint())
- {
- SetRun(mRun);
- me->GetMotionMaster()->MovePoint(wp->id, wp->x, wp->y, wp->z);
- }
- }
+ me->SetWalk(false);
+ me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, me->GetHomePosition());
}
void SmartAI::UpdateAI(uint32 diff)
{
CheckConditions(diff);
+
GetScript()->OnUpdate(diff);
+
UpdatePath(diff);
+ UpdateFollow(diff);
UpdateDespawn(diff);
- /// @todo move to void
- if (mFollowGuid)
- {
- if (mFollowArrivedTimer < diff)
- {
- if (me->FindNearestCreature(mFollowArrivedEntry, INTERACTION_DISTANCE, true))
- {
- StopFollow(true);
- return;
- }
-
- mFollowArrivedTimer = 1000;
- }
- else
- mFollowArrivedTimer -= diff;
- }
-
if (!IsAIControlled())
return;
@@ -450,24 +339,70 @@ bool SmartAI::IsEscortInvokerInRange()
return true;
}
-void SmartAI::MovepointReached(uint32 id)
+///@todo move escort related logic
+void SmartAI::WaypointPathStarted(uint32 nodeId, uint32 pathId)
+{
+ if (!HasEscortState(SMART_ESCORT_ESCORTING))
+ {
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, nodeId, pathId);
+ return;
+ }
+}
+
+///@todo Implement new smart event SMART_EVENT_WAYPOINT_STARTED
+void SmartAI::WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/)
+{
+}
+
+void SmartAI::WaypointReached(uint32 nodeId, uint32 pathId)
{
- if (id != SMART_ESCORT_LAST_OOC_POINT && mLastWPIDReached != id)
- GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, id);
+ if (!HasEscortState(SMART_ESCORT_ESCORTING))
+ {
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, nodeId, pathId);
+ return;
+ }
+
+ _currentWaypointNode = nodeId;
+
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, _currentWaypointNode, pathId);
+
+ if (_waypointPauseTimer && !_waypointPauseForced)
+ {
+ _waypointReached = true;
+ me->PauseMovement();
+ me->SetHomePosition(me->GetPosition());
+ }
+ else if (HasEscortState(SMART_ESCORT_ESCORTING) && me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
+ {
+ if (_currentWaypointNode == _path.nodes.size())
+ _waypointPathEnded = true;
+ else
+ SetRun(mRun);
+ }
+}
- mLastWPIDReached = id;
- mWPReached = true;
+///@todo move escort related logic
+void SmartAI::WaypointPathEnded(uint32 nodeId, uint32 pathId)
+{
+ if (!HasEscortState(SMART_ESCORT_ESCORTING))
+ {
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, nodeId, pathId);
+ return;
+ }
}
-void SmartAI::MovementInform(uint32 MovementType, uint32 Data)
+void SmartAI::MovementInform(uint32 type, uint32 id)
{
- if ((MovementType == POINT_MOTION_TYPE && Data == SMART_ESCORT_LAST_OOC_POINT) || MovementType == FOLLOW_MOTION_TYPE)
+ if (type == POINT_MOTION_TYPE && id == SMART_ESCORT_LAST_OOC_POINT)
me->ClearUnitState(UNIT_STATE_EVADE);
- GetScript()->ProcessEventsFor(SMART_EVENT_MOVEMENTINFORM, nullptr, MovementType, Data);
- if (MovementType != POINT_MOTION_TYPE || !HasEscortState(SMART_ESCORT_ESCORTING))
+ GetScript()->ProcessEventsFor(SMART_EVENT_MOVEMENTINFORM, nullptr, type, id);
+
+ if (!HasEscortState(SMART_ESCORT_ESCORTING))
return;
- MovepointReached(Data);
+
+ if (type == POINT_MOTION_TYPE && id == SMART_ESCORT_LAST_OOC_POINT)
+ _OOCReached = true;
}
void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
@@ -489,10 +424,16 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
me->AddUnitState(UNIT_STATE_EVADE);
- GetScript()->ProcessEventsFor(SMART_EVENT_EVADE);//must be after aura clear so we can cast spells from db
+ GetScript()->ProcessEventsFor(SMART_EVENT_EVADE); // must be after _EnterEvadeMode (spells, auras, ...)
SetRun(mRun);
- if (HasEscortState(SMART_ESCORT_ESCORTING))
+
+ if (Unit* owner = me->GetCharmerOrOwner())
+ {
+ me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
+ me->ClearUnitState(UNIT_STATE_EVADE);
+ }
+ else if (HasEscortState(SMART_ESCORT_ESCORTING))
{
AddEscortState(SMART_ESCORT_RETURNING);
ReturnToLastOOCPos();
@@ -503,16 +444,11 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
// evade is not cleared in MoveFollow, so we can't keep it
me->ClearUnitState(UNIT_STATE_EVADE);
}
- else if (Unit* owner = me->GetCharmerOrOwner())
- {
- me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
- me->ClearUnitState(UNIT_STATE_EVADE);
- }
else
me->GetMotionMaster()->MoveTargetedHome();
- if (!HasEscortState(SMART_ESCORT_ESCORTING)) //dont mess up escort movement after combat
- SetRun(mRun);
+ if (!me->HasUnitState(UNIT_STATE_EVADE))
+ GetScript()->OnReset();
}
void SmartAI::MoveInLineOfSight(Unit* who)
@@ -525,7 +461,7 @@ void SmartAI::MoveInLineOfSight(Unit* who)
if (!IsAIControlled())
return;
- if (AssistPlayerInCombatAgainst(who))
+ if (HasEscortState(SMART_ESCORT_ESCORTING) && AssistPlayerInCombatAgainst(who))
return;
CreatureAI::MoveInLineOfSight(who);
@@ -544,19 +480,32 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who)
if (!who || !who->GetVictim())
return false;
- //experimental (unknown) flag not present
+ // experimental (unknown) flag not present
if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST))
return false;
- //not a player
+ // not a player
if (!who->EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
return false;
- //never attack friendly
+ if (!who->isInAccessiblePlaceFor(me))
+ return false;
+
+ if (!CanAIAttack(who))
+ return false;
+
+ // we cannot attack in evade mode
+ if (me->IsInEvadeMode())
+ return false;
+
+ // or if enemy is in evade mode
+ if (who->GetTypeId() == TYPEID_UNIT && who->ToCreature()->IsInEvadeMode())
+ return false;
+
if (!me->IsValidAssistTarget(who->GetVictim()))
return false;
- //too far away and no free sight?
+ // too far away and no free sight
if (me->IsWithinDistInMap(who, SMART_MAX_AID_DIST) && me->IsWithinLOSInMap(who))
{
me->EngageWithTarget(who);
@@ -566,18 +515,21 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who)
return false;
}
-void SmartAI::JustRespawned()
+void SmartAI::JustAppeared()
{
mDespawnTime = 0;
mDespawnState = 0;
- mEscortState = SMART_ESCORT_NONE;
+ _escortState = SMART_ESCORT_NONE;
+
me->SetVisible(true);
+
if (me->GetFaction() != me->GetCreatureTemplate()->faction)
me->RestoreFaction();
- mJustReset = true;
- JustReachedHome();
+
+ GetScript()->OnReset();
GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
- mFollowGuid.Clear();//do not reset follower on Reset(), we need it after combat evade
+
+ mFollowGuid.Clear(); // do not reset follower on Reset(), we need it after combat evade
mFollowDist = 0;
mFollowAngle = 0;
mFollowCredit = 0;
@@ -589,16 +541,18 @@ void SmartAI::JustRespawned()
void SmartAI::JustReachedHome()
{
GetScript()->OnReset();
+ GetScript()->ProcessEventsFor(SMART_EVENT_REACHED_HOME);
- if (!mJustReset)
+ CreatureGroup* formation = me->GetFormation();
+ if (!formation || formation->getLeader() == me || !formation->isFormed())
{
- GetScript()->ProcessEventsFor(SMART_EVENT_REACHED_HOME);
-
- if (!UpdateVictim() && me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE && me->GetWaypointPath())
+ if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_IDLE) != WAYPOINT_MOTION_TYPE && me->GetWaypointPath())
me->GetMotionMaster()->MovePath(me->GetWaypointPath(), true);
+ else
+ me->ResumeMovement();
}
-
- mJustReset = false;
+ else if (formation->isFormed())
+ me->GetMotionMaster()->MoveIdle(); // wait the order of leader
}
void SmartAI::EnterCombat(Unit* enemy)
@@ -607,24 +561,14 @@ void SmartAI::EnterCombat(Unit* enemy)
me->InterruptNonMeleeSpells(false); // must be before ProcessEvents
GetScript()->ProcessEventsFor(SMART_EVENT_AGGRO, enemy);
-
- if (!IsAIControlled())
- return;
- mLastOOCPos = me->GetPosition();
- SetRun(mRun);
- if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == POINT_MOTION_TYPE)
- me->GetMotionMaster()->MovementExpired();
}
void SmartAI::JustDied(Unit* killer)
{
- GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, killer);
if (HasEscortState(SMART_ESCORT_ESCORTING))
- {
EndPath(true);
- me->StopMoving();//force stop
- me->GetMotionMaster()->MoveIdle();
- }
+
+ GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, killer);
}
void SmartAI::KilledUnit(Unit* victim)
@@ -642,15 +586,21 @@ void SmartAI::AttackStart(Unit* who)
// dont allow charmed npcs to act on their own
if (!IsAIControlled())
{
- if (who && mCanAutoAttack)
- me->Attack(who, true);
+ if (who)
+ me->Attack(who, mCanAutoAttack);
return;
}
- if (who && me->Attack(who, me->IsWithinMeleeRange(who)))
+ if (who && me->Attack(who, mCanAutoAttack))
{
+ me->GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE);
+ me->PauseMovement();
+
if (mCanCombatMove)
+ {
+ SetRun(mRun);
me->GetMotionMaster()->MoveChase(who);
+ }
}
}
@@ -713,12 +663,9 @@ void SmartAI::PassengerBoarded(Unit* who, int8 seatId, bool apply)
void SmartAI::InitializeAI()
{
GetScript()->OnInitialize(me);
+
if (!me->isDead())
- {
- mJustReset = true;
- JustReachedHome();
- GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
- }
+ GetScript()->OnReset();
}
void SmartAI::OnCharmed(bool apply)
@@ -727,13 +674,13 @@ void SmartAI::OnCharmed(bool apply)
{
if (HasEscortState(SMART_ESCORT_ESCORTING | SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING))
EndPath(true);
- me->StopMoving();
}
+
mIsCharmed = apply;
if (!apply && !me->IsInEvadeMode())
{
- if (mCanRepeatPath)
+ if (_repeatWaypointPath)
StartPath(mRun, GetScript()->GetPathId(), true);
else
me->SetWalk(!mRun);
@@ -826,30 +773,21 @@ void SmartAI::SetCombatMove(bool on)
{
if (mCanCombatMove == on)
return;
+
mCanCombatMove = on;
+
if (!IsAIControlled())
return;
- if (!HasEscortState(SMART_ESCORT_ESCORTING))
+
+ if (me->IsEngaged())
{
- if (on && me->GetVictim())
+ if (on && !me->HasReactState(REACT_PASSIVE) && me->GetVictim() && me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == MAX_MOTION_TYPE)
{
- if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE)
- {
- SetRun(mRun);
- me->GetMotionMaster()->MoveChase(me->GetVictim());
- me->CastStop();
- }
- }
- else
- {
- if (me->HasUnitState(UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE))
- return;
-
- me->GetMotionMaster()->MovementExpired();
- me->GetMotionMaster()->Clear(true);
- me->StopMoving();
- me->GetMotionMaster()->MoveIdle();
+ SetRun(mRun);
+ me->GetMotionMaster()->MoveChase(me->GetVictim());
}
+ else if (!on && me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == CHASE_MOTION_TYPE)
+ me->GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE);
}
}
@@ -881,7 +819,6 @@ void SmartAI::StopFollow(bool complete)
mFollowArrivedTimer = 1000;
mFollowArrivedEntry = 0;
mFollowCreditType = 0;
- me->StopMoving();
me->GetMotionMaster()->MoveIdle();
if (!complete)
@@ -949,6 +886,96 @@ void SmartAI::CheckConditions(uint32 diff)
mConditionsTimer -= diff;
}
+void SmartAI::UpdatePath(uint32 diff)
+{
+ if (!HasEscortState(SMART_ESCORT_ESCORTING))
+ return;
+
+ if (_escortInvokerCheckTimer < diff)
+ {
+ if (!IsEscortInvokerInRange())
+ {
+ StopPath(0, mEscortQuestID, true);
+
+ // allow to properly hook out of range despawn action, which in most cases should perform the same operation as dying
+ GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, me);
+ me->DespawnOrUnsummon();
+ return;
+ }
+ _escortInvokerCheckTimer = 1000;
+ }
+ else
+ _escortInvokerCheckTimer -= diff;
+
+ // handle pause
+ if (HasEscortState(SMART_ESCORT_PAUSED) && (_waypointReached || _waypointPauseForced))
+ {
+ if (_waypointPauseTimer <= diff)
+ {
+ if (!me->IsInCombat() && !HasEscortState(SMART_ESCORT_RETURNING))
+ ResumePath();
+ }
+ else
+ _waypointPauseTimer -= diff;
+ }
+ else if (_waypointPathEnded) // end path
+ {
+ _waypointPathEnded = false;
+ StopPath();
+ return;
+ }
+
+ if (HasEscortState(SMART_ESCORT_RETURNING))
+ {
+ if (_OOCReached) // reached OOC WP
+ {
+ _OOCReached = false;
+ RemoveEscortState(SMART_ESCORT_RETURNING);
+ if (!HasEscortState(SMART_ESCORT_PAUSED))
+ ResumePath();
+ }
+ }
+}
+
+void SmartAI::UpdateFollow(uint32 diff)
+{
+ if (mFollowGuid)
+ {
+ if (mFollowArrivedTimer < diff)
+ {
+ if (me->FindNearestCreature(mFollowArrivedEntry, INTERACTION_DISTANCE, true))
+ {
+ StopFollow(true);
+ return;
+ }
+
+ mFollowArrivedTimer = 1000;
+ }
+ else
+ mFollowArrivedTimer -= diff;
+ }
+}
+
+void SmartAI::UpdateDespawn(uint32 diff)
+{
+ if (mDespawnState <= 1 || mDespawnState > 3)
+ return;
+
+ if (mDespawnTime < diff)
+ {
+ if (mDespawnState == 2)
+ {
+ me->SetVisible(false);
+ mDespawnTime = 5000;
+ mDespawnState++;
+ }
+ else
+ me->DespawnOrUnsummon();
+ }
+ else
+ mDespawnTime -= diff;
+}
+
void SmartGameObjectAI::UpdateAI(uint32 diff)
{
GetScript()->OnUpdate(diff);
@@ -957,10 +984,6 @@ void SmartGameObjectAI::UpdateAI(uint32 diff)
void SmartGameObjectAI::InitializeAI()
{
GetScript()->OnInitialize(me);
-
- // do not call respawn event if go is not spawned
- if (me->isSpawned())
- GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
//Reset();
}
diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h
index b5ed74c4edb..ecf9aaeb97a 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.h
+++ b/src/server/game/AI/SmartScripts/SmartAI.h
@@ -23,8 +23,7 @@
#include "GameObjectAI.h"
#include "Position.h"
#include "SmartScript.h"
-
-struct WayPoint;
+#include "WaypointDefines.h"
enum SmartEscortState
{
@@ -43,35 +42,42 @@ enum SmartEscortVars
class TC_GAME_API SmartAI : public CreatureAI
{
public:
- ~SmartAI(){ }
+ ~SmartAI() { }
explicit SmartAI(Creature* c);
+ // core related
+ static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
+
// Check whether we are currently permitted to make the creature take action
bool IsAIControlled() const;
// Start moving to the desired MovePoint
- void StartPath(bool run = false, uint32 path = 0, bool repeat = false, Unit* invoker = nullptr);
+ void StartPath(bool run = false, uint32 pathId = 0, bool repeat = false, Unit* invoker = nullptr, uint32 nodeId = 1);
bool LoadPath(uint32 entry);
void PausePath(uint32 delay, bool forced = false);
void StopPath(uint32 DespawnTime = 0, uint32 quest = 0, bool fail = false);
void EndPath(bool fail = false);
void ResumePath();
- WayPoint* GetNextWayPoint();
- bool HasEscortState(uint32 uiEscortState) const { return (mEscortState & uiEscortState) != 0; }
- void AddEscortState(uint32 uiEscortState) { mEscortState |= uiEscortState; }
- void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; }
+ bool HasEscortState(uint32 uiEscortState) const { return (_escortState & uiEscortState) != 0; }
+ void AddEscortState(uint32 uiEscortState) { _escortState |= uiEscortState; }
+ void RemoveEscortState(uint32 uiEscortState) { _escortState &= ~uiEscortState; }
void SetAutoAttack(bool on) { mCanAutoAttack = on; }
void SetCombatMove(bool on);
bool CanCombatMove() { return mCanCombatMove; }
void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0);
void StopFollow(bool complete);
+ bool IsEscortInvokerInRange();
+
+ void WaypointPathStarted(uint32 nodeId, uint32 pathId) override;
+ void WaypointStarted(uint32 nodeId, uint32 pathId) override;
+ void WaypointReached(uint32 nodeId, uint32 pathId) override;
+ void WaypointPathEnded(uint32 nodeId, uint32 pathId) override;
void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker);
SmartScript* GetScript() { return &mScript; }
- bool IsEscortInvokerInRange();
// Called when creature is spawned or respawned
- void JustRespawned() override;
+ void JustAppeared() override;
// Called at reaching home after evade, InitializeAI(), EnterEvadeMode() for resetting variables
void JustReachedHome() override;
@@ -157,12 +163,6 @@ class TC_GAME_API SmartAI : public CreatureAI
// Used in scripts to share variables
ObjectGuid GetGUID(int32 id = 0) const override;
- //core related
- static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
-
- // Called at movepoint reached
- void MovepointReached(uint32 id);
-
// Makes the creature run/walk
void SetRun(bool run = true);
@@ -183,8 +183,6 @@ class TC_GAME_API SmartAI : public CreatureAI
void QuestReward(Player* player, Quest const* quest, uint32 opt) override;
void OnGameEvent(bool start, uint16 eventId) override;
- uint32 mEscortQuestID;
-
void SetDespawnTime (uint32 t)
{
mDespawnTime = t;
@@ -194,11 +192,22 @@ class TC_GAME_API SmartAI : public CreatureAI
void OnSpellClick(Unit* clicker, bool& result) override;
- void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
+ void SetWPPauseTimer(uint32 time) { _waypointPauseTimer = time; }
void SetGossipReturn(bool val) { _gossipReturn = val; }
+ void SetEscortQuest(uint32 questID) { mEscortQuestID = questID; }
+
private:
+ bool AssistPlayerInCombatAgainst(Unit* who);
+ void ReturnToLastOOCPos();
+ void CheckConditions(uint32 diff);
+ void UpdatePath(uint32 diff);
+ void UpdateFollow(uint32 diff);
+ void UpdateDespawn(uint32 diff);
+
+ SmartScript mScript;
+
bool mIsCharmed;
uint32 mFollowCreditType;
uint32 mFollowArrivedTimer;
@@ -208,41 +217,35 @@ class TC_GAME_API SmartAI : public CreatureAI
float mFollowDist;
float mFollowAngle;
- void ReturnToLastOOCPos();
- void UpdatePath(const uint32 diff);
- SmartScript mScript;
- WPPath* mWayPoints;
- uint32 mEscortState;
- uint32 mCurrentWPID;
- uint32 mLastWPIDReached;
- bool mWPReached;
- uint32 mWPPauseTimer;
- uint32 mEscortNPCFlags;
- WayPoint* mLastWP;
- Position mLastOOCPos;//set on enter combat
- uint32 GetWPCount() const { return mWayPoints ? uint32(mWayPoints->size()) : 0; }
- bool mCanRepeatPath;
+ uint32 _escortState;
+ uint32 _escortNPCFlags;
+ uint32 _escortInvokerCheckTimer;
+ WaypointPath _path;
+ uint32 _currentWaypointNode;
+ bool _waypointReached;
+ uint32 _waypointPauseTimer;
+ bool _waypointPauseForced;
+ bool _repeatWaypointPath;
+ bool _OOCReached;
+ bool _waypointPathEnded;
+
bool mRun;
bool mEvadeDisabled;
bool mCanAutoAttack;
bool mCanCombatMove;
- bool mForcedPaused;
uint32 mInvincibilityHpLevel;
- bool AssistPlayerInCombatAgainst(Unit* who);
uint32 mDespawnTime;
uint32 mDespawnState;
- void UpdateDespawn(uint32 diff);
- uint32 mEscortInvokerCheckTimer;
- bool mJustReset;
// Vehicle conditions
- void CheckConditions(uint32 diff);
bool mHasConditions;
uint32 mConditionsTimer;
// Gossip
bool _gossipReturn;
+
+ uint32 mEscortQuestID;
};
class TC_GAME_API SmartGameObjectAI : public GameObjectAI
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index 7b7bb00416d..30fa5deadc3 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -39,6 +39,7 @@
#include "SpellMgr.h"
#include "TemporarySummon.h"
#include "Vehicle.h"
+#include "WaypointDefines.h"
#include <G3D/Quat.h>
SmartScript::SmartScript()
@@ -309,7 +310,11 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
if (IsUnit(target))
{
- target->PlayDirectSound(e.action.sound.sound, e.action.sound.onlySelf ? target->ToPlayer() : nullptr);
+ if (e.action.sound.distance == 1)
+ target->PlayDistanceSound(e.action.sound.sound, e.action.sound.onlySelf ? target->ToPlayer() : nullptr);
+ else
+ target->PlayDirectSound(e.action.sound.sound, e.action.sound.onlySelf ? target->ToPlayer() : nullptr);
+
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SOUND: target: %s (GuidLow: %u), sound: %u, onlyself: %u",
target->GetName().c_str(), target->GetGUID().GetCounter(), e.action.sound.sound, e.action.sound.onlySelf);
}
@@ -936,7 +941,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
case 1:
instance->SetBossState(e.action.setInstanceData.field, static_cast<EncounterState>(e.action.setInstanceData.data));
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_INST_DATA: SetBossState BossId: %u, State: %u (%s)",
- e.action.setInstanceData.field, e.action.setInstanceData.data, InstanceScript::GetBossStateName(e.action.setInstanceData.data).c_str());
+ e.action.setInstanceData.field, e.action.setInstanceData.data, InstanceScript::GetBossStateName(e.action.setInstanceData.data));
break;
default: // Static analysis
break;
@@ -1039,8 +1044,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
else
creature->DespawnOrUnsummon(respawnDelay);
}
- else if (GameObject* go = target->ToGameObject())
- go->SetRespawnTime(respawnDelay);
+ else if (GameObject* goTarget = target->ToGameObject())
+ goTarget->SetRespawnTime(respawnDelay);
}
break;
}
@@ -1331,7 +1336,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
uint32 quest = e.action.wpStart.quest;
uint32 DespawnTime = e.action.wpStart.despawnTime;
- ENSURE_AI(SmartAI, me->AI())->mEscortQuestID = quest;
+ ENSURE_AI(SmartAI, me->AI())->SetEscortQuest(quest);
ENSURE_AI(SmartAI, me->AI())->SetDespawnTime(DespawnTime);
break;
}
@@ -1588,10 +1593,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (IsSmart(creature))
ENSURE_AI(SmartAI, creature->AI())->SetScript9(e, e.action.timedActionList.id, GetLastInvoker());
}
- else if (GameObject* go = target->ToGameObject())
+ else if (GameObject* goTarget = target->ToGameObject())
{
- if (IsSmartGO(go))
- ENSURE_AI(SmartGameObjectAI, go->AI())->SetScript9(e, e.action.timedActionList.id, GetLastInvoker());
+ if (IsSmartGO(goTarget))
+ ENSURE_AI(SmartGameObjectAI, goTarget->AI())->SetScript9(e, e.action.timedActionList.id, GetLastInvoker());
}
}
break;
@@ -1675,10 +1680,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (IsSmart(creature))
ENSURE_AI(SmartAI, creature->AI())->SetScript9(e, id, GetLastInvoker());
}
- else if (GameObject* go = target->ToGameObject())
+ else if (GameObject* goTarget = target->ToGameObject())
{
- if (IsSmartGO(go))
- ENSURE_AI(SmartGameObjectAI, go->AI())->SetScript9(e, id, GetLastInvoker());
+ if (IsSmartGO(goTarget))
+ ENSURE_AI(SmartGameObjectAI, goTarget->AI())->SetScript9(e, id, GetLastInvoker());
}
}
break;
@@ -1699,10 +1704,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (IsSmart(creature))
ENSURE_AI(SmartAI, creature->AI())->SetScript9(e, id, GetLastInvoker());
}
- else if (GameObject* go = target->ToGameObject())
+ else if (GameObject* goTarget = target->ToGameObject())
{
- if (IsSmartGO(go))
- ENSURE_AI(SmartGameObjectAI, go->AI())->SetScript9(e, id, GetLastInvoker());
+ if (IsSmartGO(goTarget))
+ ENSURE_AI(SmartGameObjectAI, goTarget->AI())->SetScript9(e, id, GetLastInvoker());
}
}
break;
@@ -1899,7 +1904,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
for (WorldObject* target : targets)
if (IsCreature(target))
- target->ToCreature()->setRegeneratingHealth(e.action.setHealthRegen.regenHealth != 0);
+ target->ToCreature()->SetRegenerateHealth(e.action.setHealthRegen.regenHealth != 0);
break;
}
case SMART_ACTION_SET_ROOT:
@@ -1990,7 +1995,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
std::back_inserter(waypoints), [](uint32 wp) { return wp != 0; });
float distanceToClosest = std::numeric_limits<float>::max();
- WayPoint* closestWp = nullptr;
+ std::pair<uint32, uint32> closest = { 0, 0 };
for (WorldObject* target : targets)
{
@@ -1998,29 +2003,27 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
if (IsSmart(creature))
{
- for (uint32 wp : waypoints)
+ for (uint32 pathId : waypoints)
{
- WPPath* path = sSmartWaypointMgr->GetPath(wp);
- if (!path || path->empty())
+ WaypointPath const* path = sSmartWaypointMgr->GetPath(pathId);
+ if (!path || path->nodes.empty())
continue;
- auto itrWp = path->find(0);
- if (itrWp != path->end())
+ for (auto itr = path->nodes.begin(); itr != path->nodes.end(); ++itr)
{
- if (WayPoint* wp = itrWp->second)
+ WaypointNode const waypoint = *itr;
+ float distamceToThisNode = creature->GetDistance(waypoint.x, waypoint.y, waypoint.z);
+ if (distamceToThisNode < distanceToClosest)
{
- float distToThisPath = creature->GetDistance(wp->x, wp->y, wp->z);
- if (distToThisPath < distanceToClosest)
- {
- distanceToClosest = distToThisPath;
- closestWp = wp;
- }
+ distanceToClosest = distamceToThisNode;
+ closest.first = pathId;
+ closest.second = waypoint.id;
}
}
}
- if (closestWp)
- CAST_AI(SmartAI, creature->AI())->StartPath(false, closestWp->id, true);
+ if (closest.first != 0)
+ ENSURE_AI(SmartAI, creature->AI())->StartPath(false, closest.first, true, nullptr, closest.second);
}
}
}
@@ -2038,7 +2041,12 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (IsUnit(target))
{
uint32 sound = Trinity::Containers::SelectRandomContainerElement(sounds);
- target->PlayDirectSound(sound, onlySelf ? target->ToPlayer() : nullptr);
+
+ if (e.action.randomSound.distance == 1)
+ target->PlayDistanceSound(sound, onlySelf ? target->ToPlayer() : nullptr);
+ else
+ target->PlayDirectSound(sound, onlySelf ? target->ToPlayer() : nullptr);
+
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_RANDOM_SOUND: target: %s (%s), sound: %u, onlyself: %s",
target->GetName().c_str(), target->GetGUID().ToString().c_str(), sound, onlySelf ? "true" : "false");
}
@@ -2052,6 +2060,92 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (IsCreature(target))
target->ToCreature()->SetCorpseDelay(e.action.corpseDelay.timer);
}
+
+ break;
+ }
+ case SMART_ACTION_SPAWN_SPAWNGROUP:
+ {
+ if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0)
+ {
+ bool const ignoreRespawn = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_IGNORE_RESPAWN) != 0);
+ bool const force = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_FORCE_SPAWN) != 0);
+
+ // Instant spawn
+ GetBaseObject()->GetMap()->SpawnGroupSpawn(e.action.groupSpawn.groupId, ignoreRespawn, force);
+ }
+ else
+ {
+ // Delayed spawn (use values from parameter to schedule event to call us back
+ SmartEvent ne = SmartEvent();
+ ne.type = (SMART_EVENT)SMART_EVENT_UPDATE;
+ ne.event_chance = 100;
+
+ ne.minMaxRepeat.min = e.action.groupSpawn.minDelay;
+ ne.minMaxRepeat.max = e.action.groupSpawn.maxDelay;
+ ne.minMaxRepeat.repeatMin = 0;
+ ne.minMaxRepeat.repeatMax = 0;
+
+ ne.event_flags = 0;
+ ne.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
+
+ SmartAction ac = SmartAction();
+ ac.type = (SMART_ACTION)SMART_ACTION_SPAWN_SPAWNGROUP;
+ ac.groupSpawn.groupId = e.action.groupSpawn.groupId;
+ ac.groupSpawn.minDelay = 0;
+ ac.groupSpawn.maxDelay = 0;
+ ac.groupSpawn.spawnflags = e.action.groupSpawn.spawnflags;
+ ac.timeEvent.id = e.action.timeEvent.id;
+
+ SmartScriptHolder ev = SmartScriptHolder();
+ ev.event = ne;
+ ev.event_id = e.event_id;
+ ev.target = e.target;
+ ev.action = ac;
+ InitTimer(ev);
+ mStoredEvents.push_back(ev);
+ }
+ break;
+ }
+ case SMART_ACTION_DESPAWN_SPAWNGROUP:
+ {
+ if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0)
+ {
+ bool const deleteRespawnTimes = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN) != 0);
+
+ // Instant spawn
+ GetBaseObject()->GetMap()->SpawnGroupDespawn(e.action.groupSpawn.groupId, deleteRespawnTimes);
+ }
+ else
+ {
+ // Delayed spawn (use values from parameter to schedule event to call us back
+ SmartEvent ne = SmartEvent();
+ ne.type = (SMART_EVENT)SMART_EVENT_UPDATE;
+ ne.event_chance = 100;
+
+ ne.minMaxRepeat.min = e.action.groupSpawn.minDelay;
+ ne.minMaxRepeat.max = e.action.groupSpawn.maxDelay;
+ ne.minMaxRepeat.repeatMin = 0;
+ ne.minMaxRepeat.repeatMax = 0;
+
+ ne.event_flags = 0;
+ ne.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
+
+ SmartAction ac = SmartAction();
+ ac.type = (SMART_ACTION)SMART_ACTION_DESPAWN_SPAWNGROUP;
+ ac.groupSpawn.groupId = e.action.groupSpawn.groupId;
+ ac.groupSpawn.minDelay = 0;
+ ac.groupSpawn.maxDelay = 0;
+ ac.groupSpawn.spawnflags = e.action.groupSpawn.spawnflags;
+ ac.timeEvent.id = e.action.timeEvent.id;
+
+ SmartScriptHolder ev = SmartScriptHolder();
+ ev.event = ne;
+ ev.event_id = e.event_id;
+ ev.target = e.target;
+ ev.action = ac;
+ InitTimer(ev);
+ mStoredEvents.push_back(ev);
+ }
break;
}
case SMART_ACTION_DISABLE_EVADE:
@@ -2947,7 +3041,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
case SMART_EVENT_WAYPOINT_STOPPED:
case SMART_EVENT_WAYPOINT_ENDED:
{
- if (!me || (e.event.waypoint.pointID && var0 != e.event.waypoint.pointID) || (e.event.waypoint.pathID && GetPathId() != e.event.waypoint.pathID))
+ if (!me || (e.event.waypoint.pointID && var0 != e.event.waypoint.pointID) || (e.event.waypoint.pathID && var1 != e.event.waypoint.pathID))
return;
ProcessAction(e, unit);
break;
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index 31282a934ac..b4713b7ed4d 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -29,6 +29,7 @@
#include "SpellMgr.h"
#include "Timer.h"
#include "UnitDefines.h"
+#include "WaypointDefines.h"
SmartWaypointMgr* SmartWaypointMgr::instance()
{
@@ -40,15 +41,7 @@ void SmartWaypointMgr::LoadFromDB()
{
uint32 oldMSTime = getMSTime();
- for (std::unordered_map<uint32, WPPath*>::iterator itr = waypoint_map.begin(); itr != waypoint_map.end(); ++itr)
- {
- for (WPPath::iterator pathItr = itr->second->begin(); pathItr != itr->second->end(); ++pathItr)
- delete pathItr->second;
-
- delete itr->second;
- }
-
- waypoint_map.clear();
+ _waypointStore.clear();
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP);
PreparedQueryResult result = WorldDatabase.Query(stmt);
@@ -62,50 +55,47 @@ void SmartWaypointMgr::LoadFromDB()
uint32 count = 0;
uint32 total = 0;
- uint32 last_entry = 0;
- uint32 last_id = 1;
+ uint32 lastEntry = 0;
+ uint32 lastId = 1;
do
{
Field* fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
uint32 id = fields[1].GetUInt32();
- float x, y, z;
- x = fields[2].GetFloat();
- y = fields[3].GetFloat();
- z = fields[4].GetFloat();
+ float x = fields[2].GetFloat();
+ float y = fields[3].GetFloat();
+ float z = fields[4].GetFloat();
- if (last_entry != entry)
+ if (lastEntry != entry)
{
- waypoint_map[entry] = new WPPath();
- last_id = 1;
- count++;
+ lastId = 1;
+ ++count;
}
- if (last_id != id)
- TC_LOG_ERROR("sql.sql", "SmartWaypointMgr::LoadFromDB: Path entry %u, unexpected point id %u, expected %u.", entry, id, last_id);
+ if (lastId != id)
+ TC_LOG_ERROR("sql.sql", "SmartWaypointMgr::LoadFromDB: Path entry %u, unexpected point id %u, expected %u.", entry, id, lastId);
- last_id++;
- (*waypoint_map[entry])[id] = new WayPoint(id, x, y, z);
+ ++lastId;
+ WaypointPath& path = _waypointStore[entry];
+ path.id = entry;
+ path.nodes.emplace_back(id, x, y, z);
- last_entry = entry;
- total++;
+ lastEntry = entry;
+ ++total;
}
while (result->NextRow());
TC_LOG_INFO("server.loading", ">> Loaded %u SmartAI waypoint paths (total %u waypoints) in %u ms", count, total, GetMSTimeDiffToNow(oldMSTime));
}
-SmartWaypointMgr::~SmartWaypointMgr()
+WaypointPath const* SmartWaypointMgr::GetPath(uint32 id)
{
- for (std::unordered_map<uint32, WPPath*>::iterator itr = waypoint_map.begin(); itr != waypoint_map.end(); ++itr)
- {
- for (WPPath::iterator pathItr = itr->second->begin(); pathItr != itr->second->end(); ++pathItr)
- delete pathItr->second;
-
- delete itr->second;
- }
+ auto itr = _waypointStore.find(id);
+ if (itr != _waypointStore.end())
+ return &itr->second;
+ return nullptr;
}
SmartAIMgr* SmartAIMgr::instance()
@@ -228,7 +218,7 @@ void SmartAIMgr::LoadSmartAIFromDB()
}
case SMART_SCRIPT_TYPE_GAMEOBJECT:
{
- GameObjectData const* gameObject = sObjectMgr->GetGOData(uint32(std::abs(temp.entryOrGuid)));
+ GameObjectData const* gameObject = sObjectMgr->GetGameObjectData(uint32(std::abs(temp.entryOrGuid)));
if (!gameObject)
{
TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: GameObject guid (%u) does not exist, skipped loading.", uint32(std::abs(temp.entryOrGuid)));
@@ -932,7 +922,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
return false;
}
- if (e.event.distance.guid != 0 && !sObjectMgr->GetGOData(e.event.distance.guid))
+ if (e.event.distance.guid != 0 && !sObjectMgr->GetGameObjectData(e.event.distance.guid))
{
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject guid %u, skipped.", e.event.distance.guid);
return false;
@@ -1314,7 +1304,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
break;
case SMART_ACTION_WP_START:
{
- if (!sSmartWaypointMgr->GetPath(e.action.wpStart.pathID))
+ WaypointPath const* path = sSmartWaypointMgr->GetPath(e.action.wpStart.pathID);
+ if (!path || path->nodes.empty())
{
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature %d Event %u Action %u uses non-existent WaypointPath id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.pathID);
return false;
@@ -1508,6 +1499,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT:
case SMART_ACTION_SET_COUNTER:
case SMART_ACTION_REMOVE_ALL_GAMEOBJECTS:
+ case SMART_ACTION_SPAWN_SPAWNGROUP:
+ case SMART_ACTION_DESPAWN_SPAWNGROUP:
break;
default:
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type(%u), event_type(%u), Entry %d SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
index 7a1433bc7fb..499b1ad458e 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -20,6 +20,7 @@
#include "Define.h"
#include "ObjectGuid.h"
+#include "WaypointDefines.h"
#include <map>
#include <string>
#include <unordered_map>
@@ -27,22 +28,6 @@
class WorldObject;
enum SpellEffIndex : uint8;
-struct WayPoint
-{
- WayPoint(uint32 _id, float _x, float _y, float _z)
- {
- id = _id;
- x = _x;
- y = _y;
- z = _z;
- }
-
- uint32 id;
- float x;
- float y;
- float z;
-};
-
enum eSmartAI
{
SMART_EVENT_PARAM_COUNT = 4,
@@ -589,8 +574,12 @@ enum SMART_ACTION
SMART_ACTION_REMOVE_ALL_GAMEOBJECTS = 126,
SMART_ACTION_STOP_MOTION = 127, // stopMoving, movementExpired
SMART_ACTION_PLAY_ANIMKIT = 128, // don't use on 3.3.5a
+ SMART_ACTION_SCENE_PLAY = 129, // don't use on 3.3.5a
+ SMART_ACTION_SCENE_CANCEL = 130, // don't use on 3.3.5a
+ SMART_ACTION_SPAWN_SPAWNGROUP = 131, // Group ID, min secs, max secs, spawnflags
+ SMART_ACTION_DESPAWN_SPAWNGROUP = 132, // Group ID, min secs, max secs, spawnflags
- SMART_ACTION_END = 129
+ SMART_ACTION_END = 133
};
struct SmartAction
@@ -621,6 +610,7 @@ struct SmartAction
{
uint32 sound;
uint32 onlySelf;
+ uint32 distance;
} sound;
struct
@@ -1081,8 +1071,9 @@ struct SmartAction
struct
{
- uint32 sounds[SMART_ACTION_PARAM_COUNT - 1];
+ uint32 sounds[SMART_ACTION_PARAM_COUNT - 2];
uint32 onlySelf;
+ uint32 distance;
} randomSound;
struct
@@ -1094,6 +1085,13 @@ struct SmartAction
{
uint32 disable;
} disableEvade;
+ struct
+ {
+ uint32 groupId;
+ uint32 minDelay;
+ uint32 maxDelay;
+ uint32 spawnflags;
+ } groupSpawn;
struct
{
@@ -1138,6 +1136,14 @@ struct SmartAction
};
};
+enum SMARTAI_SPAWN_FLAGS
+{
+ SMARTAI_SPAWN_FLAG_NONE = 0x00,
+ SMARTAI_SPAWN_FLAG_IGNORE_RESPAWN = 0x01,
+ SMARTAI_SPAWN_FLAG_FORCE_SPAWN = 0x02,
+ SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN = 0x04,
+};
+
enum SMARTAI_TEMPLATE
{
SMARTAI_TEMPLATE_BASIC = 0, //nothing is preset
@@ -1495,8 +1501,6 @@ struct SmartScriptHolder
operator bool() const { return entryOrGuid != 0; }
};
-typedef std::unordered_map<uint32, WayPoint*> WPPath;
-
typedef std::vector<WorldObject*> ObjectVector;
class ObjectGuidVector
@@ -1523,26 +1527,22 @@ typedef std::unordered_map<uint32, ObjectGuidVector> ObjectVectorMap;
class TC_GAME_API SmartWaypointMgr
{
- private:
- SmartWaypointMgr() { }
- ~SmartWaypointMgr();
-
public:
static SmartWaypointMgr* instance();
void LoadFromDB();
- WPPath* GetPath(uint32 id)
- {
- if (waypoint_map.find(id) != waypoint_map.end())
- return waypoint_map[id];
- else return nullptr;
- }
+ WaypointPath const* GetPath(uint32 id);
private:
- std::unordered_map<uint32, WPPath*> waypoint_map;
+ SmartWaypointMgr() { }
+ ~SmartWaypointMgr() { }
+
+ std::unordered_map<uint32, WaypointPath> _waypointStore;
};
+#define sSmartWaypointMgr SmartWaypointMgr::instance()
+
// all events for a single entry
typedef std::vector<SmartScriptHolder> SmartAIEventList;
typedef std::vector<SmartScriptHolder> SmartAIEventStoredList;
@@ -1608,5 +1608,5 @@ class TC_GAME_API SmartAIMgr
};
#define sSmartScriptMgr SmartAIMgr::instance()
-#define sSmartWaypointMgr SmartWaypointMgr::instance()
+
#endif
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h
index 8fc5f220d43..0f8e031ba20 100644
--- a/src/server/game/Accounts/RBAC.h
+++ b/src/server/game/Accounts/RBAC.h
@@ -57,7 +57,7 @@ enum RBACPermissions
RBAC_PERM_JOIN_RANDOM_BG = 4,
RBAC_PERM_JOIN_ARENAS = 5,
RBAC_PERM_JOIN_DUNGEON_FINDER = 6,
- // 7 - reuse
+ RBAC_PERM_IGNORE_IDLE_CONNECTION = 7,
// 8 - reuse
// 9 - reuse
RBAC_PERM_USE_CHARACTER_TEMPLATES = 10, // not on 3.3.5a
@@ -748,7 +748,7 @@ enum RBACPermissions
RBAC_PERM_COMMAND_SERVER_RESTART_FORCE = 840,
RBAC_PERM_COMMAND_NEARGRAVEYARD = 841,
RBAC_PERM_COMMAND_RELOAD_CHARACTER_TEMPLATE = 842, // not on 3.3.5a
- RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING = 843, // not on 3.3.5a
+ RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING = 843,
RBAC_PERM_COMMAND_SCENE = 844, // not on 3.3.5a
RBAC_PERM_COMMAND_SCENE_DEBUG = 845, // not on 3.3.5a
RBAC_PERM_COMMAND_SCENE_PLAY = 846, // not on 3.3.5a
@@ -761,16 +761,18 @@ enum RBACPermissions
RBAC_PERM_COMMAND_RELOAD_CONVERSATION_TEMPLATE = 853, // not on 3.3.5a
RBAC_PERM_COMMAND_DEBUG_CONVERSATION = 854, // not on 3.3.5a
RBAC_PERM_COMMAND_DEBUG_PLAY_MUSIC = 855,
- RBAC_PERM_COMMAND_NPC_SPAWNGROUP = 856, // reserved for dynamic_spawning
- RBAC_PERM_COMMAND_NPC_DESPAWNGROUP = 857, // reserved for dynamic_spawning
- RBAC_PERM_COMMAND_GOBJECT_SPAWNGROUP = 858, // reserved for dynamic_spawning
- RBAC_PERM_COMMAND_GOBJECT_DESPAWNGROUP = 859, // reserved for dynamic_spawning
- RBAC_PERM_COMMAND_LIST_RESPAWNS = 860, // reserved for dynamic_spawning
+ RBAC_PERM_COMMAND_NPC_SPAWNGROUP = 856,
+ RBAC_PERM_COMMAND_NPC_DESPAWNGROUP = 857,
+ RBAC_PERM_COMMAND_GOBJECT_SPAWNGROUP = 858,
+ RBAC_PERM_COMMAND_GOBJECT_DESPAWNGROUP = 859,
+ RBAC_PERM_COMMAND_LIST_RESPAWNS = 860,
RBAC_PERM_COMMAND_GROUP_SET = 861,
RBAC_PERM_COMMAND_GROUP_ASSISTANT = 862,
RBAC_PERM_COMMAND_GROUP_MAINTANK = 863,
RBAC_PERM_COMMAND_GROUP_MAINASSIST = 864,
RBAC_PERM_COMMAND_NPC_SHOWLOOT = 865,
+ RBAC_PERM_COMMAND_LIST_SPAWNPOINTS = 866,
+ RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING_LOCALE = 867,
// custom permissions 1000+
RBAC_PERM_MAX
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp
index a43ef238521..f904614960a 100644
--- a/src/server/game/Achievements/AchievementMgr.cpp
+++ b/src/server/game/Achievements/AchievementMgr.cpp
@@ -2287,7 +2287,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList()
if (sAchievementCriteriaStore.GetNumRows() == 0)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 achievement criteria.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 achievement criteria.");
return;
}
@@ -2574,7 +2574,7 @@ void AchievementGlobalMgr::LoadRewards()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
return;
}
diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp
index 27e83185e27..92b78216cb0 100644
--- a/src/server/game/Battlefield/Battlefield.cpp
+++ b/src/server/game/Battlefield/Battlefield.cpp
@@ -776,18 +776,15 @@ Creature* Battlefield::SpawnCreature(uint32 entry, Position const& pos)
return nullptr;
}
- float x, y, z, o;
- pos.GetPosition(x, y, z, o);
-
Creature* creature = new Creature();
- if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, x, y, z, o))
+ if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, pos))
{
TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Can't create creature entry: %u", entry);
delete creature;
return nullptr;
}
- creature->SetHomePosition(x, y, z, o);
+ creature->SetHomePosition(pos);
// Set creature in world
map->AddToMap(creature);
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index 41591c419e9..e184c22354e 100644
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -1515,7 +1515,7 @@ Creature* Battleground::AddCreature(uint32 entry, uint32 type, float x, float y,
Creature* creature = new Creature();
- if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, x, y, z, o))
+ if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, { x, y, z, o }))
{
TC_LOG_ERROR("bg.battleground", "Battleground::AddCreature: cannot create creature (entry: %u) for BG (map: %u, instance id: %u)!",
entry, m_MapId, m_InstanceID);
diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp
index 1b8fb8ee907..76e3d7ea08c 100644
--- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp
@@ -535,7 +535,7 @@ void BattlegroundMgr::LoadBattlegroundTemplates()
QueryResult result = WorldDatabase.Query("SELECT ID, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, AllianceStartLoc, AllianceStartO, HordeStartLoc, HordeStartO, StartMaxDist, Weight, ScriptName FROM battleground_template");
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 battlegrounds. DB table `battleground_template` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 battlegrounds. DB table `battleground_template` is empty.");
return;
}
@@ -973,7 +973,6 @@ BattlegroundTypeId BattlegroundMgr::GetRandomBG(BattlegroundTypeId bgTypeId)
{
if (BattlegroundTemplate const* bgTemplate = GetBattlegroundTemplateByTypeId(bgTypeId))
{
- BattlegroundSelectionWeightMap selectionWeights;
std::vector<BattlegroundTypeId> ids;
ids.reserve(16);
std::vector<double> weights;
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp
index 6ef3bbcc3c3..3ebca71a326 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp
@@ -176,7 +176,7 @@ void BattlegroundAB::PostUpdateImpl(uint32 diff)
if (team == TEAM_ALLIANCE)
UpdateWorldState(BG_AB_OP_RESOURCES_ALLY, m_TeamScores[team]);
- else if (team == TEAM_HORDE)
+ else
UpdateWorldState(BG_AB_OP_RESOURCES_HORDE, m_TeamScores[team]);
// update achievement flags
// we increased m_TeamScores[team] so we just need to check if it is 500 more than other teams resources
@@ -418,12 +418,12 @@ void BattlegroundAB::_NodeOccupied(uint8 node, Team team)
void BattlegroundAB::_NodeDeOccupied(uint8 node)
{
+ //only dynamic nodes, no start points
if (node >= BG_AB_DYNAMIC_NODES_COUNT)
return;
//remove bonus honor aura trigger creature when node is lost
- if (node < BG_AB_DYNAMIC_NODES_COUNT)//only dynamic nodes, no start points
- DelCreature(node+7);//NULL checks are in DelCreature! 0-6 spirit guides
+ DelCreature(node+7);//NULL checks are in DelCreature! 0-6 spirit guides
RelocateDeadPlayers(BgCreatures[node]);
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAB.h b/src/server/game/Battlegrounds/Zones/BattlegroundAB.h
index 7f68ef3eb0d..f7215989093 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundAB.h
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundAB.h
@@ -212,11 +212,11 @@ const uint32 BG_AB_GraveyardIds[BG_AB_ALL_NODES_COUNT] = {895, 894, 893, 897, 89
// x, y, z, o
const float BG_AB_BuffPositions[BG_AB_DYNAMIC_NODES_COUNT][4] =
{
- {1185.71f, 1185.24f, -56.36f, 2.56f}, // stables
- {990.75f, 1008.18f, -42.60f, 2.43f}, // blacksmith
- {817.66f, 843.34f, -56.54f, 3.01f}, // farm
- {807.46f, 1189.16f, 11.92f, 5.44f}, // lumber mill
- {1146.62f, 816.94f, -98.49f, 6.14f} // gold mine
+ {1185.566f, 1184.629f, -56.36329f, 2.303831f}, // stables
+ {990.1131f, 1008.73f, -42.60328f, 0.8203033f}, // blacksmith
+ {818.0089f, 842.3543f, -56.54062f, 3.176533f}, // farm
+ {808.8463f, 1185.417f, 11.92161f, 5.619962f}, // lumber mill
+ {1147.091f, 816.8362f, -98.39896f, 6.056293f} // gold mine
};
Position const BG_AB_SpiritGuidePos[BG_AB_ALL_NODES_COUNT] =
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp
index fc2bee156ee..506e4d2dea3 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp
@@ -314,6 +314,7 @@ Creature* BattlegroundAV::AddAVCreature(uint16 cinfoid, uint16 type)
|| (cinfoid >= AV_NPC_H_GRAVEDEFENSE0 && cinfoid <= AV_NPC_H_GRAVEDEFENSE3)))
{
CreatureData &data = sObjectMgr->NewOrExistCreatureData(creature->GetSpawnId());
+ data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup();
data.spawndist = 5;
}
//else spawndist will be 15, so creatures move maximum=10
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h
index 9adaea025d3..7336caaf0d7 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h
@@ -987,19 +987,19 @@ Position const BG_AV_CreaturePos[AV_CPLACE_MAX] =
enum BG_AV_CreatureIds
{
- AV_NPC_A_TOWERDEFENSE = 0, // stormpike bowman
- AV_NPC_A_GRAVEDEFENSE0 = 1, // stormpike Defender
- AV_NPC_A_GRAVEDEFENSE1 = 2, // seasoned defender
- AV_NPC_A_GRAVEDEFENSE2 = 3, // veteran defender
- AV_NPC_A_GRAVEDEFENSE3 = 4, // champion defender
+ AV_NPC_A_GRAVEDEFENSE0 = 0, // stormpike Defender
+ AV_NPC_A_GRAVEDEFENSE1 = 1, // seasoned defender
+ AV_NPC_A_GRAVEDEFENSE2 = 2, // veteran defender
+ AV_NPC_A_GRAVEDEFENSE3 = 3, // champion defender
+ AV_NPC_A_TOWERDEFENSE = 4, // stormpike bowman
AV_NPC_A_CAPTAIN = 5, // balinda
AV_NPC_A_BOSS = 6, // vanndar
- AV_NPC_H_TOWERDEFENSE = 7, // frostwolf bowman
- AV_NPC_H_GRAVEDEFENSE0 = 8, // frostwolf guardian
- AV_NPC_H_GRAVEDEFENSE1 = 9, // seasoned guardian
- AV_NPC_H_GRAVEDEFENSE2 = 10, // veteran guardian
- AV_NPC_H_GRAVEDEFENSE3 = 11, // champion guardian
+ AV_NPC_H_GRAVEDEFENSE0 = 7, // frostwolf guardian
+ AV_NPC_H_GRAVEDEFENSE1 = 8, // seasoned guardian
+ AV_NPC_H_GRAVEDEFENSE2 = 9, // veteran guardian
+ AV_NPC_H_GRAVEDEFENSE3 = 10, // champion guardian
+ AV_NPC_H_TOWERDEFENSE = 11, // frostwolf bowman
AV_NPC_H_CAPTAIN = 12, // galvangar
AV_NPC_H_BOSS = 13, // drek thar
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
index e1d1394cb05..a686d52cda2 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
@@ -529,8 +529,7 @@ bool BattlegroundEY::SetupBattleground()
TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Could not spawn Speedbuff Fel Reaver.");
}
- WorldSafeLocsEntry const* sg = nullptr;
- sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_ALLIANCE);
+ WorldSafeLocsEntry const* sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_ALLIANCE);
if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, TEAM_ALLIANCE))
{
TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn spirit guide. The battleground was not created.");
@@ -774,8 +773,7 @@ void BattlegroundEY::EventTeamCapturedPoint(Player* player, uint32 Point)
if (BgCreatures[Point])
DelCreature(Point);
- WorldSafeLocsEntry const* sg = nullptr;
- sg = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[Point].GraveyardId);
+ WorldSafeLocsEntry const* sg = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[Point].GraveyardId);
if (!sg || !AddSpiritGuide(Point, sg->x, sg->y, sg->z, 3.124139f, GetTeamIndexByTeamId(Team)))
TC_LOG_ERROR("bg.battleground", "BatteGroundEY: Failed to spawn spirit guide. point: %u, team: %u, graveyard_id: %u",
Point, Team, m_CapturingPointTypes[Point].GraveyardId);
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp
index b28f31cd09c..90fd95b5780 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp
@@ -198,8 +198,7 @@ bool BattlegroundSA::ResetObjs()
//Graveyards
for (uint8 i = 0; i < BG_SA_MAX_GY; i++)
{
- WorldSafeLocsEntry const* sg = nullptr;
- sg = sWorldSafeLocsStore.LookupEntry(BG_SA_GYEntries[i]);
+ WorldSafeLocsEntry const* sg = sWorldSafeLocsStore.LookupEntry(BG_SA_GYEntries[i]);
if (!sg)
{
diff --git a/src/server/game/Calendar/CalendarMgr.cpp b/src/server/game/Calendar/CalendarMgr.cpp
index 08a406c4f15..d93786d66f1 100644
--- a/src/server/game/Calendar/CalendarMgr.cpp
+++ b/src/server/game/Calendar/CalendarMgr.cpp
@@ -57,6 +57,8 @@ CalendarMgr* CalendarMgr::instance()
void CalendarMgr::LoadFromDB()
{
+ uint32 oldMSTime = getMSTime();
+
uint32 count = 0;
_maxEventId = 0;
_maxInviteId = 0;
@@ -90,7 +92,7 @@ void CalendarMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u calendar events", count);
+ TC_LOG_INFO("server.loading", ">> Loaded %u calendar events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
count = 0;
// 0 1 2 3 4 5 6 7
@@ -117,7 +119,7 @@ void CalendarMgr::LoadFromDB()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u calendar invites", count);
+ TC_LOG_INFO("server.loading", ">> Loaded %u calendar invites in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
for (uint64 i = 1; i < _maxEventId; ++i)
if (!GetEvent(i))
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index 3e15ecc9319..46c6a26760c 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -33,6 +33,7 @@
#include "Realm.h"
#include "ScriptMgr.h"
#include "World.h"
+#include <boost/algorithm/string/replace.hpp>
ChatCommand::ChatCommand(char const* name, uint32 permission, bool allowConsole, pHandler handler, std::string help, std::vector<ChatCommand> childCommands /*= std::vector<ChatCommand>()*/)
: Name(ASSERT_NOTNULL(name)), Permission(permission), AllowConsole(allowConsole), Handler(handler), Help(std::move(help)), ChildCommands(std::move(childCommands))
@@ -347,6 +348,7 @@ bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, c
SendSysMessage(table[i].Help.c_str());
else
SendSysMessage(LANG_CMD_SYNTAX);
+ SetSentErrorMessage(true);
}
return true;
@@ -410,41 +412,39 @@ bool ChatHandler::SetDataForCommandInTable(std::vector<ChatCommand>& table, char
return false;
}
+bool ChatHandler::_ParseCommands(char const* text)
+{
+ if (ExecuteCommandInTable(getCommandTable(), text, text))
+ return true;
+
+ // Pretend commands don't exist for regular players
+ if (m_session && !m_session->HasPermission(rbac::RBAC_PERM_COMMANDS_NOTIFY_COMMAND_NOT_FOUND_ERROR))
+ return false;
+
+ // Send error message for GMs
+ SendSysMessage(LANG_NO_CMD);
+ SetSentErrorMessage(true);
+ return true;
+}
+
bool ChatHandler::ParseCommands(char const* text)
{
ASSERT(text);
ASSERT(*text);
- std::string fullcmd = text;
-
/// chat case (.command or !command format)
- if (m_session)
- {
- if (text[0] != '!' && text[0] != '.')
- return false;
- }
+ if (text[0] != '!' && text[0] != '.')
+ return false;
/// ignore single . and ! in line
- if (strlen(text) < 2)
+ if (!text[1])
return false;
- // original `text` can't be used. It content destroyed in command code processing.
/// ignore messages staring from many dots.
- if ((text[0] == '.' && text[1] == '.') || (text[0] == '!' && text[1] == '!'))
+ if (text[1] == '!' || text[1] == '.')
return false;
- /// skip first . or ! (in console allowed use command with . and ! and without its)
- if (text[0] == '!' || text[0] == '.')
- ++text;
-
- if (!ExecuteCommandInTable(getCommandTable(), text, fullcmd))
- {
- if (m_session && !m_session->HasPermission(rbac::RBAC_PERM_COMMANDS_NOTIFY_COMMAND_NOT_FOUND_ERROR))
- return false;
-
- SendSysMessage(LANG_NO_CMD);
- }
- return true;
+ return _ParseCommands(text+1);
}
bool ChatHandler::isValidChatMessage(char const* message)
@@ -1232,6 +1232,16 @@ void CliHandler::SendSysMessage(const char *str, bool /*escapeCharacters*/)
m_print(m_callbackArg, "\r\n");
}
+bool CliHandler::ParseCommands(char const* str)
+{
+ if (!str[0])
+ return false;
+ // Console allows using commands both with and without leading indicator
+ if (str[0] == '.' || str[0] == '!')
+ ++str;
+ return _ParseCommands(str);
+}
+
std::string CliHandler::GetNameLink() const
{
return GetTrinityString(LANG_CONSOLE_COMMAND);
@@ -1295,3 +1305,102 @@ int CliHandler::GetSessionDbLocaleIndex() const
{
return sObjectMgr->GetDBCLocaleIndex();
}
+
+bool AddonChannelCommandHandler::ParseCommands(char const* str)
+{
+ if (memcmp(str, "TrinityCore\t", 12))
+ return false;
+ char opcode = str[12];
+ if (!opcode) // str[12] is opcode
+ return false;
+ if (!str[13] || !str[14] || !str[15] || !str[16]) // str[13] through str[16] is 4-character command counter
+ return false;
+ echo = str+13;
+
+ switch (opcode)
+ {
+ case 'p': // p Ping
+ SendAck();
+ return true;
+ case 'h': // h Issue human-readable command
+ case 'i': // i Issue command
+ if (!str[17])
+ return false;
+ humanReadable = (opcode == 'h');
+ if (_ParseCommands(str + 17)) // actual command starts at str[17]
+ {
+ if (!hadAck)
+ SendAck();
+ if (HasSentErrorMessage())
+ SendFailed();
+ else
+ SendOK();
+ }
+ else
+ {
+ SendSysMessage(LANG_NO_CMD);
+ SendFailed();
+ }
+ return true;
+ default:
+ return false;
+ }
+}
+
+void AddonChannelCommandHandler::Send(std::string const& msg)
+{
+ WorldPacket data;
+ ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, LANG_ADDON, GetSession()->GetPlayer(), GetSession()->GetPlayer(), msg);
+ GetSession()->SendPacket(&data);
+}
+
+void AddonChannelCommandHandler::SendAck() // a Command acknowledged, no body
+{
+ ASSERT(echo);
+ char ack[18] = "TrinityCore\ta";
+ memcpy(ack+13, echo, 4);
+ ack[17] = '\0';
+ Send(ack);
+ hadAck = true;
+}
+
+void AddonChannelCommandHandler::SendOK() // o Command OK, no body
+{
+ ASSERT(echo);
+ char ok[18] = "TrinityCore\to";
+ memcpy(ok+13, echo, 4);
+ ok[17] = '\0';
+ Send(ok);
+}
+
+void AddonChannelCommandHandler::SendFailed() // f Command failed, no body
+{
+ ASSERT(echo);
+ char fail[18] = "TrinityCore\tf";
+ memcpy(fail + 13, echo, 4);
+ fail[17] = '\0';
+ Send(fail);
+}
+
+// m Command message, message in body
+void AddonChannelCommandHandler::SendSysMessage(char const* str, bool escapeCharacters)
+{
+ ASSERT(echo);
+ if (!hadAck)
+ SendAck();
+
+ std::string msg = "TrinityCore\tm";
+ msg.append(echo, 4);
+ std::string body(str);
+ if (escapeCharacters)
+ boost::replace_all(body, "|", "||");
+ size_t pos, lastpos;
+ for (lastpos = 0, pos = body.find('\n', lastpos); pos != std::string::npos; lastpos = pos + 1, pos = body.find('\n', lastpos))
+ {
+ std::string line(msg);
+ line.append(body, lastpos, pos - lastpos);
+ Send(line);
+ }
+ msg.append(body, lastpos, pos - lastpos);
+ Send(msg);
+}
diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h
index f2ab3772a0d..8bbcf49c7a1 100644
--- a/src/server/game/Chat/Chat.h
+++ b/src/server/game/Chat/Chat.h
@@ -93,7 +93,8 @@ class TC_GAME_API ChatHandler
return Trinity::StringFormat(GetTrinityString(entry), std::forward<Args>(args)...);
}
- bool ParseCommands(char const* text);
+ bool _ParseCommands(char const* text);
+ virtual bool ParseCommands(char const* text);
static std::vector<ChatCommand> const& getCommandTable();
static void invalidateCommandTable();
@@ -105,6 +106,7 @@ class TC_GAME_API ChatHandler
// function with different implementation for chat/console
virtual bool isAvailable(ChatCommand const& cmd) const;
+ virtual bool IsHumanReadable() const { return true; }
virtual bool HasPermission(uint32 permission) const;
virtual std::string GetNameLink() const;
virtual bool needReportToTarget(Player* chr) const;
@@ -171,6 +173,7 @@ class TC_GAME_API CliHandler : public ChatHandler
bool isAvailable(ChatCommand const& cmd) const override;
bool HasPermission(uint32 /*permission*/) const override { return true; }
void SendSysMessage(const char *str, bool escapeCharacters) override;
+ bool ParseCommands(char const* str) override;
std::string GetNameLink() const override;
bool needReportToTarget(Player* chr) const override;
LocaleConstant GetSessionDbcLocale() const override;
@@ -181,4 +184,24 @@ class TC_GAME_API CliHandler : public ChatHandler
Print* m_print;
};
+class TC_GAME_API AddonChannelCommandHandler : public ChatHandler
+{
+ public:
+ using ChatHandler::ChatHandler;
+ bool ParseCommands(char const* str) override;
+ void SendSysMessage(char const* str, bool escapeCharacters) override;
+ using ChatHandler::SendSysMessage;
+ bool IsHumanReadable() const override { return humanReadable; }
+
+ private:
+ void Send(std::string const& msg);
+ void SendAck();
+ void SendOK();
+ void SendFailed();
+
+ char const* echo = nullptr;
+ bool hadAck = false;
+ bool humanReadable = false;
+};
+
#endif
diff --git a/src/server/game/Chat/ChatLink.cpp b/src/server/game/Chat/ChatLink.cpp
index db07e9e2efd..e7fd0b97703 100644
--- a/src/server/game/Chat/ChatLink.cpp
+++ b/src/server/game/Chat/ChatLink.cpp
@@ -153,7 +153,7 @@ bool ItemChatLink::Initialize(std::istringstream& iss)
return false;
}
}
- else if (id < 0)
+ else
{
_suffix = sItemRandomSuffixStore.LookupEntry(-id);
if (!_suffix)
diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h
index a6432d62216..21d975ccea1 100644
--- a/src/server/game/Combat/ThreatManager.h
+++ b/src/server/game/Combat/ThreatManager.h
@@ -20,11 +20,11 @@
#define _THREATMANAGER
#include "Common.h"
+#include "IteratorPair.h"
#include "SharedDefines.h"
#include "LinkedReference/Reference.h"
#include "UnitEvents.h"
#include "ObjectGuid.h"
-#include "Containers.h"
#include <list>
@@ -220,8 +220,8 @@ class TC_GAME_API ThreatManager
{
public:
// -- compatibility layer for combat rewrite (PR #19930)
- Trinity::Containers::IteratorPair<std::list<ThreatReference*>::const_iterator> GetSortedThreatList() const { auto& list = iThreatContainer.getThreatList(); return { list.cbegin(), list.cend() }; }
- Trinity::Containers::IteratorPair<std::list<ThreatReference*>::const_iterator> GetUnsortedThreatList() const { return GetSortedThreatList(); }
+ Trinity::IteratorPair<std::list<ThreatReference*>::const_iterator> GetSortedThreatList() const { auto& list = iThreatContainer.getThreatList(); return { list.cbegin(), list.cend() }; }
+ Trinity::IteratorPair<std::list<ThreatReference*>::const_iterator> GetUnsortedThreatList() const { return GetSortedThreatList(); }
std::list<ThreatReference*> GetModifiableThreatList() const { return iThreatContainer.getThreatList(); }
Unit* SelectVictim() { return getHostilTarget(); }
Unit* GetCurrentVictim() const { if (ThreatReference* ref = getCurrentVictim()) return ref->GetVictim(); else return nullptr; }
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index 146d2cb3bde..5b7e85d6d3e 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -1036,7 +1036,7 @@ void ConditionMgr::LoadConditions(bool isReload)
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 conditions. DB table `conditions` is empty!");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 conditions. DB table `conditions` is empty!");
return;
}
@@ -1677,7 +1677,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) const
}
break;
}
- case CONDITION_SOURCE_TYPE_QUEST_ACCEPT:
+ case CONDITION_SOURCE_TYPE_QUEST_AVAILABLE:
if (!sObjectMgr->GetQuestTemplate(cond->SourceEntry))
{
TC_LOG_ERROR("sql.sql", "%s SourceEntry specifies non-existing quest, skipped.", cond->ToString().c_str());
@@ -2011,7 +2011,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) const
}
if (cond->ConditionValue3)
{
- if (GameObjectData const* goData = sObjectMgr->GetGOData(cond->ConditionValue3))
+ if (GameObjectData const* goData = sObjectMgr->GetGameObjectData(cond->ConditionValue3))
{
if (cond->ConditionValue2 && goData->id != cond->ConditionValue2)
{
diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h
index ff3f096abae..4f6c98e87d9 100644
--- a/src/server/game/Conditions/ConditionMgr.h
+++ b/src/server/game/Conditions/ConditionMgr.h
@@ -133,7 +133,7 @@ enum ConditionSourceType
CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE = 16,
CONDITION_SOURCE_TYPE_SPELL = 17,
CONDITION_SOURCE_TYPE_SPELL_CLICK_EVENT = 18,
- CONDITION_SOURCE_TYPE_QUEST_ACCEPT = 19,
+ CONDITION_SOURCE_TYPE_QUEST_AVAILABLE = 19,
// Condition source type 20 unused
CONDITION_SOURCE_TYPE_VEHICLE_SPELL = 21,
CONDITION_SOURCE_TYPE_SMART_EVENT = 22,
diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h
index f7d3fe325dc..923ed40b93a 100644
--- a/src/server/game/DataStores/DBCEnums.h
+++ b/src/server/game/DataStores/DBCEnums.h
@@ -248,7 +248,7 @@ enum AreaFlags
AREA_FLAG_UNK0 = 0x00000001, // Unknown
AREA_FLAG_UNK1 = 0x00000002, // Razorfen Downs, Naxxramas and Acherus: The Ebon Hold (3.3.5a)
AREA_FLAG_UNK2 = 0x00000004, // Only used for areas on map 571 (development before)
- AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // city and city subsones
+ AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // city and city subzones
AREA_FLAG_UNK3 = 0x00000010, // can't find common meaning
AREA_FLAG_SLAVE_CAPITAL2 = 0x00000020, // slave capital city flag?
AREA_FLAG_ALLOW_DUELS = 0x00000040, // allow to duel here
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index e72408205c0..f41e9b4e205 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -239,7 +239,7 @@ inline void LoadDBC(uint32& availableDbcLocales, StoreProblemList& errors, DBCSt
localizedName.push_back('/');
localizedName.append(filename);
- if (!storage.LoadStringsFrom(localizedName.c_str()))
+ if (!storage.LoadStringsFrom(localizedName))
availableDbcLocales &= ~(1 << i); // mark as not available for speedup next checks
}
@@ -435,10 +435,10 @@ void LoadDBCStores(const std::string& dataPath)
ASSERT(Utf8toWStr(namesProfanity->Name, wname));
if (namesProfanity->Language != -1)
- NamesProfaneValidators[namesProfanity->Language].emplace_back(wname, Trinity::regex::icase | Trinity::regex::optimize);
+ NamesProfaneValidators[namesProfanity->Language].emplace_back(wname, Trinity::regex::perl | Trinity::regex::icase | Trinity::regex::optimize);
else
for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
- NamesProfaneValidators[i].emplace_back(wname, Trinity::regex::icase | Trinity::regex::optimize);
+ NamesProfaneValidators[i].emplace_back(wname, Trinity::regex::perl | Trinity::regex::icase | Trinity::regex::optimize);
}
for (NamesReservedEntry const* namesReserved : sNamesReservedStore)
@@ -448,10 +448,10 @@ void LoadDBCStores(const std::string& dataPath)
ASSERT(Utf8toWStr(namesReserved->Name, wname));
if (namesReserved->Language != -1)
- NamesReservedValidators[namesReserved->Language].emplace_back(wname, Trinity::regex::icase | Trinity::regex::optimize);
+ NamesReservedValidators[namesReserved->Language].emplace_back(wname, Trinity::regex::perl | Trinity::regex::icase | Trinity::regex::optimize);
else
for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
- NamesReservedValidators[i].emplace_back(wname, Trinity::regex::icase | Trinity::regex::optimize);
+ NamesReservedValidators[i].emplace_back(wname, Trinity::regex::perl | Trinity::regex::icase | Trinity::regex::optimize);
}
for (PvPDifficultyEntry const* entry : sPvPDifficultyStore)
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp
index 39d30a28c4f..344759a2022 100644
--- a/src/server/game/DungeonFinding/LFGMgr.cpp
+++ b/src/server/game/DungeonFinding/LFGMgr.cpp
@@ -129,7 +129,7 @@ void LFGMgr::LoadRewards()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 lfg dungeon rewards. DB table `lfg_dungeon_rewards` is empty!");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 lfg dungeon rewards. DB table `lfg_dungeon_rewards` is empty!");
return;
}
@@ -215,7 +215,7 @@ void LFGMgr::LoadLFGDungeons(bool reload /* = false */)
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 lfg entrance positions. DB table `lfg_dungeon_template` is empty!");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 lfg entrance positions. DB table `lfg_dungeon_template` is empty!");
return;
}
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index b11a5a8018c..27ea877ca08 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -233,14 +233,12 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
return true;
}
-Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(),
-m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0),
-m_lootRecipient(), m_lootRecipientGroup(0), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0),
-m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE),
-m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
-m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
-m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0), m_shouldReacquireTarget(false), m_suppressedOrientation(0.0f),
-_lastDamagedTime(0)
+Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipient(), m_lootRecipientGroup(0), _pickpocketLootRestore(0),
+ m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE),
+ m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_cannotReachTarget(false), m_cannotReachTimer(0),
+ m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0),
+ m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), m_focusSpell(nullptr), m_focusDelay(0), m_shouldReacquireTarget(false), m_suppressedOrientation(0.0f), _lastDamagedTime(0),
+ _regenerateHealth(true), _regenerateHealthLock(false)
{
m_regenTimer = CREATURE_REGEN_INTERVAL;
m_valuesCount = UNIT_END;
@@ -254,7 +252,6 @@ _lastDamagedTime(0)
m_CombatDistance = 0;//MELEE_RANGE;
ResetLootMode(); // restore default loot mode
- m_TriggerJustRespawned = false;
m_isTempWorldObject = false;
}
@@ -314,6 +311,14 @@ void Creature::DisappearAndDie()
ForcedDespawn(0);
}
+bool Creature::IsReturningHome() const
+{
+ if (GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == HOME_MOTION_TYPE)
+ return true;
+
+ return false;
+}
+
void Creature::SearchFormation()
{
if (IsSummon())
@@ -328,48 +333,96 @@ void Creature::SearchFormation()
sFormationMgr->AddCreatureToGroup(frmdata->second->leaderGUID, this);
}
+bool Creature::IsFormationLeader() const
+{
+ if (!m_formation)
+ return false;
+
+ return m_formation->IsLeader(this);
+}
+
+void Creature::SignalFormationMovement(Position const& destination, uint32 id/* = 0*/, uint32 moveType/* = 0*/, bool orientation/* = false*/)
+{
+ if (!m_formation)
+ return;
+
+ if (!m_formation->IsLeader(this))
+ return;
+
+ m_formation->LeaderMoveTo(destination, id, moveType, orientation);
+}
+
+bool Creature::IsFormationLeaderMoveAllowed() const
+{
+ if (!m_formation)
+ return false;
+
+ return m_formation->CanLeaderStartMoving();
+}
+
void Creature::RemoveCorpse(bool setSpawnTime, bool destroyForNearbyPlayers)
{
if (getDeathState() != CORPSE)
return;
- m_corpseRemoveTime = time(nullptr);
- setDeathState(DEAD);
- RemoveAllAuras();
- loot.clear();
- uint32 respawnDelay = m_respawnDelay;
- if (IsAIEnabled)
- AI()->CorpseRemoved(respawnDelay);
+ if (m_respawnCompatibilityMode)
+ {
+ m_corpseRemoveTime = time(nullptr);
+ setDeathState(DEAD);
+ RemoveAllAuras();
+ loot.clear();
+ uint32 respawnDelay = m_respawnDelay;
+ if (IsAIEnabled)
+ AI()->CorpseRemoved(respawnDelay);
- if (destroyForNearbyPlayers)
- DestroyForNearbyPlayers();
+ if (destroyForNearbyPlayers)
+ DestroyForNearbyPlayers();
- // Should get removed later, just keep "compatibility" with scripts
- if (setSpawnTime)
- m_respawnTime = std::max<time_t>(time(nullptr) + respawnDelay, m_respawnTime);
+ // Should get removed later, just keep "compatibility" with scripts
+ if (setSpawnTime)
+ m_respawnTime = std::max<time_t>(time(nullptr) + respawnDelay, m_respawnTime);
- // if corpse was removed during falling, the falling will continue and override relocation to respawn position
- if (IsFalling())
- StopMoving();
+ // if corpse was removed during falling, the falling will continue and override relocation to respawn position
+ if (IsFalling())
+ StopMoving();
- float x, y, z, o;
- GetRespawnPosition(x, y, z, &o);
+ float x, y, z, o;
+ GetRespawnPosition(x, y, z, &o);
- // We were spawned on transport, calculate real position
- if (IsSpawnedOnTransport())
- {
- Position& pos = m_movementInfo.transport.pos;
- pos.m_positionX = x;
- pos.m_positionY = y;
- pos.m_positionZ = z;
- pos.SetOrientation(o);
+ // We were spawned on transport, calculate real position
+ if (IsSpawnedOnTransport())
+ {
+ Position& pos = m_movementInfo.transport.pos;
+ pos.m_positionX = x;
+ pos.m_positionY = y;
+ pos.m_positionZ = z;
+ pos.SetOrientation(o);
+
+ if (TransportBase* transport = GetDirectTransport())
+ transport->CalculatePassengerPosition(x, y, z, &o);
+ }
- if (TransportBase* transport = GetDirectTransport())
- transport->CalculatePassengerPosition(x, y, z, &o);
+ SetHomePosition(x, y, z, o);
+ GetMap()->CreatureRelocation(this, x, y, z, o);
}
+ else
+ {
+ // In case this is called directly and normal respawn timer not set
+ // Since this timer will be longer than the already present time it
+ // will be ignored if the correct place added a respawn timer
+ if (setSpawnTime)
+ {
+ uint32 respawnDelay = m_respawnDelay;
+ m_respawnTime = std::max<time_t>(time(NULL) + respawnDelay, m_respawnTime);
+
+ SaveRespawnTime(0, false);
+ }
- SetHomePosition(x, y, z, o);
- GetMap()->CreatureRelocation(this, x, y, z, o);
+ if (TempSummon* summon = ToTempSummon())
+ summon->UnSummon();
+ else
+ AddObjectToRemoveList();
+ }
}
/**
@@ -478,7 +531,7 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/,
CreatureTemplate const* cInfo = GetCreatureTemplate();
- m_regenHealth = cInfo->RegenHealth;
+ _regenerateHealth = cInfo->RegenHealth;
// creatures always have melee weapon ready if any unless specified otherwise
if (!GetCreatureAddon())
@@ -561,12 +614,12 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/,
void Creature::Update(uint32 diff)
{
- if (IsAIEnabled && m_TriggerJustRespawned)
+ if (IsAIEnabled && m_triggerJustAppeared && m_deathState == ALIVE)
{
- m_TriggerJustRespawned = false;
- AI()->JustRespawned();
- if (m_vehicleKit)
+ if (m_respawnCompatibilityMode && m_vehicleKit)
m_vehicleKit->Reset();
+ m_triggerJustAppeared = false;
+ AI()->JustAppeared();
}
UpdateMovementFlags();
@@ -579,30 +632,34 @@ void Creature::Update(uint32 diff)
break;
case JUST_DIED:
// Must not be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting.
- TC_LOG_ERROR("entities.unit", "Creature (GUID: %u Entry: %u) in wrong state: JUST_DEAD (1)", GetGUID().GetCounter(), GetEntry());
+ TC_LOG_ERROR("entities.unit", "Creature (GUID: %u Entry: %u) in wrong state: JUST_DIED (1)", GetGUID().GetCounter(), GetEntry());
break;
case DEAD:
{
time_t now = time(nullptr);
if (m_respawnTime <= now)
{
+
// First check if there are any scripts that object to us respawning
- if (!sScriptMgr->CanSpawn(GetSpawnId(), GetEntry(), GetCreatureTemplate(), GetCreatureData(), GetMap()))
- break; // Will be rechecked on next Update call
+ if (!sScriptMgr->CanSpawn(GetSpawnId(), GetEntry(), GetCreatureData(), GetMap()))
+ {
+ m_respawnTime = now + urand(4,7);
+ break; // Will be rechecked on next Update call after delay expires
+ }
ObjectGuid dbtableHighGuid(HighGuid::Unit, GetEntry(), m_spawnId);
- time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
- if (!linkedRespawntime) // Can respawn
+ time_t linkedRespawnTime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
+ if (!linkedRespawnTime) // Can respawn
Respawn();
else // the master is dead
{
ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
- if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
- SetRespawnTime(DAY);
+ if (targetGuid == dbtableHighGuid) // if linking self, never respawn
+ SetRespawnTime(WEEK);
else
{
// else copy time from master and add a little
- time_t baseRespawnTime = std::max(linkedRespawntime, now);
+ 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
@@ -742,7 +799,7 @@ void Creature::Update(uint32 diff)
if (m_regenTimer == 0)
{
- bool bInCombat = IsInCombat() && (!GetVictim() || // if IsInCombat() is true and this has no victim
+ bool bInCombat = IsInCombat() && (!GetVictim() || // if IsInCombat() is true and this has no victim
!EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() || // or the victim/owner/charmer is not a player
!EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()->IsGameMaster()); // or the victim/owner/charmer is not a GameMaster
@@ -830,7 +887,7 @@ void Creature::Regenerate(Powers power)
void Creature::RegenerateHealth()
{
- if (!isRegeneratingHealth())
+ if (!CanRegenerateHealth())
return;
uint32 curValue = GetHealth();
@@ -947,12 +1004,16 @@ void Creature::Motion_Initialize()
GetMotionMaster()->Initialize();
}
-bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, float x, float y, float z, float ang, CreatureData const* data /*= nullptr*/, uint32 vehId /*= 0*/)
+bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, Position const& pos, CreatureData const* data /*= nullptr*/, uint32 vehId /*= 0*/, bool dynamic)
{
ASSERT(map);
SetMap(map);
SetPhaseMask(phaseMask, false);
+ // Set if this creature can handle dynamic spawns
+ if (!dynamic)
+ SetRespawnCompatibilityMode();
+
CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry);
if (!cinfo)
{
@@ -962,15 +1023,16 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
//! Relocate before CreateFromProto, to initialize coords and allow
//! returning correct zone id for selecting OutdoorPvP/Battlefield script
- Relocate(x, y, z, ang);
+ Relocate(pos);
// Check if the position is valid before calling CreateFromProto(), otherwise we might add Auras to Creatures at
// invalid position, triggering a crash about Auras not removed in the destructor
if (!IsPositionValid())
{
- TC_LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow %d, entry %d) are not valid (X: %f, Y: %f, Z: %f, O: %f)", guidlow, entry, x, y, z, ang);
+ TC_LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow %d, entry %d) are not valid (X: %f, Y: %f, Z: %f, O: %f)", guidlow, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation());
return false;
}
+ UpdatePositionData();
// Allow players to see those units while dead, do it here (mayby altered by addon auras)
if (cinfo->type_flags & CREATURE_TYPE_FLAG_GHOST_VISIBLE)
@@ -1004,10 +1066,8 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
if (HasUnitMovementFlag(MOVEMENTFLAG_HOVER))
{
- z += GetFloatValue(UNIT_FIELD_HOVERHEIGHT);
-
//! Relocate again with updated Z coord
- Relocate(x, y, z, ang);
+ m_positionZ += GetFloatValue(UNIT_FIELD_HOVERHEIGHT);
}
LastUsedScriptID = GetScriptId();
@@ -1187,27 +1247,17 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
dynamicflags = 0;
}
- // data->guid = guid must not be updated at save
+ if (!data.spawnId)
+ data.spawnId = m_spawnId;
+ ASSERT(data.spawnId == m_spawnId);
data.id = GetEntry();
- data.mapid = mapid;
data.phaseMask = phaseMask;
data.displayid = displayId;
data.equipmentId = GetCurrentEquipmentId();
if (!GetTransport())
- {
- data.posX = GetPositionX();
- data.posY = GetPositionY();
- data.posZ = GetPositionZMinusOffset();
- data.orientation = GetOrientation();
- }
+ data.spawnPoint.WorldRelocate(this);
else
- {
- data.posX = GetTransOffsetX();
- data.posY = GetTransOffsetY();
- data.posZ = GetTransOffsetZ();
- data.orientation = GetTransOffsetO();
- }
-
+ data.spawnPoint.WorldRelocate(mapid, GetTransOffsetX(), GetTransOffsetY(), GetTransOffsetZ(), GetTransOffsetO());
data.spawntimesecs = m_respawnDelay;
// prevent add data integrity problems
data.spawndist = GetDefaultMovementType() == IDLE_MOTION_TYPE ? 0.0f : m_respawnradius;
@@ -1221,6 +1271,8 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
data.npcflag = npcflag;
data.unit_flags = unit_flags;
data.dynamicflags = dynamicflags;
+ if (!data.spawnGroupData)
+ data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup();
// update in DB
SQLTransaction trans = WorldDatabase.BeginTransaction();
@@ -1425,7 +1477,7 @@ bool Creature::CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, Creatu
return true;
}
-bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate)
+bool Creature::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate)
{
if (!allowDuplicate)
{
@@ -1466,31 +1518,41 @@ bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool ad
}
m_spawnId = spawnId;
+
+ m_respawnCompatibilityMode = ((data->spawnGroupData->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE) != 0);
m_creatureData = data;
m_respawnradius = data->spawndist;
m_respawnDelay = data->spawntimesecs;
- if (!Create(map->GenerateLowGuid<HighGuid::Unit>(), map, data->phaseMask, data->id, data->posX, data->posY, data->posZ, data->orientation, data))
+
+ // Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update)
+ if (!m_respawnCompatibilityMode && !m_respawnTime && !sScriptMgr->CanSpawn(spawnId, data->id, data, map))
+ {
+ SaveRespawnTime(urand(4,7));
+ return false;
+ }
+
+ if (!Create(map->GenerateLowGuid<HighGuid::Unit>(), map, data->phaseMask, data->id, data->spawnPoint, data, 0U , !m_respawnCompatibilityMode))
return false;
//We should set first home position, because then AI calls home movement
- SetHomePosition(data->posX, data->posY, data->posZ, data->orientation);
+ SetHomePosition(data->spawnPoint);
m_deathState = ALIVE;
m_respawnTime = GetMap()->GetCreatureRespawnTime(m_spawnId);
- // Is the creature script objecting to us spawning? If yes, delay by one second (then re-check in ::Update)
- if (!m_respawnTime && !sScriptMgr->CanSpawn(spawnId, GetEntry(), GetCreatureTemplate(), GetCreatureData(), map))
- m_respawnTime = time(nullptr)+1;
+ // Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update)
+ if (m_respawnCompatibilityMode && !m_respawnTime && !sScriptMgr->CanSpawn(spawnId, GetEntry(), GetCreatureData(), map))
+ m_respawnTime = time(nullptr)+urand(4,7);
if (m_respawnTime) // respawn on Update
{
m_deathState = DEAD;
if (CanFly())
{
- float tz = map->GetHeight(GetPhaseMask(), data->posX, data->posY, data->posZ, true, MAX_FALL_DISTANCE);
- if (data->posZ - tz > 0.1f && Trinity::IsValidMapCoord(tz))
- Relocate(data->posX, data->posY, tz);
+ float tz = map->GetHeight(GetPhaseMask(), data->spawnPoint, true, MAX_FALL_DISTANCE);
+ if (data->spawnPoint.GetPositionZ() - tz > 0.1f && Trinity::IsValidMapCoord(tz))
+ Relocate(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY(), tz);
}
}
@@ -1535,8 +1597,11 @@ void Creature::LoadEquipment(int8 id, bool force /*= true*/)
void Creature::SetSpawnHealth()
{
+ if (_regenerateHealthLock)
+ return;
+
uint32 curhealth;
- if (m_creatureData && !m_regenHealth)
+ if (m_creatureData && !_regenerateHealth)
{
curhealth = m_creatureData->curhealth;
if (curhealth)
@@ -1586,15 +1651,24 @@ void Creature::DeleteFromDB()
return;
}
- GetMap()->RemoveCreatureRespawnTime(m_spawnId);
+ // remove any scheduled respawns
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId);
+
+ // delete data from memory
sObjectMgr->DeleteCreatureData(m_spawnId);
+ // delete data and all its associations from DB
SQLTransaction trans = WorldDatabase.BeginTransaction();
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE);
stmt->setUInt32(0, m_spawnId);
trans->Append(stmt);
+ stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_SPAWNGROUP_MEMBER);
+ stmt->setUInt8(0, uint8(SPAWN_TYPE_CREATURE));
+ stmt->setUInt32(1, m_spawnId);
+ trans->Append(stmt);
+
stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE_ADDON);
stmt->setUInt32(0, m_spawnId);
trans->Append(stmt);
@@ -1608,6 +1682,11 @@ void Creature::DeleteFromDB()
trans->Append(stmt);
WorldDatabase.CommitTransaction(trans);
+
+ // then delete any active instances of the creature
+ auto const& spawnMap = GetMap()->GetCreatureBySpawnIdStore();
+ for (auto it = spawnMap.find(m_spawnId); it != spawnMap.end(); it = spawnMap.find(m_spawnId))
+ it->second->AddObjectToRemoveList();
}
bool Creature::IsInvisibleDueToDespawn() const
@@ -1734,14 +1813,31 @@ void Creature::setDeathState(DeathState s)
if (s == JUST_DIED)
{
m_corpseRemoveTime = time(nullptr) + m_corpseDelay;
- if (IsDungeonBoss() && !m_respawnDelay)
- m_respawnTime = std::numeric_limits<time_t>::max(); // never respawn in this instance
+
+ uint32 respawnDelay = m_respawnDelay;
+ if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE))
+ GetMap()->ApplyDynamicModeRespawnScaling(this, m_spawnId, respawnDelay, scalingMode);
+ // @todo remove the boss respawn time hack in a dynspawn follow-up once we have creature groups in instances
+ if (m_respawnCompatibilityMode)
+ {
+ if (IsDungeonBoss() && !m_respawnDelay)
+ m_respawnTime = std::numeric_limits<time_t>::max(); // never respawn in this instance
+ else
+ m_respawnTime = time(nullptr) + respawnDelay + m_corpseDelay;
+ }
else
- m_respawnTime = time(nullptr) + m_respawnDelay + m_corpseDelay;
+ {
+ if (IsDungeonBoss() && !m_respawnDelay)
+ m_respawnTime = std::numeric_limits<time_t>::max(); // never respawn in this instance
+ else
+ m_respawnTime = time(nullptr) + respawnDelay;
+ }
// always save boss respawn time at death to prevent crash cheating
if (sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY) || isWorldBoss())
SaveRespawnTime();
+ else if (!m_respawnCompatibilityMode)
+ SaveRespawnTime(0, false);
ReleaseFocus(nullptr, false); // remove spellcast focus
DoNotReacquireTarget(); // cancel delayed re-target
@@ -1809,8 +1905,6 @@ void Creature::setDeathState(DeathState s)
void Creature::Respawn(bool force)
{
- DestroyForNearbyPlayers();
-
if (force)
{
if (IsAlive())
@@ -1819,51 +1913,59 @@ void Creature::Respawn(bool force)
setDeathState(CORPSE);
}
- RemoveCorpse(false, false);
-
- if (getDeathState() == DEAD)
+ if (m_respawnCompatibilityMode)
{
- if (m_spawnId)
- GetMap()->RemoveCreatureRespawnTime(m_spawnId);
+ DestroyForNearbyPlayers();
+ RemoveCorpse(false, false);
- TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)",
- GetName().c_str(), GetGUID().ToString().c_str());
- m_respawnTime = 0;
- ResetPickPocketRefillTimer();
- loot.clear();
+ if (getDeathState() == DEAD)
+ {
+ if (m_spawnId)
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId);
- if (m_originalEntry != GetEntry())
- UpdateEntry(m_originalEntry);
+ TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)", GetName().c_str(), GetGUID().ToString().c_str());
+ m_respawnTime = 0;
+ ResetPickPocketRefillTimer();
+ loot.clear();
- SelectLevel();
+ if (m_originalEntry != GetEntry())
+ UpdateEntry(m_originalEntry);
- setDeathState(JUST_RESPAWNED);
+ SelectLevel();
- uint32 displayID = GetNativeDisplayId();
- if (sObjectMgr->GetCreatureModelRandomGender(&displayID))
- {
- SetDisplayId(displayID);
- SetNativeDisplayId(displayID);
- }
+ setDeathState(JUST_RESPAWNED);
- GetMotionMaster()->InitDefault();
- //Re-initialize reactstate that could be altered by movementgenerators
- InitializeReactState();
+ uint32 displayID = GetNativeDisplayId();
+ if (sObjectMgr->GetCreatureModelRandomGender(&displayID))
+ {
+ SetDisplayId(displayID);
+ SetNativeDisplayId(displayID);
+ }
- //Call AI respawn virtual function
- if (IsAIEnabled)
- {
- //reset the AI to be sure no dirty or uninitialized values will be used till next tick
- AI()->Reset();
- m_TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
- }
+ GetMotionMaster()->InitDefault();
+ //Re-initialize reactstate that could be altered by movementgenerators
+ InitializeReactState();
- uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<Creature>(GetSpawnId()) : 0;
- if (poolid)
- sPoolMgr->UpdatePool<Creature>(poolid, GetSpawnId());
+ if (IsAIEnabled) // reset the AI to be sure no dirty or uninitialized values will be used till next tick
+ AI()->Reset();
+
+ m_triggerJustAppeared = true;
+
+ uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<Creature>(GetSpawnId()) : 0;
+ if (poolid)
+ sPoolMgr->UpdatePool<Creature>(poolid, GetSpawnId());
+ }
+ UpdateObjectVisibility();
+ }
+ else
+ {
+ if (m_spawnId)
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId, true);
}
- UpdateObjectVisibility();
+ TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)",
+ GetName().c_str(), GetGUID().ToString().c_str());
+
}
void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds const& forceRespawnTimer)
@@ -1874,30 +1976,48 @@ void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds const& forceRespawn
return;
}
- uint32 corpseDelay = GetCorpseDelay();
- uint32 respawnDelay = GetRespawnDelay();
+ if (m_respawnCompatibilityMode)
+ {
+ uint32 corpseDelay = GetCorpseDelay();
+ uint32 respawnDelay = GetRespawnDelay();
+
+ // do it before killing creature
+ DestroyForNearbyPlayers();
- // do it before killing creature
- DestroyForNearbyPlayers();
+ bool overrideRespawnTime = false;
+ if (IsAlive())
+ {
+ if (forceRespawnTimer > Seconds::zero())
+ {
+ SetCorpseDelay(0);
+ SetRespawnDelay(forceRespawnTimer.count());
+ overrideRespawnTime = true;
+ }
- bool overrideRespawnTime = true;
- if (IsAlive())
+ setDeathState(JUST_DIED);
+ }
+
+ // Skip corpse decay time
+ RemoveCorpse(!overrideRespawnTime, false);
+
+ SetCorpseDelay(corpseDelay);
+ SetRespawnDelay(respawnDelay);
+ }
+ else
{
if (forceRespawnTimer > Seconds::zero())
+ SaveRespawnTime(forceRespawnTimer.count());
+ else
{
- SetCorpseDelay(0);
- SetRespawnDelay(forceRespawnTimer.count());
- overrideRespawnTime = false;
+ uint32 respawnDelay = m_respawnDelay;
+ if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE))
+ GetMap()->ApplyDynamicModeRespawnScaling(this, m_spawnId, respawnDelay, scalingMode);
+ m_respawnTime = time(NULL) + respawnDelay;
+ SaveRespawnTime();
}
- setDeathState(JUST_DIED);
+ AddObjectToRemoveList();
}
-
- // Skip corpse decay time
- RemoveCorpse(overrideRespawnTime, false);
-
- SetCorpseDelay(corpseDelay);
- SetRespawnDelay(respawnDelay);
}
void Creature::DespawnOrUnsummon(uint32 msTimeToDespawn /*= 0*/, Seconds const& forceRespawnTimer /*= 0*/)
@@ -2156,9 +2276,6 @@ void Creature::CallForHelp(float radius)
bool Creature::CanAssistTo(Unit const* u, Unit const* enemy, bool checkfaction /*= true*/) const
{
- if (IsInEvadeMode())
- return false;
-
// is it true?
if (!HasReactState(REACT_AGGRESSIVE))
return false;
@@ -2244,12 +2361,19 @@ bool Creature::_IsTargetAcceptable(Unit const* target) const
return false;
}
-void Creature::SaveRespawnTime()
+void Creature::SaveRespawnTime(uint32 forceDelay, bool savetodb)
{
if (IsSummon() || !m_spawnId || (m_creatureData && !m_creatureData->dbData))
return;
- GetMap()->SaveCreatureRespawnTime(m_spawnId, m_respawnTime);
+ if (m_respawnCompatibilityMode)
+ {
+ GetMap()->SaveRespawnTimeDB(SPAWN_TYPE_CREATURE, m_spawnId, m_respawnTime);
+ return;
+ }
+
+ time_t thisRespawnTime = forceDelay ? time(NULL) + forceDelay : m_respawnTime;
+ GetMap()->SaveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId, GetEntry(), thisRespawnTime, GetMap()->GetZoneId(GetHomePosition()), Trinity::ComputeGridCoord(GetHomePosition().GetPositionX(), GetHomePosition().GetPositionY()).GetId(), savetodb && m_creatureData && m_creatureData->dbData);
}
// this should not be called by petAI or
@@ -2365,9 +2489,9 @@ bool Creature::LoadCreaturesAddon()
if (cainfo->emote != 0)
SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote);
- //Load Path
+ // Load Path
if (cainfo->path_id != 0)
- m_path_id = cainfo->path_id;
+ _waypointPathId = cainfo->path_id;
if (!cainfo->auras.empty())
{
@@ -2461,33 +2585,26 @@ time_t Creature::GetRespawnTimeEx() const
void Creature::GetRespawnPosition(float &x, float &y, float &z, float* ori, float* dist) const
{
- if (m_spawnId)
+ if (m_creatureData)
{
- // for npcs on transport, this will return transport offset
- if (CreatureData const* data = sObjectMgr->GetCreatureData(GetSpawnId()))
- {
- x = data->posX;
- y = data->posY;
- z = data->posZ;
- if (ori)
- *ori = data->orientation;
- if (dist)
- *dist = data->spawndist;
+ if (ori)
+ m_creatureData->spawnPoint.GetPosition(x, y, z, *ori);
+ else
+ m_creatureData->spawnPoint.GetPosition(x, y, z);
- return;
- }
+ if (dist)
+ *dist = m_creatureData->spawndist;
+ }
+ else
+ {
+ Position const& homePos = GetHomePosition();
+ if (ori)
+ homePos.GetPosition(x, y, z, *ori);
+ else
+ homePos.GetPosition(x, y, z);
+ if (dist)
+ *dist = 0;
}
-
- // changed this from current position to home position, fixes world summons with infinite duration (wg npcs for example)
- Position homePos = GetHomePosition();
- x = homePos.GetPositionX();
- y = homePos.GetPositionY();
- z = homePos.GetPositionZ();
- if (ori)
- *ori = homePos.GetOrientation();
-
- if (dist)
- *dist = 0;
}
void Creature::AllLootRemovedFromCorpse()
@@ -2538,7 +2655,7 @@ std::string Creature::GetScriptName() const
uint32 Creature::GetScriptId() const
{
if (CreatureData const* creatureData = GetCreatureData())
- if (uint32 scriptId = creatureData->ScriptId)
+ if (uint32 scriptId = creatureData->scriptId)
return scriptId;
return sObjectMgr->GetCreatureTemplate(GetEntry())->ScriptID;
@@ -3081,3 +3198,11 @@ bool Creature::CanGiveExperience() const
&& !IsTotem()
&& !(GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL);
}
+
+bool Creature::IsEscortNPC(bool onlyIfActive)
+{
+ if (!IsAIEnabled)
+ return false;
+
+ return AI()->IsEscortNPC(onlyIfActive);
+}
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index cc12efbd7eb..4a7c3362f30 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -35,6 +35,7 @@ class Quest;
class Player;
class SpellInfo;
class WorldSession;
+
enum MovementGeneratorType : uint8;
struct VendorItemCount
@@ -72,7 +73,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void DisappearAndDie();
- bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, float x, float y, float z, float ang, CreatureData const* data = nullptr, uint32 vehId = 0);
+ bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, Position const& pos, CreatureData const* data = nullptr, uint32 vehId = 0, bool dynamic = false);
bool LoadCreaturesAddon();
void SelectLevel();
void UpdateLevelDependantStats();
@@ -83,7 +84,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void Update(uint32 time) override; // overwrited Unit::Update
void GetRespawnPosition(float &x, float &y, float &z, float* ori = nullptr, float* dist = nullptr) const;
- bool IsSpawnedOnTransport() const { return m_creatureData && m_creatureData->mapid != GetMapId(); }
+ bool IsSpawnedOnTransport() const { return m_creatureData && m_creatureData->spawnPoint.GetMapId() != GetMapId(); }
void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; }
uint32 GetCorpseDelay() const { return m_corpseDelay; }
@@ -176,8 +177,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void setDeathState(DeathState s) override; // override virtual Unit::setDeathState
- bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map) { return LoadCreatureFromDB(spawnId, map, false); }
- bool LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true, bool allowDuplicate = false);
+ bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate);
void SaveToDB();
// overriden in Pet
virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask);
@@ -239,7 +239,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
time_t GetRespawnTimeEx() const;
void SetRespawnTime(uint32 respawn) { m_respawnTime = respawn ? time(nullptr) + respawn : 0; }
void Respawn(bool force = false);
- void SaveRespawnTime() override;
+ void SaveRespawnTime(uint32 forceDelay = 0, bool savetodb = true) override;
uint32 GetRespawnDelay() const { return m_respawnDelay; }
void SetRespawnDelay(uint32 delay) { m_respawnDelay = delay; }
@@ -266,8 +266,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool hasQuest(uint32 quest_id) const override;
bool hasInvolvedQuest(uint32 quest_id) const override;
- bool isRegeneratingHealth() { return m_regenHealth; }
- void setRegeneratingHealth(bool regenHealth) { m_regenHealth = regenHealth; }
+ bool CanRegenerateHealth() { return !_regenerateHealthLock && _regenerateHealth; }
+ void SetRegenerateHealth(bool value) { _regenerateHealthLock = !value; }
virtual uint8 GetPetAutoSpellSize() const { return MAX_SPELL_CHARM; }
virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const;
float GetPetChaseDistance() const;
@@ -291,15 +291,21 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void GetTransportHomePosition(float& x, float& y, float& z, float& ori) const { m_transportHomePosition.GetPosition(x, y, z, ori); }
Position const& GetTransportHomePosition() const { return m_transportHomePosition; }
- uint32 GetWaypointPath() const { return m_path_id; }
- void LoadPath(uint32 pathid) { m_path_id = pathid; }
+ uint32 GetWaypointPath() const { return _waypointPathId; }
+ void LoadPath(uint32 pathid) { _waypointPathId = pathid; }
+
+ // nodeId, pathId
+ std::pair<uint32, uint32> GetCurrentWaypointInfo() const { return _currentWaypointNodeInfo; }
+ void UpdateCurrentWaypointInfo(uint32 nodeId, uint32 pathId) { _currentWaypointNodeInfo = { nodeId, pathId }; }
- uint32 GetCurrentWaypointID() const { return m_waypointID; }
- void UpdateWaypointID(uint32 wpID) { m_waypointID = wpID; }
+ bool IsReturningHome() const;
void SearchFormation();
CreatureGroup* GetFormation() { return m_formation; }
void SetFormation(CreatureGroup* formation) { m_formation = formation; }
+ bool IsFormationLeader() const;
+ void SignalFormationMovement(Position const& destination, uint32 id = 0, uint32 moveType = 0, bool orientation = false);
+ bool IsFormationLeaderMoveAllowed() const;
Unit* SelectVictim();
@@ -313,6 +319,10 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
uint32 GetOriginalEntry() const { return m_originalEntry; }
void SetOriginalEntry(uint32 entry) { m_originalEntry = entry; }
+ // There's many places not ready for dynamic spawns. This allows them to live on for now.
+ void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
+ bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; }
+
static float _GetDamageMod(int32 Rank);
float m_SightDistance, m_CombatDistance;
@@ -336,6 +346,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
CreatureTextRepeatIds GetTextRepeatGroup(uint8 textGroup);
void SetTextRepeatId(uint8 textGroup, uint8 id);
void ClearTextRepeatGroup(uint8 textGroup);
+ bool IsEscortNPC(bool onlyIfActive = true);
bool CanGiveExperience() const;
@@ -372,7 +383,6 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool m_AlreadyCallAssistance;
bool m_AlreadySearchedAssistance;
- bool m_regenHealth;
bool m_cannotReachTarget;
uint32 m_cannotReachTimer;
bool m_AI_locked;
@@ -397,13 +407,14 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void ForcedDespawn(uint32 timeMSToDespawn = 0, Seconds const& forceRespawnTimer = Seconds(0));
bool CheckNoGrayAggroConfig(uint32 playerLevel, uint32 creatureLevel) const; // No aggro from gray creatures
- //WaypointMovementGenerator vars
- uint32 m_waypointID;
- uint32 m_path_id;
+ // Waypoint path
+ uint32 _waypointPathId;
+ std::pair<uint32/*nodeId*/, uint32/*pathId*/> _currentWaypointNodeInfo;
//Formation var
CreatureGroup* m_formation;
- bool m_TriggerJustRespawned;
+ bool m_triggerJustAppeared;
+ bool m_respawnCompatibilityMode;
/* Spell focus system */
Spell const* m_focusSpell; // Locks the target during spell cast for proper facing
@@ -414,6 +425,10 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
time_t _lastDamagedTime; // Part of Evade mechanics
CreatureTextRepeatGroup m_textRepeat;
+
+ // Regenerate health
+ bool _regenerateHealth; // Set on creation
+ bool _regenerateHealthLock; // Dynamically set
};
class TC_GAME_API AssistDelayEvent : public BasicEvent
diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h
index 754963f9812..de169c97136 100644
--- a/src/server/game/Entities/Creature/CreatureData.h
+++ b/src/server/game/Entities/Creature/CreatureData.h
@@ -20,6 +20,7 @@
#include "DBCEnums.h"
#include "SharedDefines.h"
+#include "SpawnData.h"
#include "UnitDefines.h"
#include "WorldPacket.h"
#include <string>
@@ -235,33 +236,19 @@ struct EquipmentInfo
};
// from `creature` table
-struct CreatureData
+struct CreatureData : public SpawnData
{
- CreatureData() : id(0), mapid(0), phaseMask(0), displayid(0), equipmentId(0),
- posX(0.0f), posY(0.0f), posZ(0.0f), orientation(0.0f), spawntimesecs(0),
- spawndist(0.0f), currentwaypoint(0), curhealth(0), curmana(0), movementType(0),
- spawnMask(0), npcflag(0), unit_flags(0), dynamicflags(0), ScriptId(0), dbData(true) { }
- uint32 id; // entry in creature_template
- uint16 mapid;
- uint32 phaseMask;
- uint32 displayid;
- int8 equipmentId;
- float posX;
- float posY;
- float posZ;
- float orientation;
- uint32 spawntimesecs;
- float spawndist;
- uint32 currentwaypoint;
- uint32 curhealth;
- uint32 curmana;
- uint8 movementType;
- uint8 spawnMask;
- uint32 npcflag;
- uint32 unit_flags; // enum UnitFlags mask values
- uint32 dynamicflags;
- uint32 ScriptId;
- bool dbData;
+ CreatureData() : SpawnData(SPAWN_TYPE_CREATURE) { }
+ uint32 displayid = 0;
+ int8 equipmentId = 0;
+ float spawndist = 0.0f;
+ uint32 currentwaypoint = 0;
+ uint32 curhealth = 0;
+ uint32 curmana = 0;
+ uint8 movementType = 0;
+ uint32 npcflag = 0;
+ uint32 unit_flags = 0;
+ uint32 dynamicflags = 0;
};
struct CreatureModelInfo
diff --git a/src/server/game/Entities/Creature/CreatureGroups.cpp b/src/server/game/Entities/Creature/CreatureGroups.cpp
index 3984a81cf71..4297a745ccc 100644
--- a/src/server/game/Entities/Creature/CreatureGroups.cpp
+++ b/src/server/game/Entities/Creature/CreatureGroups.cpp
@@ -93,7 +93,7 @@ void FormationMgr::LoadCreatureFormations()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 creatures in formations. DB table `creature_formations` is empty!");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 creatures in formations. DB table `creature_formations` is empty!");
return;
}
@@ -236,7 +236,7 @@ void CreatureGroup::LeaderMoveTo(Position const& destination, uint32 id /*= 0*/,
continue;
if (itr->second->point_1)
- if (m_leader->GetCurrentWaypointID() == itr->second->point_1 - 1 || m_leader->GetCurrentWaypointID() == itr->second->point_2 - 1)
+ if (m_leader->GetCurrentWaypointInfo().first == itr->second->point_1 || m_leader->GetCurrentWaypointInfo().first == itr->second->point_2)
itr->second->follow_angle = float(M_PI) * 2 - itr->second->follow_angle;
float angle = itr->second->follow_angle;
@@ -258,3 +258,17 @@ void CreatureGroup::LeaderMoveTo(Position const& destination, uint32 id /*= 0*/,
member->SetHomePosition(dx, dy, dz, pathangle);
}
}
+
+bool CreatureGroup::CanLeaderStartMoving() const
+{
+ for (auto itr = m_members.begin(); itr != m_members.end(); ++itr)
+ {
+ if (itr->first != m_leader && itr->first->IsAlive())
+ {
+ if (itr->first->IsEngaged() || itr->first->IsReturningHome())
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/server/game/Entities/Creature/CreatureGroups.h b/src/server/game/Entities/Creature/CreatureGroups.h
index b36144db42f..52eb4eddf2a 100644
--- a/src/server/game/Entities/Creature/CreatureGroups.h
+++ b/src/server/game/Entities/Creature/CreatureGroups.h
@@ -84,6 +84,7 @@ class TC_GAME_API CreatureGroup
uint32 GetId() const { return m_groupID; }
bool isEmpty() const { return m_members.empty(); }
bool isFormed() const { return m_Formed; }
+ bool IsLeader(Creature const* creature) const { return m_leader == creature; }
void AddMember(Creature* member);
void RemoveMember(Creature* member);
@@ -91,6 +92,7 @@ class TC_GAME_API CreatureGroup
void LeaderMoveTo(Position const& destination, uint32 id = 0, uint32 moveType = 0, bool orientation = false);
void MemberEngagingTarget(Creature* member, Unit* target);
+ bool CanLeaderStartMoving() const;
};
#define sFormationMgr FormationMgr::instance()
diff --git a/src/server/game/Entities/Creature/GossipDef.cpp b/src/server/game/Entities/Creature/GossipDef.cpp
index caa700f6672..4a39c233fc2 100644
--- a/src/server/game/Entities/Creature/GossipDef.cpp
+++ b/src/server/game/Entities/Creature/GossipDef.cpp
@@ -323,16 +323,33 @@ void QuestMenu::ClearMenu()
_questMenuItems.clear();
}
-void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string& Title, ObjectGuid npcGUID)
+void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string& Title, ObjectGuid guid)
{
WorldPacket data(SMSG_QUESTGIVER_QUEST_LIST, 100); // guess size
- data << uint64(npcGUID);
- data << Title;
- data << uint32(eEmote._Delay); // player emote
- data << uint32(eEmote._Emote); // NPC emote
+ data << uint64(guid);
+
+ if (QuestGreeting const* questGreeting = sObjectMgr->GetQuestGreeting(guid))
+ {
+ std::string strGreeting = questGreeting->greeting;
+
+ LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
+ if (localeConstant != LOCALE_enUS)
+ if (QuestGreetingLocale const* questGreetingLocale = sObjectMgr->GetQuestGreetingLocale(MAKE_PAIR32(guid.GetEntry(), guid.GetTypeId())))
+ ObjectMgr::GetLocaleString(questGreetingLocale->greeting, localeConstant, strGreeting);
+
+ data << strGreeting;
+ data << uint32(questGreeting->greetEmoteDelay);
+ data << uint32(questGreeting->greetEmoteType);
+ }
+ else
+ {
+ data << Title;
+ data << uint32(eEmote._Delay); // player emote
+ data << uint32(eEmote._Emote); // NPC emote
+ }
size_t count_pos = data.wpos();
- data << uint8 (0);
+ data << uint8(0);
uint32 count = 0;
// Store this instead of checking the Singleton every loop iteration
@@ -340,9 +357,9 @@ void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string
for (uint32 i = 0; i < _questMenu.GetMenuItemCount(); ++i)
{
- QuestMenuItem const& qmi = _questMenu.GetItem(i);
+ QuestMenuItem const& questMenuItem = _questMenu.GetItem(i);
- uint32 questID = qmi.QuestId;
+ uint32 questID = questMenuItem.QuestId;
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questID))
{
@@ -351,14 +368,14 @@ void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string
LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
if (localeConstant != LOCALE_enUS)
- if (QuestLocale const* localeData = sObjectMgr->GetQuestLocale(questID))
- ObjectMgr::GetLocaleString(localeData->Title, localeConstant, title);
+ if (QuestLocale const* questTemplateLocaleData = sObjectMgr->GetQuestLocale(questID))
+ ObjectMgr::GetLocaleString(questTemplateLocaleData->Title, localeConstant, title);
if (questLevelInTitle)
Quest::AddQuestLevelToTitle(title, quest->GetQuestLevel());
data << uint32(questID);
- data << uint32(qmi.QuestIcon);
+ data << uint32(questMenuItem.QuestIcon);
data << int32(quest->GetQuestLevel());
data << uint32(quest->GetFlags()); // 3.3.3 quest flags
data << uint8(0); // 3.3.3 changes icon: blue question or yellow exclamation
@@ -368,7 +385,8 @@ void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string
data.put<uint8>(count_pos, count);
_session->SendPacket(&data);
- TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_LIST NPC=%s", npcGUID.ToString().c_str());
+
+ TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_LIST (QuestGiver: %s)", guid.ToString().c_str());
}
void PlayerMenu::SendQuestGiverStatus(uint8 questStatus, ObjectGuid npcGUID) const
diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp
index 7e9cf6bf7a3..70a7e3446d4 100644
--- a/src/server/game/Entities/Creature/TemporarySummon.cpp
+++ b/src/server/game/Entities/Creature/TemporarySummon.cpp
@@ -57,6 +57,7 @@ void TempSummon::Update(uint32 diff)
switch (m_type)
{
case TEMPSUMMON_MANUAL_DESPAWN:
+ case TEMPSUMMON_DEAD_DESPAWN:
break;
case TEMPSUMMON_TIMED_DESPAWN:
{
@@ -104,7 +105,7 @@ void TempSummon::Update(uint32 diff)
case TEMPSUMMON_CORPSE_DESPAWN:
{
// if m_deathState is DEAD, CORPSE was skipped
- if (m_deathState == CORPSE || m_deathState == DEAD)
+ if (m_deathState == CORPSE)
{
UnSummon();
return;
@@ -112,19 +113,9 @@ void TempSummon::Update(uint32 diff)
break;
}
- case TEMPSUMMON_DEAD_DESPAWN:
- {
- if (m_deathState == DEAD)
- {
- UnSummon();
- return;
- }
- break;
- }
case TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN:
{
- // if m_deathState is DEAD, CORPSE was skipped
- if (m_deathState == CORPSE || m_deathState == DEAD)
+ if (m_deathState == CORPSE)
{
UnSummon();
return;
@@ -146,13 +137,6 @@ void TempSummon::Update(uint32 diff)
}
case TEMPSUMMON_TIMED_OR_DEAD_DESPAWN:
{
- // if m_deathState is DEAD, CORPSE was skipped
- if (m_deathState == DEAD)
- {
- UnSummon();
- return;
- }
-
if (!IsInCombat() && IsAlive())
{
if (m_timer <= diff)
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index be0406be5b0..ce9658ef37e 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -101,7 +101,7 @@ QuaternionData QuaternionData::fromEulerAnglesZYX(float Z, float Y, float X)
}
GameObject::GameObject() : WorldObject(false), MapObject(),
- m_model(nullptr), m_goValue(), m_AI(nullptr)
+ m_model(nullptr), m_goValue(), m_AI(nullptr), m_respawnCompatibilityMode(false)
{
m_objectType |= TYPEMASK_GAMEOBJECT;
m_objectTypeId = TYPEID_GAMEOBJECT;
@@ -240,7 +240,7 @@ void GameObject::RemoveFromWorld()
}
}
-bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit /*= 0*/)
+bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit /*= 0*/, bool dynamic, ObjectGuid::LowType spawnid)
{
ASSERT(map);
SetMap(map);
@@ -253,7 +253,12 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
return false;
}
+ // Set if this object can handle dynamic spawns
+ if (!dynamic)
+ SetRespawnCompatibilityMode();
+
SetPhaseMask(phaseMask, false);
+ UpdatePositionData();
SetZoneScript();
if (m_zoneScript)
@@ -375,6 +380,9 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
if (map->Is25ManRaid())
loot.maxDuplicates = 3;
+ if (spawnid)
+ m_spawnId = spawnid;
+
if (uint32 linkedEntry = GetGOInfo()->GetLinkedGameObjectEntry())
{
GameObject* linkedGO = new GameObject();
@@ -486,83 +494,90 @@ void GameObject::Update(uint32 diff)
}
case GO_READY:
{
- if (m_respawnTime > 0) // timer on
+ if (m_respawnCompatibilityMode)
{
- time_t now = time(nullptr);
- if (m_respawnTime <= now) // timer expired
+ if (m_respawnTime > 0) // timer on
{
- ObjectGuid dbtableHighGuid(HighGuid::GameObject, GetEntry(), m_spawnId);
- time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
- if (linkedRespawntime) // Can't respawn, the master is dead
+ time_t now = time(nullptr);
+ if (m_respawnTime <= now) // timer expired
{
- ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
- 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
- SaveRespawnTime(); // also save to DB immediately
- return;
- }
+ ObjectGuid dbtableHighGuid(HighGuid::GameObject, GetEntry(), m_spawnId);
+ time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
+ if (linkedRespawntime) // Can't respawn, the master is dead
+ {
+ ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
+ if (targetGuid == dbtableHighGuid) // if linking self, never respawn
+ SetRespawnTime(WEEK);
+ else
+ m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
+ SaveRespawnTime(); // also save to DB immediately
+ return;
+ }
- m_respawnTime = 0;
- m_SkillupList.clear();
- m_usetimes = 0;
+ m_respawnTime = 0;
+ m_SkillupList.clear();
+ m_usetimes = 0;
- // If nearby linked trap exists, respawn it
- if (GameObject* linkedTrap = GetLinkedTrap())
- linkedTrap->SetLootState(GO_READY);
+ // If nearby linked trap exists, respawn it
+ if (GameObject* linkedTrap = GetLinkedTrap())
+ linkedTrap->SetLootState(GO_READY);
- switch (GetGoType())
- {
- case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
+ switch (GetGoType())
{
- Unit* caster = GetOwner();
- if (caster && caster->GetTypeId() == TYPEID_PLAYER)
+ case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
{
- caster->ToPlayer()->RemoveGameObject(this, false);
-
- WorldPacket data(SMSG_FISH_ESCAPED, 0);
- caster->ToPlayer()->SendDirectMessage(&data);
+ Unit* caster = GetOwner();
+ if (caster && caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ caster->ToPlayer()->RemoveGameObject(this, false);
+
+ WorldPacket data(SMSG_FISH_ESCAPED, 0);
+ caster->ToPlayer()->SendDirectMessage(&data);
+ }
+ // can be delete
+ m_lootState = GO_JUST_DEACTIVATED;
+ return;
}
- // can be delete
- m_lootState = GO_JUST_DEACTIVATED;
- return;
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ // We need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds)
+ if (GetGoState() != GO_STATE_READY)
+ ResetDoorOrButton();
+ break;
+ case GAMEOBJECT_TYPE_FISHINGHOLE:
+ // Initialize a new max fish count on respawn
+ m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishinghole.minSuccessOpens, GetGOInfo()->fishinghole.maxSuccessOpens);
+ break;
+ default:
+ break;
}
- case GAMEOBJECT_TYPE_DOOR:
- case GAMEOBJECT_TYPE_BUTTON:
- // We need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds)
- if (GetGoState() != GO_STATE_READY)
- ResetDoorOrButton();
- break;
- case GAMEOBJECT_TYPE_FISHINGHOLE:
- // Initialize a new max fish count on respawn
- m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishinghole.minSuccessOpens, GetGOInfo()->fishinghole.maxSuccessOpens);
- break;
- default:
- break;
- }
- // Despawn timer
- if (!m_spawnedByDefault)
- {
- // Can be despawned or destroyed
- SetLootState(GO_JUST_DEACTIVATED);
- return;
- }
+ // Despawn timer
+ if (!m_spawnedByDefault)
+ {
+ // Can be despawned or destroyed
+ SetLootState(GO_JUST_DEACTIVATED);
+ return;
+ }
- // Call AI Reset (required for example in SmartAI to clear one time events)
- if (AI())
- AI()->Reset();
+ // Call AI Reset (required for example in SmartAI to clear one time events)
+ if (AI())
+ AI()->Reset();
- // Respawn timer
- uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<GameObject>(GetSpawnId()) : 0;
- if (poolid)
- sPoolMgr->UpdatePool<GameObject>(poolid, GetSpawnId());
- else
- GetMap()->AddToMap(this);
+ // Respawn timer
+ uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<GameObject>(GetSpawnId()) : 0;
+ if (poolid)
+ sPoolMgr->UpdatePool<GameObject>(poolid, GetSpawnId());
+ else
+ GetMap()->AddToMap(this);
+ }
}
}
+ // Set respawn timer
+ if (!m_respawnCompatibilityMode && m_respawnTime > 0)
+ SaveRespawnTime(0, false);
+
if (isSpawned())
{
GameObjectTemplate const* goInfo = GetGOInfo();
@@ -750,6 +765,7 @@ void GameObject::Update(uint32 diff)
if (!m_respawnDelayTime)
return;
+ // ToDo: Decide if we should properly despawn these. Maybe they expect to be able to manually respawn from script?
if (!m_spawnedByDefault)
{
m_respawnTime = 0;
@@ -757,12 +773,28 @@ void GameObject::Update(uint32 diff)
return;
}
- m_respawnTime = time(nullptr) + m_respawnDelayTime;
+ uint32 respawnDelay = m_respawnDelayTime;
+ if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE))
+ GetMap()->ApplyDynamicModeRespawnScaling(this, this->m_spawnId, respawnDelay, scalingMode);
+ m_respawnTime = time(nullptr) + respawnDelay;
// if option not set then object will be saved at grid unload
+ // Otherwise just save respawn time to map object memory
if (sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY))
SaveRespawnTime();
+ if (!m_respawnCompatibilityMode)
+ {
+ // Respawn time was just saved if set to save to DB
+ // If not, we save only to map memory
+ if (!sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY))
+ SaveRespawnTime(0, false);
+
+ // Then despawn
+ AddObjectToRemoveList();
+ return;
+ }
+
DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
break;
}
@@ -848,7 +880,7 @@ void GameObject::SaveToDB()
{
// this should only be used when the gameobject has already been loaded
// preferably after adding to map, because mapid may not be valid otherwise
- GameObjectData const* data = sObjectMgr->GetGOData(m_spawnId);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(m_spawnId);
if (!data)
{
TC_LOG_ERROR("misc", "GameObject::SaveToDB failed, cannot get gameobject data!");
@@ -868,22 +900,22 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
m_spawnId = sObjectMgr->GenerateGameObjectSpawnId();
// update in loaded data (changing data only in this place)
- GameObjectData& data = sObjectMgr->NewGOData(m_spawnId);
+ GameObjectData& data = sObjectMgr->NewOrExistGameObjectData(m_spawnId);
- // data->guid = guid must not be updated at save
+ if (!data.spawnId)
+ data.spawnId = m_spawnId;
+ ASSERT(data.spawnId == m_spawnId);
data.id = GetEntry();
- data.mapid = mapid;
+ data.spawnPoint.WorldRelocate(this);
data.phaseMask = phaseMask;
- data.posX = GetPositionX();
- data.posY = GetPositionY();
- data.posZ = GetPositionZ();
- data.orientation = GetOrientation();
data.rotation = m_worldRotation;
data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
data.animprogress = GetGoAnimProgress();
- data.go_state = GetGoState();
+ data.goState = GetGoState();
data.spawnMask = spawnMask;
data.artKit = GetGoArtKit();
+ if (!data.spawnGroupData)
+ data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup();
// Update in DB
SQLTransaction trans = WorldDatabase.BeginTransaction();
@@ -916,9 +948,9 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
WorldDatabase.CommitTransaction(trans);
}
-bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap)
+bool GameObject::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool)
{
- GameObjectData const* data = sObjectMgr->GetGOData(spawnId);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
if (!data)
{
@@ -929,14 +961,14 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo
uint32 entry = data->id;
//uint32 map_id = data->mapid; // already used before call
uint32 phaseMask = data->phaseMask;
- Position pos(data->posX, data->posY, data->posZ, data->orientation);
uint32 animprogress = data->animprogress;
- GOState go_state = data->go_state;
+ GOState go_state = data->goState;
uint32 artKit = data->artKit;
m_spawnId = spawnId;
- if (!Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, phaseMask, pos, data->rotation, animprogress, go_state, artKit))
+ m_respawnCompatibilityMode = ((data->spawnGroupData->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE) != 0);
+ if (!Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, phaseMask, data->spawnPoint, data->rotation, animprogress, go_state, artKit, !m_respawnCompatibilityMode))
return false;
if (data->spawntimesecs >= 0)
@@ -958,7 +990,7 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo
if (m_respawnTime && m_respawnTime <= time(nullptr))
{
m_respawnTime = 0;
- GetMap()->RemoveGORespawnTime(m_spawnId);
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId);
}
}
}
@@ -979,20 +1011,25 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo
void GameObject::DeleteFromDB()
{
- GetMap()->RemoveGORespawnTime(m_spawnId);
- sObjectMgr->DeleteGOData(m_spawnId);
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId);
+ sObjectMgr->DeleteGameObjectData(m_spawnId);
- PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
+ SQLTransaction trans = WorldDatabase.BeginTransaction();
+ PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
stmt->setUInt32(0, m_spawnId);
+ trans->Append(stmt);
- WorldDatabase.Execute(stmt);
+ stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_SPAWNGROUP_MEMBER);
+ stmt->setUInt8(0, uint8(SPAWN_TYPE_GAMEOBJECT));
+ stmt->setUInt32(1, m_spawnId);
+ trans->Append(stmt);
stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_EVENT_GAMEOBJECT);
-
stmt->setUInt32(0, m_spawnId);
+ trans->Append(stmt);
- WorldDatabase.Execute(stmt);
+ WorldDatabase.CommitTransaction(trans);
}
/*********************************************************/
@@ -1055,10 +1092,19 @@ Unit* GameObject::GetOwner() const
return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
}
-void GameObject::SaveRespawnTime()
+void GameObject::SaveRespawnTime(uint32 forceDelay, bool savetodb)
{
- if (m_goData && m_goData->dbData && m_respawnTime > time(nullptr) && m_spawnedByDefault)
- GetMap()->SaveGORespawnTime(m_spawnId, m_respawnTime);
+ if (m_goData && m_respawnTime > time(nullptr) && m_spawnedByDefault)
+ {
+ if (m_respawnCompatibilityMode)
+ {
+ GetMap()->SaveRespawnTimeDB(SPAWN_TYPE_GAMEOBJECT, m_spawnId, m_respawnTime);
+ return;
+ }
+
+ uint32 thisRespawnTime = forceDelay ? time(nullptr) + forceDelay : m_respawnTime;
+ GetMap()->SaveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId, GetEntry(), thisRespawnTime, GetZoneId(), Trinity::ComputeGridCoord(GetPositionX(), GetPositionY()).GetId(), m_goData->dbData ? savetodb : false);
+ }
}
bool GameObject::IsNeverVisible() const
@@ -1123,7 +1169,7 @@ void GameObject::Respawn()
if (m_spawnedByDefault && m_respawnTime > 0)
{
m_respawnTime = time(nullptr);
- GetMap()->RemoveGORespawnTime(m_spawnId);
+ GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId, true);
}
}
@@ -1227,7 +1273,7 @@ void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = f
void GameObject::SetGoArtKit(uint8 kit)
{
SetByteValue(GAMEOBJECT_BYTES_1, 2, kit);
- GameObjectData* data = const_cast<GameObjectData*>(sObjectMgr->GetGOData(m_spawnId));
+ GameObjectData* data = const_cast<GameObjectData*>(sObjectMgr->GetGameObjectData(m_spawnId));
if (data)
data->artKit = kit;
}
@@ -1238,10 +1284,10 @@ void GameObject::SetGoArtKit(uint8 artkit, GameObject* go, ObjectGuid::LowType l
if (go)
{
go->SetGoArtKit(artkit);
- data = go->GetGOData();
+ data = go->GetGameObjectData();
}
else if (lowguid)
- data = sObjectMgr->GetGOData(lowguid);
+ data = sObjectMgr->GetGameObjectData(lowguid);
if (data)
const_cast<GameObjectData*>(data)->artKit = artkit;
@@ -1971,8 +2017,8 @@ void GameObject::EventInform(uint32 eventId, WorldObject* invoker /*= nullptr*/)
uint32 GameObject::GetScriptId() const
{
- if (GameObjectData const* gameObjectData = GetGOData())
- if (uint32 scriptId = gameObjectData->ScriptId)
+ if (GameObjectData const* gameObjectData = GetGameObjectData())
+ if (uint32 scriptId = gameObjectData->scriptId)
return scriptId;
return GetGOInfo()->ScriptId;
@@ -2410,24 +2456,20 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t
void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = nullptr*/) const
{
- if (m_spawnId)
+ if (m_goData)
{
- if (GameObjectData const* data = sObjectMgr->GetGOData(GetSpawnId()))
- {
- x = data->posX;
- y = data->posY;
- z = data->posZ;
- if (ori)
- *ori = data->orientation;
- return;
- }
+ if (ori)
+ m_goData->spawnPoint.GetPosition(x, y, z, *ori);
+ else
+ m_goData->spawnPoint.GetPosition(x, y, z);
+ }
+ else
+ {
+ if (ori)
+ GetPosition(x, y, z, *ori);
+ else
+ GetPosition(x, y, z);
}
-
- x = GetPositionX();
- y = GetPositionY();
- z = GetPositionZ();
- if (ori)
- *ori = GetOrientation();
}
float GameObject::GetInteractionDistance() const
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 3080bd94860..784af8fda27 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -89,11 +89,11 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void RemoveFromWorld() override;
void CleanupsBeforeDelete(bool finalCleanup = true) override;
- bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0);
+ bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0, bool dynamic = false, ObjectGuid::LowType spawnid = 0);
void Update(uint32 p_time) override;
GameObjectTemplate const* GetGOInfo() const { return m_goInfo; }
GameObjectTemplateAddon const* GetTemplateAddon() const { return m_goTemplateAddon; }
- GameObjectData const* GetGOData() const { return m_goData; }
+ GameObjectData const* GetGameObjectData() const { return m_goData; }
GameObjectValue const* GetGOValue() const { return &m_goValue; }
bool IsTransport() const;
@@ -113,8 +113,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void SaveToDB();
void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask);
- bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map) { return LoadGameObjectFromDB(spawnId, map, false); }
- bool LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true);
+ bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool = true); // arg4 is unused, only present to match the signature on Creature
void DeleteFromDB();
void SetOwnerGUID(ObjectGuid owner)
@@ -212,7 +211,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
uint32 GetUseCount() const { return m_usetimes; }
uint32 GetUniqueUseCount() const { return m_unique_users.size(); }
- void SaveRespawnTime() override;
+ void SaveRespawnTime(uint32 forceDelay = 0, bool savetodb = true) override;
Loot loot;
@@ -264,6 +263,10 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void EventInform(uint32 eventId, WorldObject* invoker = nullptr);
+ // There's many places not ready for dynamic spawns. This allows them to live on for now.
+ void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
+ bool GetRespawnCompatibilityMode() {return m_respawnCompatibilityMode; }
+
uint32 GetScriptId() const;
GameObjectAI* AI() const { return m_AI; }
@@ -344,5 +347,6 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
return IsInRange(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), dist2compare);
}
GameObjectAI* m_AI;
+ bool m_respawnCompatibilityMode;
};
#endif
diff --git a/src/server/game/Entities/GameObject/GameObjectData.h b/src/server/game/Entities/GameObject/GameObjectData.h
index e2d2f27e5b5..6e1a07786f9 100644
--- a/src/server/game/Entities/GameObject/GameObjectData.h
+++ b/src/server/game/Entities/GameObject/GameObjectData.h
@@ -20,6 +20,7 @@
#include "Common.h"
#include "SharedDefines.h"
+#include "SpawnData.h"
#include "WorldPacket.h"
#include <string>
#include <vector>
@@ -593,26 +594,14 @@ struct GameObjectAddon
uint32 InvisibilityValue;
};
-// from `gameobject`
-struct GameObjectData
+// `gameobject` table
+struct GameObjectData : public SpawnData
{
- explicit GameObjectData() : id(0), mapid(0), phaseMask(0), posX(0.0f), posY(0.0f), posZ(0.0f), orientation(0.0f), spawntimesecs(0),
- animprogress(0), go_state(GO_STATE_ACTIVE), spawnMask(0), artKit(0), ScriptId(0), dbData(true) { }
- uint32 id; // entry in gamobject_template
- uint16 mapid;
- uint32 phaseMask;
- float posX;
- float posY;
- float posZ;
- float orientation;
+ GameObjectData() : SpawnData(SPAWN_TYPE_GAMEOBJECT) { }
QuaternionData rotation;
- int32 spawntimesecs;
- uint32 animprogress;
- GOState go_state;
- uint8 spawnMask;
- uint8 artKit;
- uint32 ScriptId;
- bool dbData;
+ uint32 animprogress = 0;
+ GOState goState = GO_STATE_ACTIVE;
+ uint8 artKit = 0;
};
#endif // GameObjectData_h__
diff --git a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp
index 50e0805797c..1468d168ffa 100644
--- a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp
+++ b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp
@@ -76,7 +76,7 @@ void LoadRandomEnchantmentsTable()
TC_LOG_INFO("server.loading", ">> Loaded %u Item Enchantment definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 Item Enchantment definitions. DB table `item_enchantment_template` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 Item Enchantment definitions. DB table `item_enchantment_template` is empty.");
}
uint32 GetItemEnchantMod(int32 entry)
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 508a89a86c8..9e2546f2df7 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1105,7 +1105,7 @@ void WorldObject::RemoveFromWorld()
Object::RemoveFromWorld();
}
-InstanceScript* WorldObject::GetInstanceScript()
+InstanceScript* WorldObject::GetInstanceScript() const
{
Map* map = GetMap();
return map->IsDungeon() ? ((InstanceMap*)map)->GetInstanceScript() : nullptr;
@@ -1908,7 +1908,7 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropert
break;
}
- if (!summon->Create(GenerateLowGuid<HighGuid::Unit>(), this, phase, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), nullptr, vehId))
+ if (!summon->Create(GenerateLowGuid<HighGuid::Unit>(), this, phase, entry, pos, nullptr, vehId, true))
{
delete summon;
return nullptr;
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index ca1b547f68b..b6cee547126 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -291,7 +291,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
uint32 GetAreaId() const { return m_areaId; }
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const { zoneid = m_zoneId, areaid = m_areaId; }
- InstanceScript* GetInstanceScript();
+ InstanceScript* GetInstanceScript() const;
std::string const& GetName() const { return m_name; }
void SetName(std::string const& newname) { m_name = newname; }
@@ -342,7 +342,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
void SendObjectDeSpawnAnim(ObjectGuid guid);
- virtual void SaveRespawnTime() { }
+ virtual void SaveRespawnTime(uint32 /*forceDelay*/ = 0, bool /*saveToDB*/ = true) { }
void AddObjectToRemoveList();
float GetGridActivationRange() const;
diff --git a/src/server/game/Entities/Object/ObjectGuid.cpp b/src/server/game/Entities/Object/ObjectGuid.cpp
index 3a26dfd0d7f..71837474ca9 100644
--- a/src/server/game/Entities/Object/ObjectGuid.cpp
+++ b/src/server/game/Entities/Object/ObjectGuid.cpp
@@ -98,6 +98,14 @@ void ObjectGuidGeneratorBase::HandleCounterOverflow(HighGuid high)
World::StopNow(ERROR_EXIT_CODE);
}
+void ObjectGuidGeneratorBase::CheckGuidTrigger(ObjectGuid::LowType guidlow)
+{
+ if (!sWorld->IsGuidAlert() && guidlow > sWorld->getIntConfig(CONFIG_RESPAWN_GUIDALERTLEVEL))
+ sWorld->TriggerGuidAlert();
+ else if (!sWorld->IsGuidWarning() && guidlow > sWorld->getIntConfig(CONFIG_RESPAWN_GUIDWARNLEVEL))
+ sWorld->TriggerGuidWarning();
+}
+
#define GUID_TRAIT_INSTANTIATE_GUID( HIGH_GUID ) \
template class TC_GAME_API ObjectGuidGenerator< HIGH_GUID >;
diff --git a/src/server/game/Entities/Object/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h
index 75410aa4f28..d1e34d3a38c 100644
--- a/src/server/game/Entities/Object/ObjectGuid.h
+++ b/src/server/game/Entities/Object/ObjectGuid.h
@@ -287,6 +287,7 @@ public:
protected:
static void HandleCounterOverflow(HighGuid high);
+ static void CheckGuidTrigger(ObjectGuid::LowType guid);
ObjectGuid::LowType _nextGuid;
};
@@ -300,6 +301,10 @@ public:
{
if (_nextGuid >= ObjectGuid::GetMaxCounter(high) - 1)
HandleCounterOverflow(high);
+
+ if (high == HighGuid::Unit || high == HighGuid::GameObject)
+ CheckGuidTrigger(_nextGuid);
+
return _nextGuid++;
}
};
diff --git a/src/server/game/Entities/Object/Position.h b/src/server/game/Entities/Object/Position.h
index 98cb82df2e1..11867c6fed6 100644
--- a/src/server/game/Entities/Object/Position.h
+++ b/src/server/game/Entities/Object/Position.h
@@ -207,15 +207,9 @@ public:
return GetExactDist2dSq(pos) < dist * dist;
}
- bool IsInDist(float x, float y, float z, float dist) const
- {
- return GetExactDistSq(x, y, z) < dist * dist;
- }
-
- bool IsInDist(Position const* pos, float dist) const
- {
- return GetExactDistSq(pos) < dist * dist;
- }
+ bool IsInDist(float x, float y, float z, float dist) const { return GetExactDistSq(x, y, z) < dist * dist; }
+ bool IsInDist(Position const& pos, float dist) const { return GetExactDistSq(pos) < dist * dist; }
+ bool IsInDist(Position const* pos, float dist) const { return GetExactDistSq(pos) < dist * dist; }
bool IsWithinBox(Position const& center, float xradius, float yradius, float zradius) const;
@@ -245,15 +239,12 @@ class WorldLocation : public Position
WorldLocation(WorldLocation const& loc)
: Position(loc), m_mapId(loc.GetMapId()) { }
- void WorldRelocate(WorldLocation const& loc)
- {
- m_mapId = loc.GetMapId();
- Relocate(loc);
- }
-
- void WorldRelocate(uint32 _mapId = MAPID_INVALID, float x = 0.f, float y = 0.f, float z = 0.f, float o = 0.f)
+ void WorldRelocate(WorldLocation const& loc) { m_mapId = loc.GetMapId(); Relocate(loc); }
+ void WorldRelocate(WorldLocation const* loc) { m_mapId = loc->GetMapId(); Relocate(loc); }
+ void WorldRelocate(uint32 mapId, Position const& pos) { m_mapId = mapId; Relocate(pos); }
+ void WorldRelocate(uint32 mapId = MAPID_INVALID, float x = 0.f, float y = 0.f, float z = 0.f, float o = 0.f)
{
- m_mapId = _mapId;
+ m_mapId = mapId;
Relocate(x, y, z, o);
}
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index 20f9e57b666..6890113178a 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -1306,24 +1306,22 @@ bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpel
if (itr != m_spells.end())
{
if (itr->second.state == PETSPELL_REMOVED)
- {
- m_spells.erase(itr);
state = PETSPELL_CHANGED;
- }
- else if (state == PETSPELL_UNCHANGED && itr->second.state != PETSPELL_UNCHANGED)
+ else
{
- // can be in case spell loading but learned at some previous spell loading
- itr->second.state = PETSPELL_UNCHANGED;
+ if (state == PETSPELL_UNCHANGED && itr->second.state != PETSPELL_UNCHANGED)
+ {
+ // can be in case spell loading but learned at some previous spell loading
+ itr->second.state = PETSPELL_UNCHANGED;
- if (active == ACT_ENABLED)
- ToggleAutocast(spellInfo, true);
- else if (active == ACT_DISABLED)
- ToggleAutocast(spellInfo, false);
+ if (active == ACT_ENABLED)
+ ToggleAutocast(spellInfo, true);
+ else if (active == ACT_DISABLED)
+ ToggleAutocast(spellInfo, false);
+ }
return false;
}
- else
- return false;
}
PetSpell newspell;
@@ -1520,7 +1518,7 @@ bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
}
// if remove last rank or non-ranked then update action bar at server and client if need
- if (m_charmInfo->RemoveSpellFromActionBar(spell_id) && !learn_prev && clear_ab)
+ if (clear_ab && !learn_prev && m_charmInfo->RemoveSpellFromActionBar(spell_id))
{
if (!m_loading)
GetOwner()->PetSpellInitialize(); // need update action bar for last removed rank
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 032c02f2288..7bf4e185efd 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -176,6 +176,7 @@ Player::Player(WorldSession* session): Unit(true)
m_session = session;
m_ingametime = 0;
+ m_sharedQuestId = 0;
m_ExtraFlags = 0;
@@ -490,6 +491,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
}
SetMap(sMapMgr->CreateMap(info->mapId, this));
+ UpdatePositionData();
uint8 powertype = cEntry->powerType;
@@ -6444,6 +6446,12 @@ void Player::CheckAreaExploreAndOutdoor()
XP = uint32(sObjectMgr->GetBaseXP(areaEntry->area_level)*sWorld->getRate(RATE_XP_EXPLORE));
}
+ if (sWorld->getIntConfig(CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO))
+ {
+ uint32 minScaledXP = uint32(sObjectMgr->GetBaseXP(areaEntry->area_level)*sWorld->getRate(RATE_XP_EXPLORE)) * sWorld->getIntConfig(CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO) / 100;
+ XP = std::max(minScaledXP, XP);
+ }
+
GiveXP(XP, nullptr);
SendExplorationExperience(areaId, XP);
}
@@ -6993,24 +7001,25 @@ void Player::UpdateArea(uint32 newArea)
void Player::UpdateZone(uint32 newZone, uint32 newArea)
{
- if (m_zoneUpdateId != newZone)
+ if (!IsInWorld())
+ return;
+ uint32 const oldZone = m_zoneUpdateId;
+ m_zoneUpdateId = newZone;
+ m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
+
+ GetMap()->UpdatePlayerZoneStats(oldZone, newZone);
+
+ // call leave script hooks immedately (before updating flags)
+ if (oldZone != newZone)
{
sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
- sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone);
sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
- sBattlefieldMgr->HandlePlayerEnterZone(this, newZone);
- SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange...
- if (Guild* guild = GetGuild())
- guild->UpdateMemberData(this, GUILD_MEMBER_DATA_ZONEID, newZone);
}
// group update
if (GetGroup())
SetGroupUpdateFlag(GROUP_UPDATE_FULL);
- m_zoneUpdateId = newZone;
- m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
-
// zone changed, so area changed as well, update it
UpdateArea(newArea);
@@ -7027,8 +7036,6 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
WeatherMgr::SendFineWeatherUpdateToPlayer(this);
}
- sScriptMgr->OnPlayerUpdateZone(this, newZone, newArea);
-
// in PvP, any not controlled zone (except zone->team == 6, default case)
// in PvE, only opposition team capital
switch (zone->team)
@@ -7075,6 +7082,17 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
UpdateLocalChannels(newZone);
UpdateZoneDependentAuras(newZone);
+
+ // call enter script hooks after everyting else has processed
+ sScriptMgr->OnPlayerUpdateZone(this, newZone, newArea);
+ if (oldZone != newZone)
+ {
+ sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone);
+ sBattlefieldMgr->HandlePlayerEnterZone(this, newZone);
+ SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange...
+ if (Guild* guild = GetGuild())
+ guild->UpdateMemberData(this, GUILD_MEMBER_DATA_ZONEID, newZone);
+ }
}
//If players are too far away from the duel flag... they lose the duel
@@ -11194,6 +11212,9 @@ InventoryResult Player::CanEquipItem(uint8 slot, uint16 &dest, Item* pItem, bool
if (HasUnitState(UNIT_STATE_STUNNED))
return EQUIP_ERR_YOU_ARE_STUNNED;
+ if (IsCharmed())
+ return EQUIP_ERR_CANT_DO_RIGHT_NOW; // @todo is this the correct error?
+
// do not allow equipping gear except weapons, offhands, projectiles, relics in
// - combat
// - in-progress arenas
@@ -11351,6 +11372,9 @@ InventoryResult Player::CanUnequipItem(uint16 pos, bool swap) const
if (pItem->m_lootGenerated)
return EQUIP_ERR_ALREADY_LOOTED;
+ if (IsCharmed())
+ return EQUIP_ERR_CANT_DO_RIGHT_NOW; // @todo is this the correct error?
+
// do not allow unequipping gear except weapons, offhands, projectiles, relics in
// - combat
// - in-progress arenas
@@ -11775,14 +11799,11 @@ void Player::SetAmmo(uint32 item)
return;
// check ammo
- if (item)
+ InventoryResult msg = CanUseAmmo(item);
+ if (msg != EQUIP_ERR_OK)
{
- InventoryResult msg = CanUseAmmo(item);
- if (msg != EQUIP_ERR_OK)
- {
- SendEquipError(msg, nullptr, nullptr, item);
- return;
- }
+ SendEquipError(msg, nullptr, nullptr, item);
+ return;
}
SetUInt32Value(PLAYER_AMMO_ID, item);
@@ -13984,7 +14005,7 @@ void Player::SendItemDurations()
}
}
-void Player::SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast)
+void Player::SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast, bool sendChatMessage)
{
if (!item) // prevent crash
return;
@@ -13994,7 +14015,7 @@ void Player::SendNewItem(Item* item, uint32 count, bool received, bool created,
data << uint64(GetGUID()); // player GUID
data << uint32(received); // 0=looted, 1=from npc
data << uint32(created); // 0=received, 1=created
- data << uint32(1); // bool print error to chat
+ data << uint32(sendChatMessage); // bool print message to chat
data << uint8(item->GetBagSlot()); // bagslot
// item slot, but when added to stack: 0xFFFFFFFF
data << uint32((item->GetCount() == count) ? item->GetSlot() : -1);
@@ -15009,7 +15030,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, quest->RewardItemIdCount[i]) == EQUIP_ERR_OK)
{
Item* item = StoreNewItem(dest, itemId, true, GenerateItemRandomPropertyId(itemId));
- SendNewItem(item, quest->RewardItemIdCount[i], true, false);
+ SendNewItem(item, quest->RewardItemIdCount[i], true, false, false, false);
}
else if (quest->IsDFQuest())
SendItemRetrievalMail(itemId, quest->RewardItemIdCount[i]);
@@ -15025,7 +15046,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, quest->RewardChoiceItemCount[reward]) == EQUIP_ERR_OK)
{
Item* item = StoreNewItem(dest, itemId, true, GenerateItemRandomPropertyId(itemId));
- SendNewItem(item, quest->RewardChoiceItemCount[reward], true, false);
+ SendNewItem(item, quest->RewardChoiceItemCount[reward], true, false, false, false);
}
}
}
@@ -15502,7 +15523,7 @@ bool Player::SatisfyQuestStatus(Quest const* qInfo, bool msg) const
bool Player::SatisfyQuestConditions(Quest const* qInfo, bool msg)
{
- if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_ACCEPT, qInfo->GetQuestId(), this))
+ if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_AVAILABLE, qInfo->GetQuestId(), this))
{
if (msg)
{
@@ -15901,7 +15922,7 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver)
if (!quest)
continue;
- if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_ACCEPT, quest->GetQuestId(), this))
+ if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_AVAILABLE, quest->GetQuestId(), this))
continue;
QuestStatus status = GetQuestStatus(questId);
@@ -17148,7 +17169,6 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
//join player to battleground group
currentBg->EventPlayerLoggedIn(this);
- currentBg->AddOrSetPlayerToCorrectBgGroup(this, m_bgData.bgTeam);
SetInviteForBattlegroundQueueType(bgQueueTypeId, currentBg->GetInstanceID());
}
@@ -17373,6 +17393,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
SetMap(map);
StoreRaidMapDifficulty();
+ UpdatePositionData();
// now that map position is determined, check instance validity
if (!CheckInstanceValidity(true) && !IsInstanceLoginGameMasterException())
@@ -21217,7 +21238,14 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
uint32 money = GetMoney();
if (npc)
- totalcost = (uint32)ceil(totalcost*GetReputationPriceDiscount(npc));
+ {
+ float discount = GetReputationPriceDiscount(npc);
+ totalcost = uint32(ceil(totalcost * discount));
+ firstcost = uint32(ceil(firstcost * discount));
+ m_taxi.SetFlightMasterFactionTemplateId(npc->GetFaction());
+ }
+ else
+ m_taxi.SetFlightMasterFactionTemplateId(0);
if (money < totalcost)
{
@@ -21325,6 +21353,27 @@ void Player::ContinueTaxiFlight() const
GetSession()->SendDoFlight(mountDisplayId, path, startNode);
}
+void Player::SendTaxiNodeStatusMultiple()
+{
+ for (auto itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
+ {
+ if (!itr->IsCreature())
+ continue;
+ Creature* creature = ObjectAccessor::GetCreature(*this, *itr);
+ if (!creature || creature->IsHostileTo(this))
+ continue;
+ if (!creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_FLIGHTMASTER))
+ continue;
+ uint32 nearestNode = sObjectMgr->GetNearestTaxiNode(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetMapId(), GetTeam());
+ if (!nearestNode)
+ continue;
+ WorldPacket data(SMSG_TAXINODE_STATUS, 9);
+ data << *itr;
+ data << uint8(m_taxi.IsTaximaskNodeKnown(nearestNode) ? 1 : 0);
+ SendDirectMessage(&data);
+ }
+}
+
void Player::InitDataForForm(bool reapplyMods)
{
ShapeshiftForm form = GetShapeshiftForm();
@@ -22517,6 +22566,8 @@ void Player::SendInitialPacketsAfterAddToMap()
SendAurasForTarget(this);
SendEnchantmentDurations(); // must be after add to map
SendItemDurations(); // must be after add to map
+ SendQuestGiverStatusMultiple();
+ SendTaxiNodeStatusMultiple();
// raid downscaling - send difficulty to player
if (GetMap()->IsRaid())
@@ -23168,11 +23219,15 @@ bool Player::GetBGAccessByLevel(BattlegroundTypeId bgTypeId) const
float Player::GetReputationPriceDiscount(Creature const* creature) const
{
- FactionTemplateEntry const* vendor_faction = creature->GetFactionTemplateEntry();
- if (!vendor_faction || !vendor_faction->faction)
+ return GetReputationPriceDiscount(creature->GetFactionTemplateEntry());
+}
+
+float Player::GetReputationPriceDiscount(FactionTemplateEntry const* factionTemplate) const
+{
+ if (!factionTemplate || !factionTemplate->faction)
return 1.0f;
- ReputationRank rank = GetReputationRank(vendor_faction->faction);
+ ReputationRank rank = GetReputationRank(factionTemplate->faction);
if (rank <= REP_NEUTRAL)
return 1.0f;
@@ -23557,7 +23612,7 @@ bool Player::isHonorOrXPTarget(Unit* victim) const
uint8 k_grey = Trinity::XP::GetGrayLevel(getLevel());
// Victim level less gray level
- if (v_level <= k_grey)
+ if (v_level <= k_grey && !sWorld->getIntConfig(CONFIG_MIN_CREATURE_SCALED_XP_RATIO))
return false;
if (Creature const* creature = victim->ToCreature())
@@ -24030,7 +24085,7 @@ void Player::ProcessTerrainStatusUpdate(ZLiquidStatus status, Optional<LiquidDat
else
m_MirrorTimerFlags &= ~UNDERWATER_INLAVA;
}
-
+
// Slime state (any contact)
if (liquidData->type_flags & MAP_LIQUID_TYPE_SLIME)
{
@@ -26240,9 +26295,6 @@ bool Player::SetDisableGravity(bool disable, bool packetOnly /*= false*/)
bool Player::SetCanFly(bool apply, bool packetOnly /*= false*/)
{
- if (!packetOnly && !Unit::SetCanFly(apply))
- return false;
-
if (!apply)
SetFallInformation(0, GetPositionZ());
@@ -26251,11 +26303,16 @@ bool Player::SetCanFly(bool apply, bool packetOnly /*= false*/)
data << uint32(0); //! movement counter
SendDirectMessage(&data);
- data.Initialize(MSG_MOVE_UPDATE_CAN_FLY, 64);
- data << GetPackGUID();
- BuildMovementPacket(&data);
- SendMessageToSet(&data, false);
- return true;
+ if (packetOnly || Unit::SetCanFly(apply))
+ {
+ data.Initialize(MSG_MOVE_UPDATE_CAN_FLY, 64);
+ data << GetPackGUID();
+ BuildMovementPacket(&data);
+ SendMessageToSet(&data, false);
+ return true;
+ }
+ else
+ return false;
}
bool Player::SetHover(bool apply, bool packetOnly /*= false*/)
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 4a2a82000b4..bc72066307f 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -946,6 +946,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
bool ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid = 0);
void CleanupAfterTaxiFlight();
void ContinueTaxiFlight() const;
+ void SendTaxiNodeStatusMultiple();
// mount_id can be used in scripting calls
bool isAcceptWhispers() const { return (m_ExtraFlags & PLAYER_EXTRA_ACCEPT_WHISPERS) != 0; }
void SetAcceptWhispers(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_ACCEPT_WHISPERS; else m_ExtraFlags &= ~PLAYER_EXTRA_ACCEPT_WHISPERS; }
@@ -1120,11 +1121,12 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
bool IsUseEquipedWeapon(bool mainhand) const;
bool IsTwoHandUsed() const;
bool IsUsingTwoHandedWeaponInOneHand() const;
- void SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast = false);
+ void SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast = false, bool sendChatMessage = true);
bool BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot);
bool _StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot, int32 price, ItemTemplate const* pProto, Creature* pVendor, VendorItem const* crItem, bool bStore);
float GetReputationPriceDiscount(Creature const* creature) const;
+ float GetReputationPriceDiscount(FactionTemplateEntry const* factionTemplate) const;
Player* GetTrader() const;
TradeData* GetTradeData() const { return m_trade; }
diff --git a/src/server/game/Entities/Player/PlayerTaxi.cpp b/src/server/game/Entities/Player/PlayerTaxi.cpp
index 9a53838e4a9..74a11230d4e 100644
--- a/src/server/game/Entities/Player/PlayerTaxi.cpp
+++ b/src/server/game/Entities/Player/PlayerTaxi.cpp
@@ -91,9 +91,13 @@ bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, uint3
{
ClearTaxiDestinations();
- Tokenizer Tokenizer(values, ' ');
+ Tokenizer tokens(values, ' ');
+ auto iter = tokens.begin();
+ if (iter != tokens.end())
+ m_flightMasterFactionId = atoul(*iter);
- for (Tokenizer::const_iterator iter = Tokenizer.begin(); iter != Tokenizer.end(); ++iter)
+ ++iter;
+ for (; iter != tokens.end(); ++iter)
{
uint32 node = atoul(*iter);
AddTaxiDestination(node);
@@ -128,6 +132,7 @@ std::string PlayerTaxi::SaveTaxiDestinationsToString()
return "";
std::ostringstream ss;
+ ss << m_flightMasterFactionId << ' ';
for (size_t i = 0; i < m_TaxiDestinations.size(); ++i)
ss << m_TaxiDestinations[i] << ' ';
@@ -154,3 +159,8 @@ std::ostringstream& operator<<(std::ostringstream& ss, PlayerTaxi const& taxi)
ss << taxi.m_taximask[i] << ' ';
return ss;
}
+
+FactionTemplateEntry const* PlayerTaxi::GetFlightMasterFactionTemplate() const
+{
+ return sFactionTemplateStore.LookupEntry(m_flightMasterFactionId);
+}
diff --git a/src/server/game/Entities/Player/PlayerTaxi.h b/src/server/game/Entities/Player/PlayerTaxi.h
index ae5052b3e7a..a4791c0ff4e 100644
--- a/src/server/game/Entities/Player/PlayerTaxi.h
+++ b/src/server/game/Entities/Player/PlayerTaxi.h
@@ -24,11 +24,12 @@
#include <iosfwd>
class ByteBuffer;
+struct FactionTemplateEntry;
class TC_GAME_API PlayerTaxi
{
public:
- PlayerTaxi() { m_taximask.fill(0); }
+ PlayerTaxi() : m_flightMasterFactionId(0) { m_taximask.fill(0); }
~PlayerTaxi() { }
// Nodes
void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level);
@@ -71,11 +72,14 @@ class TC_GAME_API PlayerTaxi
std::deque<uint32> const& GetPath() const { return m_TaxiDestinations; }
bool empty() const { return m_TaxiDestinations.empty(); }
+ FactionTemplateEntry const* GetFlightMasterFactionTemplate() const;
+ void SetFlightMasterFactionTemplateId(uint32 factionTemplateId) { m_flightMasterFactionId = factionTemplateId; }
friend std::ostringstream& operator<<(std::ostringstream& ss, PlayerTaxi const& taxi);
private:
TaxiMask m_taximask;
std::deque<uint32> m_TaxiDestinations;
+ uint32 m_flightMasterFactionId;
};
std::ostringstream& operator<<(std::ostringstream& ss, PlayerTaxi const& taxi);
diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp
index 6d36095ee7d..2b9b82b392b 100644
--- a/src/server/game/Entities/Transport/Transport.cpp
+++ b/src/server/game/Entities/Transport/Transport.cpp
@@ -302,16 +302,14 @@ Creature* Transport::CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData c
Map* map = GetMap();
Creature* creature = new Creature();
- if (!creature->LoadCreatureFromDB(guid, map, false))
+ if (!creature->LoadFromDB(guid, map, false, true))
{
delete creature;
return nullptr;
}
- float x = data->posX;
- float y = data->posY;
- float z = data->posZ;
- float o = data->orientation;
+ float x, y, z, o;
+ data->spawnPoint.GetPosition(x, y, z, o);
creature->SetTransport(this);
creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT);
@@ -349,7 +347,7 @@ GameObject* Transport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectDat
Map* map = GetMap();
GameObject* go = new GameObject();
- if (!go->LoadGameObjectFromDB(guid, map, false))
+ if (!go->LoadFromDB(guid, map, false))
{
delete go;
return nullptr;
@@ -357,10 +355,8 @@ GameObject* Transport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectDat
ASSERT(data);
- float x = data->posX;
- float y = data->posY;
- float z = data->posZ;
- float o = data->orientation;
+ float x, y, z, o;
+ data->spawnPoint.GetPosition(x, y, z, o);
go->SetTransport(this);
go->m_movementInfo.transport.guid = GetGUID();
@@ -468,7 +464,7 @@ TempSummon* Transport::SummonPassenger(uint32 entry, Position const& pos, TempSu
pos.GetPosition(x, y, z, o);
CalculatePassengerPosition(x, y, z, &o);
- if (!summon->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, phase, entry, x, y, z, o, nullptr, vehId))
+ if (!summon->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, phase, entry, { x, y, z, o }, nullptr, vehId))
{
delete summon;
return nullptr;
@@ -545,7 +541,7 @@ void Transport::LoadStaticPassengers()
// GameObjects on transport
guidEnd = cellItr->second.gameobjects.end();
for (CellGuidSet::const_iterator guidItr = cellItr->second.gameobjects.begin(); guidItr != guidEnd; ++guidItr)
- CreateGOPassenger(*guidItr, sObjectMgr->GetGOData(*guidItr));
+ CreateGOPassenger(*guidItr, sObjectMgr->GetGameObjectData(*guidItr));
}
}
}
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 733c3231915..bd4c71c06e6 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -40,6 +40,7 @@
#include "Log.h"
#include "LootMgr.h"
#include "MotionMaster.h"
+#include "MovementGenerator.h"
#include "MoveSpline.h"
#include "MoveSplineInit.h"
#include "ObjectAccessor.h"
@@ -2607,7 +2608,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo
SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const
{
// Can`t miss on dead target (on skinning for example)
- if ((!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER))
+ if (!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER)
return SPELL_MISS_NONE;
SpellSchoolMask schoolMask = spellInfo->GetSchoolMask();
@@ -3391,7 +3392,8 @@ void Unit::ProcessTerrainStatusUpdate(ZLiquidStatus status, Optional<LiquidData>
{
if (_lastLiquid && _lastLiquid->SpellId)
RemoveAurasDueToSpell(_lastLiquid->SpellId);
- if (curLiquid && curLiquid->SpellId)
+ Player* player = GetCharmerOrOwnerPlayerOrPlayerItself();
+ if (curLiquid && curLiquid->SpellId && (!player || !player->IsGameMaster()))
CastSpell(this, curLiquid->SpellId, true);
_lastLiquid = curLiquid;
}
@@ -5675,8 +5677,6 @@ ReputationRank Unit::GetFactionReactionTo(FactionTemplateEntry const* factionTem
return REP_NEUTRAL;
FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry();
- if (!targetFactionTemplateEntry)
- return REP_NEUTRAL;
if (Player const* targetPlayerOwner = target->GetAffectingPlayer())
{
@@ -5728,7 +5728,7 @@ bool Unit::IsFriendlyTo(Unit const* unit) const
bool Unit::IsHostileToPlayers() const
{
FactionTemplateEntry const* my_faction = GetFactionTemplateEntry();
- if (!my_faction || !my_faction->faction)
+ if (!my_faction->faction)
return false;
FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction);
@@ -5741,7 +5741,7 @@ bool Unit::IsHostileToPlayers() const
bool Unit::IsNeutralToAll() const
{
FactionTemplateEntry const* my_faction = GetFactionTemplateEntry();
- if (!my_faction || !my_faction->faction)
+ if (!my_faction->faction)
return true;
FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction);
@@ -9683,11 +9683,10 @@ Unit* Creature::SelectVictim()
// last case when creature must not go to evade mode:
// it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list
- // for example at owner command to pet attack some far away creature
// Note: creature does not have targeted movement generator but has attacker in this case
for (AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr)
{
- if ((*itr) && !CanCreatureAttack(*itr) && (*itr)->GetTypeId() != TYPEID_PLAYER
+ if ((*itr) && CanCreatureAttack(*itr) && (*itr)->GetTypeId() != TYPEID_PLAYER
&& !(*itr)->ToCreature()->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
return nullptr;
}
@@ -11407,6 +11406,27 @@ void Unit::StopMoving()
init.Stop();
}
+void Unit::PauseMovement(uint32 timer/* = 0*/, uint8 slot/* = 0*/, bool forced/* = true*/)
+{
+ if (slot >= MAX_MOTION_SLOT)
+ return;
+
+ if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(MovementSlot(slot)))
+ movementGenerator->Pause(timer);
+
+ if (forced)
+ StopMoving();
+}
+
+void Unit::ResumeMovement(uint32 timer/* = 0*/, uint8 slot/* = 0*/)
+{
+ if (slot >= MAX_MOTION_SLOT)
+ return;
+
+ if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(MovementSlot(slot)))
+ movementGenerator->Resume(timer);
+}
+
void Unit::SendMovementFlagUpdate(bool self /* = false */)
{
WorldPacket data;
@@ -12597,8 +12617,14 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
if (GetTypeId() == TYPEID_UNIT)
{
+ if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE))
+ movementGenerator->Pause(0);
+
+ GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE);
+
+ StopMoving();
+
ToCreature()->AI()->OnCharmed(true);
- GetMotionMaster()->MoveIdle();
}
else if (Player* player = ToPlayer())
{
@@ -12712,6 +12738,7 @@ void Unit::RemoveCharmedBy(Unit* charmer)
else
RestoreFaction();
+ ///@todo Handle SLOT_IDLE motion resume
GetMotionMaster()->InitDefault();
if (Creature* creature = ToCreature())
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 4fc9a43e464..cfe2f840915 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -1279,7 +1279,9 @@ class TC_GAME_API Unit : public WorldObject
void CastSpell(SpellCastTargets const& targets, SpellInfo const* spellInfo, CustomSpellValues const* value, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
void CastSpell(Unit* victim, uint32 spellId, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
+ void CastSpell(std::nullptr_t, uint32 spellId, bool triggered, Item* castItem = nullptr, AuraEffect* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty) { CastSpell((Unit*)nullptr, spellId, triggered, castItem, triggeredByAura, originalCaster); }
void CastSpell(Unit* victim, uint32 spellId, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
+ void CastSpell(std::nullptr_t, uint32 spellId, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty) { CastSpell((Unit*)nullptr, spellId, triggerFlags, castItem, triggeredByAura, originalCaster); }
void CastSpell(Unit* victim, SpellInfo const* spellInfo, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
void CastSpell(Unit* victim, SpellInfo const* spellInfo, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
@@ -1792,6 +1794,8 @@ class TC_GAME_API Unit : public WorldObject
bool IsStopped() const { return !(HasUnitState(UNIT_STATE_MOVING)); }
void StopMoving();
+ void PauseMovement(uint32 timer = 0, uint8 slot = 0, bool forced = true); // timer in ms
+ void ResumeMovement(uint32 timer = 0, uint8 slot = 0); // timer in ms
void AddUnitMovementFlag(uint32 f) { m_movementInfo.flags |= f; }
void RemoveUnitMovementFlag(uint32 f) { m_movementInfo.flags &= ~f; }
diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h
index 47f0ed28923..04d3521e40c 100644
--- a/src/server/game/Entities/Unit/UnitDefines.h
+++ b/src/server/game/Entities/Unit/UnitDefines.h
@@ -131,7 +131,7 @@ enum UnitFlags : uint32
UNIT_FLAG_IMMUNE_TO_PC = 0x00000100, // disables combat/assistance with PlayerCharacters (PC) - see Unit::_IsValidAttackTarget, Unit::_IsValidAssistTarget
UNIT_FLAG_IMMUNE_TO_NPC = 0x00000200, // disables combat/assistance with NonPlayerCharacters (NPC) - see Unit::_IsValidAttackTarget, Unit::_IsValidAssistTarget
UNIT_FLAG_LOOTING = 0x00000400, // loot animation
- UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // in combat?, 2.0.8
+ UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // on player pets: whether the pet is chasing a target to attack || on other units: whether any of the unit's minions is in combat
UNIT_FLAG_PVP = 0x00001000, // changed in 3.0.3
UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1
UNIT_FLAG_CANNOT_SWIM = 0x00004000, // 2.0.8
diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp
index 357cceb99b6..8b259438efc 100755
--- a/src/server/game/Entities/Vehicle/Vehicle.cpp
+++ b/src/server/game/Entities/Vehicle/Vehicle.cpp
@@ -148,7 +148,8 @@ void Vehicle::Reset(bool evading /*= false*/)
TC_LOG_DEBUG("entities.vehicle", "Vehicle::Reset (Entry: %u, GuidLow: %u, DBGuid: %u)", GetCreatureEntry(), _me->GetGUID().GetCounter(), _me->ToCreature()->GetSpawnId());
ApplyAllImmunities();
- InstallAllAccessories(evading);
+ if (GetBase()->IsAlive())
+ InstallAllAccessories(evading);
sScriptMgr->OnReset(this);
}
@@ -401,7 +402,7 @@ void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 typ
* @author Machiavelli
* @date 17-2-2013
*
- * @param [in, out] The prospective passenger.
+ * @param unit The prospective passenger.
* @param seatId Identifier for the seat. Value of -1 indicates the next available seat.
*
* @return true if it succeeds, false if it fails.
@@ -772,6 +773,13 @@ bool VehicleJoinEvent::Execute(uint64, uint32)
Target->RemovePendingEventsForSeat(Seat->first);
Target->RemovePendingEventsForPassenger(Passenger);
+ // Passenger might've died in the meantime - abort if this is the case
+ if (!Passenger->IsAlive())
+ {
+ Abort(0);
+ return true;
+ }
+
Passenger->SetVehicle(Target);
Seat->second.Passenger.Guid = Passenger->GetGUID();
Seat->second.Passenger.IsUnselectable = Passenger->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp
index fc00ce5ee9e..14fdccce85e 100644
--- a/src/server/game/Events/GameEventMgr.cpp
+++ b/src/server/game/Events/GameEventMgr.cpp
@@ -218,12 +218,12 @@ void GameEventMgr::LoadFromDB()
{
{
uint32 oldMSTime = getMSTime();
- // 0 1 2 3 4 5 6 7 8
- QueryResult result = WorldDatabase.Query("SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, description, world_event, announce FROM game_event");
+ // 0 1 2 3 4 5 6 7 8 9
+ QueryResult result = WorldDatabase.Query("SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, holidayStage, description, world_event, announce FROM game_event");
if (!result)
{
mGameEvent.clear();
- TC_LOG_ERROR("server.loading", ">> Loaded 0 game events. DB table `game_event` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 game events. DB table `game_event` is empty.");
return;
}
@@ -247,10 +247,13 @@ void GameEventMgr::LoadFromDB()
pGameEvent.occurence = fields[3].GetUInt64();
pGameEvent.length = fields[4].GetUInt64();
pGameEvent.holiday_id = HolidayIds(fields[5].GetUInt32());
-
- pGameEvent.state = (GameEventState)(fields[7].GetUInt8());
+ pGameEvent.holidayStage = fields[6].GetUInt8();
+ pGameEvent.description = fields[7].GetString();
+ pGameEvent.state = (GameEventState)(fields[8].GetUInt8());
+ pGameEvent.announce = fields[9].GetUInt8();
pGameEvent.nextstart = 0;
- pGameEvent.announce = fields[8].GetUInt8();
+
+ ++count;
if (pGameEvent.length == 0 && pGameEvent.state == GAMEEVENT_NORMAL) // length>0 is validity check
{
@@ -264,12 +267,18 @@ void GameEventMgr::LoadFromDB()
{
TC_LOG_ERROR("sql.sql", "`game_event`: game event id (%i) contains nonexisting holiday id %u.", event_id, pGameEvent.holiday_id);
pGameEvent.holiday_id = HOLIDAY_NONE;
+ continue;
+ }
+ if (pGameEvent.holidayStage > MAX_HOLIDAY_DURATIONS)
+ {
+ TC_LOG_ERROR("sql.sql", "`game_event` game event id (%i) has out of range holidayStage %u.", event_id, pGameEvent.holidayStage);
+ pGameEvent.holidayStage = 0;
+ continue;
}
- }
- pGameEvent.description = fields[6].GetString();
+ SetHolidayEventTime(pGameEvent);
+ }
- ++count;
}
while (result->NextRow());
@@ -436,7 +445,7 @@ void GameEventMgr::LoadFromDB()
int32 internal_event_id = mGameEvent.size() + event_id - 1;
- GameObjectData const* data = sObjectMgr->GetGOData(guid);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(guid);
if (!data)
{
TC_LOG_ERROR("sql.sql", "`game_event_gameobject` contains gameobject (GUID: %u) not found in `gameobject` table.", guid);
@@ -929,6 +938,45 @@ void GameEventMgr::LoadFromDB()
}
}
+void GameEventMgr::LoadHolidayDates()
+{
+ uint32 oldMSTime = getMSTime();
+
+ // 0 1 2
+ QueryResult result = WorldDatabase.Query("SELECT id, date_id, date_value FROM holiday_dates");
+
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 holiday dates. DB table `holiday_dates` is empty.");
+ return;
+ }
+
+ uint32 count = 0;
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 holidayId = fields[0].GetUInt32();
+ HolidaysEntry* entry = const_cast<HolidaysEntry*>(sHolidaysStore.LookupEntry(holidayId));
+ if (!entry)
+ {
+ TC_LOG_ERROR("sql.sql", "holiday_dates entry has invalid holiday id %u.", holidayId);
+ continue;
+ }
+ uint8 dateId = fields[1].GetUInt8();
+ if (dateId >= MAX_HOLIDAY_DATES)
+ {
+ TC_LOG_ERROR("sql.sql", "holiday_dates entry has out of range date_id %u.", dateId);
+ continue;
+ }
+ entry->Date[dateId] = fields[2].GetUInt32();
+ modifiedHolidays.insert(entry->Id);
+ ++count;
+
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u holiday dates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+}
+
uint32 GameEventMgr::GetNPCFlag(Creature* cr)
{
uint32 mask = 0;
@@ -1140,7 +1188,7 @@ void GameEventMgr::UpdateEventNPCFlags(uint16 event_id)
for (NPCFlagList::iterator itr = mGameEventNPCFlags[event_id].begin(); itr != mGameEventNPCFlags[event_id].end(); ++itr)
// get the creature data from the low guid to get the entry, to be able to find out the whole guid
if (CreatureData const* data = sObjectMgr->GetCreatureData(itr->first))
- creaturesByMap[data->mapid].insert(itr->first);
+ creaturesByMap[data->spawnPoint.GetMapId()].insert(itr->first);
for (auto const& p : creaturesByMap)
{
@@ -1203,13 +1251,13 @@ void GameEventMgr::GameEventSpawn(int16 event_id)
sObjectMgr->AddCreatureToGrid(*itr, data);
// Spawn if necessary (loaded grids only)
- Map* map = sMapMgr->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
// We use spawn coords to spawn
- if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
{
Creature* creature = new Creature();
//TC_LOG_DEBUG("misc", "Spawning creature %u", *itr);
- if (!creature->LoadCreatureFromDB(*itr, map))
+ if (!creature->LoadFromDB(*itr, map, true, false))
delete creature;
}
}
@@ -1225,19 +1273,19 @@ void GameEventMgr::GameEventSpawn(int16 event_id)
for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin(); itr != mGameEventGameobjectGuids[internal_event_id].end(); ++itr)
{
// Add to correct cell
- if (GameObjectData const* data = sObjectMgr->GetGOData(*itr))
+ if (GameObjectData const* data = sObjectMgr->GetGameObjectData(*itr))
{
sObjectMgr->AddGameobjectToGrid(*itr, data);
// Spawn if necessary (loaded grids only)
// this base map checked as non-instanced and then only existed
- Map* map = sMapMgr->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
// We use current coords to unspawn, not spawn coords since creature can have changed grid
- if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
{
GameObject* pGameobject = new GameObject;
//TC_LOG_DEBUG("misc", "Spawning gameobject %u", *itr);
/// @todo find out when it is add to map
- if (!pGameobject->LoadGameObjectFromDB(*itr, map, false))
+ if (!pGameobject->LoadFromDB(*itr, map, false))
delete pGameobject;
else
{
@@ -1280,7 +1328,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id)
{
sObjectMgr->RemoveCreatureFromGrid(*itr, data);
- sMapMgr->DoForAllMapsWithMapId(data->mapid, [&itr](Map* map)
+ sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr](Map* map)
{
auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(*itr);
for (auto itr2 = creatureBounds.first; itr2 != creatureBounds.second;)
@@ -1306,11 +1354,11 @@ void GameEventMgr::GameEventUnspawn(int16 event_id)
if (event_id >0 && hasGameObjectActiveEventExcept(*itr, event_id))
continue;
// Remove the gameobject from grid
- if (GameObjectData const* data = sObjectMgr->GetGOData(*itr))
+ if (GameObjectData const* data = sObjectMgr->GetGameObjectData(*itr))
{
sObjectMgr->RemoveGameobjectFromGrid(*itr, data);
- sMapMgr->DoForAllMapsWithMapId(data->mapid, [&itr](Map* map)
+ sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr](Map* map)
{
auto gameobjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(*itr);
for (auto itr2 = gameobjectBounds.first; itr2 != gameobjectBounds.second;)
@@ -1344,7 +1392,7 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate)
continue;
// Update if spawned
- sMapMgr->DoForAllMapsWithMapId(data->mapid, [&itr, activate](Map* map)
+ sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr, activate](Map* map)
{
auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(itr->first);
@@ -1667,6 +1715,84 @@ void GameEventMgr::RunSmartAIScripts(uint16 event_id, bool activate)
});
}
+void GameEventMgr::SetHolidayEventTime(GameEventData& event)
+{
+ if (!event.holidayStage) // Ignore holiday
+ return;
+
+ const HolidaysEntry* holiday = sHolidaysStore.LookupEntry(event.holiday_id);
+
+ if (!holiday->Date[0] || !holiday->Duration[0]) // Invalid definitions
+ {
+ TC_LOG_ERROR("sql.sql", "Missing date or duration for holiday %u.", event.holiday_id);
+ return;
+ }
+
+ uint8 stageIndex = event.holidayStage - 1;
+ event.length = holiday->Duration[stageIndex] * HOUR / MINUTE;
+
+ time_t stageOffset = 0;
+ for (int i = 0; i < stageIndex; ++i)
+ stageOffset += holiday->Duration[i] * HOUR;
+
+ switch (holiday->CalendarFilterType)
+ {
+ case -1: // Yearly
+ event.occurence = YEAR / MINUTE; // Not all too useful
+ break;
+ case 0: // Weekly
+ event.occurence = WEEK / MINUTE;
+ break;
+ case 1: // Defined dates only (Darkmoon Faire)
+ break;
+ case 2: // Only used for looping events (Call to Arms)
+ break;
+ }
+
+ if (holiday->Looping)
+ {
+ event.occurence = 0;
+ for (int i = 0; i < MAX_HOLIDAY_DURATIONS && holiday->Duration[i]; ++i)
+ event.occurence += holiday->Duration[i] * HOUR / MINUTE;
+ }
+
+ bool singleDate = ((holiday->Date[0] >> 24) & 0x1F) == 31; // Events with fixed date within year have - 1
+
+ time_t curTime = time(NULL);
+ for (int i = 0; i < MAX_HOLIDAY_DATES && holiday->Date[i]; ++i)
+ {
+ uint32 date = holiday->Date[i];
+
+ tm timeInfo;
+ if (singleDate)
+ timeInfo.tm_year = localtime(&curTime)->tm_year - 1; // First try last year (event active through New Year)
+ else
+ timeInfo.tm_year = ((date >> 24) & 0x1F) + 100;
+
+ timeInfo.tm_mon = (date >> 20) & 0xF;
+ timeInfo.tm_mday = ((date >> 14) & 0x3F) + 1;
+ timeInfo.tm_hour = (date >> 6) & 0x1F;
+ timeInfo.tm_min = date & 0x3F;
+ timeInfo.tm_sec = 0;
+ timeInfo.tm_isdst = -1;
+ tm tmCopy = timeInfo;
+
+ time_t startTime = mktime(&timeInfo);
+ if (curTime < startTime + event.length * MINUTE)
+ {
+ event.start = startTime + stageOffset;
+ return;
+ }
+ else if (singleDate)
+ {
+ tmCopy.tm_year = localtime(&curTime)->tm_year; // This year
+ event.start = mktime(&tmCopy) + stageOffset;
+ return;
+ }
+ }
+ TC_LOG_ERROR("sql.sql", "No suitable start date found for holiday %u.", event.holiday_id);
+}
+
bool IsHolidayActive(HolidayIds id)
{
if (id == HOLIDAY_NONE)
diff --git a/src/server/game/Events/GameEventMgr.h b/src/server/game/Events/GameEventMgr.h
index bee6dfc8fb3..4d1ce718422 100644
--- a/src/server/game/Events/GameEventMgr.h
+++ b/src/server/game/Events/GameEventMgr.h
@@ -60,7 +60,7 @@ typedef std::map<uint32 /*condition id*/, GameEventFinishCondition> GameEventCon
struct GameEventData
{
- GameEventData() : start(1), end(0), nextstart(0), occurence(0), length(0), holiday_id(HOLIDAY_NONE), state(GAMEEVENT_NORMAL),
+ GameEventData() : start(1), end(0), nextstart(0), occurence(0), length(0), holiday_id(HOLIDAY_NONE), holidayStage(0), state(GAMEEVENT_NORMAL),
announce(0) { }
time_t start; // occurs after this time
time_t end; // occurs before this time
@@ -68,6 +68,7 @@ struct GameEventData
uint32 occurence; // time between end and start
uint32 length; // length of the event (minutes) after finishing all conditions
HolidayIds holiday_id;
+ uint8 holidayStage;
GameEventState state; // state of the game event, these are saved into the game_event table on change!
GameEventConditionMap conditions; // conditions to finish
std::set<uint16 /*gameevent id*/> prerequisite_events; // events that must be completed before starting this event
@@ -114,6 +115,7 @@ class TC_GAME_API GameEventMgr
bool CheckOneGameEvent(uint16 entry) const;
uint32 NextCheck(uint16 entry) const;
void LoadFromDB();
+ void LoadHolidayDates();
uint32 Update();
bool IsActiveEvent(uint16 event_id) { return (m_ActiveEvents.find(event_id) != m_ActiveEvents.end()); }
uint32 StartSystem();
@@ -147,6 +149,7 @@ class TC_GAME_API GameEventMgr
bool hasGameObjectQuestActiveEventExcept(uint32 quest_id, uint16 event_id);
bool hasCreatureActiveEventExcept(ObjectGuid::LowType creature_guid, uint16 event_id);
bool hasGameObjectActiveEventExcept(ObjectGuid::LowType go_guid, uint16 event_id);
+ void SetHolidayEventTime(GameEventData& event);
typedef std::list<ObjectGuid::LowType> GuidList;
typedef std::list<uint32> IdList;
@@ -181,6 +184,7 @@ class TC_GAME_API GameEventMgr
public:
GameEventGuidMap mGameEventCreatureGuids;
GameEventGuidMap mGameEventGameobjectGuids;
+ std::set<uint32> modifiedHolidays;
};
#define sGameEventMgr GameEventMgr::instance()
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 1b7713ab363..2f1e4fa48f3 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -30,6 +30,7 @@
#include "GroupMgr.h"
#include "GuildMgr.h"
#include "InstanceSaveMgr.h"
+#include "InstanceScript.h"
#include "Language.h"
#include "LFGMgr.h"
#include "Log.h"
@@ -831,7 +832,7 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid1 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid1);
const_cast<CreatureTemplate*>(cInfo)->Modelid1 = 0;
}
- else if (!displayScaleEntry)
+ else
displayScaleEntry = displayEntry;
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid1);
@@ -1123,7 +1124,7 @@ void ObjectMgr::LoadGameObjectAddons()
ObjectGuid::LowType guid = fields[0].GetUInt32();
- GameObjectData const* goData = GetGOData(guid);
+ GameObjectData const* goData = GetGameObjectData(guid);
if (!goData)
{
TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) does not exist but has a record in `gameobject_addon`", guid);
@@ -1427,7 +1428,7 @@ void ObjectMgr::LoadLinkedRespawn()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
return;
}
@@ -1461,8 +1462,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- MapEntry const* const map = sMapStore.LookupEntry(master->mapid);
- if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
+ MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
+ if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1490,7 +1491,7 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- GameObjectData const* master = GetGOData(linkedGuidLow);
+ GameObjectData const* master = GetGameObjectData(linkedGuidLow);
if (!master)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '%u' not found in gameobject table", linkedGuidLow);
@@ -1498,8 +1499,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- MapEntry const* const map = sMapStore.LookupEntry(master->mapid);
- if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
+ MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
+ if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1519,7 +1520,7 @@ void ObjectMgr::LoadLinkedRespawn()
}
case GO_TO_GO:
{
- GameObjectData const* slave = GetGOData(guidLow);
+ GameObjectData const* slave = GetGameObjectData(guidLow);
if (!slave)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '%u' not found in gameobject table", guidLow);
@@ -1527,7 +1528,7 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- GameObjectData const* master = GetGOData(linkedGuidLow);
+ GameObjectData const* master = GetGameObjectData(linkedGuidLow);
if (!master)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '%u' not found in gameobject table", linkedGuidLow);
@@ -1535,8 +1536,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- MapEntry const* const map = sMapStore.LookupEntry(master->mapid);
- if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
+ MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
+ if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1556,7 +1557,7 @@ void ObjectMgr::LoadLinkedRespawn()
}
case GO_TO_CREATURE:
{
- GameObjectData const* slave = GetGOData(guidLow);
+ GameObjectData const* slave = GetGameObjectData(guidLow);
if (!slave)
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '%u' not found in gameobject table", guidLow);
@@ -1572,8 +1573,8 @@ void ObjectMgr::LoadLinkedRespawn()
break;
}
- MapEntry const* const map = sMapStore.LookupEntry(master->mapid);
- if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
+ MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
+ if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -1626,8 +1627,8 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid
return false;
}
- MapEntry const* map = sMapStore.LookupEntry(master->mapid);
- if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
+ MapEntry const* map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
+ if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
{
TC_LOG_ERROR("sql.sql", "Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
return false;
@@ -1741,8 +1742,8 @@ void ObjectMgr::LoadCreatures()
{
uint32 oldMSTime = getMSTime();
- // 0 1 2 3 4 5 6 7 8 9 10
- QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, "
+ // 0 1 2 3 4 5 6 7 8 9 10
+ QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, spawndist, "
// 11 12 13 14 15 16 17 18 19 20 21
"currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.dynamicflags, "
// 22
@@ -1753,7 +1754,7 @@ void ObjectMgr::LoadCreatures()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 creatures. DB table `creature` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 creatures. DB table `creature` is empty.");
return;
}
@@ -1782,14 +1783,11 @@ void ObjectMgr::LoadCreatures()
}
CreatureData& data = _creatureDataStore[guid];
+ data.spawnId = guid;
data.id = entry;
- data.mapid = fields[2].GetUInt16();
- data.displayid = fields[3].GetUInt32();
- data.equipmentId = fields[4].GetInt8();
- data.posX = fields[5].GetFloat();
- data.posY = fields[6].GetFloat();
- data.posZ = fields[7].GetFloat();
- data.orientation = fields[8].GetFloat();
+ data.spawnPoint.WorldRelocate(fields[2].GetUInt16(), fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
+ data.displayid = fields[7].GetUInt32();
+ data.equipmentId = fields[8].GetInt8();
data.spawntimesecs = fields[9].GetUInt32();
data.spawndist = fields[10].GetFloat();
data.currentwaypoint= fields[11].GetUInt32();
@@ -1803,18 +1801,19 @@ void ObjectMgr::LoadCreatures()
data.npcflag = fields[19].GetUInt32();
data.unit_flags = fields[20].GetUInt32();
data.dynamicflags = fields[21].GetUInt32();
- data.ScriptId = GetScriptId(fields[22].GetString());
+ data.scriptId = GetScriptId(fields[22].GetString());
+ data.spawnGroupData = &_spawnGroupDataStore[0];
- MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
+ MapEntry const* mapEntry = sMapStore.LookupEntry(data.spawnPoint.GetMapId());
if (!mapEntry)
{
- TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.mapid);
+ TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.spawnPoint.GetMapId());
continue;
}
// Skip spawnMask check for transport maps
- if (!IsTransportMap(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid])
- TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that have wrong spawn mask %u including unsupported difficulty modes for map (Id: %u).", guid, data.spawnMask, data.mapid);
+ if (!IsTransportMap(data.spawnPoint.GetMapId()) && data.spawnMask & ~spawnMasks[data.spawnPoint.GetMapId()])
+ TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that have wrong spawn mask %u including unsupported difficulty modes for map (Id: %u).", guid, data.spawnMask, data.spawnPoint.GetMapId());
bool ok = true;
for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
@@ -1879,17 +1878,11 @@ void ObjectMgr::LoadCreatures()
data.phaseMask = 1;
}
- if (std::abs(data.orientation) > 2 * float(M_PI))
- {
- TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u Entry: %u) with abs(`orientation`) > 2*PI (orientation is expressed in radians), normalized.", guid, data.id);
- data.orientation = Position::NormalizeOrientation(data.orientation);
- }
-
if (sWorld->getBoolConfig(CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA))
{
uint32 zoneId = 0;
uint32 areaId = 0;
- sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
+ sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.spawnPoint);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA);
@@ -1916,8 +1909,8 @@ void ObjectMgr::AddCreatureToGrid(ObjectGuid::LowType guid, CreatureData const*
{
if (mask & 1)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()];
cell_guids.creatures.insert(guid);
}
}
@@ -1930,14 +1923,14 @@ void ObjectMgr::RemoveCreatureFromGrid(ObjectGuid::LowType guid, CreatureData co
{
if (mask & 1)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()];
cell_guids.creatures.erase(guid);
}
}
}
-ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay /*= 0*/)
+ObjectGuid::LowType ObjectMgr::AddGameObjectData(uint32 entry, uint32 mapId, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay /*= 0*/)
{
GameObjectTemplate const* goinfo = GetGameObjectTemplate(entry);
if (!goinfo)
@@ -1947,41 +1940,40 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, Position co
if (!map)
return 0;
- ObjectGuid::LowType guid = GenerateGameObjectSpawnId();
+ ObjectGuid::LowType spawnId = GenerateGameObjectSpawnId();
- GameObjectData& data = NewGOData(guid);
+ GameObjectData& data = NewOrExistGameObjectData(spawnId);
+ data.spawnId = spawnId;
data.id = entry;
- data.mapid = mapId;
-
- pos.GetPosition(data.posX, data.posY, data.posZ, data.orientation);
-
+ data.spawnPoint.WorldRelocate(mapId,pos);
data.rotation = rot;
data.spawntimesecs = spawntimedelay;
data.animprogress = 100;
data.spawnMask = 1;
- data.go_state = GO_STATE_READY;
+ data.goState = GO_STATE_READY;
data.phaseMask = PHASEMASK_NORMAL;
data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0;
- data.dbData = false;
+ data.dbData = false;
+ data.spawnGroupData = GetLegacySpawnGroup();
- AddGameobjectToGrid(guid, &data);
+ AddGameobjectToGrid(spawnId, &data);
// Spawn if necessary (loaded grids only)
// We use spawn coords to spawn
- if (!map->Instanceable() && map->IsGridLoaded(data.posX, data.posY))
+ if (!map->Instanceable() && map->IsGridLoaded(data.spawnPoint))
{
GameObject* go = new GameObject;
- if (!go->LoadGameObjectFromDB(guid, map))
+ if (!go->LoadFromDB(spawnId, map, true))
{
- TC_LOG_ERROR("misc", "AddGOData: cannot add gameobject entry %u to map", entry);
+ TC_LOG_ERROR("misc", "AddGameObjectData: cannot add gameobject entry %u to map", entry);
delete go;
return 0;
}
}
- TC_LOG_DEBUG("maps", "AddGOData: dbguid %u entry %u map %u x %f y %f z %f o %f", guid, entry, mapId, data.posX, data.posY, data.posZ, data.orientation);
+ TC_LOG_DEBUG("maps", "AddGameObjectData: dbguid %u entry %u map %u pos %s", spawnId, entry, mapId, data.spawnPoint.ToString().c_str());
- return guid;
+ return spawnId;
}
@@ -1997,15 +1989,13 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit
if (!map)
return 0;
- ObjectGuid::LowType guid = GenerateCreatureSpawnId();
- CreatureData& data = NewOrExistCreatureData(guid);
+ ObjectGuid::LowType spawnId = GenerateCreatureSpawnId();
+ CreatureData& data = NewOrExistCreatureData(spawnId);
+ data.spawnId = spawnId;
data.id = entry;
- data.mapid = mapId;
+ data.spawnPoint.WorldRelocate(mapId, pos);
data.displayid = 0;
data.equipmentId = 0;
-
- pos.GetPosition(data.posX, data.posY, data.posZ, data.orientation);
-
data.spawntimesecs = spawntimedelay;
data.spawndist = 0;
data.currentwaypoint = 0;
@@ -2018,14 +2008,15 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit
data.npcflag = cInfo->npcflag;
data.unit_flags = cInfo->unit_flags;
data.dynamicflags = cInfo->dynamicflags;
+ data.spawnGroupData = GetLegacySpawnGroup();
- AddCreatureToGrid(guid, &data);
+ AddCreatureToGrid(spawnId, &data);
// We use spawn coords to spawn
- if (!map->Instanceable() && !map->IsRemovalGrid(data.posX, data.posY))
+ if (!map->Instanceable() && !map->IsRemovalGrid(data.spawnPoint))
{
Creature* creature = new Creature();
- if (!creature->LoadCreatureFromDB(guid, map))
+ if (!creature->LoadFromDB(spawnId, map, true, true))
{
TC_LOG_ERROR("misc", "AddCreature: Cannot add creature entry %u to map", entry);
delete creature;
@@ -2033,10 +2024,10 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit
}
}
- return guid;
+ return spawnId;
}
-void ObjectMgr::LoadGameobjects()
+void ObjectMgr::LoadGameObjects()
{
uint32 oldMSTime = getMSTime();
@@ -2051,7 +2042,7 @@ void ObjectMgr::LoadGameobjects()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
return;
}
@@ -2100,22 +2091,20 @@ void ObjectMgr::LoadGameobjects()
GameObjectData& data = _gameObjectDataStore[guid];
+ data.spawnId = guid;
data.id = entry;
- data.mapid = fields[2].GetUInt16();
- data.posX = fields[3].GetFloat();
- data.posY = fields[4].GetFloat();
- data.posZ = fields[5].GetFloat();
- data.orientation = fields[6].GetFloat();
+ data.spawnPoint.WorldRelocate(fields[2].GetUInt16(), fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
data.rotation.x = fields[7].GetFloat();
data.rotation.y = fields[8].GetFloat();
data.rotation.z = fields[9].GetFloat();
data.rotation.w = fields[10].GetFloat();
data.spawntimesecs = fields[11].GetInt32();
+ data.spawnGroupData = &_spawnGroupDataStore[0];
- MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
+ MapEntry const* mapEntry = sMapStore.LookupEntry(data.spawnPoint.GetMapId());
if (!mapEntry)
{
- TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) spawned on a non-existed map (Id: %u), skip", guid, data.id, data.mapid);
+ TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) spawned on a non-existed map (Id: %u), skip", guid, data.id, data.spawnPoint.GetMapId());
continue;
}
@@ -2133,24 +2122,18 @@ void ObjectMgr::LoadGameobjects()
TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid, data.id, go_state);
continue;
}
- data.go_state = GOState(go_state);
+ data.goState = GOState(go_state);
data.spawnMask = fields[14].GetUInt8();
- if (!IsTransportMap(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid])
- TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) that has wrong spawn mask %u including unsupported difficulty modes for map (Id: %u), skip", guid, data.id, data.spawnMask, data.mapid);
+ if (!IsTransportMap(data.spawnPoint.GetMapId()) && data.spawnMask & ~spawnMasks[data.spawnPoint.GetMapId()])
+ TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) that has wrong spawn mask %u including unsupported difficulty modes for map (Id: %u), skip", guid, data.id, data.spawnMask, data.spawnPoint.GetMapId());
data.phaseMask = fields[15].GetUInt32();
int16 gameEvent = fields[16].GetInt8();
uint32 PoolId = fields[17].GetUInt32();
- data.ScriptId = GetScriptId(fields[18].GetString());
-
- if (std::abs(data.orientation) > 2 * float(M_PI))
- {
- TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with abs(`orientation`) > 2*PI (orientation is expressed in radians), normalized.", guid, data.id);
- data.orientation = Position::NormalizeOrientation(data.orientation);
- }
+ data.scriptId = GetScriptId(fields[18].GetString());
if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
{
@@ -2176,7 +2159,7 @@ void ObjectMgr::LoadGameobjects()
continue;
}
- if (!MapManager::IsValidMapCoord(data.mapid, data.posX, data.posY, data.posZ, data.orientation))
+ if (!MapManager::IsValidMapCoord(data.spawnPoint))
{
TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid coordinates, skip", guid, data.id);
continue;
@@ -2192,7 +2175,7 @@ void ObjectMgr::LoadGameobjects()
{
uint32 zoneId = 0;
uint32 areaId = 0;
- sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
+ sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.spawnPoint);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA);
@@ -2211,6 +2194,207 @@ void ObjectMgr::LoadGameobjects()
TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " gameobjects in %u ms", _gameObjectDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
}
+void ObjectMgr::LoadSpawnGroupTemplates()
+{
+ uint32 oldMSTime = getMSTime();
+
+ // 0 1 2
+ QueryResult result = WorldDatabase.Query("SELECT groupId, groupName, groupFlags FROM spawn_group_template");
+
+ if (result)
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 groupId = fields[0].GetUInt32();
+ SpawnGroupTemplateData& group = _spawnGroupDataStore[groupId];
+ group.groupId = groupId;
+ group.name = fields[1].GetString();
+ group.mapId = SPAWNGROUP_MAP_UNSET;
+ uint32 flags = fields[2].GetUInt32();
+ if (flags & ~SPAWNGROUP_FLAGS_ALL)
+ {
+ flags &= SPAWNGROUP_FLAGS_ALL;
+ TC_LOG_ERROR("sql.sql", "Invalid spawn group flag %u on group ID %u (%s), reduced to valid flag %u.", flags, groupId, group.name.c_str(), uint32(group.flags));
+ }
+ if (flags & SPAWNGROUP_FLAG_SYSTEM && flags & SPAWNGROUP_FLAG_MANUAL_SPAWN)
+ {
+ flags &= ~SPAWNGROUP_FLAG_MANUAL_SPAWN;
+ TC_LOG_ERROR("sql.sql", "System spawn group %u (%s) has invalid manual spawn flag. Ignored.", groupId, group.name.c_str());
+ }
+ group.flags = SpawnGroupFlags(flags);
+ } while (result->NextRow());
+ }
+
+ if (_spawnGroupDataStore.find(0) == _spawnGroupDataStore.end())
+ {
+ TC_LOG_ERROR("sql.sql", "Default spawn group (index 0) is missing from DB! Manually inserted.");
+ SpawnGroupTemplateData& data = _spawnGroupDataStore[0];
+ data.groupId = 0;
+ data.name = "Default Group";
+ data.mapId = 0;
+ data.flags = SPAWNGROUP_FLAG_SYSTEM;
+ }
+ if (_spawnGroupDataStore.find(1) == _spawnGroupDataStore.end())
+ {
+ TC_LOG_ERROR("sql.sql", "Default legacy spawn group (index 1) is missing from DB! Manually inserted.");
+ SpawnGroupTemplateData&data = _spawnGroupDataStore[1];
+ data.groupId = 1;
+ data.name = "Legacy Group";
+ data.mapId = 0;
+ data.flags = SpawnGroupFlags(SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_COMPATIBILITY_MODE);
+ }
+
+ if (result)
+ TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " spawn group templates in %u ms", _spawnGroupDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
+ else
+ TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group templates. DB table `spawn_group_template` is empty.");
+
+ return;
+}
+
+void ObjectMgr::LoadSpawnGroups()
+{
+ uint32 oldMSTime = getMSTime();
+
+ // 0 1 2
+ QueryResult result = WorldDatabase.Query("SELECT groupId, spawnType, spawnId FROM spawn_group");
+
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group members. DB table `spawn_group` is empty.");
+ return;
+ }
+
+ uint32 numMembers = 0;
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 groupId = fields[0].GetUInt32();
+ SpawnObjectType spawnType;
+ {
+ uint32 type = fields[1].GetUInt8();
+ if (type >= SPAWN_TYPE_MAX)
+ {
+ TC_LOG_ERROR("sql.sql", "Spawn data with invalid type %u listed for spawn group %u. Skipped.", type, groupId);
+ continue;
+ }
+ spawnType = SpawnObjectType(type);
+ }
+ ObjectGuid::LowType spawnId = fields[2].GetUInt32();
+
+ SpawnData const* data = GetSpawnData(spawnType, spawnId);
+ if (!data)
+ {
+ TC_LOG_ERROR("sql.sql", "Spawn data with ID (%u,%u) not found, but is listed as a member of spawn group %u!", uint32(spawnType), spawnId, groupId);
+ continue;
+ }
+ else if (data->spawnGroupData->groupId)
+ {
+ TC_LOG_ERROR("sql.sql", "Spawn with ID (%u,%u) is listed as a member of spawn group %u, but is already a member of spawn group %u. Skipping.", uint32(spawnType), spawnId, groupId, data->spawnGroupData->groupId);
+ continue;
+ }
+ auto it = _spawnGroupDataStore.find(groupId);
+ if (it == _spawnGroupDataStore.end())
+ {
+ TC_LOG_ERROR("sql.sql", "Spawn group %u assigned to spawn ID (%u,%u), but group is found!", groupId, uint32(spawnType), spawnId);
+ continue;
+ }
+ else
+ {
+ SpawnGroupTemplateData& groupTemplate = it->second;
+ if (groupTemplate.mapId == SPAWNGROUP_MAP_UNSET)
+ groupTemplate.mapId = data->spawnPoint.GetMapId();
+ else if (groupTemplate.mapId != data->spawnPoint.GetMapId() && !(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
+ {
+ TC_LOG_ERROR("sql.sql", "Spawn group %u has map ID %u, but spawn (%u,%u) has map id %u - spawn NOT added to group!", groupId, groupTemplate.mapId, uint32(spawnType), spawnId, data->spawnPoint.GetMapId());
+ continue;
+ }
+ const_cast<SpawnData*>(data)->spawnGroupData = &groupTemplate;
+ if (!(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
+ _spawnGroupMapStore.emplace(groupId, data);
+ ++numMembers;
+ }
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u spawn group members in %u ms", numMembers, GetMSTimeDiffToNow(oldMSTime));
+}
+
+void ObjectMgr::LoadInstanceSpawnGroups()
+{
+ uint32 oldMSTime = getMSTime();
+
+ // 0 1 2 3 4
+ QueryResult result = WorldDatabase.Query("SELECT instanceMapId, bossStateId, bossStates, spawnGroupId, flags FROM instance_spawn_groups");
+
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 instance spawn groups. DB table `instance_spawn_groups` is empty.");
+ return;
+ }
+
+ uint32 n = 0;
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 const spawnGroupId = fields[3].GetUInt32();
+ auto it = _spawnGroupDataStore.find(spawnGroupId);
+ if (it == _spawnGroupDataStore.end() || (it->second.flags & SPAWNGROUP_FLAG_SYSTEM))
+ {
+ TC_LOG_ERROR("sql.sql", "Invalid spawn group %u specified for instance %u. Skipped.", spawnGroupId, fields[0].GetUInt16());
+ continue;
+ }
+
+ uint16 const instanceMapId = fields[0].GetUInt16();
+ auto& vector = _instanceSpawnGroupStore[instanceMapId];
+ vector.emplace_back();
+ InstanceSpawnGroupInfo& info = vector.back();
+ info.SpawnGroupId = spawnGroupId;
+ info.BossStateId = fields[1].GetUInt8();
+
+ uint8 const ALL_STATES = (1 << TO_BE_DECIDED) - 1;
+ uint8 const states = fields[2].GetUInt8();
+ if (states & ~ALL_STATES)
+ {
+ info.BossStates = states & ALL_STATES;
+ TC_LOG_ERROR("sql.sql", "Instance spawn group (%u,%u) had invalid boss state mask %u - truncated to %u.", instanceMapId, spawnGroupId, states, info.BossStates);
+ }
+ else
+ info.BossStates = states;
+
+ uint8 const flags = fields[4].GetUInt8();
+ if (flags & ~InstanceSpawnGroupInfo::FLAG_ALL)
+ {
+ info.Flags = flags & InstanceSpawnGroupInfo::FLAG_ALL;
+ TC_LOG_ERROR("sql.sql", "Instance spawn group (%u,%u) had invalid flags %u - truncated to %u.", instanceMapId, spawnGroupId, flags, info.Flags);
+ }
+ else
+ info.Flags = flags;
+
+ ++n;
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u instance spawn groups in %u ms", n, GetMSTimeDiffToNow(oldMSTime));
+}
+
+void ObjectMgr::OnDeleteSpawnData(SpawnData const* data)
+{
+ auto templateIt = _spawnGroupDataStore.find(data->spawnGroupData->groupId);
+ ASSERT(templateIt != _spawnGroupDataStore.end(), "Creature data for (%u,%u) is being deleted and has invalid spawn group index %u!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId);
+ if (templateIt->second.flags & SPAWNGROUP_FLAG_SYSTEM) // system groups don't store their members in the map
+ return;
+
+ auto pair = _spawnGroupMapStore.equal_range(data->spawnGroupData->groupId);
+ for (auto it = pair.first; it != pair.second; ++it)
+ {
+ if (it->second != data)
+ continue;
+ _spawnGroupMapStore.erase(it);
+ return;
+ }
+ ASSERT(false, "Spawn data (%u,%u) being removed is member of spawn group %u, but not actually listed in the lookup table for that group!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId);
+}
+
void ObjectMgr::AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData const* data)
{
uint8 mask = data->spawnMask;
@@ -2218,8 +2402,8 @@ void ObjectMgr::AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData con
{
if (mask & 1)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()];
cell_guids.gameobjects.insert(guid);
}
}
@@ -2232,8 +2416,8 @@ void ObjectMgr::RemoveGameobjectFromGrid(ObjectGuid::LowType guid, GameObjectDat
{
if (mask & 1)
{
- CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
- CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
+ CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
+ CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()];
cell_guids.gameobjects.erase(guid);
}
}
@@ -3011,7 +3195,7 @@ void ObjectMgr::LoadVehicleTemplateAccessories()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty.");
return;
}
@@ -3105,7 +3289,7 @@ void ObjectMgr::LoadPetLevelInfo()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty.");
return;
}
@@ -3399,7 +3583,7 @@ void ObjectMgr::LoadPlayerInfo()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty.");
}
else
{
@@ -3473,7 +3657,7 @@ void ObjectMgr::LoadPlayerInfo()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty.");
}
else
{
@@ -3534,7 +3718,7 @@ void ObjectMgr::LoadPlayerInfo()
QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, spell FROM playercreateinfo_cast_spell");
if (!result)
- TC_LOG_ERROR("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty.");
else
{
uint32 count = 0;
@@ -3591,7 +3775,7 @@ void ObjectMgr::LoadPlayerInfo()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty.");
}
else
{
@@ -4019,7 +4203,7 @@ void ObjectMgr::LoadQuests()
" FROM quest_template");
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty.");
return;
}
@@ -4070,7 +4254,7 @@ void ObjectMgr::LoadQuests()
{
QueryResult result = WorldDatabase.PQuery("SELECT %s FROM %s", loader.QueryFields, loader.TableName);
if (!result)
- TC_LOG_ERROR("server.loading", ">> Loaded 0 quest %s. DB table `%s` is empty.", loader.TableDesc, loader.TableName);
+ TC_LOG_INFO("server.loading", ">> Loaded 0 quest %s. DB table `%s` is empty.", loader.TableDesc, loader.TableName);
else
{
do
@@ -4881,7 +5065,7 @@ void ObjectMgr::LoadScripts(ScriptsType type)
case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
{
- GameObjectData const* data = GetGOData(tmp.RespawnGameobject.GOGuid);
+ GameObjectData const* data = GetGameObjectData(tmp.RespawnGameobject.GOGuid);
if (!data)
{
TC_LOG_ERROR("sql.sql", "Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
@@ -4931,7 +5115,7 @@ void ObjectMgr::LoadScripts(ScriptsType type)
case SCRIPT_COMMAND_OPEN_DOOR:
case SCRIPT_COMMAND_CLOSE_DOOR:
{
- GameObjectData const* data = GetGOData(tmp.ToggleDoor.GOGuid);
+ GameObjectData const* data = GetGameObjectData(tmp.ToggleDoor.GOGuid);
if (!data)
{
TC_LOG_ERROR("sql.sql", "Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u",
@@ -5409,7 +5593,7 @@ void ObjectMgr::LoadInstanceEncounters()
QueryResult result = WorldDatabase.Query("SELECT entry, creditType, creditEntry, lastEncounterDungeon FROM instance_encounters");
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 instance encounters, table is empty!");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 instance encounters, table is empty!");
return;
}
@@ -5695,6 +5879,10 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
stmt->setUInt32(0, itr2->item_guid);
CharacterDatabase.Execute(stmt);
}
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
+ stmt->setUInt32(0, m->messageID);
+ CharacterDatabase.Execute(stmt);
}
else
{
@@ -5794,6 +5982,134 @@ void ObjectMgr::LoadQuestAreaTriggers()
TC_LOG_INFO("server.loading", ">> Loaded %u quest trigger points in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
+QuestGreeting const* ObjectMgr::GetQuestGreeting(ObjectGuid guid) const
+{
+ auto itr = _questGreetingStore.find(guid.GetTypeId());
+ if (itr == _questGreetingStore.end())
+ return nullptr;
+
+ auto questItr = itr->second.find(guid.GetEntry());
+ if (questItr == itr->second.end())
+ return nullptr;
+
+ return questItr->second;
+}
+
+void ObjectMgr::LoadQuestGreetings()
+{
+ uint32 oldMSTime = getMSTime();
+
+ _questGreetingStore.clear(); // need for reload case
+
+ // 0 1 2 3 4
+ QueryResult result = WorldDatabase.Query("SELECT ID, Type, GreetEmoteType, GreetEmoteDelay, Greeting FROM quest_greeting");
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 quest greetings. DB table `quest_greeting` is empty.");
+ return;
+ }
+
+ _questGreetingStore.rehash(result->GetRowCount());
+
+ uint32 count = 0;
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 id = fields[0].GetUInt32();
+ uint8 type = fields[1].GetUInt8();
+ // overwrite
+ switch (type)
+ {
+ case 0: // Creature
+ type = TYPEID_UNIT;
+ if (!sObjectMgr->GetCreatureTemplate(id))
+ {
+ TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: creature template (entry: %u) does not exist.", id);
+ continue;
+ }
+ break;
+ case 1: // GameObject
+ type = TYPEID_GAMEOBJECT;
+ if (!sObjectMgr->GetGameObjectTemplate(id))
+ {
+ TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: gameobject template (entry: %u) does not exist.", id);
+ continue;
+ }
+ break;
+ default:
+ TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: unknown type = %u for entry = %u. Skipped.", type, id);
+ continue;
+ }
+
+ uint16 greetEmoteType = fields[2].GetUInt16();
+
+ if (greetEmoteType > 0 && !sEmotesStore.LookupEntry(greetEmoteType))
+ {
+ TC_LOG_DEBUG("sql.sql", "Table `quest_greeting`: entry %u has greetEmoteType = %u but emote does not exist. Set to 0.", id, greetEmoteType);
+ greetEmoteType = 0;
+ }
+
+ uint32 greetEmoteDelay = fields[3].GetUInt32();
+ std::string greeting = fields[4].GetString();
+
+ _questGreetingStore[type][id] = new QuestGreeting(greetEmoteType, greetEmoteDelay, greeting);
+
+ ++count;
+ }
+ while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u quest_greeting in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+}
+
+void ObjectMgr::LoadQuestGreetingsLocales()
+{
+ uint32 oldMSTime = getMSTime();
+
+ _questGreetingLocaleStore.clear(); // need for reload case
+
+ // 0 1 2 3
+ QueryResult result = WorldDatabase.Query("SELECT ID, Type, Locale, Greeting FROM quest_greeting_locale");
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 quest_greeting locales. DB table `quest_greeting_locale` is empty.");
+ return;
+ }
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 id = fields[0].GetUInt32();
+ uint8 type = fields[1].GetUInt8();
+ // overwrite
+ switch (type)
+ {
+ case 0: // Creature
+ type = TYPEID_UNIT;
+ break;
+ case 1: // GameObject
+ type = TYPEID_GAMEOBJECT;
+ break;
+ default:
+ break;
+ }
+
+ std::string localeName = fields[2].GetString();
+ std::string greeting = fields[3].GetString();
+
+ QuestGreetingLocale& data = _questGreetingLocaleStore[MAKE_PAIR32(id, type)];
+ LocaleConstant locale = GetLocaleByName(localeName);
+ if (locale == LOCALE_enUS)
+ continue;
+
+ AddLocaleString(greeting, locale, data.greeting);
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u quest greeting locale strings in %u ms", uint32(_questGreetingLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
+}
+
void ObjectMgr::LoadTavernAreaTriggers()
{
uint32 oldMSTime = getMSTime();
@@ -6586,7 +6902,7 @@ uint32 ObjectMgr::GenerateGameObjectSpawnId()
{
if (_gameObjectSpawnId >= uint32(0xFFFFFF))
{
- TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
+ TC_LOG_ERROR("misc", "GameObject spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
World::StopNow(ERROR_EXIT_CODE);
}
return _gameObjectSpawnId++;
@@ -6937,7 +7253,7 @@ void ObjectMgr::LoadExplorationBaseXP()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty.");
return;
}
@@ -7044,7 +7360,7 @@ void ObjectMgr::LoadReputationRewardRate()
QueryResult result = WorldDatabase.Query("SELECT faction, quest_rate, quest_daily_rate, quest_weekly_rate, quest_monthly_rate, quest_repeatable_rate, creature_rate, spell_rate FROM reputation_reward_rate");
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded `reputation_reward_rate`, table is empty!");
+ TC_LOG_INFO("server.loading", ">> Loaded `reputation_reward_rate`, table is empty!");
return;
}
@@ -7139,7 +7455,7 @@ void ObjectMgr::LoadReputationOnKill()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
return;
}
@@ -7299,7 +7615,7 @@ void ObjectMgr::LoadPointsOfInterest()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
return;
}
@@ -7344,7 +7660,7 @@ void ObjectMgr::LoadQuestPOI()
QueryResult result = WorldDatabase.Query("SELECT QuestID, id, ObjectiveIndex, MapID, WorldMapAreaId, Floor, Priority, Flags FROM quest_poi order by QuestID");
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
return;
}
@@ -7436,7 +7752,7 @@ void ObjectMgr::LoadNPCSpellClickSpells()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
return;
}
@@ -7497,17 +7813,23 @@ void ObjectMgr::DeleteCreatureData(ObjectGuid::LowType guid)
// remove mapid*cellid -> guid_set map
CreatureData const* data = GetCreatureData(guid);
if (data)
+ {
RemoveCreatureFromGrid(guid, data);
+ OnDeleteSpawnData(data);
+ }
_creatureDataStore.erase(guid);
}
-void ObjectMgr::DeleteGOData(ObjectGuid::LowType guid)
+void ObjectMgr::DeleteGameObjectData(ObjectGuid::LowType guid)
{
// remove mapid*cellid -> guid_set map
- GameObjectData const* data = GetGOData(guid);
+ GameObjectData const* data = GetGameObjectData(guid);
if (data)
+ {
RemoveGameobjectFromGrid(guid, data);
+ OnDeleteSpawnData(data);
+ }
_gameObjectDataStore.erase(guid);
}
@@ -7524,7 +7846,7 @@ void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReve
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 quest relations from `%s`, table is empty.", table.c_str());
+ TC_LOG_INFO("server.loading", ">> Loaded 0 quest relations from `%s`, table is empty.", table.c_str());
return;
}
@@ -7550,7 +7872,7 @@ void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReve
if (reverseMap)
reverseMap->insert(QuestRelationsReverse::value_type(quest, id));
}
- else if (starter)
+ else
poolRelationMap->insert(PooledQuestRelation::value_type(quest, id));
++count;
@@ -7884,7 +8206,7 @@ bool ObjectMgr::LoadTrinityStrings()
QueryResult result = WorldDatabase.Query("SELECT entry, content_default, content_loc1, content_loc2, content_loc3, content_loc4, content_loc5, content_loc6, content_loc7, content_loc8 FROM trinity_string");
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 trinity strings. DB table `trinity_string` is empty. You have imported an incorrect database for more info search for TCE00003 on forum.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 trinity strings. DB table `trinity_string` is empty. You have imported an incorrect database for more info search for TCE00003 on forum.");
return false;
}
@@ -7930,7 +8252,7 @@ void ObjectMgr::LoadFishingBaseSkillLevel()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty.");
return;
}
@@ -8049,7 +8371,7 @@ void ObjectMgr::LoadGameTele()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
return;
}
@@ -8207,7 +8529,7 @@ void ObjectMgr::LoadMailLevelRewards()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
return;
}
@@ -8474,7 +8796,7 @@ void ObjectMgr::LoadGossipMenu()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 gossip_menu IDs. DB table `gossip_menu` is empty!");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu IDs. DB table `gossip_menu` is empty!");
return;
}
@@ -8512,7 +8834,7 @@ void ObjectMgr::LoadGossipMenuItems()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 gossip_menu_option IDs. DB table `gossip_menu_option` is empty!");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu_option IDs. DB table `gossip_menu_option` is empty!");
return;
}
@@ -8733,7 +9055,7 @@ void ObjectMgr::LoadScriptNames()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded empty set of Script Names!");
+ TC_LOG_INFO("server.loading", ">> Loaded empty set of Script Names!");
return;
}
@@ -9002,7 +9324,7 @@ void ObjectMgr::LoadFactionChangeAchievements()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
return;
}
@@ -9072,7 +9394,7 @@ void ObjectMgr::LoadFactionChangeQuests()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 faction change quest pairs. DB table `player_factionchange_quests` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 faction change quest pairs. DB table `player_factionchange_quests` is empty.");
return;
}
@@ -9142,7 +9464,7 @@ void ObjectMgr::LoadFactionChangeSpells()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
return;
}
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index 95e7019dacf..8a07723a34e 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -23,8 +23,10 @@
#include "ConditionMgr.h"
#include "CreatureData.h"
#include "DatabaseEnvFwd.h"
+#include "Errors.h"
#include "GameObjectData.h"
#include "ItemTemplate.h"
+#include "IteratorPair.h"
#include "NPCHandler.h"
#include "ObjectDefines.h"
#include "ObjectGuid.h"
@@ -38,6 +40,7 @@
class Item;
class Unit;
class Vehicle;
+class Map;
struct AccessRequirement;
struct DeclinedName;
struct DungeonEncounterEntry;
@@ -98,7 +101,7 @@ struct TempSummonData
// DB scripting commands
enum ScriptCommands
{
- SCRIPT_COMMAND_TALK = 0, // source/target = Creature, target = any, datalong = talk type (0=say, 1=whisper, 2=yell, 3=emote text, 4=boss emote text), datalong2 & 1 = player talk (instead of creature), dataint = string_id
+ SCRIPT_COMMAND_TALK = 0, // source/target = Creature, target = any, datalong = talk type (see ChatType enum), datalong2 & 1 = player talk (instead of creature), dataint = string_id
SCRIPT_COMMAND_EMOTE = 1, // source/target = Creature, datalong = emote id, datalong2 = 0: set emote state; > 0: play emote state
SCRIPT_COMMAND_FIELD_SET = 2, // source/target = Creature, datalong = field id, datalog2 = value
SCRIPT_COMMAND_MOVE_TO = 3, // source/target = Creature, datalong2 = time to reach, x/y/z = destination
@@ -419,6 +422,21 @@ std::string GetScriptsTableNameByType(ScriptsType type);
ScriptMapMap* GetScriptsMapByType(ScriptsType type);
std::string GetScriptCommandName(ScriptCommands command);
+struct TC_GAME_API InstanceSpawnGroupInfo
+{
+ enum
+ {
+ FLAG_ACTIVATE_SPAWN = 0x01,
+ FLAG_BLOCK_SPAWN = 0x02,
+
+ FLAG_ALL = (FLAG_ACTIVATE_SPAWN | FLAG_BLOCK_SPAWN)
+ };
+ uint8 BossStateId;
+ uint8 BossStates;
+ uint32 SpawnGroupId;
+ uint8 Flags;
+};
+
struct TC_GAME_API SpellClickInfo
{
uint32 spellId;
@@ -514,6 +532,11 @@ struct TrinityString
std::vector<std::string> Content;
};
+struct QuestGreetingLocale
+{
+ std::vector<std::string> greeting;
+};
+
typedef std::map<ObjectGuid, ObjectGuid> LinkedRespawnContainer;
typedef std::unordered_map<uint32, CreatureTemplate> CreatureTemplateContainer;
typedef std::unordered_map<uint32, CreatureAddon> CreatureTemplateAddonContainer;
@@ -529,6 +552,9 @@ typedef std::unordered_map<uint32, GameObjectTemplateAddon> GameObjectTemplateAd
typedef std::unordered_map<ObjectGuid::LowType, GameObjectData> GameObjectDataContainer;
typedef std::unordered_map<ObjectGuid::LowType, GameObjectAddon> GameObjectAddonContainer;
typedef std::unordered_map<uint32, std::vector<uint32>> GameObjectQuestItemMap;
+typedef std::unordered_map<uint32, SpawnGroupTemplateData> SpawnGroupDataContainer;
+typedef std::multimap<uint32, SpawnData const*> SpawnGroupLinkContainer;
+typedef std::unordered_map<uint16, std::vector<InstanceSpawnGroupInfo>> InstanceSpawnGroupContainer;
typedef std::map<TempSummonGroupKey, std::vector<TempSummonData>> TempSummonDataContainer;
typedef std::unordered_map<uint32, CreatureLocale> CreatureLocaleContainer;
typedef std::unordered_map<uint32, GameObjectLocale> GameObjectLocaleContainer;
@@ -553,6 +579,7 @@ struct PointOfInterestLocale
};
typedef std::unordered_map<uint32, PointOfInterestLocale> PointOfInterestLocaleContainer;
+typedef std::unordered_map<uint32, QuestGreetingLocale> QuestGreetingLocaleContainer;
typedef std::unordered_map<uint32, TrinityString> TrinityStringContainer;
@@ -773,6 +800,19 @@ struct QuestPOIWrapper
typedef std::unordered_map<uint32, QuestPOIWrapper> QuestPOIContainer;
+struct QuestGreeting
+{
+ uint16 greetEmoteType;
+ uint32 greetEmoteDelay;
+ std::string greeting;
+
+ QuestGreeting() : greetEmoteType(0), greetEmoteDelay(0) { }
+ QuestGreeting(uint16 _greetEmoteType, uint32 _greetEmoteDelay, std::string _greeting)
+ : greetEmoteType(_greetEmoteType), greetEmoteDelay(_greetEmoteDelay), greeting(_greeting) { }
+};
+
+typedef std::unordered_map<uint8, std::unordered_map<uint32, QuestGreeting const*>> QuestGreetingContainer;
+
struct GraveyardData
{
uint32 safeLocId;
@@ -803,6 +843,7 @@ SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry);
#define MAX_CHARTER_NAME 24 // max allowed by client name length
TC_GAME_API bool normalizePlayerName(std::string& name);
+#define SPAWNGROUP_MAP_UNSET 0xFFFFFFFF
struct LanguageDesc
{
@@ -976,6 +1017,7 @@ class TC_GAME_API ObjectMgr
}
GossipText const* GetGossipText(uint32 Text_ID) const;
+ QuestGreeting const* GetQuestGreeting(ObjectGuid guid) const;
WorldSafeLocsEntry const* GetDefaultGraveyard(uint32 team) const;
WorldSafeLocsEntry const* GetClosestGraveyard(float x, float y, float z, uint32 MapId, uint32 team) const;
@@ -1120,7 +1162,10 @@ class TC_GAME_API ObjectMgr
void LoadCreatureModelInfo();
void LoadEquipmentTemplates();
void LoadGameObjectLocales();
- void LoadGameobjects();
+ void LoadGameObjects();
+ void LoadSpawnGroupTemplates();
+ void LoadSpawnGroups();
+ void LoadInstanceSpawnGroups();
void LoadItemTemplates();
void LoadItemLocales();
void LoadItemSetNames();
@@ -1130,6 +1175,7 @@ class TC_GAME_API ObjectMgr
void LoadPageTextLocales();
void LoadGossipMenuItemsLocales();
void LoadPointOfInterestLocales();
+ void LoadQuestGreetingsLocales();
void LoadInstanceTemplate();
void LoadInstanceEncounters();
void LoadMailLevelRewards();
@@ -1141,6 +1187,7 @@ class TC_GAME_API ObjectMgr
void LoadAreaTriggerTeleports();
void LoadAccessRequirements();
void LoadQuestAreaTriggers();
+ void LoadQuestGreetings();
void LoadAreaTriggerScripts();
void LoadTavernAreaTriggers();
void LoadGameObjectForQuests();
@@ -1202,8 +1249,14 @@ class TC_GAME_API ObjectMgr
uint64 GenerateEquipmentSetGuid();
uint32 GenerateMailID();
uint32 GeneratePetNumber();
- uint32 GenerateCreatureSpawnId();
- uint32 GenerateGameObjectSpawnId();
+ ObjectGuid::LowType GenerateCreatureSpawnId();
+ ObjectGuid::LowType GenerateGameObjectSpawnId();
+
+ SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const { auto it = _spawnGroupDataStore.find(groupId); return it != _spawnGroupDataStore.end() ? &it->second : nullptr; }
+ SpawnGroupTemplateData const* GetDefaultSpawnGroup() const { return &_spawnGroupDataStore.at(0); }
+ SpawnGroupTemplateData const* GetLegacySpawnGroup() const { return &_spawnGroupDataStore.at(1); }
+ Trinity::IteratorPair<SpawnGroupLinkContainer::const_iterator> GetSpawnDataForGroup(uint32 groupId) const { return Trinity::Containers::MapEqualRange(_spawnGroupMapStore, groupId); }
+ std::vector<InstanceSpawnGroupInfo> const* GetSpawnGroupsForInstance(uint32 instanceId) const { auto it = _instanceSpawnGroupStore.find(instanceId); return it != _instanceSpawnGroupStore.end() ? &it->second : nullptr; }
MailLevelReward const* GetMailLevelReward(uint32 level, uint32 raceMask) const
{
@@ -1254,6 +1307,18 @@ class TC_GAME_API ObjectMgr
return nullptr;
}
+ SpawnData const* GetSpawnData(SpawnObjectType type, ObjectGuid::LowType guid)
+ {
+ if (type == SPAWN_TYPE_CREATURE)
+ return GetCreatureData(guid);
+ else if (type == SPAWN_TYPE_GAMEOBJECT)
+ return GetGameObjectData(guid);
+ else
+ ASSERT(false, "Invalid spawn object type %u", uint32(type));
+ return nullptr;
+ }
+ void OnDeleteSpawnData(SpawnData const* data);
+ CreatureDataContainer const& GetAllCreatureData() const { return _creatureDataStore; }
CreatureData const* GetCreatureData(ObjectGuid::LowType guid) const
{
CreatureDataContainer::const_iterator itr = _creatureDataStore.find(guid);
@@ -1274,6 +1339,15 @@ class TC_GAME_API ObjectMgr
if (itr == _creatureLocaleStore.end()) return nullptr;
return &itr->second;
}
+ GameObjectDataContainer const& GetAllGameObjectData() const { return _gameObjectDataStore; }
+ GameObjectData const* GetGameObjectData(ObjectGuid::LowType guid) const
+ {
+ GameObjectDataContainer::const_iterator itr = _gameObjectDataStore.find(guid);
+ if (itr == _gameObjectDataStore.end()) return nullptr;
+ return &itr->second;
+ }
+ GameObjectData& NewOrExistGameObjectData(ObjectGuid::LowType guid) { return _gameObjectDataStore[guid]; }
+ void DeleteGameObjectData(ObjectGuid::LowType guid);
GameObjectLocale const* GetGameObjectLocale(uint32 entry) const
{
GameObjectLocaleContainer::const_iterator itr = _gameObjectLocaleStore.find(entry);
@@ -1322,15 +1396,12 @@ class TC_GAME_API ObjectMgr
if (itr == _pointOfInterestLocaleStore.end()) return nullptr;
return &itr->second;
}
-
- GameObjectData const* GetGOData(ObjectGuid::LowType guid) const
+ QuestGreetingLocale const* GetQuestGreetingLocale(uint32 id) const
{
- GameObjectDataContainer::const_iterator itr = _gameObjectDataStore.find(guid);
- if (itr == _gameObjectDataStore.end()) return nullptr;
+ QuestGreetingLocaleContainer::const_iterator itr = _questGreetingLocaleStore.find(id);
+ if (itr == _questGreetingLocaleStore.end()) return nullptr;
return &itr->second;
}
- GameObjectData& NewGOData(ObjectGuid::LowType guid) { return _gameObjectDataStore[guid]; }
- void DeleteGOData(ObjectGuid::LowType guid);
TrinityString const* GetTrinityString(uint32 entry) const
{
@@ -1349,7 +1420,7 @@ class TC_GAME_API ObjectMgr
void RemoveCreatureFromGrid(ObjectGuid::LowType guid, CreatureData const* data);
void AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData const* data);
void RemoveGameobjectFromGrid(ObjectGuid::LowType guid, GameObjectData const* data);
- ObjectGuid::LowType AddGOData(uint32 entry, uint32 map, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay = 0);
+ ObjectGuid::LowType AddGameObjectData(uint32 entry, uint32 map, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay = 0);
ObjectGuid::LowType AddCreatureData(uint32 entry, uint32 map, Position const& pos, uint32 spawntimedelay = 0);
// reserved names
@@ -1458,8 +1529,8 @@ class TC_GAME_API ObjectMgr
std::atomic<uint32> _mailId;
std::atomic<uint32> _hiPetNumber;
- uint32 _creatureSpawnId;
- uint32 _gameObjectSpawnId;
+ ObjectGuid::LowType _creatureSpawnId;
+ ObjectGuid::LowType _gameObjectSpawnId;
// first free low guid for selected guid type
template<HighGuid high>
@@ -1484,6 +1555,7 @@ class TC_GAME_API ObjectMgr
TavernAreaTriggerContainer _tavernAreaTriggerStore;
GameObjectForQuestContainer _gameObjectForQuestStore;
GossipTextContainer _gossipTextStore;
+ QuestGreetingContainer _questGreetingStore;
AreaTriggerContainer _areaTriggerStore;
AreaTriggerScriptContainer _areaTriggerScriptStore;
AccessRequirementContainer _accessRequirementStore;
@@ -1579,6 +1651,9 @@ class TC_GAME_API ObjectMgr
GameObjectLocaleContainer _gameObjectLocaleStore;
GameObjectTemplateContainer _gameObjectTemplateStore;
GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore;
+ SpawnGroupDataContainer _spawnGroupDataStore;
+ SpawnGroupLinkContainer _spawnGroupMapStore;
+ InstanceSpawnGroupContainer _instanceSpawnGroupStore;
/// Stores temp summon data grouped by summoner's entry, summoner's type and group id
TempSummonDataContainer _tempSummonDataStore;
@@ -1591,6 +1666,7 @@ class TC_GAME_API ObjectMgr
PageTextLocaleContainer _pageTextLocaleStore;
GossipMenuItemsLocaleContainer _gossipMenuItemsLocaleStore;
PointOfInterestLocaleContainer _pointOfInterestLocaleStore;
+ QuestGreetingLocaleContainer _questGreetingLocaleStore;
TrinityStringContainer _trinityStringStore;
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h
index 897e43d5e03..16583516243 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.h
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.h
@@ -552,6 +552,11 @@ namespace Trinity
: ContainerInserter<Player*>(container),
i_phaseMask(searcher->GetPhaseMask()), i_check(check) { }
+ template<typename Container>
+ PlayerListSearcher(uint32 phaseMask, Container& container, Check & check)
+ : ContainerInserter<Player*>(container),
+ i_phaseMask(phaseMask), i_check(check) { }
+
void Visit(PlayerMapType &m);
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) { }
@@ -928,9 +933,9 @@ namespace Trinity
return false;
float searchRadius = i_range;
- if (i_incOwnRadius)
+ if (i_incOwnRadius)
searchRadius += i_obj->GetCombatReach();
- if (i_incTargetRadius)
+ if (i_incTargetRadius)
searchRadius += u->GetCombatReach();
if (!u->IsInMap(i_obj) || !u->InSamePhase(i_obj) || !u->IsWithinDoubleVerticalCylinder(i_obj, searchRadius, searchRadius))
@@ -954,7 +959,7 @@ namespace Trinity
class AnyGroupedUnitInObjectRangeCheck
{
public:
- AnyGroupedUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, bool raid, bool playerOnly = false, bool incOwnRadius = true, bool incTargetRadius = true)
+ AnyGroupedUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, bool raid, bool playerOnly = false, bool incOwnRadius = true, bool incTargetRadius = true)
: _source(obj), _refUnit(funit), _range(range), _raid(raid), _playerOnly(playerOnly), i_incOwnRadius(incOwnRadius), i_incTargetRadius(incTargetRadius) { }
bool operator()(Unit* u) const
@@ -977,9 +982,9 @@ namespace Trinity
return false;
float searchRadius = _range;
- if (i_incOwnRadius)
+ if (i_incOwnRadius)
searchRadius += _source->GetCombatReach();
- if (i_incTargetRadius)
+ if (i_incTargetRadius)
searchRadius += u->GetCombatReach();
return u->IsInMap(_source) && u->InSamePhase(_source) && u->IsWithinDoubleVerticalCylinder(_source, searchRadius, searchRadius);
@@ -1022,7 +1027,7 @@ namespace Trinity
bool operator()(Unit* u)
{
if (u->isTargetableForAttack() && i_obj->IsWithinDistInMap(u, i_range) &&
- !i_funit->IsFriendlyTo(u) && i_funit->CanSeeOrDetect(u))
+ (i_funit->IsInCombatWith(u) || i_funit->IsHostileTo(u)) && i_obj->CanSeeOrDetect(u))
{
i_range = i_obj->GetDistance(u); // use found unit range as new range limit for next check
return true;
@@ -1064,9 +1069,9 @@ namespace Trinity
return false;
float searchRadius = i_range;
- if (i_incOwnRadius)
+ if (i_incOwnRadius)
searchRadius += i_obj->GetCombatReach();
- if (i_incTargetRadius)
+ if (i_incTargetRadius)
searchRadius += u->GetCombatReach();
return u->IsInMap(i_obj) && u->InSamePhase(i_obj) && u->IsWithinDoubleVerticalCylinder(i_obj, searchRadius, searchRadius);
@@ -1321,6 +1326,27 @@ namespace Trinity
bool _reqAlive;
};
+ class AnyPlayerInPositionRangeCheck
+ {
+ public:
+ AnyPlayerInPositionRangeCheck(Position const* pos, float range, bool reqAlive = true) : _pos(pos), _range(range), _reqAlive(reqAlive) { }
+ bool operator()(Player* u)
+ {
+ if (_reqAlive && !u->IsAlive())
+ return false;
+
+ if (!u->IsWithinDist3d(_pos, _range))
+ return false;
+
+ return true;
+ }
+
+ private:
+ Position const* _pos;
+ float _range;
+ bool _reqAlive;
+ };
+
class NearestPlayerInObjectRangeCheck
{
public:
diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp
index 653c9d51d11..4dfca6f98c0 100644
--- a/src/server/game/Grids/ObjectGridLoader.cpp
+++ b/src/server/game/Grids/ObjectGridLoader.cpp
@@ -27,6 +27,7 @@
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "World.h"
+#include "ScriptMgr.h"
void ObjectGridEvacuator::Visit(CreatureMapType &m)
{
@@ -119,15 +120,56 @@ void LoadHelper(CellGuidSet const& guid_set, CellCoord &cell, GridRefManager<T>
for (CellGuidSet::const_iterator i_guid = guid_set.begin(); i_guid != guid_set.end(); ++i_guid)
{
T* obj = new T;
- ObjectGuid::LowType guid = *i_guid;
- //TC_LOG_INFO("misc", "DEBUG: LoadHelper from table: %s for (guid: %u) Loading", table, guid);
- if (!obj->LoadFromDB(guid, map))
+
+ // Don't spawn at all if there's a respawn time
+ if ((obj->GetTypeId() == TYPEID_UNIT && !map->GetCreatureRespawnTime(*i_guid)) || (obj->GetTypeId() == TYPEID_GAMEOBJECT && !map->GetGORespawnTime(*i_guid)))
{
- delete obj;
- continue;
- }
+ ObjectGuid::LowType guid = *i_guid;
+ //TC_LOG_INFO("misc", "DEBUG: LoadHelper from table: %s for (guid: %u) Loading", table, guid);
- AddObjectHelper(cell, m, count, map, obj);
+ if (obj->GetTypeId() == TYPEID_UNIT)
+ {
+ CreatureData const* cdata = sObjectMgr->GetCreatureData(guid);
+ ASSERT(cdata, "Tried to load creature with spawnId %u, but no such creature exists.", guid);
+ SpawnGroupTemplateData const* const group = cdata->spawnGroupData;
+ // If creature in manual spawn group, don't spawn here, unless group is already active.
+ if (!(group->flags & SPAWNGROUP_FLAG_SYSTEM))
+ if (!map->IsSpawnGroupActive(group->groupId))
+ {
+ delete obj;
+ continue;
+ }
+
+ // If script is blocking spawn, don't spawn but queue for a re-check in a little bit
+ if (!(group->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE) && !sScriptMgr->CanSpawn(guid, cdata->id, cdata, map))
+ {
+ map->SaveRespawnTime(SPAWN_TYPE_CREATURE, guid, cdata->id, time(NULL) + urand(4,7), map->GetZoneId(cdata->spawnPoint), Trinity::ComputeGridCoord(cdata->spawnPoint.GetPositionX(), cdata->spawnPoint.GetPositionY()).GetId(), false);
+ delete obj;
+ continue;
+ }
+ }
+ else if (obj->GetTypeId() == TYPEID_GAMEOBJECT)
+ {
+ // If gameobject in manual spawn group, don't spawn here, unless group is already active.
+ GameObjectData const* godata = sObjectMgr->GetGameObjectData(guid);
+ ASSERT(godata, "Tried to load gameobject with spawnId %u, but no such object exists.", guid);
+ if (!(godata->spawnGroupData->flags & SPAWNGROUP_FLAG_SYSTEM))
+ if (!map->IsSpawnGroupActive(godata->spawnGroupData->groupId))
+ {
+ delete obj;
+ continue;
+ }
+ }
+
+ if (!obj->LoadFromDB(guid, map, false, false))
+ {
+ delete obj;
+ continue;
+ }
+ AddObjectHelper(cell, m, count, map, obj);
+ }
+ else
+ delete obj;
}
}
diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp
index 76b7de68ea9..79df88b61a5 100644
--- a/src/server/game/Groups/Group.cpp
+++ b/src/server/game/Groups/Group.cpp
@@ -1534,8 +1534,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, Map* allowedMap)
// remove is_blocked so that the item is lootable by all players
LootItem* item = &(roll->itemSlot >= roll->getLoot()->items.size() ? roll->getLoot()->quest_items[roll->itemSlot - roll->getLoot()->items.size()] : roll->getLoot()->items[roll->itemSlot]);
- if (item)
- item->is_blocked = false;
+ item->is_blocked = false;
}
RollId.erase(rollI);
diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp
index 8b03ff91240..bb8f52b90fd 100644
--- a/src/server/game/Handlers/BattleGroundHandler.cpp
+++ b/src/server/game/Handlers/BattleGroundHandler.cpp
@@ -51,7 +51,8 @@ void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket& recvData)
return;
// Stop the npc if moving
- unit->StopMoving();
+ unit->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER));
+ unit->SetHomePosition(unit->GetPosition());
BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(unit->GetEntry());
diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp
index 6d884ca2ee9..4384ff82474 100644
--- a/src/server/game/Handlers/CalendarHandler.cpp
+++ b/src/server/game/Handlers/CalendarHandler.cpp
@@ -41,6 +41,7 @@ Copied events should probably have a new owner
#include "CharacterCache.h"
#include "DatabaseEnv.h"
#include "DBCStores.h"
+#include "GameEventMgr.h"
#include "Guild.h"
#include "GuildMgr.h"
#include "InstanceSaveMgr.h"
@@ -151,12 +152,10 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recvData*/)
data << uint32(boundCounter);
data.append(dataBuffer);
- /// @todo Fix this, how we do know how many and what holidays to send?
- uint32 holidayCount = 0;
- data << uint32(holidayCount);
- for (uint32 i = 0; i < holidayCount; ++i)
+ data << uint32(sGameEventMgr->modifiedHolidays.size());
+ for (uint32 entry : sGameEventMgr->modifiedHolidays)
{
- HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(666);
+ HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(entry);
data << uint32(holiday->Id); // m_ID
data << uint32(holiday->Region); // m_region, might be looping
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 9bdf294c9b3..df8f418c245 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -1560,6 +1560,9 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket& recvData)
InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sDest, uItem, false);
if (msg == EQUIP_ERR_OK)
{
+ if (_player->CanEquipItem(NULL_SLOT, dstpos, uItem, false) != EQUIP_ERR_OK)
+ continue;
+
_player->RemoveItem(INVENTORY_SLOT_BAG_0, i, true);
_player->StoreItem(sDest, uItem, true);
}
@@ -1572,6 +1575,9 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket& recvData)
if (item->GetPos() == dstpos)
continue;
+ if (_player->CanUnequipItem(dstpos, true) != EQUIP_ERR_OK)
+ continue;
+
_player->SwapItem(item->GetPos(), dstpos);
}
@@ -2084,7 +2090,7 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptr<Charact
ss << knownTitles[index] << ' ';
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_TITLES_FACTION_CHANGE);
- stmt->setString(0, ss.str().c_str());
+ stmt->setString(0, ss.str());
stmt->setUInt32(1, lowGuid);
trans->Append(stmt);
diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp
index eb3200ea87d..259bc08838e 100644
--- a/src/server/game/Handlers/ChatHandler.cpp
+++ b/src/server/game/Handlers/ChatHandler.cpp
@@ -126,7 +126,7 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
// LANG_ADDON should not be changed nor be affected by flood control
else
{
- // send in universal language if player in .gmon mode (ignore spell effects)
+ // send in universal language if player in .gm on mode (ignore spell effects)
if (sender->IsGameMaster())
lang = LANG_UNIVERSAL;
else
@@ -216,11 +216,15 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
if (msg.empty())
return;
- if (ChatHandler(this).ParseCommands(msg.c_str()))
- return;
-
+ if (lang == LANG_ADDON)
+ {
+ if (AddonChannelCommandHandler(this).ParseCommands(msg.c_str()))
+ return;
+ }
if (lang != LANG_ADDON)
{
+ if (ChatHandler(this).ParseCommands(msg.c_str()))
+ return;
// Strip invisible characters for non-addon messages
if (sWorld->getBoolConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
stripLineInvisibleChars(msg);
@@ -466,7 +470,7 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
if (Channel* chn = ChannelMgr::GetChannelForPlayerByNamePart(channel, sender))
{
sScriptMgr->OnPlayerChat(sender, type, lang, msg, chn);
- chn->Say(sender->GetGUID(), msg.c_str(), lang);
+ chn->Say(sender->GetGUID(), msg, lang);
}
break;
}
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index 9f3e2a52efe..ee2666c9f96 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -615,8 +615,8 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid)
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// Stop the npc if moving
- if (vendor->HasUnitState(UNIT_STATE_MOVING))
- vendor->StopMoving();
+ vendor->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER));
+ vendor->SetHomePosition(vendor->GetPosition());
VendorItemData const* items = vendor->GetVendorItems();
if (!items)
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index a632bf1979c..9a130fab45f 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -51,6 +51,7 @@
#include "WhoListStorage.h"
#include "World.h"
#include "WorldPacket.h"
+#include <cstdarg>
#include <zlib.h>
void WorldSession::HandleRepopRequestOpcode(WorldPacket& recvData)
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index 8b64e0e08bb..a8b365e5db2 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -24,9 +24,10 @@
#include "Corpse.h"
#include "Player.h"
#include "MapManager.h"
+#include "MotionMaster.h"
+#include "MovementGenerator.h"
#include "Transport.h"
#include "Battleground.h"
-#include "WaypointMovementGenerator.h"
#include "InstanceSaveMgr.h"
#include "ObjectMgr.h"
#include "Vehicle.h"
@@ -133,8 +134,8 @@ void WorldSession::HandleMoveWorldportAck()
if (!_player->InBattleground())
{
// short preparations to continue flight
- FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
- flight->Initialize(GetPlayer());
+ MovementGenerator* movementGenerator = GetPlayer()->GetMotionMaster()->top();
+ movementGenerator->Initialize(GetPlayer());
return;
}
diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp
index ddde13ae860..59344509283 100644
--- a/src/server/game/Handlers/NPCHandler.cpp
+++ b/src/server/game/Handlers/NPCHandler.cpp
@@ -38,6 +38,7 @@
#include "ScriptMgr.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
+#include "World.h"
#include "WorldPacket.h"
enum StableResultCode
@@ -322,11 +323,9 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData)
//if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
// GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
- // and if he has pure gossip or is banker and moves or is tabard designer?
- //if (unit->IsArmorer() || unit->IsCivilian() || unit->IsQuestGiver() || unit->IsServiceProvider() || unit->IsGuard())
- {
- unit->StopMoving();
- }
+ // Stop the npc if moving
+ unit->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER));
+ unit->SetHomePosition(unit->GetPosition());
// If spiritguide, no need for gossip menu, just put player into resurrect queue
if (unit->IsSpiritGuide())
diff --git a/src/server/game/Handlers/PetitionsHandler.cpp b/src/server/game/Handlers/PetitionsHandler.cpp
index 4e47f260fff..03087ce5e7e 100644
--- a/src/server/game/Handlers/PetitionsHandler.cpp
+++ b/src/server/game/Handlers/PetitionsHandler.cpp
@@ -420,7 +420,7 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData)
{
if (_player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
- SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", _player->GetName().c_str(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", _player->GetName(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
return;
}
@@ -430,13 +430,13 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData)
if (_player->GetArenaTeamId(slot))
{
- SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName().c_str(), ERR_ALREADY_IN_ARENA_TEAM_S);
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_IN_ARENA_TEAM_S);
return;
}
if (_player->GetArenaTeamIdInvited())
{
- SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName().c_str(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
return;
}
}
@@ -549,7 +549,7 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket& recvData)
if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
// player is too low level to join an arena team
- SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName().c_str(), "", ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
return;
}
@@ -560,13 +560,13 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket& recvData)
if (player->GetArenaTeamId(slot))
{
// player is already in an arena team
- SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName().c_str(), "", ERR_ALREADY_IN_ARENA_TEAM_S);
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S);
return;
}
if (player->GetArenaTeamIdInvited())
{
- SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName().c_str(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
+ SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
return;
}
}
diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp
index f95fb01ed36..23c0ac25524 100644
--- a/src/server/game/Handlers/QuestHandler.cpp
+++ b/src/server/game/Handlers/QuestHandler.cpp
@@ -91,8 +91,10 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket& recvData)
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
// Stop the npc if moving
- creature->StopMoving();
+ creature->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER));
+ creature->SetHomePosition(creature->GetPosition());
_player->PlayerTalkClass->ClearMenus();
if (creature->AI()->GossipHello(_player))
diff --git a/src/server/game/Handlers/TaxiHandler.cpp b/src/server/game/Handlers/TaxiHandler.cpp
index 3912707ea25..6adc2d04aed 100644
--- a/src/server/game/Handlers/TaxiHandler.cpp
+++ b/src/server/game/Handlers/TaxiHandler.cpp
@@ -18,9 +18,12 @@
#include "WorldSession.h"
#include "Common.h"
+#include "Creature.h"
#include "DatabaseEnv.h"
#include "DBCStores.h"
#include "Log.h"
+#include "MotionMaster.h"
+#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "Player.h"
@@ -39,25 +42,22 @@ void WorldSession::HandleTaxiNodeStatusQueryOpcode(WorldPacket& recvData)
void WorldSession::SendTaxiStatus(ObjectGuid guid)
{
- // cheating checks
- Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
- if (!unit)
+ Player* const player = GetPlayer();
+ Creature* unit = ObjectAccessor::GetCreature(*player, guid);
+ if (!unit || unit->IsHostileTo(player) || !unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_FLIGHTMASTER))
{
TC_LOG_DEBUG("network", "WorldSession::SendTaxiStatus - %s not found or you can't interact with him.", guid.ToString().c_str());
return;
}
- uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeam());
-
- // not found nearest
- if (curloc == 0)
+ // find taxi node
+ uint32 nearest = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), player->GetTeam());
+ if (!nearest)
return;
- TC_LOG_DEBUG("network", "WORLD: current location %u ", curloc);
-
WorldPacket data(SMSG_TAXINODE_STATUS, 9);
data << guid;
- data << uint8(GetPlayer()->m_taxi.IsTaximaskNodeKnown(curloc) ? 1 : 0);
+ data << uint8(player->m_taxi.IsTaximaskNodeKnown(nearest) ? 1 : 0);
SendPacket(&data);
}
@@ -119,8 +119,7 @@ void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathN
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
- while (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
- GetPlayer()->GetMotionMaster()->MovementExpired(false);
+ GetPlayer()->GetMotionMaster()->Clear(MOTION_SLOT_CONTROLLED);
if (mountDisplayId)
GetPlayer()->Mount(mountDisplayId);
diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp
index 3fa7eb52d6e..154254e25b2 100644
--- a/src/server/game/Instances/InstanceScript.cpp
+++ b/src/server/game/Instances/InstanceScript.cpp
@@ -46,7 +46,7 @@ BossBoundaryData::~BossBoundaryData()
delete it->Boundary;
}
-InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0)
+InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0), _instanceSpawnGroups(sObjectMgr->GetSpawnGroupsForInstance(map->GetId()))
{
#ifdef TRINITY_API_USE_DYNAMIC_LINKING
uint32 scriptId = sObjectMgr->GetInstanceTemplate(map->GetId())->ScriptId;
@@ -186,27 +186,6 @@ void InstanceScript::LoadObjectData(ObjectData const* data, ObjectInfoMap& objec
}
}
-void InstanceScript::UpdateMinionState(Creature* minion, EncounterState state)
-{
- switch (state)
- {
- case NOT_STARTED:
- if (!minion->IsAlive())
- minion->Respawn();
- else if (minion->IsInCombat())
- minion->AI()->EnterEvadeMode();
- break;
- case IN_PROGRESS:
- if (!minion->IsAlive())
- minion->Respawn();
- else if (!minion->GetVictim())
- minion->AI()->DoZoneInCombat();
- break;
- default:
- break;
- }
-}
-
void InstanceScript::UpdateDoorState(GameObject* door)
{
DoorInfoMapBounds range = doors.equal_range(door->GetEntry());
@@ -236,6 +215,60 @@ void InstanceScript::UpdateDoorState(GameObject* door)
door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY);
}
+void InstanceScript::UpdateMinionState(Creature* minion, EncounterState state)
+{
+ switch (state)
+ {
+ case NOT_STARTED:
+ if (!minion->IsAlive())
+ minion->Respawn();
+ else if (minion->IsInCombat())
+ minion->AI()->EnterEvadeMode();
+ break;
+ case IN_PROGRESS:
+ if (!minion->IsAlive())
+ minion->Respawn();
+ else if (!minion->GetVictim())
+ minion->AI()->DoZoneInCombat();
+ break;
+ default:
+ break;
+ }
+}
+
+void InstanceScript::UpdateSpawnGroups()
+{
+ if (!_instanceSpawnGroups)
+ return;
+ enum states { BLOCK, SPAWN, FORCEBLOCK };
+ std::unordered_map<uint32, states> newStates;
+ for (auto it = _instanceSpawnGroups->begin(), end = _instanceSpawnGroups->end(); it != end; ++it)
+ {
+ InstanceSpawnGroupInfo const& info = *it;
+ states& curValue = newStates[info.SpawnGroupId]; // makes sure there's a BLOCK value in the map
+ if (curValue == FORCEBLOCK) // nothing will change this
+ continue;
+ if (!((1 << GetBossState(info.BossStateId)) & info.BossStates))
+ continue;
+ if (info.Flags & InstanceSpawnGroupInfo::FLAG_BLOCK_SPAWN)
+ curValue = FORCEBLOCK;
+ else if (info.Flags & InstanceSpawnGroupInfo::FLAG_ACTIVATE_SPAWN)
+ curValue = SPAWN;
+ }
+ for (auto const& pair : newStates)
+ {
+ uint32 const groupId = pair.first;
+ bool const doSpawn = (pair.second == SPAWN);
+ if (instance->IsSpawnGroupActive(groupId) == doSpawn)
+ continue; // nothing to do here
+ // if we should spawn group, then spawn it...
+ if (doSpawn)
+ instance->SpawnGroupSpawn(groupId);
+ else // otherwise, set it as inactive so it no longer respawns (but don't despawn it)
+ instance->SetSpawnGroupActive(groupId, false);
+ }
+}
+
BossInfo* InstanceScript::GetBossInfo(uint32 id)
{
ASSERT(id < bosses.size());
@@ -310,7 +343,7 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
if (bossInfo->state == TO_BE_DECIDED) // loading
{
bossInfo->state = state;
- //TC_LOG_ERROR("misc", "Inialize boss %u state as %u.", id, (uint32)state);
+ TC_LOG_DEBUG("scripts", "InstanceScript: Initialize boss %u state as %s (map %u, %u).", id, GetBossStateName(state), instance->GetId(), instance->GetInstanceId());
return false;
}
else
@@ -318,6 +351,12 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
if (bossInfo->state == state)
return false;
+ if (bossInfo->state == DONE)
+ {
+ TC_LOG_ERROR("map", "InstanceScript: Tried to set instance state from %s back to %s for map %u, instance id %u. Blocked!", GetBossStateName(bossInfo->state), GetBossStateName(state), instance->GetId(), instance->GetInstanceId());
+ return false;
+ }
+
if (state == DONE)
for (GuidSet::iterator i = bossInfo->minion.begin(); i != bossInfo->minion.end(); ++i)
if (Creature* minion = instance->GetCreature(*i))
@@ -337,6 +376,7 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
if (Creature* minion = instance->GetCreature(*i))
UpdateMinionState(minion, state);
+ UpdateSpawnGroups();
return true;
}
return false;
@@ -347,6 +387,13 @@ bool InstanceScript::_SkipCheckRequiredBosses(Player const* player /*= nullptr*/
return player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES);
}
+void InstanceScript::Create()
+{
+ for (size_t i = 0; i < bosses.size(); ++i)
+ SetBossState(i, NOT_STARTED);
+ UpdateSpawnGroups();
+}
+
void InstanceScript::Load(char const* data)
{
if (!data)
@@ -397,6 +444,7 @@ void InstanceScript::ReadSaveDataBossStates(std::istringstream& data)
if (buff < TO_BE_DECIDED)
SetBossState(bossId, EncounterState(buff));
}
+ UpdateSpawnGroups();
}
std::string InstanceScript::GetSaveData()
@@ -691,7 +739,7 @@ void InstanceScript::UpdateEncounterStateForSpellCast(uint32 spellId, Unit* sour
UpdateEncounterState(ENCOUNTER_CREDIT_CAST_SPELL, spellId, source);
}
-std::string InstanceScript::GetBossStateName(uint8 state)
+/*static*/ char const* InstanceScript::GetBossStateName(uint8 state)
{
// See enum EncounterState in InstanceScript.h
switch (state)
diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h
index 55452ebf3c1..d9802aa548b 100644
--- a/src/server/game/Instances/InstanceScript.h
+++ b/src/server/game/Instances/InstanceScript.h
@@ -34,6 +34,7 @@
class AreaBoundary;
class Creature;
class GameObject;
+struct InstanceSpawnGroupInfo;
class Map;
class ModuleReference;
class Player;
@@ -156,7 +157,10 @@ class TC_GAME_API InstanceScript : public ZoneScript
// KEEPING THIS METHOD ONLY FOR BACKWARD COMPATIBILITY !!!
virtual void Initialize() { }
- // On load
+ // On instance load, exactly ONE of these methods will ALWAYS be called:
+ // if we're starting without any saved instance data
+ virtual void Create();
+ // if we're loading existing instance save data
virtual void Load(char const* data);
// When save is needed, this function generates the data
@@ -223,7 +227,7 @@ class TC_GAME_API InstanceScript : public ZoneScript
virtual bool SetBossState(uint32 id, EncounterState state);
EncounterState GetBossState(uint32 id) const { return id < bosses.size() ? bosses[id].state : TO_BE_DECIDED; }
- static std::string GetBossStateName(uint8 state);
+ static char const* GetBossStateName(uint8 state);
CreatureBoundary const* GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : nullptr; }
// Achievement criteria additional requirements check
@@ -249,6 +253,11 @@ class TC_GAME_API InstanceScript : public ZoneScript
uint32 GetEncounterCount() const { return bosses.size(); }
+ // Only used by areatriggers that inherit from OnlyOnceAreaTriggerScript
+ void MarkAreaTriggerDone(uint32 id) { _activatedAreaTriggers.insert(id); }
+ void ResetAreaTriggerDone(uint32 id) { _activatedAreaTriggers.erase(id); }
+ bool IsAreaTriggerDone(uint32 id) const { return _activatedAreaTriggers.find(id) != _activatedAreaTriggers.end(); }
+
protected:
void SetHeaders(std::string const& dataHeaders);
void SetBossNumber(uint32 number) { bosses.resize(number); }
@@ -267,6 +276,8 @@ class TC_GAME_API InstanceScript : public ZoneScript
virtual void UpdateDoorState(GameObject* door);
void UpdateMinionState(Creature* minion, EncounterState state);
+ void UpdateSpawnGroups();
+
// Exposes private data that should never be modified unless exceptional cases.
// Pay very much attention at how the returned BossInfo data is modified to avoid issues.
BossInfo* GetBossInfo(uint32 id);
@@ -293,6 +304,8 @@ class TC_GAME_API InstanceScript : public ZoneScript
ObjectInfoMap _gameObjectInfo;
ObjectGuidMap _objectGuids;
uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets
+ std::vector<InstanceSpawnGroupInfo> const* const _instanceSpawnGroups;
+ std::unordered_set<uint32> _activatedAreaTriggers;
#ifdef TRINITY_API_USE_DYNAMIC_LINKING
// Strong reference to the associated script module
diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp
index 7c6cc28fb48..f93ee0a91bc 100644
--- a/src/server/game/Loot/LootMgr.cpp
+++ b/src/server/game/Loot/LootMgr.cpp
@@ -812,7 +812,7 @@ void LoadLootTemplates_Creature()
if (count)
TC_LOG_INFO("server.loading", ">> Loaded %u creature loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 creature loot templates. DB table `creature_loot_template` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 creature loot templates. DB table `creature_loot_template` is empty");
}
void LoadLootTemplates_Disenchant()
@@ -845,7 +845,7 @@ void LoadLootTemplates_Disenchant()
if (count)
TC_LOG_INFO("server.loading", ">> Loaded %u disenchanting loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 disenchanting loot templates. DB table `disenchant_loot_template` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 disenchanting loot templates. DB table `disenchant_loot_template` is empty");
}
void LoadLootTemplates_Fishing()
@@ -868,7 +868,7 @@ void LoadLootTemplates_Fishing()
if (count)
TC_LOG_INFO("server.loading", ">> Loaded %u fishing loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 fishing loot templates. DB table `fishing_loot_template` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 fishing loot templates. DB table `fishing_loot_template` is empty");
}
void LoadLootTemplates_Gameobject()
@@ -902,7 +902,7 @@ void LoadLootTemplates_Gameobject()
if (count)
TC_LOG_INFO("server.loading", ">> Loaded %u gameobject loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 gameobject loot templates. DB table `gameobject_loot_template` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject loot templates. DB table `gameobject_loot_template` is empty");
}
void LoadLootTemplates_Item()
@@ -926,7 +926,7 @@ void LoadLootTemplates_Item()
if (count)
TC_LOG_INFO("server.loading", ">> Loaded %u item loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 item loot templates. DB table `item_loot_template` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 item loot templates. DB table `item_loot_template` is empty");
}
void LoadLootTemplates_Milling()
@@ -955,7 +955,7 @@ void LoadLootTemplates_Milling()
if (count)
TC_LOG_INFO("server.loading", ">> Loaded %u milling loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 milling loot templates. DB table `milling_loot_template` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 milling loot templates. DB table `milling_loot_template` is empty");
}
void LoadLootTemplates_Pickpocketing()
@@ -989,7 +989,7 @@ void LoadLootTemplates_Pickpocketing()
if (count)
TC_LOG_INFO("server.loading", ">> Loaded %u pickpocketing loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 pickpocketing loot templates. DB table `pickpocketing_loot_template` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 pickpocketing loot templates. DB table `pickpocketing_loot_template` is empty");
}
void LoadLootTemplates_Prospecting()
@@ -1018,7 +1018,7 @@ void LoadLootTemplates_Prospecting()
if (count)
TC_LOG_INFO("server.loading", ">> Loaded %u prospecting loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 prospecting loot templates. DB table `prospecting_loot_template` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 prospecting loot templates. DB table `prospecting_loot_template` is empty");
}
void LoadLootTemplates_Mail()
@@ -1042,7 +1042,7 @@ void LoadLootTemplates_Mail()
if (count)
TC_LOG_INFO("server.loading", ">> Loaded %u mail loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 mail loot templates. DB table `mail_loot_template` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 mail loot templates. DB table `mail_loot_template` is empty");
}
void LoadLootTemplates_Skinning()
@@ -1076,7 +1076,7 @@ void LoadLootTemplates_Skinning()
if (count)
TC_LOG_INFO("server.loading", ">> Loaded %u skinning loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 skinning loot templates. DB table `skinning_loot_template` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 skinning loot templates. DB table `skinning_loot_template` is empty");
}
void LoadLootTemplates_Spell()
@@ -1116,7 +1116,7 @@ void LoadLootTemplates_Spell()
if (count)
TC_LOG_INFO("server.loading", ">> Loaded %u spell loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
else
- TC_LOG_ERROR("server.loading", ">> Loaded 0 spell loot templates. DB table `spell_loot_template` is empty");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 spell loot templates. DB table `spell_loot_template` is empty");
}
void LoadLootTemplates_Reference()
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 0793422f8ad..61f24f2b6e5 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -23,6 +23,7 @@
#include "DisableMgr.h"
#include "DynamicTree.h"
#include "GameObjectModel.h"
+#include "GameTime.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "GridStates.h"
@@ -37,6 +38,7 @@
#include "ObjectGridLoader.h"
#include "ObjectMgr.h"
#include "Pet.h"
+#include "PoolMgr.h"
#include "ScriptMgr.h"
#include "Transport.h"
#include "Vehicle.h"
@@ -61,6 +63,10 @@ Map::~Map()
sScriptMgr->OnDestroyMap(this);
+ // Delete all waiting spawns, else there will be a memory leak
+ // This doesn't delete from database.
+ DeleteRespawnInfo();
+
while (!i_worldObjects.empty())
{
WorldObject* obj = *i_worldObjects.begin();
@@ -253,7 +259,7 @@ m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE),
m_VisibilityNotifyPeriod(DEFAULT_VISIBILITY_NOTIFY_PERIOD),
m_activeNonPlayersIter(m_activeNonPlayers.end()), _transportsUpdateIter(_transports.end()),
i_gridExpiry(expiry),
-i_scriptLock(false), _defaultLight(GetDefaultMapLight(id))
+i_scriptLock(false), _respawnCheckTimer(0), _defaultLight(GetDefaultMapLight(id))
{
m_parentMap = (_parent ? _parent : this);
for (unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx)
@@ -266,6 +272,8 @@ i_scriptLock(false), _defaultLight(GetDefaultMapLight(id))
}
}
+ _zonePlayerCountMap.clear();
+
//lets initialize visibility distance for map
Map::InitVisibilityDistance();
@@ -522,6 +530,29 @@ bool Map::EnsureGridLoaded(Cell const& cell)
return false;
}
+void Map::GridMarkNoUnload(uint32 x, uint32 y)
+{
+ // First make sure this grid is loaded
+ float gX = ((float(x) - 0.5f - CENTER_GRID_ID) * SIZE_OF_GRIDS) + (CENTER_GRID_OFFSET * 2);
+ float gY = ((float(y) - 0.5f - CENTER_GRID_ID) * SIZE_OF_GRIDS) + (CENTER_GRID_OFFSET * 2);
+ Cell cell = Cell(gX, gY);
+ EnsureGridLoaded(cell);
+
+ // Mark as don't unload
+ NGridType* grid = getNGrid(x, y);
+ grid->setUnloadExplicitLock(true);
+}
+
+void Map::GridUnmarkNoUnload(uint32 x, uint32 y)
+{
+ // If grid is loaded, clear unload lock
+ if (IsGridLoaded(GridCoord(x, y)))
+ {
+ NGridType* grid = getNGrid(x, y);
+ grid->setUnloadExplicitLock(false);
+ }
+}
+
void Map::LoadGrid(float x, float y)
{
EnsureGridLoaded(Cell(x, y));
@@ -689,6 +720,21 @@ void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Trinity::Obj
}
}
+void Map::UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone)
+{
+ // Nothing to do if no change
+ if (oldZone == newZone)
+ return;
+
+ if (oldZone != MAP_INVALID_ZONE)
+ {
+ uint32& oldZoneCount = _zonePlayerCountMap[oldZone];
+ ASSERT(oldZoneCount, "A player left zone %u (went to %u) - but there were no players in the zone!", oldZone, newZone);
+ --oldZoneCount;
+ }
+ ++_zonePlayerCountMap[newZone];
+}
+
void Map::Update(uint32 t_diff)
{
_dynamicTree.update(t_diff);
@@ -704,6 +750,16 @@ void Map::Update(uint32 t_diff)
session->Update(t_diff, updater);
}
}
+
+ /// process any due respawns
+ if (_respawnCheckTimer <= t_diff)
+ {
+ ProcessRespawns();
+ _respawnCheckTimer = sWorld->getIntConfig(CONFIG_RESPAWN_MINCHECKINTERVALMS);
+ }
+ else
+ _respawnCheckTimer -= t_diff;
+
/// update active cells around players and active objects
resetMarkedCells();
@@ -885,6 +941,8 @@ void Map::ProcessRelocationNotifies(const uint32 diff)
void Map::RemovePlayerFromMap(Player* player, bool remove)
{
+ // Before leaving map, update zone/area for stats
+ player->UpdateZone(MAP_INVALID_ZONE, 0);
sScriptMgr->OnPlayerLeaveMap(this, player);
player->getHostileRefManager().deleteReferences(); // multithreading crashfix
@@ -2639,10 +2697,7 @@ void Map::GetFullTerrainStatusForPosition(float x, float y, float z, PositionFul
else
{
data.floorZ = mapHeight;
- if (gmap)
- data.areaId = gmap->getArea(x, y);
- else
- data.areaId = 0;
+ data.areaId = gmap->getArea(x, y);
if (!data.areaId)
data.areaId = i_mapEntry->linked_zone;
@@ -2744,11 +2799,6 @@ bool Map::getObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float
return result;
}
-float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=true*/, float maxSearchDist/*=DEFAULT_HEIGHT_SEARCH*/) const
-{
- return std::max<float>(GetHeight(x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phasemask, x, y, z, maxSearchDist));
-}
-
bool Map::IsInWater(float x, float y, float pZ, LiquidData* data) const
{
LiquidData liquid_status;
@@ -2867,6 +2917,474 @@ void Map::SendObjectUpdates()
}
}
+bool Map::CheckRespawn(RespawnInfo* info)
+{
+ uint32 poolId = info->spawnId ? sPoolMgr->IsPartOfAPool(info->type, info->spawnId) : 0;
+ // First, check if there's already an instance of this object that would block the respawn
+ // Only do this for unpooled spawns
+ if (!poolId)
+ {
+ bool doDelete = false;
+ switch (info->type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ {
+ // escort check for creatures only (if the world config boolean is set)
+ bool isEscort = false;
+ if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && info->type == SPAWN_TYPE_CREATURE)
+ if (CreatureData const* cdata = sObjectMgr->GetCreatureData(info->spawnId))
+ if (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC)
+ isEscort = true;
+
+ auto range = _creatureBySpawnIdStore.equal_range(info->spawnId);
+ for (auto it = range.first; it != range.second; ++it)
+ {
+ Creature* creature = it->second;
+ if (!creature->IsAlive())
+ continue;
+ // escort NPCs are allowed to respawn as long as all other instances are already escorting
+ if (isEscort && creature->IsEscortNPC(true))
+ continue;
+ doDelete = true;
+ break;
+ }
+ break;
+ }
+ case SPAWN_TYPE_GAMEOBJECT:
+ // gameobject check is simpler - they cannot be dead or escorting
+ if (_gameobjectBySpawnIdStore.find(info->spawnId) != _gameobjectBySpawnIdStore.end())
+ doDelete = true;
+ break;
+ default:
+ ASSERT(false, "Invalid spawn type %u with spawnId %u on map %u", uint32(info->type), info->spawnId, GetId());
+ return true;
+ }
+ if (doDelete)
+ {
+ info->respawnTime = 0;
+ return false;
+ }
+ }
+
+ // next, check linked respawn time
+ ObjectGuid thisGUID = ObjectGuid((info->type == SPAWN_TYPE_GAMEOBJECT) ? HighGuid::GameObject : HighGuid::Unit, info->entry, info->spawnId);
+ if (time_t linkedTime = GetLinkedRespawnTime(thisGUID))
+ {
+ time_t now = time(NULL);
+ time_t respawnTime;
+ if (linkedTime == std::numeric_limits<time_t>::max())
+ respawnTime = linkedTime;
+ else if (sObjectMgr->GetLinkedRespawnGuid(thisGUID) == thisGUID) // never respawn, save "something" in DB
+ respawnTime = now + WEEK;
+ else // set us to check again shortly after linked unit
+ respawnTime = std::max<time_t>(now, linkedTime) + urand(5, 15);
+ info->respawnTime = respawnTime;
+ return false;
+ }
+
+ // now, check if we're part of a pool
+ if (poolId)
+ {
+ // ok, part of a pool - hand off to pool logic to handle this, we're just going to remove the respawn and call it a day
+ if (info->type == SPAWN_TYPE_GAMEOBJECT)
+ sPoolMgr->UpdatePool<GameObject>(poolId, info->spawnId);
+ else if (info->type == SPAWN_TYPE_CREATURE)
+ sPoolMgr->UpdatePool<Creature>(poolId, info->spawnId);
+ else
+ ASSERT(false, "Invalid spawn type %u (spawnid %u) on map %u", uint32(info->type), info->spawnId, GetId());
+ info->respawnTime = 0;
+ return false;
+ }
+
+ // if we're a creature, see if the script objects to us spawning
+ if (info->type == SPAWN_TYPE_CREATURE)
+ {
+ if (!sScriptMgr->CanSpawn(info->spawnId, info->entry, sObjectMgr->GetCreatureData(info->spawnId), this))
+ { // if a script blocks our respawn, schedule next check in a little bit
+ info->respawnTime = time(NULL) + urand(4, 7);
+ return false;
+ }
+ }
+ return true;
+}
+
+void Map::DoRespawn(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 gridId)
+{
+ if (!IsGridLoaded(gridId)) // if grid isn't loaded, this will be processed in grid load handler
+ return;
+
+ switch (type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ {
+ Creature* obj = new Creature();
+ if (!obj->LoadFromDB(spawnId, this, true, true))
+ delete obj;
+ break;
+ }
+ case SPAWN_TYPE_GAMEOBJECT:
+ {
+ GameObject* obj = new GameObject();
+ if (!obj->LoadFromDB(spawnId, this, true))
+ delete obj;
+ break;
+ }
+ default:
+ ASSERT(false, "Invalid spawn type %u (spawnid %u) on map %u", uint32(type), spawnId, GetId());
+ }
+}
+
+void Map::Respawn(RespawnInfo* info, bool force, SQLTransaction dbTrans)
+{
+ if (!force && !CheckRespawn(info))
+ {
+ if (info->respawnTime)
+ SaveRespawnTime(info->type, info->spawnId, info->entry, info->respawnTime, info->zoneId, info->gridId, true, true, dbTrans);
+ else
+ RemoveRespawnTime(info);
+ return;
+ }
+
+ // remove the actual respawn record first - since this deletes it, we save what we need
+ SpawnObjectType const type = info->type;
+ uint32 const gridId = info->gridId;
+ ObjectGuid::LowType const spawnId = info->spawnId;
+ RemoveRespawnTime(info);
+ DoRespawn(type, spawnId, gridId);
+}
+
+void Map::Respawn(RespawnVector& respawnData, bool force, SQLTransaction dbTrans)
+{
+ SQLTransaction trans = dbTrans ? dbTrans : CharacterDatabase.BeginTransaction();
+ for (RespawnInfo* info : respawnData)
+ Respawn(info, force, trans);
+ if (!dbTrans)
+ CharacterDatabase.CommitTransaction(trans);
+}
+
+void Map::AddRespawnInfo(RespawnInfo& info, bool replace)
+{
+ if (!info.spawnId)
+ return;
+
+ RespawnInfoMap& bySpawnIdMap = GetRespawnMapForType(info.type);
+
+ auto it = bySpawnIdMap.find(info.spawnId);
+ if (it != bySpawnIdMap.end()) // spawnid already has a respawn scheduled
+ {
+ RespawnInfo* const existing = it->second;
+ if (replace || info.respawnTime < existing->respawnTime) // delete existing in this case
+ DeleteRespawnInfo(existing);
+ else // don't delete existing, instead replace respawn time so caller saves the correct time
+ {
+ info.respawnTime = existing->respawnTime;
+ return;
+ }
+ }
+
+ // if we get to this point, we should insert the respawninfo (there either was no prior entry, or it was deleted already)
+ RespawnInfo * ri = new RespawnInfo(info);
+ ri->handle = _respawnTimes.push(ri);
+ bool success = bySpawnIdMap.emplace(ri->spawnId, ri).second;
+ ASSERT(success, "Insertion of respawn info with id (%u,%u) into spawn id map failed - state desync.", uint32(ri->type), ri->spawnId);
+}
+
+static void PushRespawnInfoFrom(RespawnVector& data, RespawnInfoMap const& map, uint32 zoneId)
+{
+ for (auto const& pair : map)
+ if (!zoneId || pair.second->zoneId == zoneId)
+ data.push_back(pair.second);
+}
+void Map::GetRespawnInfo(RespawnVector& respawnData, SpawnObjectTypeMask types, uint32 zoneId) const
+{
+ if (types & SPAWN_TYPEMASK_CREATURE)
+ PushRespawnInfoFrom(respawnData, _creatureRespawnTimesBySpawnId, zoneId);
+ if (types & SPAWN_TYPEMASK_GAMEOBJECT)
+ PushRespawnInfoFrom(respawnData, _gameObjectRespawnTimesBySpawnId, zoneId);
+}
+
+RespawnInfo* Map::GetRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId) const
+{
+ RespawnInfoMap const& map = GetRespawnMapForType(type);
+ auto it = map.find(spawnId);
+ if (it == map.end())
+ return nullptr;
+ return it->second;
+}
+
+void Map::DeleteRespawnInfo() // delete everything
+{
+ for (RespawnInfo* info : _respawnTimes)
+ delete info;
+ _respawnTimes.clear();
+ _creatureRespawnTimesBySpawnId.clear();
+ _gameObjectRespawnTimesBySpawnId.clear();
+}
+
+void Map::DeleteRespawnInfo(RespawnInfo* info)
+{
+ // Delete from all relevant containers to ensure consistency
+ ASSERT(info);
+
+ // spawnid store
+ size_t const n = GetRespawnMapForType(info->type).erase(info->spawnId);
+ ASSERT(n == 1, "Respawn stores inconsistent for map %u, spawnid %u (type %u)", GetId(), info->spawnId, uint32(info->type));
+
+ //respawn heap
+ _respawnTimes.erase(info->handle);
+
+ // then cleanup the object
+ delete info;
+}
+
+void Map::RemoveRespawnTime(RespawnInfo* info, bool doRespawn, SQLTransaction dbTrans)
+{
+ PreparedStatement* stmt;
+ switch (info->type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN);
+ break;
+ case SPAWN_TYPE_GAMEOBJECT:
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN);
+ break;
+ default:
+ ASSERT(false, "Invalid respawninfo type %u for spawnid %u map %u", uint32(info->type), info->spawnId, GetId());
+ return;
+ }
+ stmt->setUInt32(0, info->spawnId);
+ stmt->setUInt16(1, GetId());
+ stmt->setUInt32(2, GetInstanceId());
+ CharacterDatabase.ExecuteOrAppend(dbTrans, stmt);
+
+ if (doRespawn)
+ Respawn(info);
+ else
+ DeleteRespawnInfo(info);
+}
+
+void Map::RemoveRespawnTime(RespawnVector& respawnData, bool doRespawn, SQLTransaction dbTrans)
+{
+ SQLTransaction trans = dbTrans ? dbTrans : CharacterDatabase.BeginTransaction();
+ for (RespawnInfo* info : respawnData)
+ RemoveRespawnTime(info, doRespawn, trans);
+ if (!dbTrans)
+ CharacterDatabase.CommitTransaction(trans);
+}
+
+void Map::ProcessRespawns()
+{
+ time_t now = time(NULL);
+ while (!_respawnTimes.empty())
+ {
+ RespawnInfo* next = _respawnTimes.top();
+ if (now < next->respawnTime) // done for this tick
+ break;
+ if (CheckRespawn(next)) // see if we're allowed to respawn
+ {
+ // ok, respawn
+ _respawnTimes.pop();
+ GetRespawnMapForType(next->type).erase(next->spawnId);
+ DoRespawn(next->type, next->spawnId, next->gridId);
+ delete next;
+ }
+ else if (!next->respawnTime) // just remove respawn entry without rescheduling
+ {
+ _respawnTimes.pop();
+ GetRespawnMapForType(next->type).erase(next->spawnId);
+ delete next;
+ }
+ else // value changed, update heap position
+ {
+ ASSERT(now < next->respawnTime); // infinite loop guard
+ _respawnTimes.decrease(next->handle);
+ }
+ }
+}
+
+void Map::ApplyDynamicModeRespawnScaling(WorldObject const* obj, ObjectGuid::LowType spawnId, uint32& respawnDelay, uint32 mode) const
+{
+ ASSERT(mode == 1);
+ ASSERT(obj->GetMap() == this);
+
+ if (IsBattlegroundOrArena())
+ return;
+
+ SpawnObjectType type;
+ switch (obj->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ type = SPAWN_TYPE_CREATURE;
+ break;
+ case TYPEID_GAMEOBJECT:
+ type = SPAWN_TYPE_GAMEOBJECT;
+ break;
+ default:
+ return;
+ }
+
+ SpawnData const* data = sObjectMgr->GetSpawnData(type, spawnId);
+ if (!data || !data->spawnGroupData || !(data->spawnGroupData->flags & SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE))
+ return;
+
+ auto it = _zonePlayerCountMap.find(obj->GetZoneId());
+ if (it == _zonePlayerCountMap.end())
+ return;
+ uint32 const playerCount = it->second;
+ if (!playerCount)
+ return;
+ double const adjustFactor = sWorld->getFloatConfig(type == SPAWN_TYPE_GAMEOBJECT ? CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT : CONFIG_RESPAWN_DYNAMICRATE_CREATURE) / playerCount;
+ if (adjustFactor >= 1.0) // nothing to do here
+ return;
+ uint32 const timeMinimum = sWorld->getIntConfig(type == SPAWN_TYPE_GAMEOBJECT ? CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT : CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE);
+ if (respawnDelay <= timeMinimum)
+ return;
+
+ respawnDelay = std::max<uint32>(ceil(respawnDelay * adjustFactor), timeMinimum);
+}
+
+SpawnGroupTemplateData const* Map::GetSpawnGroupData(uint32 groupId) const
+{
+ SpawnGroupTemplateData const* data = sObjectMgr->GetSpawnGroupData(groupId);
+ if (data && data->mapId == GetId())
+ return data;
+ return nullptr;
+}
+
+bool Map::SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn, bool force, std::vector<WorldObject*>* spawnedObjects)
+{
+ SpawnGroupTemplateData const* groupData = GetSpawnGroupData(groupId);
+ if (!groupData || groupData->flags & SPAWNGROUP_FLAG_SYSTEM)
+ {
+ TC_LOG_ERROR("maps", "Tried to spawn non-existing (or system) spawn group %u on map %u. Blocked.", groupId, GetId());
+ return false;
+ }
+
+ for (auto& pair : sObjectMgr->GetSpawnDataForGroup(groupId))
+ {
+ SpawnData const* data = pair.second;
+ ASSERT(groupData->mapId == data->spawnPoint.GetMapId());
+ // Check if there's already an instance spawned
+ if (!force)
+ if (WorldObject* obj = GetWorldObjectBySpawnId(data->type, data->spawnId))
+ if ((data->type != SPAWN_TYPE_CREATURE) || obj->ToCreature()->IsAlive())
+ continue;
+
+ time_t respawnTime = GetRespawnTime(data->type, data->spawnId);
+ if (respawnTime && respawnTime > time(NULL))
+ {
+ if (!force && !ignoreRespawn)
+ continue;
+
+ // we need to remove the respawn time, otherwise we'd end up double spawning
+ RemoveRespawnTime(data->type, data->spawnId, false);
+ }
+
+ // don't spawn if the grid isn't loaded (will be handled in grid loader)
+ if (!IsGridLoaded(data->spawnPoint))
+ continue;
+
+ // Everything OK, now do the actual (re)spawn
+ switch (data->type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ {
+ Creature* creature = new Creature();
+ if (!creature->LoadFromDB(data->spawnId, this, true, force))
+ delete creature;
+ else if (spawnedObjects)
+ spawnedObjects->push_back(creature);
+ break;
+ }
+ case SPAWN_TYPE_GAMEOBJECT:
+ {
+ GameObject* gameobject = new GameObject();
+ if (!gameobject->LoadFromDB(data->spawnId, this, true))
+ delete gameobject;
+ else if (spawnedObjects)
+ spawnedObjects->push_back(gameobject);
+ break;
+ }
+ default:
+ ASSERT(false, "Invalid spawn type %u with spawnId %u", uint32(data->type), data->spawnId);
+ return false;
+ }
+ }
+ SetSpawnGroupActive(groupId, true); // start processing respawns for the group
+ return true;
+}
+
+bool Map::SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes)
+{
+ SpawnGroupTemplateData const* groupData = GetSpawnGroupData(groupId);
+ if (!groupData || groupData->flags & SPAWNGROUP_FLAG_SYSTEM)
+ {
+ TC_LOG_ERROR("maps", "Tried to despawn non-existing (or system) spawn group %u on map %u. Blocked.", groupId, GetId());
+ return false;
+ }
+
+ std::vector<WorldObject*> toUnload; // unload after iterating, otherwise iterator invalidation
+ for (auto const& pair : sObjectMgr->GetSpawnDataForGroup(groupId))
+ {
+ SpawnData const* data = pair.second;
+ ASSERT(groupData->mapId == data->spawnPoint.GetMapId());
+ if (deleteRespawnTimes)
+ RemoveRespawnTime(data->type, data->spawnId);
+ switch (data->type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ {
+ auto bounds = GetCreatureBySpawnIdStore().equal_range(data->spawnId);
+ for (auto it = bounds.first; it != bounds.second; ++it)
+ toUnload.emplace_back(it->second);
+ break;
+ }
+ case SPAWN_TYPE_GAMEOBJECT:
+ {
+ auto bounds = GetGameObjectBySpawnIdStore().equal_range(data->spawnId);
+ for (auto it = bounds.first; it != bounds.second; ++it)
+ toUnload.emplace_back(it->second);
+ break;
+ }
+ default:
+ ASSERT(false, "Invalid spawn type %u in spawn data with spawnId %u.", uint32(data->type), data->spawnId);
+ return false;
+ }
+ }
+ // now do the actual despawning
+ for (WorldObject* obj : toUnload)
+ obj->AddObjectToRemoveList();
+ SetSpawnGroupActive(groupId, false); // stop processing respawns for the group, too
+ return true;
+}
+
+void Map::SetSpawnGroupActive(uint32 groupId, bool state)
+{
+ SpawnGroupTemplateData const* const data = GetSpawnGroupData(groupId);
+ if (!data || data->flags & SPAWNGROUP_FLAG_SYSTEM)
+ {
+ TC_LOG_ERROR("maps", "Tried to set non-existing (or system) spawn group %u to %s on map %u. Blocked.", groupId, state ? "active" : "inactive", GetId());
+ return;
+ }
+ if (state != !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN)) // toggled
+ _toggledSpawnGroupIds.insert(groupId);
+ else
+ _toggledSpawnGroupIds.erase(groupId);
+}
+
+bool Map::IsSpawnGroupActive(uint32 groupId) const
+{
+ SpawnGroupTemplateData const* const data = GetSpawnGroupData(groupId);
+ if (!data)
+ {
+ TC_LOG_WARN("maps", "Tried to query state of non-existing spawn group %u on map %u.", groupId, GetId());
+ return false;
+ }
+ if (data->flags & SPAWNGROUP_FLAG_SYSTEM)
+ return true;
+ return (_toggledSpawnGroupIds.find(groupId) != _toggledSpawnGroupIds.end()) != !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN);
+}
+
void Map::DelayedUpdate(uint32 t_diff)
{
for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();)
@@ -3364,6 +3882,8 @@ void InstanceMap::CreateInstanceData(bool load)
}
}
}
+ else
+ i_data->Create();
}
/*
@@ -3693,6 +4213,34 @@ Creature* Map::GetCreature(ObjectGuid const& guid)
return _objectsStore.Find<Creature>(guid);
}
+Creature* Map::GetCreatureBySpawnId(ObjectGuid::LowType spawnId) const
+{
+ auto const bounds = GetCreatureBySpawnIdStore().equal_range(spawnId);
+ if (bounds.first == bounds.second)
+ return nullptr;
+
+ std::unordered_multimap<uint32, Creature*>::const_iterator creatureItr = std::find_if(bounds.first, bounds.second, [](Map::CreatureBySpawnIdContainer::value_type const& pair)
+ {
+ return pair.second->IsAlive();
+ });
+
+ return creatureItr != bounds.second ? creatureItr->second : bounds.first->second;
+}
+
+GameObject* Map::GetGameObjectBySpawnId(ObjectGuid::LowType spawnId) const
+{
+ auto const bounds = GetGameObjectBySpawnIdStore().equal_range(spawnId);
+ if (bounds.first == bounds.second)
+ return nullptr;
+
+ std::unordered_multimap<uint32, GameObject*>::const_iterator creatureItr = std::find_if(bounds.first, bounds.second, [](Map::GameObjectBySpawnIdContainer::value_type const& pair)
+ {
+ return pair.second->isSpawned();
+ });
+
+ return creatureItr != bounds.second ? creatureItr->second : bounds.first->second;
+}
+
GameObject* Map::GetGameObject(ObjectGuid const& guid)
{
return _objectsStore.Find<GameObject>(guid);
@@ -3723,64 +4271,37 @@ void Map::UpdateIteratorBack(Player* player)
m_mapRefIter = m_mapRefIter->nocheck_prev();
}
-void Map::SaveCreatureRespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime)
+void Map::SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 zoneId, uint32 gridId, bool writeDB, bool replace, SQLTransaction dbTrans)
{
if (!respawnTime)
{
// Delete only
- RemoveCreatureRespawnTime(dbGuid);
+ RemoveRespawnTime(type, spawnId, false, dbTrans);
return;
}
- _creatureRespawnTimes[dbGuid] = respawnTime;
+ RespawnInfo ri;
+ ri.type = type;
+ ri.spawnId = spawnId;
+ ri.entry = entry;
+ ri.respawnTime = respawnTime;
+ ri.gridId = gridId;
+ ri.zoneId = zoneId;
+ AddRespawnInfo(ri, replace);
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CREATURE_RESPAWN);
- stmt->setUInt32(0, dbGuid);
- stmt->setUInt64(1, uint64(respawnTime));
- stmt->setUInt16(2, GetId());
- stmt->setUInt32(3, GetInstanceId());
- CharacterDatabase.Execute(stmt);
+ if (writeDB)
+ SaveRespawnTimeDB(type, spawnId, ri.respawnTime, dbTrans); // might be different from original respawn time if we didn't replace
}
-void Map::RemoveCreatureRespawnTime(ObjectGuid::LowType dbGuid)
+void Map::SaveRespawnTimeDB(SpawnObjectType type, ObjectGuid::LowType spawnId, time_t respawnTime, SQLTransaction dbTrans)
{
- _creatureRespawnTimes.erase(dbGuid);
-
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN);
- stmt->setUInt32(0, dbGuid);
- stmt->setUInt16(1, GetId());
- stmt->setUInt32(2, GetInstanceId());
- CharacterDatabase.Execute(stmt);
-}
-
-void Map::SaveGORespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime)
-{
- if (!respawnTime)
- {
- // Delete only
- RemoveGORespawnTime(dbGuid);
- return;
- }
-
- _goRespawnTimes[dbGuid] = respawnTime;
-
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GO_RESPAWN);
- stmt->setUInt32(0, dbGuid);
+ // Just here for support of compatibility mode
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement((type == SPAWN_TYPE_GAMEOBJECT) ? CHAR_REP_GO_RESPAWN : CHAR_REP_CREATURE_RESPAWN);
+ stmt->setUInt32(0, spawnId);
stmt->setUInt64(1, uint64(respawnTime));
stmt->setUInt16(2, GetId());
stmt->setUInt32(3, GetInstanceId());
- CharacterDatabase.Execute(stmt);
-}
-
-void Map::RemoveGORespawnTime(ObjectGuid::LowType dbGuid)
-{
- _goRespawnTimes.erase(dbGuid);
-
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN);
- stmt->setUInt32(0, dbGuid);
- stmt->setUInt16(1, GetId());
- stmt->setUInt32(2, GetInstanceId());
- CharacterDatabase.Execute(stmt);
+ CharacterDatabase.ExecuteOrAppend(dbTrans, stmt);
}
void Map::LoadRespawnTimes()
@@ -3796,7 +4317,9 @@ void Map::LoadRespawnTimes()
ObjectGuid::LowType loguid = fields[0].GetUInt32();
uint64 respawnTime = fields[1].GetUInt64();
- _creatureRespawnTimes[loguid] = time_t(respawnTime);
+ if (CreatureData const* cdata = sObjectMgr->GetCreatureData(loguid))
+ SaveRespawnTime(SPAWN_TYPE_CREATURE, loguid, cdata->id, time_t(respawnTime), GetZoneId(cdata->spawnPoint), Trinity::ComputeGridCoord(cdata->spawnPoint.GetPositionX(), cdata->spawnPoint.GetPositionY()).GetId(), false);
+
} while (result->NextRow());
}
@@ -3811,19 +4334,13 @@ void Map::LoadRespawnTimes()
ObjectGuid::LowType loguid = fields[0].GetUInt32();
uint64 respawnTime = fields[1].GetUInt64();
- _goRespawnTimes[loguid] = time_t(respawnTime);
+ if (GameObjectData const* godata = sObjectMgr->GetGameObjectData(loguid))
+ SaveRespawnTime(SPAWN_TYPE_GAMEOBJECT, loguid, godata->id, time_t(respawnTime), GetZoneId(godata->spawnPoint), Trinity::ComputeGridCoord(godata->spawnPoint.GetPositionX(), godata->spawnPoint.GetPositionY()).GetId(), false);
+
} while (result->NextRow());
}
}
-void Map::DeleteRespawnTimes()
-{
- _creatureRespawnTimes.clear();
- _goRespawnTimes.clear();
-
- DeleteRespawnTimesInDB(GetId(), GetInstanceId());
-}
-
void Map::DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE);
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index dd0e43158c1..3d3fcfb528d 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -21,16 +21,18 @@
#include "Define.h"
-#include "GridDefines.h"
#include "Cell.h"
-#include "Timer.h"
-#include "SharedDefines.h"
+#include "DynamicTree.h"
+#include "GridDefines.h"
#include "GridRefManager.h"
#include "MapRefManager.h"
-#include "DynamicTree.h"
#include "ObjectGuid.h"
#include "Optional.h"
-
+#include "SharedDefines.h"
+#include "SpawnData.h"
+#include "Timer.h"
+#include "Transaction.h"
+#include <boost/heap/fibonacci_heap.hpp>
#include <bitset>
#include <list>
#include <memory>
@@ -268,10 +270,41 @@ struct ZoneDynamicInfo
#define MAX_FALL_DISTANCE 250000.0f // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT
#define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations
#define MIN_UNLOAD_DELAY 1 // immediate unload
+#define MAP_INVALID_ZONE 0xFFFFFFFF
typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType;
+struct RespawnInfo; // forward declaration
+struct CompareRespawnInfo
+{
+ bool operator()(RespawnInfo const* a, RespawnInfo const* b) const;
+};
typedef std::unordered_map<uint32 /*zoneId*/, ZoneDynamicInfo> ZoneDynamicInfoMap;
+typedef boost::heap::fibonacci_heap<RespawnInfo*, boost::heap::compare<CompareRespawnInfo>> RespawnListContainer;
+typedef RespawnListContainer::handle_type RespawnListHandle;
+typedef std::unordered_map<uint32, RespawnInfo*> RespawnInfoMap;
+typedef std::vector<RespawnInfo*> RespawnVector;
+struct RespawnInfo
+{
+ SpawnObjectType type;
+ ObjectGuid::LowType spawnId;
+ uint32 entry;
+ time_t respawnTime;
+ uint32 gridId;
+ uint32 zoneId;
+ RespawnListHandle handle;
+};
+inline bool CompareRespawnInfo::operator()(RespawnInfo const* a, RespawnInfo const* b) const
+{
+ if (a == b)
+ return false;
+ if (a->respawnTime != b->respawnTime)
+ return (a->respawnTime > b->respawnTime);
+ if (a->spawnId != b->spawnId)
+ return a->spawnId < b->spawnId;
+ ASSERT(a->type != b->type, "Duplicate respawn entry for spawnId (%u,%u) found!", a->type, a->spawnId);
+ return a->type < b->type;
+}
class TC_GAME_API Map : public GridRefManager<NGridType>
{
@@ -321,17 +354,19 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
GridCoord p = Trinity::ComputeGridCoord(x, y);
return !getNGrid(p.x_coord, p.y_coord) || getNGrid(p.x_coord, p.y_coord)->GetGridState() == GRID_STATE_REMOVAL;
}
+ bool IsRemovalGrid(Position const& pos) const { return IsRemovalGrid(pos.GetPositionX(), pos.GetPositionY()); }
- bool IsGridLoaded(float x, float y) const
- {
- return IsGridLoaded(Trinity::ComputeGridCoord(x, y));
- }
+ bool IsGridLoaded(uint32 gridId) const { return IsGridLoaded(GridCoord(gridId % MAX_NUMBER_OF_GRIDS, gridId / MAX_NUMBER_OF_GRIDS)); }
+ bool IsGridLoaded(float x, float y) const { return IsGridLoaded(Trinity::ComputeGridCoord(x, y)); }
+ bool IsGridLoaded(Position const& pos) const { return IsGridLoaded(pos.GetPositionX(), pos.GetPositionY()); }
bool GetUnloadLock(GridCoord const& p) const { return getNGrid(p.x_coord, p.y_coord)->getUnloadLock(); }
void SetUnloadLock(GridCoord const& p, bool on) { getNGrid(p.x_coord, p.y_coord)->setUnloadExplicitLock(on); }
void LoadGrid(float x, float y);
void LoadAllCells();
bool UnloadGrid(NGridType& ngrid, bool pForce);
+ void GridMarkNoUnload(uint32 x, uint32 y);
+ void GridUnmarkNoUnload(uint32 x, uint32 y);
virtual void UnloadAll();
void ResetGridExpiry(NGridType &grid, float factor = 1) const
@@ -350,18 +385,16 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
Map const* GetParent() const { return m_parentMap; }
- // some calls like isInWater should not use vmaps due to processor power
- // can return INVALID_HEIGHT if under z+2 z coord not found height
- float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
- float GetMinHeight(float x, float y) const;
-
void GetFullTerrainStatusForPosition(float x, float y, float z, PositionFullTerrainStatus& data, uint8 reqLiquidType = MAP_ALL_LIQUIDS) const;
ZLiquidStatus GetLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = nullptr) const;
bool GetAreaInfo(float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const;
uint32 GetAreaId(float x, float y, float z, bool *isOutdoors = nullptr) const;
+ uint32 GetAreaId(Position const& pos) const { return GetAreaId(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
uint32 GetZoneId(float x, float y, float z) const;
+ uint32 GetZoneId(Position const& pos) const { return GetZoneId(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const;
+ void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, Position const& pos) const { GetZoneAndAreaId(zoneid, areaid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
bool IsOutdoors(float x, float y, float z) const;
@@ -464,6 +497,9 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
Corpse* GetCorpse(ObjectGuid const& guid);
Creature* GetCreature(ObjectGuid const& guid);
GameObject* GetGameObject(ObjectGuid const& guid);
+ Creature* GetCreatureBySpawnId(ObjectGuid::LowType spawnId) const;
+ GameObject* GetGameObjectBySpawnId(ObjectGuid::LowType spawnId) const;
+ WorldObject* GetWorldObjectBySpawnId(SpawnObjectType type, ObjectGuid::LowType spawnId) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? reinterpret_cast<WorldObject*>(GetGameObjectBySpawnId(spawnId)) : reinterpret_cast<WorldObject*>(GetCreatureBySpawnId(spawnId)); }
Transport* GetTransport(ObjectGuid const& guid);
DynamicObject* GetDynamicObject(ObjectGuid const& guid);
Pet* GetPet(ObjectGuid const& guid);
@@ -472,9 +508,11 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
typedef std::unordered_multimap<ObjectGuid::LowType, Creature*> CreatureBySpawnIdContainer;
CreatureBySpawnIdContainer& GetCreatureBySpawnIdStore() { return _creatureBySpawnIdStore; }
+ CreatureBySpawnIdContainer const& GetCreatureBySpawnIdStore() const { return _creatureBySpawnIdStore; }
typedef std::unordered_multimap<ObjectGuid::LowType, GameObject*> GameObjectBySpawnIdContainer;
GameObjectBySpawnIdContainer& GetGameObjectBySpawnIdStore() { return _gameobjectBySpawnIdStore; }
+ GameObjectBySpawnIdContainer const& GetGameObjectBySpawnIdStore() const { return _gameobjectBySpawnIdStore; }
std::unordered_set<Corpse*> const* GetCorpsesInCell(uint32 cellId) const
{
@@ -504,7 +542,11 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
BattlegroundMap const* ToBattlegroundMap() const { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap const*>(this); return nullptr; }
float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = nullptr, bool swim = false) const;
- float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
+ float GetMinHeight(float x, float y) const;
+ float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
+ float GetHeight(Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return GetHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); }
+ float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return std::max<float>(GetHeight(x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phasemask, x, y, z, maxSearchDist)); }
+ float GetHeight(uint32 phasemask, Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return GetHeight(phasemask, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); }
bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const;
void Balance() { _dynamicTree.balance(); }
void RemoveGameObjectModel(GameObjectModel const& model) { _dynamicTree.remove(model); }
@@ -522,28 +564,24 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
time_t GetLinkedRespawnTime(ObjectGuid guid) const;
time_t GetCreatureRespawnTime(ObjectGuid::LowType dbGuid) const
{
- std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t>::const_iterator itr = _creatureRespawnTimes.find(dbGuid);
- if (itr != _creatureRespawnTimes.end())
- return itr->second;
-
- return time_t(0);
+ RespawnInfoMap::const_iterator itr = _creatureRespawnTimesBySpawnId.find(dbGuid);
+ return itr != _creatureRespawnTimesBySpawnId.end() ? itr->second->respawnTime : 0;
}
time_t GetGORespawnTime(ObjectGuid::LowType dbGuid) const
{
- std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t>::const_iterator itr = _goRespawnTimes.find(dbGuid);
- if (itr != _goRespawnTimes.end())
- return itr->second;
-
- return time_t(0);
+ RespawnInfoMap::const_iterator itr = _gameObjectRespawnTimesBySpawnId.find(dbGuid);
+ return itr != _gameObjectRespawnTimesBySpawnId.end() ? itr->second->respawnTime : 0;
}
- void SaveCreatureRespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime);
- void RemoveCreatureRespawnTime(ObjectGuid::LowType dbGuid);
- void SaveGORespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime);
- void RemoveGORespawnTime(ObjectGuid::LowType dbGuid);
+ time_t GetRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? GetGORespawnTime(spawnId) : GetCreatureRespawnTime(spawnId); }
+
+ void UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone);
+
+ void SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 zoneId, uint32 gridId = 0, bool writeDB = true, bool replace = false, SQLTransaction dbTrans = nullptr);
+ void SaveRespawnTimeDB(SpawnObjectType type, ObjectGuid::LowType spawnId, time_t respawnTime, SQLTransaction dbTrans = nullptr);
void LoadRespawnTimes();
- void DeleteRespawnTimes();
+ void DeleteRespawnTimes() { DeleteRespawnInfo(); DeleteRespawnTimesInDB(GetId(), GetInstanceId()); }
void LoadCorpseData();
void DeleteCorpseData();
@@ -702,6 +740,65 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
typedef std::multimap<time_t, ScriptAction> ScriptScheduleMap;
ScriptScheduleMap m_scriptSchedule;
+ public:
+ void ProcessRespawns();
+ void ApplyDynamicModeRespawnScaling(WorldObject const* obj, ObjectGuid::LowType spawnId, uint32& respawnDelay, uint32 mode) const;
+
+ private:
+ // if return value is true, we can respawn
+ // if return value is false, reschedule the respawn to new value of info->respawnTime iff nonzero, delete otherwise
+ // if return value is false and info->respawnTime is nonzero, it is guaranteed to be greater than time(NULL)
+ bool CheckRespawn(RespawnInfo* info);
+ void DoRespawn(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 gridId);
+ void Respawn(RespawnInfo* info, bool force = false, SQLTransaction dbTrans = nullptr);
+ void Respawn(RespawnVector& respawnData, bool force = false, SQLTransaction dbTrans = nullptr);
+ void AddRespawnInfo(RespawnInfo& info, bool replace = false);
+ void DeleteRespawnInfo();
+ void DeleteRespawnInfo(RespawnInfo* info);
+ void DeleteRespawnInfo(RespawnVector& toDelete)
+ {
+ for (RespawnInfo* info : toDelete)
+ DeleteRespawnInfo(info);
+ toDelete.clear();
+ }
+ void DeleteRespawnInfo(SpawnObjectTypeMask types, uint32 zoneId = 0)
+ {
+ RespawnVector v;
+ GetRespawnInfo(v, types, zoneId);
+ if (!v.empty())
+ DeleteRespawnInfo(v);
+ }
+ void DeleteRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId)
+ {
+ if (RespawnInfo* info = GetRespawnInfo(type, spawnId))
+ DeleteRespawnInfo(info);
+ }
+
+ public:
+ void GetRespawnInfo(RespawnVector& respawnData, SpawnObjectTypeMask types, uint32 zoneId = 0) const;
+ RespawnInfo* GetRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId) const;
+ void RemoveRespawnTime(RespawnInfo* info, bool doRespawn = false, SQLTransaction dbTrans = nullptr);
+ void RemoveRespawnTime(RespawnVector& respawnData, bool doRespawn = false, SQLTransaction dbTrans = nullptr);
+ void RemoveRespawnTime(SpawnObjectTypeMask types = SPAWN_TYPEMASK_ALL, uint32 zoneId = 0, bool doRespawn = false, SQLTransaction dbTrans = nullptr)
+ {
+ RespawnVector v;
+ GetRespawnInfo(v, types, zoneId);
+ if (!v.empty())
+ RemoveRespawnTime(v, doRespawn, dbTrans);
+ }
+ void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, bool doRespawn = false, SQLTransaction dbTrans = nullptr)
+ {
+ if (RespawnInfo* info = GetRespawnInfo(type, spawnId))
+ RemoveRespawnTime(info, doRespawn, dbTrans);
+ }
+
+ SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const;
+ bool SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn = false, bool force = false, std::vector<WorldObject*>* spawnedObjects = nullptr);
+ bool SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes = false);
+ void SetSpawnGroupActive(uint32 groupId, bool state);
+ bool IsSpawnGroupActive(uint32 groupId) const;
+
+ private:
// Type specific code for add/remove to/from grid
template<class T>
void AddToGrid(T* object, Cell const& cell);
@@ -730,8 +827,15 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
m_activeNonPlayers.erase(obj);
}
- std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _creatureRespawnTimes;
- std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _goRespawnTimes;
+ RespawnListContainer _respawnTimes;
+ RespawnInfoMap _creatureRespawnTimesBySpawnId;
+ RespawnInfoMap _gameObjectRespawnTimesBySpawnId;
+ RespawnInfoMap& GetRespawnMapForType(SpawnObjectType type) { return (type == SPAWN_TYPE_GAMEOBJECT) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; }
+ RespawnInfoMap const& GetRespawnMapForType(SpawnObjectType type) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; }
+ std::unordered_set<uint32> _toggledSpawnGroupIds;
+
+ uint32 _respawnCheckTimer;
+ std::unordered_map<uint32, uint32> _zonePlayerCountMap;
ZoneDynamicInfoMap _zoneDynamicInfo;
uint32 _defaultLight;
diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h
index e628a7030ae..96147c7ded7 100644
--- a/src/server/game/Maps/MapManager.h
+++ b/src/server/game/Maps/MapManager.h
@@ -44,16 +44,22 @@ class TC_GAME_API MapManager
Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
return m->GetAreaId(x, y, z);
}
+ uint32 GetAreaId(uint32 mapid, Position const& pos) const { return GetAreaId(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+ uint32 GetAreaId(WorldLocation const& loc) const { return GetAreaId(loc.GetMapId(), loc); }
uint32 GetZoneId(uint32 mapid, float x, float y, float z) const
{
Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
return m->GetZoneId(x, y, z);
}
- void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z)
+ uint32 GetZoneId(uint32 mapid, Position const& pos) const { return GetZoneId(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+ uint32 GetZoneId(WorldLocation const& loc) const { return GetZoneId(loc.GetMapId(), loc); }
+ void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) const
{
Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
m->GetZoneAndAreaId(zoneid, areaid, x, y, z);
}
+ void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, Position const& pos) const { GetZoneAndAreaId(zoneid, areaid, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+ void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, WorldLocation const& loc) const { GetZoneAndAreaId(zoneid, areaid, loc.GetMapId(), loc); }
void Initialize(void);
void Update(uint32);
diff --git a/src/server/game/Maps/MapScripts.cpp b/src/server/game/Maps/MapScripts.cpp
index e365a54c594..20ebc1db460 100644
--- a/src/server/game/Maps/MapScripts.cpp
+++ b/src/server/game/Maps/MapScripts.cpp
@@ -671,7 +671,7 @@ void Map::ScriptsProcess()
Unit* uSource = nullptr;
Unit* uTarget = nullptr;
- // source/target cast spell at target/source (script->datalong2: 0: s->t 1: s->s 2: t->t 3: t->s
+ // source/target cast spell at target/source (script->datalong2: 0: s->t 1: s->s 2: t->t 3: t->s)
switch (step.script->CastSpell.Flags)
{
case SF_CASTSPELL_SOURCE_TO_TARGET: // source -> target
diff --git a/src/server/game/Maps/SpawnData.h b/src/server/game/Maps/SpawnData.h
new file mode 100644
index 00000000000..768d1fd26bd
--- /dev/null
+++ b/src/server/game/Maps/SpawnData.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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_SPAWNDATA_H
+#define TRINITY_SPAWNDATA_H
+
+#include "Position.h"
+
+enum SpawnObjectType
+{
+ SPAWN_TYPE_CREATURE = 0,
+ SPAWN_TYPE_GAMEOBJECT = 1,
+
+ SPAWN_TYPE_MAX
+};
+
+enum SpawnObjectTypeMask
+{
+ SPAWN_TYPEMASK_CREATURE = (1 << SPAWN_TYPE_CREATURE),
+ SPAWN_TYPEMASK_GAMEOBJECT = (1 << SPAWN_TYPE_GAMEOBJECT),
+
+ SPAWN_TYPEMASK_ALL = (1 << SPAWN_TYPE_MAX)-1
+};
+
+enum SpawnGroupFlags
+{
+ SPAWNGROUP_FLAG_NONE = 0x00,
+ SPAWNGROUP_FLAG_SYSTEM = 0x01,
+ SPAWNGROUP_FLAG_COMPATIBILITY_MODE = 0x02,
+ SPAWNGROUP_FLAG_MANUAL_SPAWN = 0x04,
+ SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE = 0x08,
+ SPAWNGROUP_FLAG_ESCORTQUESTNPC = 0x10,
+
+ SPAWNGROUP_FLAGS_ALL = (SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_COMPATIBILITY_MODE | SPAWNGROUP_FLAG_MANUAL_SPAWN | SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE | SPAWNGROUP_FLAG_ESCORTQUESTNPC)
+};
+
+struct SpawnGroupTemplateData
+{
+ uint32 groupId;
+ std::string name;
+ uint32 mapId;
+ SpawnGroupFlags flags;
+};
+
+struct SpawnData
+{
+ SpawnObjectType const type;
+ uint32 spawnId = 0;
+ uint32 id = 0; // entry in respective _template table
+ WorldLocation spawnPoint;
+ uint32 phaseMask = 0;
+ int32 spawntimesecs = 0;
+ uint8 spawnMask = 0;
+ SpawnGroupTemplateData const* spawnGroupData = nullptr;
+ uint32 scriptId = 0;
+ bool dbData = true;
+
+ protected:
+ SpawnData(SpawnObjectType t) : type(t) {}
+};
+
+#endif
diff --git a/src/server/game/Miscellaneous/Formulas.h b/src/server/game/Miscellaneous/Formulas.h
index 81e906ed47d..7931860868e 100644
--- a/src/server/game/Miscellaneous/Formulas.h
+++ b/src/server/game/Miscellaneous/Formulas.h
@@ -158,6 +158,13 @@ namespace Trinity
baseGain = 0;
}
+ if (sWorld->getIntConfig(CONFIG_MIN_CREATURE_SCALED_XP_RATIO))
+ {
+ // Use mob level instead of player level to avoid overscaling on gain in a min is enforced
+ uint32 baseGainMin = (mob_level * 5 + nBaseExp) * sWorld->getIntConfig(CONFIG_MIN_CREATURE_SCALED_XP_RATIO) / 100;
+ baseGain = std::max(baseGainMin, baseGain);
+ }
+
sScriptMgr->OnBaseGainCalculation(baseGain, pl_level, mob_level, content);
return baseGain;
}
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index d96b33fd968..400685c1c1b 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -1147,7 +1147,32 @@ enum TrinityStrings
LANG_COMMAND_MUTEHISTORY_EMPTY = 5060,
LANG_COMMAND_MUTEHISTORY_OUTPUT = 5061,
- // Room for more Trinity strings 5062-9999
+ // Scene debugs commands [Master only, not used in 3.3.5]
+ /*LANG_COMMAND_SCENE_DEBUG_ON = 5062,
+ LANG_COMMAND_SCENE_DEBUG_OFF = 5063,
+ LANG_COMMAND_SCENE_DEBUG_PLAY = 5064,
+ LANG_COMMAND_SCENE_DEBUG_TRIGGER = 5065,
+ LANG_COMMAND_SCENE_DEBUG_CANCEL = 5066,
+ LANG_COMMAND_SCENE_DEBUG_COMPLETE = 5067,
+ LANG_DEBUG_SCENE_OBJECT_LIST = 5068,
+ LANG_DEBUG_SCENE_OBJECT_DETAIL = 5069, */
+
+ // Strings added for dynamic_spawning
+ LANG_SPAWNINFO_GROUP_ID = 5070,
+ LANG_SPAWNINFO_COMPATIBILITY_MODE = 5071,
+ LANG_SPAWNINFO_GUIDINFO = 5072,
+ LANG_SPAWNINFO_SPAWNID_LOCATION = 5073,
+ LANG_SPAWNINFO_DISTANCEFROMPLAYER = 5074,
+ LANG_SPAWNGROUP_BADGROUP = 5075,
+ LANG_SPAWNGROUP_SPAWNCOUNT = 5076,
+ LANG_LIST_RESPAWNS_RANGE = 5077,
+ LANG_LIST_RESPAWNS_ZONE = 5078,
+ LANG_LIST_RESPAWNS_LISTHEADER = 5079,
+ LANG_LIST_RESPAWNS_OVERDUE = 5080,
+ LANG_LIST_RESPAWNS_CREATURES = 5081,
+ LANG_LIST_RESPAWNS_GAMEOBJECTS = 5082,
+
+ // Room for more Trinity strings 5084-6603
// Level requirement notifications
LANG_SAY_REQ = 6604,
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 7ea78f14ca4..d6bea21f2ad 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -1443,7 +1443,7 @@ enum Targets
TARGET_DEST_CHANNEL_CASTER = 106,
TARGET_UNK_DEST_AREA_UNK_107 = 107, // not enough info - only generic spells avalible
TARGET_GAMEOBJECT_CONE = 108,
- TARGET_DEST_UNK_110 = 110, // 1 spell
+ TARGET_UNIT_CONE_ENTRY_110 = 110, // 1 spell
TOTAL_SPELL_TARGETS
};
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index 6e771a29979..3f77e472189 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -17,25 +17,28 @@
*/
#include "MotionMaster.h"
+#include "ConfusedMovementGenerator.h"
#include "Creature.h"
#include "CreatureAISelector.h"
#include "DBCStores.h"
-#include "Log.h"
-#include "Map.h"
-#include "PathGenerator.h"
-#include "ScriptSystem.h"
-#include "ConfusedMovementGenerator.h"
#include "FleeingMovementGenerator.h"
+#include "FormationMovementGenerator.h"
#include "HomeMovementGenerator.h"
#include "IdleMovementGenerator.h"
+#include "Log.h"
+#include "Map.h"
+#include "MoveSpline.h"
+#include "MoveSplineInit.h"
+#include "PathGenerator.h"
+#include "Player.h"
#include "PointMovementGenerator.h"
-#include "TargetedMovementGenerator.h"
-#include "WaypointMovementGenerator.h"
#include "RandomMovementGenerator.h"
+#include "ScriptSystem.h"
#include "SplineChainMovementGenerator.h"
-#include "FormationMovementGenerator.h"
-#include "MoveSpline.h"
-#include "MoveSplineInit.h"
+#include "TargetedMovementGenerator.h"
+#include "Unit.h"
+#include "WaypointDefines.h"
+#include "WaypointMovementGenerator.h"
inline MovementGenerator* GetIdleMovementGenerator()
{
@@ -70,7 +73,7 @@ void MotionMaster::Initialize()
// clear ALL movement generators (including default)
while (!empty())
{
- MovementGenerator *curr = top();
+ MovementGenerator* curr = top();
pop();
if (curr)
DirectDelete(curr);
@@ -119,21 +122,15 @@ void MotionMaster::Clear(bool reset /*= true*/)
DirectClean(reset);
}
-void MotionMaster::ClearExpireList()
+void MotionMaster::Clear(MovementSlot slot)
{
- for (auto itr : _expireList)
- DirectDelete(itr);
-
- _expireList.clear();
-
- if (empty())
- Initialize();
- else if (NeedInitTop())
- InitTop();
- else if (_cleanFlag & MMCF_RESET)
- top()->Reset(_owner);
+ if (empty() || slot >= MAX_MOTION_SLOT)
+ return;
- _cleanFlag &= ~MMCF_RESET;
+ if (_cleanFlag & MMCF_UPDATE)
+ DelayedClean(slot);
+ else
+ DirectClean(slot);
}
void MotionMaster::MovementExpired(bool reset /*= true*/)
@@ -158,27 +155,32 @@ MovementGeneratorType MotionMaster::GetCurrentMovementGeneratorType() const
return top()->GetMovementGeneratorType();
}
-MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const
+MovementGeneratorType MotionMaster::GetMotionSlotType(MovementSlot slot) const
{
- if (!_slot[slot])
+ if (empty() || slot >= MAX_MOTION_SLOT || !_slot[slot])
return MAX_MOTION_TYPE;
- else
- return _slot[slot]->GetMovementGeneratorType();
+
+ return _slot[slot]->GetMovementGeneratorType();
}
-MovementGenerator* MotionMaster::GetMotionSlot(int slot) const
+MovementGenerator* MotionMaster::GetMotionSlot(MovementSlot slot) const
{
- ASSERT(slot >= 0);
+ if (empty() || slot >= MAX_MOTION_SLOT || !_slot[slot])
+ return nullptr;
+
return _slot[slot];
}
void MotionMaster::PropagateSpeedChange()
{
- for (int i = 0; i <= _top; ++i)
- {
- if (_slot[i])
- _slot[i]->UnitSpeedChanged();
- }
+ if (empty())
+ return;
+
+ MovementGenerator* movement = top();
+ if (!movement)
+ return;
+
+ movement->UnitSpeedChanged();
}
bool MotionMaster::GetDestination(float &x, float &y, float &z)
@@ -675,16 +677,21 @@ void MotionMaster::MoveDistract(uint32 timer)
Mutate(mgen, MOTION_SLOT_CONTROLLED);
}
-void MotionMaster::MovePath(uint32 path_id, bool repeatable)
+void MotionMaster::MovePath(uint32 pathId, bool repeatable)
{
- if (!path_id)
+ if (!pathId)
return;
- Mutate(new WaypointMovementGenerator<Creature>(path_id, repeatable), MOTION_SLOT_IDLE);
+ Mutate(new WaypointMovementGenerator<Creature>(pathId, repeatable), MOTION_SLOT_IDLE);
+
+ TC_LOG_DEBUG("misc", "%s (GUID: %u) starts moving over path(Id:%u, repeatable: %s).", _owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature", _owner->GetGUID().GetCounter(), pathId, repeatable ? "YES" : "NO");
+}
+
+void MotionMaster::MovePath(WaypointPath& path, bool repeatable)
+{
+ Mutate(new WaypointMovementGenerator<Creature>(path, repeatable), MOTION_SLOT_IDLE);
- TC_LOG_DEBUG("misc", "%s (GUID: %u) starts moving over path(Id:%u, repeatable: %s).",
- _owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature",
- _owner->GetGUID().GetCounter(), path_id, repeatable ? "YES" : "NO");
+ TC_LOG_DEBUG("misc", "%s (GUID: %u) start moving over path(repeatable: %s)", _owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature", _owner->GetGUID().GetCounter(), repeatable ? "YES" : "NO");
}
void MotionMaster::MoveRotate(uint32 time, RotateDirection direction)
@@ -731,7 +738,7 @@ void MotionMaster::InitTop()
void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot)
{
- if (MovementGenerator *curr = _slot[slot])
+ if (MovementGenerator* curr = _slot[slot])
{
_slot[slot] = nullptr; // in case a new one is generated in this slot during directdelete
if (_top == slot && (_cleanFlag & MMCF_UPDATE))
@@ -758,9 +765,10 @@ void MotionMaster::DirectClean(bool reset)
{
while (size() > 1)
{
- MovementGenerator *curr = top();
+ MovementGenerator* curr = top();
pop();
- if (curr) DirectDelete(curr);
+ if (curr)
+ DirectDelete(curr);
}
if (empty())
@@ -776,18 +784,47 @@ void MotionMaster::DelayedClean()
{
while (size() > 1)
{
- MovementGenerator *curr = top();
+ MovementGenerator* curr = top();
pop();
if (curr)
DelayedDelete(curr);
}
}
+void MotionMaster::DirectClean(MovementSlot slot)
+{
+ if (MovementGenerator* motion = GetMotionSlot(slot))
+ {
+ _slot[slot] = nullptr;
+ DirectDelete(motion);
+ }
+
+ while (!empty() && !top())
+ --_top;
+
+ if (empty())
+ Initialize();
+ else if (NeedInitTop())
+ InitTop();
+}
+
+void MotionMaster::DelayedClean(MovementSlot slot)
+{
+ if (MovementGenerator* motion = GetMotionSlot(slot))
+ {
+ _slot[slot] = nullptr;
+ DelayedDelete(motion);
+ }
+
+ while (!empty() && !top())
+ --_top;
+}
+
void MotionMaster::DirectExpire(bool reset)
{
if (size() > 1)
{
- MovementGenerator *curr = top();
+ MovementGenerator* curr = top();
pop();
DirectDelete(curr);
}
@@ -807,7 +844,7 @@ void MotionMaster::DelayedExpire()
{
if (size() > 1)
{
- MovementGenerator *curr = top();
+ MovementGenerator* curr = top();
pop();
DelayedDelete(curr);
}
@@ -826,9 +863,26 @@ void MotionMaster::DirectDelete(MovementGenerator* curr)
void MotionMaster::DelayedDelete(MovementGenerator* curr)
{
- TC_LOG_FATAL("misc", "Unit (Entry %u) is trying to delete its updating Movement Generator (Type %u)!", _owner->GetEntry(), curr->GetMovementGeneratorType());
+ TC_LOG_DEBUG("misc", "MotionMaster::DelayedDelete: unit (%u) delayed deleting movement generator (type %u)", _owner->GetEntry(), curr->GetMovementGeneratorType());
if (IsStatic(curr))
return;
_expireList.push_back(curr);
}
+
+void MotionMaster::ClearExpireList()
+{
+ for (auto itr : _expireList)
+ DirectDelete(itr);
+
+ _expireList.clear();
+
+ if (empty())
+ Initialize();
+ else if (NeedInitTop())
+ InitTop();
+ else if (_cleanFlag & MMCF_RESET)
+ top()->Reset(_owner);
+
+ _cleanFlag &= ~MMCF_RESET;
+}
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
index 4e20f9ab814..d132d8b30d0 100644
--- a/src/server/game/Movement/MotionMaster.h
+++ b/src/server/game/Movement/MotionMaster.h
@@ -31,6 +31,7 @@ class Unit;
class PathGenerator;
struct SplineChainLink;
struct SplineChainResumeInfo;
+struct WaypointPath;
// Creature Entry ID used for waypoints show, visible only for GMs
#define VISUAL_WAYPOINT 1
@@ -61,9 +62,9 @@ enum MovementGeneratorType : uint8
MAX_MOTION_TYPE // limit
};
-enum MovementSlot
+enum MovementSlot : uint8
{
- MOTION_SLOT_IDLE,
+ MOTION_SLOT_IDLE = 0,
MOTION_SLOT_ACTIVE,
MOTION_SLOT_CONTROLLED,
MAX_MOTION_SLOT
@@ -105,11 +106,12 @@ class TC_GAME_API MotionMaster
void UpdateMotion(uint32 diff);
void Clear(bool reset = true);
+ void Clear(MovementSlot slot);
void MovementExpired(bool reset = true);
MovementGeneratorType GetCurrentMovementGeneratorType() const;
- MovementGeneratorType GetMotionSlotType(int slot) const;
- MovementGenerator* GetMotionSlot(int slot) const;
+ MovementGeneratorType GetMotionSlotType(MovementSlot slot) const;
+ MovementGenerator* GetMotionSlot(MovementSlot slot) const;
void PropagateSpeedChange();
@@ -159,7 +161,8 @@ class TC_GAME_API MotionMaster
void MoveSeekAssistanceDistract(uint32 timer);
void MoveTaxiFlight(uint32 path, uint32 pathnode);
void MoveDistract(uint32 time);
- void MovePath(uint32 path_id, bool repeatable);
+ void MovePath(uint32 pathId, bool repeatable);
+ void MovePath(WaypointPath& path, bool repeatable);
void MoveRotate(uint32 time, RotateDirection direction);
void MoveFormation(uint32 id, Position destination, uint32 moveType, bool forceRun = false, bool forceOrientation = false);
@@ -172,10 +175,12 @@ class TC_GAME_API MotionMaster
bool NeedInitTop() const;
void InitTop();
- void Mutate(MovementGenerator *m, MovementSlot slot);
+ void Mutate(MovementGenerator* m, MovementSlot slot);
void DirectClean(bool reset);
void DelayedClean();
+ void DirectClean(MovementSlot slot);
+ void DelayedClean(MovementSlot slot);
void DirectExpire(bool reset);
void DelayedExpire();
void DirectDelete(MovementGenerator* curr);
diff --git a/src/server/game/Movement/MovementGenerator.h b/src/server/game/Movement/MovementGenerator.h
index 8f75e3a2361..39b2a16cc8b 100755
--- a/src/server/game/Movement/MovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerator.h
@@ -36,10 +36,11 @@ class TC_GAME_API MovementGenerator
virtual void Finalize(Unit*) = 0;
virtual void Reset(Unit*) = 0;
virtual bool Update(Unit*, uint32 diff) = 0;
-
virtual MovementGeneratorType GetMovementGeneratorType() const = 0;
virtual void UnitSpeedChanged() { }
+ virtual void Pause(uint32/* timer = 0*/) { } // timer in ms
+ virtual void Resume(uint32/* overrideTimer = 0*/) { } // timer in ms
// 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; }
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
index 498cdf04876..133de699e84 100644
--- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
@@ -83,11 +83,7 @@ void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner)
owner->SetWalk(true);
owner->LoadCreaturesAddon();
owner->AI()->JustReachedHome();
- if (owner->isRegeneratingHealth())
- {
- owner->SetFullHealth();
- owner->SetPower(POWER_MANA, owner->GetMaxPower(POWER_MANA));
- }
+ owner->SetSpawnHealth();
}
}
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
index a952421833d..7da90e2d515 100755
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
@@ -18,7 +18,6 @@
#include "CreatureAI.h"
#include "Creature.h"
-#include "CreatureGroups.h"
#include "Player.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
@@ -55,8 +54,7 @@ void PointMovementGenerator<T>::DoInitialize(T* owner)
// Call for creature group update
if (Creature* creature = owner->ToCreature())
- if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
- creature->GetFormation()->LeaderMoveTo(Position(_x, _y, _z), _movementId);
+ creature->SignalFormationMovement(Position(_x, _y, _z), _movementId);
}
template<class T>
@@ -90,8 +88,7 @@ bool PointMovementGenerator<T>::DoUpdate(T* owner, uint32 /*diff*/)
// Call for creature group update
if (Creature* creature = owner->ToCreature())
- if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
- creature->GetFormation()->LeaderMoveTo(Position(_x, _y, _z), _movementId);
+ creature->SignalFormationMovement(Position(_x, _y, _z), _movementId);
}
return !owner->movespline->Finalized();
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
index 0a1972c0972..b2a9e5b83b9 100644
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
@@ -18,7 +18,6 @@
#include "RandomMovementGenerator.h"
#include "Creature.h"
-#include "CreatureGroups.h"
#include "Map.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
@@ -116,8 +115,7 @@ void RandomMovementGenerator<Creature>::SetRandomLocation(Creature* owner)
_timer.Reset(traveltime + resetTimer);
// Call for creature group update
- if (owner->GetFormation() && owner->GetFormation()->getLeader() == owner)
- owner->GetFormation()->LeaderMoveTo(position);
+ owner->SignalFormationMovement(position);
}
template<class T>
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
index f292c0a8091..74a8588163c 100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
@@ -17,99 +17,135 @@
*/
#include "WaypointMovementGenerator.h"
+#include "Creature.h"
#include "CreatureAI.h"
-#include "CreatureGroups.h"
#include "Log.h"
#include "MapManager.h"
#include "MoveSpline.h"
#include "MoveSplineInit.h"
#include "ObjectMgr.h"
+#include "Player.h"
#include "Transport.h"
+#include "WaypointDefines.h"
+#include "WaypointManager.h"
#include "World.h"
+WaypointMovementGenerator<Creature>::WaypointMovementGenerator(uint32 pathId, bool repeating) : _nextMoveTime(0), _recalculateSpeed(false), _isArrivalDone(false), _pathId(pathId),
+ _repeating(repeating), _loadedFromDB(true), _stalled(false), _done(false)
+{
+}
+
+WaypointMovementGenerator<Creature>::WaypointMovementGenerator(WaypointPath& path, bool repeating) : _nextMoveTime(0), _recalculateSpeed(false), _isArrivalDone(false), _pathId(0),
+ _repeating(repeating), _loadedFromDB(false), _stalled(false), _done(false)
+{
+ _path = &path;
+}
+
void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature)
{
- if (!path_id)
- path_id = creature->GetWaypointPath();
+ if (_loadedFromDB)
+ {
+ if (!_pathId)
+ _pathId = creature->GetWaypointPath();
- i_path = sWaypointMgr->GetPath(path_id);
+ _path = sWaypointMgr->GetPath(_pathId);
+ }
- if (!i_path)
+ if (!_path)
{
// No path id found for entry
- TC_LOG_ERROR("sql.sql", "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u DB GUID: %u) doesn't have waypoint path id: %u", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUID().GetCounter(), creature->GetSpawnId(), path_id);
+ TC_LOG_ERROR("sql.sql", "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u DB GUID: %u) doesn't have waypoint path id: %u", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUID().GetCounter(), creature->GetSpawnId(), _pathId);
return;
}
- StartMoveNow(creature);
+ _nextMoveTime.Reset(1000);
+
+ // inform AI
+ if (creature->AI())
+ creature->AI()->WaypointPathStarted(1, _path->id);
}
void WaypointMovementGenerator<Creature>::DoInitialize(Creature* creature)
{
+ _done = false;
LoadPath(creature);
- creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
void WaypointMovementGenerator<Creature>::DoFinalize(Creature* creature)
{
- creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
creature->SetWalk(false);
}
void WaypointMovementGenerator<Creature>::DoReset(Creature* creature)
{
- creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- StartMoveNow(creature);
+ if (!_done && CanMove(creature))
+ StartMoveNow(creature);
+ else if (_done)
+ {
+ // mimic IdleMovementGenerator
+ if (!creature->IsStopped())
+ creature->StopMoving();
+ }
}
void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature)
{
- if (!i_path || i_path->empty())
+ if (!_path || _path->nodes.empty())
return;
- if (m_isArrivalDone)
- return;
-
- m_isArrivalDone = true;
- if (i_path->at(i_currentNode)->event_id && urand(0, 99) < i_path->at(i_currentNode)->event_chance)
+ ASSERT(_currentNode < _path->nodes.size(), "WaypointMovementGenerator::OnArrived: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->id);
+ WaypointNode const &waypoint = _path->nodes.at(_currentNode);
+ if (waypoint.delay)
{
- TC_LOG_DEBUG("maps.script", "Creature movement start script %u at point %u for %s.", i_path->at(i_currentNode)->event_id, i_currentNode, creature->GetGUID().ToString().c_str());
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
- creature->GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, creature, nullptr);
+ _nextMoveTime.Reset(waypoint.delay);
}
- // Inform script
- MovementInform(creature);
- creature->UpdateWaypointID(i_currentNode);
-
- if (i_path->at(i_currentNode)->delay)
+ if (waypoint.eventId && urand(0, 99) < waypoint.eventChance)
{
+ TC_LOG_DEBUG("maps.script", "Creature movement start script %u at point %u for %s.", waypoint.eventId, _currentNode, creature->GetGUID().ToString().c_str());
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
- Stop(i_path->at(i_currentNode)->delay);
+ creature->GetMap()->ScriptsStart(sWaypointScripts, waypoint.eventId, creature, nullptr);
+ }
+
+ // inform AI
+ if (creature->AI())
+ {
+ creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, _currentNode);
+ creature->AI()->WaypointReached(waypoint.id, _path->id);
}
+
+ creature->UpdateCurrentWaypointInfo(waypoint.id, _path->id);
}
bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
{
- if (!i_path || i_path->empty())
- return false;
+ if (!creature || !creature->IsAlive())
+ return true;
- // Dont allow dead creatures to move
- if (!creature->IsAlive())
- return false;
+ if (_done || !_path || _path->nodes.empty())
+ return true;
- if (Stopped())
+ // if the owner is the leader of its formation, check members status
+ if (creature->IsFormationLeader() && !creature->IsFormationLeaderMoveAllowed())
+ {
+ _nextMoveTime.Reset(1000);
return true;
+ }
- bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID();
+ bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && !creature->GetTransGUID().IsEmpty();
- if (m_isArrivalDone)
+ if (_isArrivalDone)
{
- if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint
+ ASSERT(_currentNode < _path->nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->id);
+ WaypointNode const &waypoint = _path->nodes.at(_currentNode);
+
+ if ((_currentNode == _path->nodes.size() - 1) && !_repeating) // If that's our last waypoint
{
- float x = i_path->at(i_currentNode)->x;
- float y = i_path->at(i_currentNode)->y;
- float z = i_path->at(i_currentNode)->z;
+ float x = waypoint.x;
+ float y = waypoint.y;
+ float z = waypoint.z;
float o = creature->GetOrientation();
if (!transportPath)
@@ -127,21 +163,31 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
transportPath = false;
// else if (vehicle) - this should never happen, vehicle offsets are const
}
+ _done = true;
+ creature->UpdateCurrentWaypointInfo(0, 0);
- creature->GetMotionMaster()->Initialize();
- return false;
+ // inform AI
+ if (creature->AI())
+ creature->AI()->WaypointPathEnded(waypoint.id, _path->id);
+ return true;
}
- i_currentNode = (i_currentNode+1) % i_path->size();
+ _currentNode = (_currentNode + 1) % _path->nodes.size();
+
+ // inform AI
+ if (creature->AI())
+ creature->AI()->WaypointStarted(waypoint.id, _path->id);
}
- WaypointData const* node = i_path->at(i_currentNode);
+ ASSERT(_currentNode < _path->nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->id);
+ WaypointNode const &waypoint = _path->nodes.at(_currentNode);
+ Position formationDest(waypoint.x, waypoint.y, waypoint.z, (waypoint.orientation && waypoint.delay) ? waypoint.orientation : 0.0f);
- m_isArrivalDone = false;
+ _isArrivalDone = false;
+ _recalculateSpeed = false;
creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
- 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
@@ -158,13 +204,13 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
//! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call
//! but formationDest contains global coordinates
- init.MoveTo(node->x, node->y, node->z);
+ init.MoveTo(waypoint.x, waypoint.y, waypoint.z);
//! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table
- if (node->orientation && node->delay)
- init.SetFacing(node->orientation);
+ if (waypoint.orientation && waypoint.delay)
+ init.SetFacing(waypoint.orientation);
- switch (node->move_type)
+ switch (waypoint.moveType)
{
case WAYPOINT_MOVE_TYPE_LAND:
init.SetAnimation(Movement::ToGround);
@@ -178,87 +224,135 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
case WAYPOINT_MOVE_TYPE_WALK:
init.SetWalk(true);
break;
+ default:
+ break;
}
init.Launch();
- // Call for creature group update
- if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
- creature->GetFormation()->LeaderMoveTo(formationDest, node->id, node->move_type, (node->orientation && node->delay) ? true : false);
+ // inform formation
+ creature->SignalFormationMovement(formationDest, waypoint.id, waypoint.moveType, (waypoint.orientation && waypoint.delay) ? true : false);
return true;
}
bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 diff)
{
- // Waypoint movement can be switched on/off
- // This is quite handy for escort quests and other stuff
- if (creature->HasUnitState(UNIT_STATE_NOT_MOVE))
+ if (!creature || !creature->IsAlive())
+ return true;
+
+ if (_done || !_path || _path->nodes.empty())
+ return true;
+
+ if (_stalled || creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting())
{
- creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->StopMoving();
return true;
}
- // prevent a crash at empty waypoint path.
- if (!i_path || i_path->empty())
- return false;
- if (Stopped())
+ if (!_nextMoveTime.Passed())
{
- if (CanMove(diff))
- return StartMove(creature);
+ if (creature->movespline->Finalized())
+ {
+ _nextMoveTime.Update(diff);
+ if (_nextMoveTime.Passed())
+ return StartMoveNow(creature);
+ }
}
else
{
- // Set home position at place on waypoint movement.
- if (!creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) || !creature->GetTransGUID())
- creature->SetHomePosition(creature->GetPosition());
-
- if (creature->IsStopped())
- Stop(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER));
- else if (creature->movespline->Finalized())
+ if (creature->movespline->Finalized())
{
OnArrived(creature);
- return StartMove(creature);
+ _isArrivalDone = true;
+
+ if (_nextMoveTime.Passed())
+ return StartMove(creature);
+ }
+ else
+ {
+ // Set home position at place on waypoint movement.
+ if (!creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) || creature->GetTransGUID().IsEmpty())
+ creature->SetHomePosition(creature->GetPosition());
+
+ if (_recalculateSpeed)
+ {
+ if (_nextMoveTime.Passed())
+ StartMove(creature);
+ }
}
}
- return true;
- }
+ return true;
+}
void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature)
{
if (creature->AI())
- creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
+ creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, _currentNode);
}
bool WaypointMovementGenerator<Creature>::GetResetPos(Creature*, float& x, float& y, float& z)
{
// prevent a crash at empty waypoint path.
- if (!i_path || i_path->empty())
+ if (!_path || _path->nodes.empty())
return false;
- WaypointData const* node = i_path->at(i_currentNode);
- x = node->x; y = node->y; z = node->z;
+ ASSERT(_currentNode < _path->nodes.size(), "WaypointMovementGenerator::GetResetPos: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->id);
+ WaypointNode const &waypoint = _path->nodes.at(_currentNode);
+
+ x = waypoint.x;
+ y = waypoint.y;
+ z = waypoint.z;
return true;
}
+void WaypointMovementGenerator<Creature>::Pause(uint32 timer/* = 0*/)
+{
+ _stalled = timer ? false : true;
+ _nextMoveTime.Reset(timer ? timer : 1);
+}
+
+void WaypointMovementGenerator<Creature>::Resume(uint32 overrideTimer/* = 0*/)
+{
+ _stalled = false;
+ if (overrideTimer)
+ _nextMoveTime.Reset(overrideTimer);
+}
+
+bool WaypointMovementGenerator<Creature>::CanMove(Creature* creature)
+{
+ return _nextMoveTime.Passed() && !creature->HasUnitState(UNIT_STATE_NOT_MOVE) && !creature->IsMovementPreventedByCasting();
+}
//----------------------------------------------------//
+#define FLIGHT_TRAVEL_UPDATE 100
+#define TIMEDIFF_NEXT_WP 250
+#define SKIP_SPLINE_POINT_DISTANCE_SQ (40.f * 40.f)
+#define PLAYER_FLIGHT_SPEED 32.0f
+
+FlightPathMovementGenerator::FlightPathMovementGenerator(uint32 startNode)
+{
+ _currentNode = startNode;
+ _endGridX = 0.0f;
+ _endGridY = 0.0f;
+ _endMapId = 0;
+ _preloadTargetNode = 0;
+}
+
uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const
{
- if (i_currentNode >= i_path.size())
- return i_path.size();
+ if (_currentNode >= _path.size())
+ return _path.size();
- uint32 curMapId = i_path[i_currentNode]->MapID;
- for (uint32 i = i_currentNode; i < i_path.size(); ++i)
- if (i_path[i]->MapID != curMapId)
- return i;
+ uint32 curMapId = _path[_currentNode]->MapID;
+ for (uint32 itr = _currentNode; itr < _path.size(); ++itr)
+ if (_path[itr]->MapID != curMapId)
+ return itr;
- return i_path.size();
+ return _path.size();
}
-#define SKIP_SPLINE_POINT_DISTANCE_SQ (40.0f * 40.0f)
-
bool IsNodeIncludedInShortenedPath(TaxiPathNodeEntry const* p1, TaxiPathNodeEntry const* p2)
{
return p1->MapID != p2->MapID || std::pow(p1->LocX - p2->LocX, 2) + std::pow(p1->LocY - p2->LocY, 2) > SKIP_SPLINE_POINT_DISTANCE_SQ;
@@ -268,6 +362,7 @@ void FlightPathMovementGenerator::LoadPath(Player* player)
{
_pointsForPathSwitch.clear();
std::deque<uint32> const& taxi = player->m_taxi.GetPath();
+ float discount = player->GetReputationPriceDiscount(player->m_taxi.GetFlightMasterFactionTemplate());
for (uint32 src = 0, dst = 1; dst < taxi.size(); src = dst++)
{
uint32 path, cost;
@@ -283,24 +378,24 @@ void FlightPathMovementGenerator::LoadPath(Player* player)
bool passedPreviousSegmentProximityCheck = false;
for (uint32 i = 0; i < nodes.size(); ++i)
{
- if (passedPreviousSegmentProximityCheck || !src || i_path.empty() || IsNodeIncludedInShortenedPath(i_path[i_path.size() - 1], nodes[i]))
+ if (passedPreviousSegmentProximityCheck || !src || _path.empty() || IsNodeIncludedInShortenedPath(_path[_path.size() - 1], nodes[i]))
{
if ((!src || (IsNodeIncludedInShortenedPath(start, nodes[i]) && i >= 2)) &&
(dst == taxi.size() - 1 || (IsNodeIncludedInShortenedPath(end, nodes[i]) && i < nodes.size() - 1)))
{
passedPreviousSegmentProximityCheck = true;
- i_path.push_back(nodes[i]);
+ _path.push_back(nodes[i]);
}
}
else
{
- i_path.pop_back();
+ _path.pop_back();
--_pointsForPathSwitch.back().PathIndex;
}
}
}
- _pointsForPathSwitch.push_back({ uint32(i_path.size() - 1), int32(cost) });
+ _pointsForPathSwitch.push_back({ uint32(_path.size() - 1), int32(ceil(cost * discount)) });
}
}
@@ -331,8 +426,6 @@ void FlightPathMovementGenerator::DoFinalize(Player* player)
player->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_TAXI_BENCHMARK);
}
-#define PLAYER_FLIGHT_SPEED 32.0f
-
void FlightPathMovementGenerator::DoReset(Player* player)
{
player->getHostileRefManager().setOnlineOfflineState(false);
@@ -343,7 +436,7 @@ void FlightPathMovementGenerator::DoReset(Player* player)
uint32 end = GetPathAtMapEnd();
for (uint32 i = GetCurrentNode(); i != end; ++i)
{
- G3D::Vector3 vertice(i_path[i]->LocX, i_path[i]->LocY, i_path[i]->LocZ);
+ G3D::Vector3 vertice(_path[i]->LocX, _path[i]->LocY, _path[i]->LocZ);
init.Path().push_back(vertice);
}
init.SetFirstPointId(GetCurrentNode());
@@ -355,13 +448,13 @@ void FlightPathMovementGenerator::DoReset(Player* player)
bool FlightPathMovementGenerator::DoUpdate(Player* player, uint32 /*diff*/)
{
uint32 pointId = (uint32)player->movespline->currentPathIdx();
- if (pointId > i_currentNode)
+ if (pointId > _currentNode)
{
bool departureEvent = true;
do
{
- DoEventIfAny(player, i_path[i_currentNode], departureEvent);
- while (!_pointsForPathSwitch.empty() && _pointsForPathSwitch.front().PathIndex <= i_currentNode)
+ DoEventIfAny(player, _path[_currentNode], departureEvent);
+ while (!_pointsForPathSwitch.empty() && _pointsForPathSwitch.front().PathIndex <= _currentNode)
{
_pointsForPathSwitch.pop_front();
player->m_taxi.NextTaxiDestination();
@@ -372,31 +465,31 @@ bool FlightPathMovementGenerator::DoUpdate(Player* player, uint32 /*diff*/)
}
}
- if (pointId == i_currentNode)
+ if (pointId == _currentNode)
break;
- if (i_currentNode == _preloadTargetNode)
+ if (_currentNode == _preloadTargetNode)
PreloadEndGrid();
- i_currentNode += (uint32)departureEvent;
+ _currentNode += departureEvent ? 1 : 0;
departureEvent = !departureEvent;
}
while (true);
}
- return i_currentNode < (i_path.size() - 1);
+ return _currentNode < (_path.size() - 1);
}
void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
{
- if (i_path.empty() || i_currentNode >= i_path.size())
+ if (_path.empty() || _currentNode >= _path.size())
return;
- uint32 map0 = i_path[i_currentNode]->MapID;
- for (size_t i = i_currentNode + 1; i < i_path.size(); ++i)
+ uint32 map0 = _path[_currentNode]->MapID;
+ for (size_t i = _currentNode + 1; i < _path.size(); ++i)
{
- if (i_path[i]->MapID != map0)
+ if (_path[i]->MapID != map0)
{
- i_currentNode = i;
+ _currentNode = i;
return;
}
}
@@ -413,7 +506,7 @@ void FlightPathMovementGenerator::DoEventIfAny(Player* player, TaxiPathNodeEntry
bool FlightPathMovementGenerator::GetResetPos(Player*, float& x, float& y, float& z)
{
- TaxiPathNodeEntry const* node = i_path[i_currentNode];
+ TaxiPathNodeEntry const* node = _path[_currentNode];
x = node->LocX;
y = node->LocY;
z = node->LocZ;
@@ -424,11 +517,11 @@ void FlightPathMovementGenerator::InitEndGridInfo()
{
/*! Storage to preload flightmaster grid at end of flight. For multi-stop flights, this will
be reinitialized for each flightmaster at the end of each spline (or stop) in the flight. */
- uint32 nodeCount = i_path.size(); //! Number of nodes in path.
- _endMapId = i_path[nodeCount - 1]->MapID; //! MapId of last node
+ uint32 nodeCount = _path.size(); //! Number of nodes in path.
+ _endMapId = _path[nodeCount - 1]->MapID; //! MapId of last node
_preloadTargetNode = nodeCount - 3;
- _endGridX = i_path[nodeCount - 1]->LocX;
- _endGridY = i_path[nodeCount - 1]->LocY;
+ _endGridX = _path[nodeCount - 1]->LocX;
+ _endGridY = _path[nodeCount - 1]->LocY;
}
void FlightPathMovementGenerator::PreloadEndGrid()
@@ -439,7 +532,7 @@ void FlightPathMovementGenerator::PreloadEndGrid()
// Load the grid
if (endMap)
{
- TC_LOG_DEBUG("misc", "Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(i_path.size() - 1));
+ TC_LOG_DEBUG("misc", "Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(_path.size() - 1));
endMap->LoadGrid(_endGridX, _endGridY);
}
else
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
index 7426a166ea1..e447bad7bb8 100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
@@ -19,103 +19,91 @@
#ifndef TRINITY_WAYPOINTMOVEMENTGENERATOR_H
#define TRINITY_WAYPOINTMOVEMENTGENERATOR_H
-/** @page PathMovementGenerator is used to generate movements
+/**
+ * @page PathMovementGenerator is used to generate movements
* of waypoints and flight paths. Each serves the purpose
* of generate activities so that it generates updated
* packets for the players.
*/
-#include "MovementGenerator.h"
-#include "Creature.h"
#include "DBCStructure.h"
-#include "Player.h"
+#include "MovementGenerator.h"
#include "Timer.h"
-#include "WaypointManager.h"
-#define FLIGHT_TRAVEL_UPDATE 100
-#define TIMEDIFF_NEXT_WP 250
+class Creature;
+class Player;
+struct WaypointPath;
-template<class T, class P>
+template<class Entity, class BasePath>
class PathMovementBase
{
public:
- PathMovementBase() : i_path(), i_currentNode(0) { }
+ PathMovementBase() : _path(), _currentNode(0) { }
virtual ~PathMovementBase() { };
- uint32 GetCurrentNode() const { return i_currentNode; }
+ uint32 GetCurrentNode() const { return _currentNode; }
protected:
- P i_path;
- uint32 i_currentNode;
+ BasePath _path;
+ uint32 _currentNode;
};
template<class T>
class WaypointMovementGenerator;
template<>
-class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Creature, WaypointMovementGenerator<Creature> >,
- public PathMovementBase<Creature, WaypointPath const*>
+class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium<Creature, WaypointMovementGenerator<Creature>>, public PathMovementBase<Creature, WaypointPath const*>
{
public:
- WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true)
- : i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating) { }
- ~WaypointMovementGenerator() { i_path = nullptr; }
+ explicit WaypointMovementGenerator(uint32 pathId = 0, bool repeating = true);
+ explicit WaypointMovementGenerator(WaypointPath& path, bool repeating = true);
+
+ ~WaypointMovementGenerator() { _path = nullptr; }
+
void DoInitialize(Creature*);
void DoFinalize(Creature*);
void DoReset(Creature*);
bool DoUpdate(Creature*, uint32 diff);
- void MovementInform(Creature*);
-
MovementGeneratorType GetMovementGeneratorType() const override { return WAYPOINT_MOTION_TYPE; }
+ void UnitSpeedChanged() override { _recalculateSpeed = true; }
+ void Pause(uint32 timer = 0) override;
+ void Resume(uint32 overrideTimer = 0) override;
- // now path movement implmementation
- void LoadPath(Creature*);
+ void MovementInform(Creature*);
bool GetResetPos(Creature*, float& x, float& y, float& z);
private:
-
- void Stop(int32 time) { i_nextMoveTime.Reset(time);}
-
- bool Stopped() { return !i_nextMoveTime.Passed();}
-
- bool CanMove(int32 diff)
- {
- i_nextMoveTime.Update(diff);
- return i_nextMoveTime.Passed();
- }
-
+ void LoadPath(Creature*);
void OnArrived(Creature*);
bool StartMove(Creature*);
-
- void StartMoveNow(Creature* creature)
+ bool CanMove(Creature*);
+ bool StartMoveNow(Creature* creature)
{
- i_nextMoveTime.Reset(0);
- StartMove(creature);
+ _nextMoveTime.Reset(0);
+ return StartMove(creature);
}
- TimeTrackerSmall i_nextMoveTime;
- bool m_isArrivalDone;
- uint32 path_id;
- bool repeating;
+ TimeTrackerSmall _nextMoveTime;
+ bool _recalculateSpeed;
+ bool _isArrivalDone;
+ uint32 _pathId;
+ bool _repeating;
+ bool _loadedFromDB;
+ bool _stalled;
+ bool _done;
};
-/** FlightPathMovementGenerator generates movement of the player for the paths
+/**
+ * FlightPathMovementGenerator generates movement of the player for the paths
* and hence generates ground and activities for the player.
*/
-class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, FlightPathMovementGenerator >,
- public PathMovementBase<Player, TaxiPathNodeList>
+class FlightPathMovementGenerator : public MovementGeneratorMedium<Player, FlightPathMovementGenerator>, public PathMovementBase<Player, TaxiPathNodeList>
{
public:
- explicit FlightPathMovementGenerator(uint32 startNode = 0)
- {
- i_currentNode = startNode;
- _endGridX = 0.0f;
- _endGridY = 0.0f;
- _endMapId = 0;
- _preloadTargetNode = 0;
- }
+ explicit FlightPathMovementGenerator(uint32 startNode = 0);
+
void LoadPath(Player* player);
void DoInitialize(Player*);
void DoReset(Player*);
@@ -123,15 +111,14 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig
bool DoUpdate(Player*, uint32);
MovementGeneratorType GetMovementGeneratorType() const override { return FLIGHT_MOTION_TYPE; }
- TaxiPathNodeList const& GetPath() { return i_path; }
+ TaxiPathNodeList const& GetPath() { return _path; }
uint32 GetPathAtMapEnd() const;
- bool HasArrived() const { return (i_currentNode >= i_path.size()); }
+ bool HasArrived() const { return (_currentNode >= _path.size()); }
void SetCurrentNodeAfterTeleport();
- void SkipCurrentNode() { ++i_currentNode; }
+ void SkipCurrentNode() { ++_currentNode; }
void DoEventIfAny(Player* player, TaxiPathNodeEntry const* node, bool departure);
bool GetResetPos(Player*, float& x, float& y, float& z);
-
void InitEndGridInfo();
void PreloadEndGrid();
diff --git a/src/server/game/Movement/Waypoints/WaypointDefines.h b/src/server/game/Movement/Waypoints/WaypointDefines.h
new file mode 100644
index 00000000000..dbb7a15fa5c
--- /dev/null
+++ b/src/server/game/Movement/Waypoints/WaypointDefines.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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_WAYPOINTDEFINES_H
+#define TRINITY_WAYPOINTDEFINES_H
+
+#include "Define.h"
+#include <vector>
+
+enum WaypointMoveType
+{
+ WAYPOINT_MOVE_TYPE_WALK,
+ WAYPOINT_MOVE_TYPE_RUN,
+ WAYPOINT_MOVE_TYPE_LAND,
+ WAYPOINT_MOVE_TYPE_TAKEOFF,
+
+ WAYPOINT_MOVE_TYPE_MAX
+};
+
+struct WaypointNode
+{
+ WaypointNode() : id(0), x(0.f), y(0.f), z(0.f), orientation(0.f), delay(0), eventId(0), moveType(WAYPOINT_MOVE_TYPE_RUN), eventChance(0) { }
+ WaypointNode(uint32 _id, float _x, float _y, float _z, float _orientation = 0.f, uint32 _delay = 0)
+ {
+ id = _id;
+ x = _x;
+ y = _y;
+ z = _z;
+ orientation = _orientation;
+ delay = _delay;
+ eventId = 0;
+ moveType = WAYPOINT_MOVE_TYPE_WALK;
+ eventChance = 100;
+ }
+
+ uint32 id;
+ float x, y, z, orientation;
+ uint32 delay;
+ uint32 eventId;
+ uint32 moveType;
+ uint8 eventChance;
+};
+
+struct WaypointPath
+{
+ WaypointPath() : id(0) { }
+ WaypointPath(uint32 _id, std::vector<WaypointNode>&& _nodes)
+ {
+ id = _id;
+ nodes = _nodes;
+ }
+
+ std::vector<WaypointNode> nodes;
+ uint32 id;
+};
+
+#endif
diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp
index 76c6228d302..71b2c29621b 100644
--- a/src/server/game/Movement/Waypoints/WaypointManager.cpp
+++ b/src/server/game/Movement/Waypoints/WaypointManager.cpp
@@ -16,27 +16,12 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "WaypointManager.h"
#include "DatabaseEnv.h"
#include "GridDefines.h"
-#include "WaypointManager.h"
#include "MapManager.h"
#include "Log.h"
-WaypointMgr::WaypointMgr() { }
-
-WaypointMgr::~WaypointMgr()
-{
- for (WaypointPathContainer::iterator itr = _waypointStore.begin(); itr != _waypointStore.end(); ++itr)
- {
- for (WaypointPath::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it)
- delete *it;
-
- itr->second.clear();
- }
-
- _waypointStore.clear();
-}
-
void WaypointMgr::Load()
{
uint32 oldMSTime = getMSTime();
@@ -46,7 +31,7 @@ void WaypointMgr::Load()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 waypoints. DB table `waypoint_data` is empty!");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 waypoints. DB table `waypoint_data` is empty!");
return;
}
@@ -55,11 +40,7 @@ void WaypointMgr::Load()
do
{
Field* fields = result->Fetch();
- WaypointData* wp = new WaypointData();
-
uint32 pathId = fields[0].GetUInt32();
- WaypointPath& path = _waypointStore[pathId];
-
float x = fields[2].GetFloat();
float y = fields[3].GetFloat();
float z = fields[4].GetFloat();
@@ -68,25 +49,27 @@ void WaypointMgr::Load()
Trinity::NormalizeMapCoord(x);
Trinity::NormalizeMapCoord(y);
- wp->id = fields[1].GetUInt32();
- wp->x = x;
- wp->y = y;
- wp->z = z;
- wp->orientation = o;
- wp->move_type = fields[6].GetUInt32();
+ WaypointNode waypoint;
+ waypoint.id = fields[1].GetUInt32();
+ waypoint.x = x;
+ waypoint.y = y;
+ waypoint.z = z;
+ waypoint.orientation = o;
+ waypoint.moveType = fields[6].GetUInt32();
- if (wp->move_type >= WAYPOINT_MOVE_TYPE_MAX)
+ if (waypoint.moveType >= WAYPOINT_MOVE_TYPE_MAX)
{
- TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp->id);
- delete wp;
+ TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", waypoint.id);
continue;
}
- wp->delay = fields[7].GetUInt32();
- wp->event_id = fields[8].GetUInt32();
- wp->event_chance = fields[9].GetInt16();
+ waypoint.delay = fields[7].GetUInt32();
+ waypoint.eventId = fields[8].GetUInt32();
+ waypoint.eventChance = fields[9].GetInt16();
- path.push_back(wp);
+ WaypointPath& path = _waypointStore[pathId];
+ path.id = pathId;
+ path.nodes.push_back(std::move(waypoint));
++count;
}
while (result->NextRow());
@@ -102,14 +85,9 @@ WaypointMgr* WaypointMgr::instance()
void WaypointMgr::ReloadPath(uint32 id)
{
- WaypointPathContainer::iterator itr = _waypointStore.find(id);
+ auto itr = _waypointStore.find(id);
if (itr != _waypointStore.end())
- {
- for (WaypointPath::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it)
- delete *it;
-
_waypointStore.erase(itr);
- }
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID);
@@ -120,13 +98,10 @@ void WaypointMgr::ReloadPath(uint32 id)
if (!result)
return;
- WaypointPath& path = _waypointStore[id];
-
+ std::vector<WaypointNode> values;
do
{
Field* fields = result->Fetch();
- WaypointData* wp = new WaypointData();
-
float x = fields[1].GetFloat();
float y = fields[2].GetFloat();
float z = fields[3].GetFloat();
@@ -135,26 +110,36 @@ void WaypointMgr::ReloadPath(uint32 id)
Trinity::NormalizeMapCoord(x);
Trinity::NormalizeMapCoord(y);
- wp->id = fields[0].GetUInt32();
- wp->x = x;
- wp->y = y;
- wp->z = z;
- wp->orientation = o;
- wp->move_type = fields[5].GetUInt32();
+ WaypointNode waypoint;
+ waypoint.id = fields[0].GetUInt32();
+ waypoint.x = x;
+ waypoint.y = y;
+ waypoint.z = z;
+ waypoint.orientation = o;
+ waypoint.moveType = fields[5].GetUInt32();
- if (wp->move_type >= WAYPOINT_MOVE_TYPE_MAX)
+ if (waypoint.moveType >= WAYPOINT_MOVE_TYPE_MAX)
{
- TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp->id);
- delete wp;
+ TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", waypoint.id);
continue;
}
- wp->delay = fields[6].GetUInt32();
- wp->event_id = fields[7].GetUInt32();
- wp->event_chance = fields[8].GetUInt8();
-
- path.push_back(wp);
+ waypoint.delay = fields[6].GetUInt32();
+ waypoint.eventId = fields[7].GetUInt32();
+ waypoint.eventChance = fields[8].GetUInt8();
+ values.push_back(std::move(waypoint));
}
while (result->NextRow());
+
+ _waypointStore[id] = WaypointPath(id, std::move(values));
+}
+
+WaypointPath const* WaypointMgr::GetPath(uint32 id) const
+{
+ auto itr = _waypointStore.find(id);
+ if (itr != _waypointStore.end())
+ return &itr->second;
+
+ return nullptr;
}
diff --git a/src/server/game/Movement/Waypoints/WaypointManager.h b/src/server/game/Movement/Waypoints/WaypointManager.h
index f62805594ef..769e45432e7 100644
--- a/src/server/game/Movement/Waypoints/WaypointManager.h
+++ b/src/server/game/Movement/Waypoints/WaypointManager.h
@@ -20,32 +20,10 @@
#define TRINITY_WAYPOINTMANAGER_H
#include "Define.h"
+#include "WaypointDefines.h"
#include <vector>
#include <unordered_map>
-enum WaypointMoveType
-{
- WAYPOINT_MOVE_TYPE_WALK,
- WAYPOINT_MOVE_TYPE_RUN,
- WAYPOINT_MOVE_TYPE_LAND,
- WAYPOINT_MOVE_TYPE_TAKEOFF,
-
- WAYPOINT_MOVE_TYPE_MAX
-};
-
-struct WaypointData
-{
- uint32 id;
- float x, y, z, orientation;
- uint32 delay;
- uint32 event_id;
- uint32 move_type;
- uint8 event_chance;
-};
-
-typedef std::vector<WaypointData*> WaypointPath;
-typedef std::unordered_map<uint32, WaypointPath> WaypointPathContainer;
-
class TC_GAME_API WaypointMgr
{
public:
@@ -58,20 +36,12 @@ class TC_GAME_API WaypointMgr
void Load();
// Returns the path from a given id
- WaypointPath const* GetPath(uint32 id) const
- {
- WaypointPathContainer::const_iterator itr = _waypointStore.find(id);
- if (itr != _waypointStore.end())
- return &itr->second;
-
- return nullptr;
- }
+ WaypointPath const* GetPath(uint32 id) const;
private:
- WaypointMgr();
- ~WaypointMgr();
+ WaypointMgr() { }
- WaypointPathContainer _waypointStore;
+ std::unordered_map<uint32, WaypointPath> _waypointStore;
};
#define sWaypointMgr WaypointMgr::instance()
diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
index a2d195bea20..b2e962abc0f 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp
+++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
@@ -91,7 +91,7 @@ void OPvPCapturePoint::AddGO(uint32 type, ObjectGuid::LowType guid, uint32 entry
{
if (!entry)
{
- GameObjectData const* data = sObjectMgr->GetGOData(guid);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(guid);
if (!data)
return;
entry = data->id;
@@ -117,7 +117,7 @@ void OPvPCapturePoint::AddCre(uint32 type, ObjectGuid::LowType guid, uint32 entr
bool OPvPCapturePoint::AddObject(uint32 type, uint32 entry, uint32 map, Position const& pos, QuaternionData const& rot)
{
- if (ObjectGuid::LowType guid = sObjectMgr->AddGOData(entry, map, pos, rot, 0))
+ if (ObjectGuid::LowType guid = sObjectMgr->AddGameObjectData(entry, map, pos, rot, 0))
{
AddGO(type, guid, entry);
return true;
@@ -149,7 +149,7 @@ bool OPvPCapturePoint::SetCapturePointData(uint32 entry, uint32 map, Position co
return false;
}
- m_capturePointSpawnId = sObjectMgr->AddGOData(entry, map, pos, rot, 0);
+ m_capturePointSpawnId = sObjectMgr->AddGameObjectData(entry, map, pos, rot, 0);
if (m_capturePointSpawnId == 0)
return false;
@@ -217,7 +217,7 @@ bool OPvPCapturePoint::DelObject(uint32 type)
go->SetRespawnTime(0);
go->Delete();
}
- sObjectMgr->DeleteGOData(spawnId);
+ sObjectMgr->DeleteGameObjectData(spawnId);
m_ObjectTypes[m_Objects[type]] = 0;
m_Objects[type] = 0;
return true;
@@ -225,7 +225,7 @@ bool OPvPCapturePoint::DelObject(uint32 type)
bool OPvPCapturePoint::DelCapturePoint()
{
- sObjectMgr->DeleteGOData(m_capturePointSpawnId);
+ sObjectMgr->DeleteGameObjectData(m_capturePointSpawnId);
m_capturePointSpawnId = 0;
if (m_capturePoint)
diff --git a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
index ebe3d568beb..48456efb424 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
+++ b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
@@ -54,7 +54,7 @@ void OutdoorPvPMgr::InitOutdoorPvP()
QueryResult result = WorldDatabase.Query("SELECT TypeId, ScriptName FROM outdoorpvp_template");
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 outdoor PvP definitions. DB table `outdoorpvp_template` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 outdoor PvP definitions. DB table `outdoorpvp_template` is empty.");
return;
}
diff --git a/src/server/game/Pools/PoolMgr.cpp b/src/server/game/Pools/PoolMgr.cpp
index 9305e226421..25b9bd5f363 100644
--- a/src/server/game/Pools/PoolMgr.cpp
+++ b/src/server/game/Pools/PoolMgr.cpp
@@ -153,34 +153,6 @@ bool PoolGroup<T>::CheckPool() const
return true;
}
-template <class T>
-PoolObject* PoolGroup<T>::RollOne(ActivePoolData& spawns, uint32 triggerFrom)
-{
- if (!ExplicitlyChanced.empty())
- {
- float roll = (float)rand_chance();
-
- for (uint32 i = 0; i < ExplicitlyChanced.size(); ++i)
- {
- roll -= ExplicitlyChanced[i].chance;
- // Triggering object is marked as spawned at this time and can be also rolled (respawn case)
- // so this need explicit check for this case
- if (roll < 0 && (ExplicitlyChanced[i].guid == triggerFrom || !spawns.IsActiveObject<T>(ExplicitlyChanced[i].guid)))
- return &ExplicitlyChanced[i];
- }
- }
- if (!EqualChanced.empty())
- {
- uint32 index = urand(0, EqualChanced.size()-1);
- // Triggering object is marked as spawned at this time and can be also rolled (respawn case)
- // so this need explicit check for this case
- if (EqualChanced[index].guid == triggerFrom || !spawns.IsActiveObject<T>(EqualChanced[index].guid))
- return &EqualChanced[index];
- }
-
- return nullptr;
-}
-
// Main method to despawn a creature or gameobject in a pool
// If no guid is passed, the pool is just removed (event end case)
// If guid is filled, cache will be used and no removal will occur, it just fill the cache
@@ -222,7 +194,7 @@ void PoolGroup<Creature>::Despawn1Object(ObjectGuid::LowType guid)
{
sObjectMgr->RemoveCreatureFromGrid(guid, data);
- Map* map = sMapMgr->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
if (!map->Instanceable())
{
auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(guid);
@@ -230,6 +202,9 @@ void PoolGroup<Creature>::Despawn1Object(ObjectGuid::LowType guid)
{
Creature* creature = itr->second;
++itr;
+ // For dynamic spawns, save respawn time here
+ if (!creature->GetRespawnCompatibilityMode())
+ creature->SaveRespawnTime(0, false);
creature->AddObjectToRemoveList();
}
}
@@ -240,11 +215,11 @@ void PoolGroup<Creature>::Despawn1Object(ObjectGuid::LowType guid)
template<>
void PoolGroup<GameObject>::Despawn1Object(ObjectGuid::LowType guid)
{
- if (GameObjectData const* data = sObjectMgr->GetGOData(guid))
+ if (GameObjectData const* data = sObjectMgr->GetGameObjectData(guid))
{
sObjectMgr->RemoveGameobjectFromGrid(guid, data);
- Map* map = sMapMgr->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
if (!map->Instanceable())
{
auto gameobjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(guid);
@@ -252,6 +227,10 @@ void PoolGroup<GameObject>::Despawn1Object(ObjectGuid::LowType guid)
{
GameObject* go = itr->second;
++itr;
+
+ // For dynamic spawns, save respawn time here
+ if (!go->GetRespawnCompatibilityMode())
+ go->SaveRespawnTime(0, false);
go->AddObjectToRemoveList();
}
}
@@ -333,7 +312,6 @@ void PoolGroup<Pool>::RemoveOneRelation(uint32 child_pool_id)
template <class T>
void PoolGroup<T>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 triggerFrom)
{
- uint32 lastDespawned = 0;
int count = limit - spawns.GetActiveObjectCount(poolId);
// If triggered from some object respawn this object is still marked as spawned
@@ -342,32 +320,70 @@ void PoolGroup<T>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 trig
if (triggerFrom)
++count;
- // This will try to spawn the rest of pool, not guaranteed
- for (int i = 0; i < count; ++i)
+ if (count > 0)
{
- PoolObject* obj = RollOne(spawns, triggerFrom);
- if (!obj)
- continue;
- if (obj->guid == lastDespawned)
- continue;
+ PoolObjectList rolledObjects;
+ rolledObjects.reserve(count);
- if (obj->guid == triggerFrom)
+ // roll objects to be spawned
+ if (!ExplicitlyChanced.empty())
{
- ReSpawn1Object(obj);
- triggerFrom = 0;
- continue;
+ while (count && ExplicitlyChanced.size() > rolledObjects.size())
+ {
+ --count;
+ float roll = (float)rand_chance();
+
+ for (PoolObject& obj : ExplicitlyChanced)
+ {
+ roll -= obj.chance;
+ // Triggering object is marked as spawned at this time and can be also rolled (respawn case)
+ // so this need explicit check for this case
+ if (roll < 0 && (obj.guid == triggerFrom || !spawns.IsActiveObject<T>(obj.guid)))
+ {
+ rolledObjects.push_back(obj);
+ break;
+ }
+ }
+ }
}
- spawns.ActivateObject<T>(obj->guid, poolId);
- Spawn1Object(obj);
+ else if (!EqualChanced.empty())
+ {
+ rolledObjects = EqualChanced;
+
+ for (auto itr = rolledObjects.begin(); itr != rolledObjects.end();)
+ {
+ // remove most of the active objects so there is higher chance inactive ones are spawned
+ if (spawns.IsActiveObject<T>(itr->guid) && urand(1, 4) != 1)
+ itr = rolledObjects.erase(itr);
+ else
+ ++itr;
+ }
- if (triggerFrom)
+ Trinity::Containers::RandomResize(rolledObjects, count);
+ }
+
+ // try to spawn rolled objects
+ for (PoolObject& obj : rolledObjects)
{
- // One spawn one despawn no count increase
- DespawnObject(spawns, triggerFrom);
- lastDespawned = triggerFrom;
- triggerFrom = 0;
+ if (spawns.IsActiveObject<T>(obj.guid))
+ continue;
+
+ if (obj.guid == triggerFrom)
+ {
+ ReSpawn1Object(&obj);
+ triggerFrom = 0;
+ }
+ else
+ {
+ spawns.ActivateObject<T>(obj.guid, poolId);
+ Spawn1Object(&obj);
+ }
}
}
+
+ // One spawn one despawn no count increase
+ if (triggerFrom)
+ DespawnObject(spawns, triggerFrom);
}
// Method that is actualy doing the spawn job on 1 creature
@@ -379,13 +395,13 @@ void PoolGroup<Creature>::Spawn1Object(PoolObject* obj)
sObjectMgr->AddCreatureToGrid(obj->guid, data);
// Spawn if necessary (loaded grids only)
- Map* map = sMapMgr->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
// We use spawn coords to spawn
- if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
{
Creature* creature = new Creature();
//TC_LOG_DEBUG("pool", "Spawning creature %u", guid);
- if (!creature->LoadCreatureFromDB(obj->guid, map))
+ if (!creature->LoadFromDB(obj->guid, map, true, false))
{
delete creature;
return;
@@ -398,18 +414,18 @@ void PoolGroup<Creature>::Spawn1Object(PoolObject* obj)
template <>
void PoolGroup<GameObject>::Spawn1Object(PoolObject* obj)
{
- if (GameObjectData const* data = sObjectMgr->GetGOData(obj->guid))
+ if (GameObjectData const* data = sObjectMgr->GetGameObjectData(obj->guid))
{
sObjectMgr->AddGameobjectToGrid(obj->guid, data);
// Spawn if necessary (loaded grids only)
// this base map checked as non-instanced and then only existed
- Map* map = sMapMgr->CreateBaseMap(data->mapid);
+ Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId());
// We use current coords to unspawn, not spawn coords since creature can have changed grid
- if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
+ if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
{
GameObject* pGameobject = new GameObject;
//TC_LOG_DEBUG("pool", "Spawning gameobject %u", guid);
- if (!pGameobject->LoadGameObjectFromDB(obj->guid, map, false))
+ if (!pGameobject->LoadFromDB(obj->guid, map, false))
{
delete pGameobject;
return;
@@ -688,7 +704,7 @@ void PoolMgr::LoadFromDB()
uint32 pool_id = fields[1].GetUInt32();
float chance = fields[2].GetFloat();
- GameObjectData const* data = sObjectMgr->GetGOData(guid);
+ GameObjectData const* data = sObjectMgr->GetGameObjectData(guid);
if (!data)
{
TC_LOG_ERROR("sql.sql", "`pool_gameobject` has a non existing gameobject spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id);
@@ -1074,6 +1090,21 @@ void PoolMgr::DespawnPool(uint32 pool_id)
mPoolQuestGroups[pool_id].DespawnObject(mSpawnedData);
}
+// Selects proper template overload to call based on passed type
+uint32 PoolMgr::IsPartOfAPool(SpawnObjectType type, ObjectGuid::LowType spawnId) const
+{
+ switch (type)
+ {
+ case SPAWN_TYPE_CREATURE:
+ return IsPartOfAPool<Creature>(spawnId);
+ case SPAWN_TYPE_GAMEOBJECT:
+ return IsPartOfAPool<GameObject>(spawnId);
+ default:
+ ASSERT(false, "Invalid spawn type %u passed to PoolMgr::IsPartOfPool (with spawnId %u)", uint32(type), spawnId);
+ return 0;
+ }
+}
+
// Method that check chance integrity of the creatures and gameobjects in this pool
bool PoolMgr::CheckPool(uint32 pool_id) const
{
diff --git a/src/server/game/Pools/PoolMgr.h b/src/server/game/Pools/PoolMgr.h
index 6c270e28851..6de34c05fb4 100644
--- a/src/server/game/Pools/PoolMgr.h
+++ b/src/server/game/Pools/PoolMgr.h
@@ -22,6 +22,7 @@
#include "Define.h"
#include "Creature.h"
#include "GameObject.h"
+#include "SpawnData.h"
#include "QuestDef.h"
struct PoolTemplateData
@@ -76,7 +77,6 @@ class TC_GAME_API PoolGroup
bool isEmpty() const { return ExplicitlyChanced.empty() && EqualChanced.empty(); }
void AddEntry(PoolObject& poolitem, uint32 maxentries);
bool CheckPool() const;
- PoolObject* RollOne(ActivePoolData& spawns, uint32 triggerFrom);
void DespawnObject(ActivePoolData& spawns, ObjectGuid::LowType guid=0);
void Despawn1Object(ObjectGuid::LowType guid);
void SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 triggerFrom);
@@ -118,6 +118,7 @@ class TC_GAME_API PoolMgr
template<typename T>
uint32 IsPartOfAPool(uint32 db_guid_or_pool_id) const;
+ uint32 IsPartOfAPool(SpawnObjectType type, ObjectGuid::LowType spawnId) const;
template<typename T>
bool IsSpawnedObject(uint32 db_guid_or_pool_id) const { return mSpawnedData.IsActiveObject<T>(db_guid_or_pool_id); }
diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp
index 55100ca7396..0c106f69b4a 100644
--- a/src/server/game/Quests/QuestDef.cpp
+++ b/src/server/game/Quests/QuestDef.cpp
@@ -213,15 +213,12 @@ uint32 Quest::XPValue(Player* player) const
else if (diffFactor > 10)
diffFactor = 10;
- uint32 xp = diffFactor * xpentry->Exp[_rewardXPDifficulty] / 10;
- if (xp <= 100)
- xp = 5 * ((xp + 2) / 5);
- else if (xp <= 500)
- xp = 10 * ((xp + 5) / 10);
- else if (xp <= 1000)
- xp = 25 * ((xp + 12) / 25);
- else
- xp = 50 * ((xp + 25) / 50);
+ uint32 xp = RoundXPValue(diffFactor * xpentry->Exp[_rewardXPDifficulty] / 10);
+ if (sWorld->getIntConfig(CONFIG_MIN_QUEST_SCALED_XP_RATIO))
+ {
+ uint32 minScaledXP = RoundXPValue(xpentry->Exp[_rewardXPDifficulty]) * sWorld->getIntConfig(CONFIG_MIN_QUEST_SCALED_XP_RATIO) / 100;
+ xp = std::max(minScaledXP, xp);
+ }
return xp;
}
@@ -447,3 +444,15 @@ void Quest::AddQuestLevelToTitle(std::string &title, int32 level)
questTitlePretty << "[" << level << "] " << title;
title = questTitlePretty.str();
}
+
+uint32 Quest::RoundXPValue(uint32 xp)
+{
+ if (xp <= 100)
+ return 5 * ((xp + 2) / 5);
+ else if (xp <= 500)
+ return 10 * ((xp + 5) / 10);
+ else if (xp <= 1000)
+ return 25 * ((xp + 12) / 25);
+ else
+ return 50 * ((xp + 25) / 50);
+}
diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h
index d2a5e0d5d84..75c5abd7911 100644
--- a/src/server/game/Quests/QuestDef.h
+++ b/src/server/game/Quests/QuestDef.h
@@ -384,6 +384,9 @@ class TC_GAME_API Quest
uint32 _startItemCount = 0;
uint32 _rewardMailSenderEntry = 0;
uint32 _specialFlags = 0; // custom flags, not sniffed/WDB
+
+ // Helpers
+ static uint32 RoundXPValue(uint32 xp);
};
struct QuestStatusData
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index 9f42ded8cc6..2a26261d9f1 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -955,11 +955,9 @@ private:
// Utility macros for looping over scripts.
#define FOR_SCRIPTS(T, C, E) \
- if (SCR_REG_LST(T).empty()) \
- return; \
- \
- for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \
- C != SCR_REG_LST(T).end(); ++C)
+ if (!SCR_REG_LST(T).empty()) \
+ for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \
+ C != SCR_REG_LST(T).end(); ++C)
#define FOR_SCRIPTS_RET(T, C, E, R) \
if (SCR_REG_LST(T).empty()) \
@@ -1566,16 +1564,32 @@ bool ScriptMgr::OnCastItemCombatSpell(Player* player, Unit* victim, SpellInfo co
return tmpscript->OnCastItemCombatSpell(player, victim, spellInfo, item);
}
-bool ScriptMgr::CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureTemplate const* actTemplate, CreatureData const* cData, Map const* map)
+bool ScriptMgr::CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureData const* cData, Map const* map)
{
- ASSERT(actTemplate);
-
+ ASSERT(map);
CreatureTemplate const* baseTemplate = sObjectMgr->GetCreatureTemplate(entry);
- if (!baseTemplate)
- baseTemplate = actTemplate;
+ ASSERT(baseTemplate);
+
+ // find out which template we'd be using
+ CreatureTemplate const* actTemplate = baseTemplate;
+ for (uint8 diff = uint8(map->GetSpawnMode()); diff > 0;)
+ {
+ if (uint32 diffEntry = baseTemplate->DifficultyEntry[diff - 1])
+ if (CreatureTemplate const* diffTemplate = sObjectMgr->GetCreatureTemplate(diffEntry))
+ {
+ actTemplate = diffTemplate;
+ break;
+ }
+ if (diff >= RAID_DIFFICULTY_10MAN_HEROIC && map->IsRaid())
+ diff -= 2;
+ else
+ diff -= 1;
+ }
+
uint32 scriptId = baseTemplate->ScriptID;
- if (cData && cData->ScriptId)
- scriptId = cData->ScriptId;
+ if (cData && cData->scriptId)
+ scriptId = cData->scriptId;
+
GET_SCRIPT_RET(CreatureScript, scriptId, tmpscript, true);
return tmpscript->CanSpawn(spawnId, entry, baseTemplate, actTemplate, cData, map);
}
@@ -2215,6 +2229,21 @@ AreaTriggerScript::AreaTriggerScript(char const* name)
ScriptRegistry<AreaTriggerScript>::Instance()->AddScript(this);
}
+bool OnlyOnceAreaTriggerScript::OnTrigger(Player* player, AreaTriggerEntry const* trigger)
+{
+ uint32 const triggerId = trigger->id;
+ if (InstanceScript* instance = player->GetInstanceScript())
+ {
+ if (instance->IsAreaTriggerDone(triggerId))
+ return true;
+ else
+ instance->MarkAreaTriggerDone(triggerId);
+ }
+ return _OnTrigger(player, trigger);
+}
+void OnlyOnceAreaTriggerScript::ResetAreaTriggerDone(InstanceScript* script, uint32 triggerId) { script->ResetAreaTriggerDone(triggerId); }
+void OnlyOnceAreaTriggerScript::ResetAreaTriggerDone(Player const* player, AreaTriggerEntry const* trigger) { if (InstanceScript* instance = player->GetInstanceScript()) ResetAreaTriggerDone(instance, trigger->id); }
+
BattlegroundScript::BattlegroundScript(char const* name)
: ScriptObject(name)
{
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index 311a7faf911..d801aba496d 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -448,6 +448,19 @@ class TC_GAME_API AreaTriggerScript : public ScriptObject
virtual bool OnTrigger(Player* /*player*/, AreaTriggerEntry const* /*trigger*/) { return false; }
};
+class TC_GAME_API OnlyOnceAreaTriggerScript : public AreaTriggerScript
+{
+ using AreaTriggerScript::AreaTriggerScript;
+
+ public:
+ bool OnTrigger(Player* /*player*/, AreaTriggerEntry const* /*trigger*/) override;
+
+ protected:
+ virtual bool _OnTrigger(Player* /*player*/, AreaTriggerEntry const* /*trigger*/) = 0;
+ void ResetAreaTriggerDone(InstanceScript* /*instance*/, uint32 /*triggerId*/);
+ void ResetAreaTriggerDone(Player const* /*player*/, AreaTriggerEntry const* /*trigger*/);
+};
+
class TC_GAME_API BattlegroundScript : public ScriptObject
{
protected:
@@ -907,7 +920,7 @@ class TC_GAME_API ScriptMgr
public: /* CreatureScript */
- bool CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureTemplate const* actTemplate, CreatureData const* cData, Map const* map);
+ bool CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureData const* cData, Map const* map);
CreatureAI* GetCreatureAI(Creature* creature);
public: /* GameObjectScript */
@@ -1053,6 +1066,61 @@ class TC_GAME_API ScriptMgr
std::string _currentContext;
};
+template <class S>
+class GenericSpellScriptLoader : public SpellScriptLoader
+{
+ public:
+ GenericSpellScriptLoader(char const* name) : SpellScriptLoader(name) { }
+ SpellScript* GetSpellScript() const override { return new S(); }
+};
+#define RegisterSpellScript(spell_script) new GenericSpellScriptLoader<spell_script>(#spell_script)
+
+template <class A>
+class GenericAuraScriptLoader : public SpellScriptLoader
+{
+ public:
+ GenericAuraScriptLoader(char const* name) : SpellScriptLoader(name) { }
+ AuraScript* GetAuraScript() const override { return new A(); }
+};
+#define RegisterAuraScript(aura_script) new GenericAuraScriptLoader<aura_script>(#aura_script)
+
+template <class S, class A>
+class GenericSpellAndAuraScriptLoader : public SpellScriptLoader
+{
+ public:
+ GenericSpellAndAuraScriptLoader(char const* name) : SpellScriptLoader(name) { }
+ SpellScript* GetSpellScript() const override { return new S(); }
+ AuraScript* GetAuraScript() const override { return new A(); }
+};
+#define RegisterSpellAndAuraScriptPair(spell_script, aura_script) new GenericSpellAndAuraScriptLoader<spell_script, aura_script>(#spell_script)
+
+template <class AI>
+class GenericCreatureScript : public CreatureScript
+{
+ public:
+ GenericCreatureScript(char const* name) : CreatureScript(name) { }
+ CreatureAI* GetAI(Creature* me) const override { return new AI(me); }
+};
+#define RegisterCreatureAI(ai_name) new GenericCreatureScript<ai_name>(#ai_name)
+
+template <class AI, AI*(*AIFactory)(Creature*)>
+class FactoryCreatureScript : public CreatureScript
+{
+ public:
+ FactoryCreatureScript(char const* name) : CreatureScript(name) { }
+ CreatureAI* GetAI(Creature* me) const override { return AIFactory(me); }
+};
+#define RegisterCreatureAIWithFactory(ai_name, factory_fn) new FactoryCreatureScript<ai_name, &factory_fn>(#ai_name)
+
+template <class AI>
+class GenericGameObjectScript : public GameObjectScript
+{
+ public:
+ GenericGameObjectScript(char const* name) : GameObjectScript(name) { }
+ GameObjectAI* GetAI(GameObject* go) const override { return new AI(go); }
+};
+#define RegisterGameObjectAI(ai_name) new GenericGameObjectScript<ai_name>(#ai_name)
+
#define sScriptMgr ScriptMgr::instance()
#endif
diff --git a/src/server/game/Scripting/ScriptSystem.cpp b/src/server/game/Scripting/ScriptSystem.cpp
index 1b4ad6b98d5..b814229151a 100644
--- a/src/server/game/Scripting/ScriptSystem.cpp
+++ b/src/server/game/Scripting/ScriptSystem.cpp
@@ -37,17 +37,17 @@ void SystemMgr::LoadScriptWaypoints()
{
uint32 oldMSTime = getMSTime();
- // Drop Existing Waypoint list
- m_mPointMoveMap.clear();
+ // drop Existing Waypoint list
+ _waypointStore.clear();
- uint64 uiCreatureCount = 0;
+ uint64 entryCount = 0;
- // Load Waypoints
+ // load Waypoints
QueryResult result = WorldDatabase.Query("SELECT COUNT(entry) FROM script_waypoint GROUP BY entry");
if (result)
- uiCreatureCount = result->GetRowCount();
+ entryCount = result->GetRowCount();
- TC_LOG_INFO("server.loading", "Loading Script Waypoints for " UI64FMTD " creature(s)...", uiCreatureCount);
+ TC_LOG_INFO("server.loading", "Loading Script Waypoints for " UI64FMTD " creature(s)...", entryCount);
// 0 1 2 3 4 5
result = WorldDatabase.Query("SELECT entry, pointid, location_x, location_y, location_z, waittime FROM script_waypoint ORDER BY pointid");
@@ -61,29 +61,28 @@ void SystemMgr::LoadScriptWaypoints()
do
{
- Field* pFields = result->Fetch();
- ScriptPointMove temp;
-
- temp.uiCreatureEntry = pFields[0].GetUInt32();
- uint32 uiEntry = temp.uiCreatureEntry;
- temp.uiPointId = pFields[1].GetUInt32();
- temp.fX = pFields[2].GetFloat();
- temp.fY = pFields[3].GetFloat();
- temp.fZ = pFields[4].GetFloat();
- temp.uiWaitTime = pFields[5].GetUInt32();
-
- CreatureTemplate const* pCInfo = sObjectMgr->GetCreatureTemplate(temp.uiCreatureEntry);
-
- if (!pCInfo)
+ Field* fields = result->Fetch();
+ uint32 entry = fields[0].GetUInt32();
+ uint32 id = fields[1].GetUInt32();
+ float x = fields[2].GetFloat();
+ float y = fields[3].GetFloat();
+ float z = fields[4].GetFloat();
+ uint32 waitTime = fields[5].GetUInt32();
+
+ CreatureTemplate const* info = sObjectMgr->GetCreatureTemplate(entry);
+ if (!info)
{
- TC_LOG_ERROR("sql.sql", "TSCR: DB table script_waypoint has waypoint for non-existant creature entry %u", temp.uiCreatureEntry);
+ TC_LOG_ERROR("sql.sql", "SystemMgr: DB table script_waypoint has waypoint for non-existant creature entry %u", entry);
continue;
}
- if (!pCInfo->ScriptID)
- TC_LOG_ERROR("sql.sql", "TSCR: DB table script_waypoint has waypoint for creature entry %u, but creature does not have ScriptName defined and then useless.", temp.uiCreatureEntry);
+ if (!info->ScriptID)
+ TC_LOG_ERROR("sql.sql", "SystemMgr: DB table script_waypoint has waypoint for creature entry %u, but creature does not have ScriptName defined and then useless.", entry);
+
+ WaypointPath& path = _waypointStore[entry];
+ path.id = entry;
+ path.nodes.emplace_back(id, x, y, z, 0.f, waitTime);
- m_mPointMoveMap[uiEntry].push_back(temp);
++count;
} while (result->NextRow());
@@ -163,6 +162,15 @@ void SystemMgr::LoadScriptSplineChains()
}
}
+WaypointPath const* SystemMgr::GetPath(uint32 creatureEntry) const
+{
+ auto itr = _waypointStore.find(creatureEntry);
+ if (itr == _waypointStore.end())
+ return nullptr;
+
+ return &itr->second;
+}
+
std::vector<SplineChainLink> const* SystemMgr::GetSplineChain(uint32 entry, uint16 chainId) const
{
auto it = m_mSplineChainsMap.find({ entry, chainId });
diff --git a/src/server/game/Scripting/ScriptSystem.h b/src/server/game/Scripting/ScriptSystem.h
index 7828c24d680..5b42c5b5a25 100644
--- a/src/server/game/Scripting/ScriptSystem.h
+++ b/src/server/game/Scripting/ScriptSystem.h
@@ -21,59 +21,37 @@
#include "Define.h"
#include "Hash.h"
+#include "WaypointDefines.h"
#include <unordered_map>
#include <vector>
class Creature;
struct SplineChainLink;
-#define TEXT_SOURCE_RANGE -1000000 //the amount of entries each text source has available
-
-struct ScriptPointMove
-{
- uint32 uiCreatureEntry;
- uint32 uiPointId;
- float fX;
- float fY;
- float fZ;
- uint32 uiWaitTime;
-};
-
-typedef std::vector<ScriptPointMove> ScriptPointVector;
-
class TC_GAME_API SystemMgr
{
- private:
- SystemMgr();
- ~SystemMgr();
- SystemMgr(SystemMgr const&) = delete;
- SystemMgr& operator=(SystemMgr const&) = delete;
-
public:
static SystemMgr* instance();
- typedef std::unordered_map<uint32, ScriptPointVector> PointMoveMap;
-
- //Database
+ // database
void LoadScriptWaypoints();
void LoadScriptSplineChains();
- ScriptPointVector const* GetPointMoveList(uint32 creatureEntry) const
- {
- PointMoveMap::const_iterator itr = m_mPointMoveMap.find(creatureEntry);
-
- if (itr == m_mPointMoveMap.end())
- return nullptr;
-
- return &itr->second;
- }
+ WaypointPath const* GetPath(uint32 creatureEntry) const;
std::vector<SplineChainLink> const* GetSplineChain(uint32 entry, uint16 chainId) const;
std::vector<SplineChainLink> const* GetSplineChain(Creature const* who, uint16 id) const;
- protected:
- PointMoveMap m_mPointMoveMap; //coordinates for waypoints
+ private:
typedef std::pair<uint32, uint16> ChainKeyType; // creature entry + chain ID
+
+ SystemMgr();
+ ~SystemMgr();
+
+ SystemMgr(SystemMgr const&) = delete;
+ SystemMgr& operator=(SystemMgr const&) = delete;
+
+ std::unordered_map<uint32, WaypointPath> _waypointStore;
std::unordered_map<ChainKeyType, std::vector<SplineChainLink>> m_mSplineChainsMap; // spline chains
};
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 05f69568507..a5a4f9139b2 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -273,7 +273,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
///- Before we process anything:
/// If necessary, kick the player because the client didn't send anything for too long
/// (or they've been idling in character select)
- if (IsConnectionIdle())
+ if (IsConnectionIdle() && !HasPermission(rbac::RBAC_PERM_IGNORE_IDLE_CONNECTION))
m_Socket->CloseSocket();
///- Retrieve packets from the receive queue and call the appropriate handlers
diff --git a/src/server/game/Skills/SkillDiscovery.cpp b/src/server/game/Skills/SkillDiscovery.cpp
index 44d9a6a153f..722b5427c72 100644
--- a/src/server/game/Skills/SkillDiscovery.cpp
+++ b/src/server/game/Skills/SkillDiscovery.cpp
@@ -56,7 +56,7 @@ void LoadSkillDiscoveryTable()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty.");
return;
}
diff --git a/src/server/game/Skills/SkillExtraItems.cpp b/src/server/game/Skills/SkillExtraItems.cpp
index 3b066e71b8b..91e4ea0d244 100644
--- a/src/server/game/Skills/SkillExtraItems.cpp
+++ b/src/server/game/Skills/SkillExtraItems.cpp
@@ -61,7 +61,7 @@ void LoadSkillPerfectItemTable()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 spell perfection definitions. DB table `skill_perfect_item_template` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 spell perfection definitions. DB table `skill_perfect_item_template` is empty.");
return;
}
@@ -148,7 +148,7 @@ void LoadSkillExtraItemTable()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 spell specialization definitions. DB table `skill_extra_item_template` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 spell specialization definitions. DB table `skill_extra_item_template` is empty.");
return;
}
@@ -208,9 +208,6 @@ bool CanCreatePerfectItem(Player* player, uint32 spellId, float &perfectCreateCh
return false;
SkillPerfectItemEntry const* thisEntry = &ret->second;
- // lack of entry means no perfection proc possible
- if (!thisEntry)
- return false;
// if you don't have the spell needed, then no procs for you
if (!player->HasSpell(thisEntry->requiredSpecialization))
@@ -233,10 +230,6 @@ bool CanCreateExtraItems(Player* player, uint32 spellId, float &additionalChance
SkillExtraItemEntry const* specEntry = &ret->second;
- // if no entry, then no extra items can be created
- if (!specEntry)
- return false;
-
// the player doesn't have the required specialization, return false
if (!player->HasSpell(specEntry->requiredSpecialization))
return false;
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 2d57d1472b4..80565e1b464 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -4563,7 +4563,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
break;
case 52172: // Coyote Spirit Despawn Aura
case 60244: // Blood Parrot Despawn Aura
- target->CastSpell((Unit*)nullptr, GetAmount(), true, nullptr, this);
+ target->CastSpell(nullptr, GetAmount(), true, nullptr, this);
break;
case 58600: // Restricted Flight Area
case 58730: // Restricted Flight Area
@@ -5121,7 +5121,7 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const
}
case 62292: // Blaze (Pool of Tar)
// should we use custom damage?
- target->CastSpell((Unit*)nullptr, m_spellInfo->Effects[m_effIndex].TriggerSpell, true);
+ target->CastSpell(nullptr, m_spellInfo->Effects[m_effIndex].TriggerSpell, true);
break;
case 62399: // Overload Circuit
if (target->GetMap()->IsDungeon() && int(target->GetAppliedAuras().count(62399)) >= (target->GetMap()->IsHeroic() ? 4 : 2))
@@ -5145,7 +5145,7 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const
// Mirror Image
if (GetId() == 55342)
// Set name of summons to name of caster
- target->CastSpell((Unit*)nullptr, m_spellInfo->Effects[m_effIndex].TriggerSpell, true);
+ target->CastSpell(nullptr, m_spellInfo->Effects[m_effIndex].TriggerSpell, true);
break;
}
case SPELLFAMILY_DRUID:
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index cff25bc8e2c..7c1cdc56de0 100644
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -947,6 +947,9 @@ bool Aura::CanBeSaved() const
if (IsPassive())
return false;
+ if (GetSpellInfo()->IsChanneled())
+ return false;
+
// Check if aura is single target, not only spell info
if (GetCasterGUID() != GetOwner()->GetGUID())
if (GetSpellInfo()->IsSingleTarget() || IsSingleTarget())
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index e56c573a43c..334e51eeb56 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -549,6 +549,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO
m_referencedFromCurrentSpell = false;
m_executedCurrently = false;
m_needComboPoints = m_spellInfo->NeedsComboPoints();
+ m_comboTarget = nullptr;
m_comboPointGain = 0;
m_delayStart = 0;
m_delayAtDamageCount = 0;
@@ -1399,12 +1400,9 @@ void Spell::SelectImplicitTargetDestTargets(SpellEffIndex effIndex, SpellImplici
default:
{
float angle = targetType.CalcDirectionAngle();
- float objSize = target->GetCombatReach();
- float dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster);
- if (dist < objSize)
- dist = objSize;
- else if (targetType.GetTarget() == TARGET_DEST_TARGET_RANDOM)
- dist = objSize + (dist - objSize) * float(rand_norm());
+ float dist = m_spellInfo->Effects[effIndex].CalcRadius(nullptr);
+ if (targetType.GetTarget() == TARGET_DEST_TARGET_RANDOM)
+ dist *= float(rand_norm());
Position pos = dest._position;
target->MovePositionToFirstCollision(pos, dist, angle);
@@ -2551,7 +2549,8 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
// for delayed spells ignore negative spells (after duel end) for friendly targets
/// @todo this cause soul transfer bugged
// 63881 - Malady of the Mind jump spell (Yogg-Saron)
- if (m_spellInfo->Speed > 0.0f && unit->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->IsPositive() && m_spellInfo->Id != 63881)
+ // 45034 - Curse of Boundless Agony jump spell (Kalecgos)
+ if (m_spellInfo->Speed > 0.0f && unit->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->IsPositive() && m_spellInfo->Id != 63881 && m_spellInfo->Id != 45034)
return SPELL_MISS_EVADE;
// assisting case, healing and resurrection
@@ -7881,7 +7880,7 @@ bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target)
if (!isInsideCylinder)
return false;
}
-
+
return WorldObjectSpellTargetCheck::operator ()(target);
}
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 852e34a059f..75c97b005af 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -821,27 +821,6 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex)
m_caster->CastSpell(unitTarget, spell->Id, true);
return;
}
- // Cloak of Shadows
- case 35729:
- {
- uint32 dispelMask = SpellInfo::GetDispelMask(DISPEL_ALL);
- Unit::AuraApplicationMap& Auras = unitTarget->GetAppliedAuras();
- for (Unit::AuraApplicationMap::iterator iter = Auras.begin(); iter != Auras.end();)
- {
- // remove all harmful spells on you...
- SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo();
- if (((spell->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && spell->GetSchoolMask() != SPELL_SCHOOL_MASK_NORMAL) // only affect magic spells
- || (spell->GetDispelMask() & dispelMask)) &&
- // ignore positive and passive auras
- !iter->second->IsPositive() && !iter->second->GetBase()->IsPassive())
- {
- m_caster->RemoveAura(iter);
- }
- else
- ++iter;
- }
- return;
- }
}
}
@@ -1008,7 +987,7 @@ void Spell::EffectTriggerRitualOfSummoning(SpellEffIndex effIndex)
finish();
- m_caster->CastSpell((Unit*)nullptr, spellInfo, false);
+ m_caster->CastSpell(nullptr, spellInfo, false);
}
void Spell::EffectJump(SpellEffIndex effIndex)
@@ -2818,8 +2797,6 @@ void Spell::EffectEnchantItemTmp(SpellEffIndex effIndex)
}
return;
}
- if (!itemTarget)
- return;
uint32 enchant_id = m_spellInfo->Effects[effIndex].MiscValue;
@@ -3209,7 +3186,7 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex)
// Mangle (Cat): CP
if (m_spellInfo->SpellFamilyFlags[1] & 0x400)
AddComboPointGain(unitTarget, 1);
-
+
// Shred, Maul - Rend and Tear
else if (m_spellInfo->SpellFamilyFlags[0] & 0x00008800 && unitTarget->HasAuraState(AURA_STATE_BLEEDING))
{
@@ -3946,6 +3923,9 @@ void Spell::EffectSanctuary(SpellEffIndex /*effIndex*/)
if (!unitTarget)
return;
+ if (unitTarget->GetTypeId() == TYPEID_PLAYER)
+ unitTarget->ToPlayer()->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel
+
unitTarget->getHostileRefManager().UpdateVisibility();
Unit::AttackerSet const& attackers = unitTarget->getAttackers();
@@ -4655,9 +4635,13 @@ void Spell::EffectChargeDest(SpellEffIndex /*effIndex*/)
if (m_targets.HasDst())
{
Position pos = destTarget->GetPosition();
- float angle = m_caster->GetRelativeAngle(pos.GetPositionX(), pos.GetPositionY());
- float dist = m_caster->GetDistance(pos);
- pos = m_caster->GetFirstCollisionPosition(dist, angle);
+
+ if (!m_caster->IsWithinLOS(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()))
+ {
+ float angle = m_caster->GetRelativeAngle(pos.GetPositionX(), pos.GetPositionY());
+ float dist = m_caster->GetDistance(pos);
+ pos = m_caster->GetFirstCollisionPosition(dist, angle);
+ }
m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
}
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 9e393f2fcad..e2946dbb45e 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -323,7 +323,7 @@ SpellImplicitTargetInfo::StaticData SpellImplicitTargetInfo::_data[TOTAL_SPELL_
{TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 107 TARGET_UNK_DEST_AREA_UNK_107
{TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 108 TARGET_GAMEOBJECT_CONE
{TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 109
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 110 TARGET_DEST_UNK_110
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_ENTRY, TARGET_DIR_NONE}, // 110 TARGET_UNIT_CONE_ENTRY_110
};
SpellEffectInfo::SpellEffectInfo(SpellEntry const* spellEntry, SpellInfo const* spellInfo, uint8 effIndex)
@@ -1345,10 +1345,9 @@ bool SpellInfo::CanPierceImmuneAura(SpellInfo const* auraSpellInfo) const
if (HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) || HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE))
{
// ...but not these (Divine shield, Ice block, Cyclone and Banish for example)
- if (!auraSpellInfo ||
- (auraSpellInfo->Mechanic != MECHANIC_IMMUNE_SHIELD &&
- auraSpellInfo->Mechanic != MECHANIC_INVULNERABILITY &&
- (auraSpellInfo->Mechanic != MECHANIC_BANISH || (IsRankOf(auraSpellInfo) && auraSpellInfo->Dispel != DISPEL_NONE)))) // Banish shouldn't be immune to itself, but Cyclone should
+ if (auraSpellInfo->Mechanic != MECHANIC_IMMUNE_SHIELD &&
+ auraSpellInfo->Mechanic != MECHANIC_INVULNERABILITY &&
+ (auraSpellInfo->Mechanic != MECHANIC_BANISH || (IsRankOf(auraSpellInfo) && auraSpellInfo->Dispel != DISPEL_NONE))) // Banish shouldn't be immune to itself, but Cyclone should
return true;
}
@@ -1370,7 +1369,8 @@ bool SpellInfo::CanDispelAura(SpellInfo const* auraSpellInfo) const
return true;
// These auras (Cyclone for example) are not dispelable
- if (auraSpellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) || auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE))
+ if ((auraSpellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) && auraSpellInfo->Mechanic != MECHANIC_NONE)
+ || auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE))
return false;
return true;
@@ -2877,13 +2877,7 @@ void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, uint8 effIndex, bool app
target->ApplySpellImmune(Id, IMMUNITY_MECHANIC, i, apply);
if (apply && HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY))
- {
- // exception for purely snare mechanic (eg. hands of freedom)!
- if (mechanicImmunity == (1 << MECHANIC_SNARE))
- target->RemoveMovementImpairingAuras(false);
- else
- target->RemoveAurasWithMechanic(mechanicImmunity, AURA_REMOVE_BY_DEFAULT, Id);
- }
+ target->RemoveAurasWithMechanic(mechanicImmunity, AURA_REMOVE_BY_DEFAULT, Id);
}
if (uint32 dispelImmunity = immuneInfo->DispelImmune)
@@ -3372,6 +3366,8 @@ bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const
{
case 29214: // Wrath of the Plaguebringer
case 34700: // Allergic Reaction
+ case 41914: // Parasitic Shadowfiend (Illidan)
+ case 41917: // Parasitic Shadowfiend (Illidan)
case 54836: // Wrath of the Plaguebringer
case 61987: // Avenging Wrath Marker
case 61988: // Divine Shield exclude aura
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index fc7b219d54d..22b16a29dde 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -3898,6 +3898,12 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_100_YARDS); // 100yd
});
+ // Coldflame (Lord Marrowgar)
+ ApplySpellFix({ 69146, 70823, 70824, 70825 }, [](SpellInfo* spellInfo)
+ {
+ spellInfo->AttributesEx4 &= ~SPELL_ATTR4_IGNORE_RESISTANCES;
+ });
+
// Shadow's Fate
ApplySpellFix({ 71169 }, [](SpellInfo* spellInfo)
{
@@ -4127,13 +4133,13 @@ void SpellMgr::LoadSpellInfoCorrections()
// Summon Shadow Trap
ApplySpellFix({ 73540 }, [](SpellInfo* spellInfo)
{
- spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(23); // 90 seconds
+ spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(3); // 60 seconds
});
// Shadow Trap (visual)
ApplySpellFix({ 73530 }, [](SpellInfo* spellInfo)
{
- spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(28); // 5 seconds
+ spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(27); // 3 seconds
});
// Shadow Trap
@@ -4459,6 +4465,18 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->ProcFlags = 0;
});
+ // Shadowstep
+ ApplySpellFix({ 36563 }, [](SpellInfo* spellInfo)
+ {
+ spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_4_YARDS); // 4yd
+ });
+
+ // Feral Charge - Cat
+ ApplySpellFix({ 49376 }, [](SpellInfo* spellInfo)
+ {
+ spellInfo->Effects[EFFECT_1].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_3_YARDS); // 3yd
+ });
+
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{
SpellInfo* spellInfo = mSpellInfoMap[i];
diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h
index 03f06961a4b..7e63770c3e1 100644
--- a/src/server/game/Spells/SpellScript.h
+++ b/src/server/game/Spells/SpellScript.h
@@ -786,13 +786,13 @@ class TC_GAME_API AuraScript : public _SpellScript
HookList<EffectAbsorbHandler> AfterEffectAbsorb;
// executed when mana shield aura effect is going to reduce damage
- // example: OnEffectManaShield += AuraEffectAbsorbFn(class::function, EffectIndexSpecifier);
+ // example: OnEffectManaShield += AuraEffectManaShieldFn(class::function, EffectIndexSpecifier);
// where function is: void function (AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount);
HookList<EffectManaShieldHandler> OnEffectManaShield;
#define AuraEffectManaShieldFn(F, I) EffectManaShieldFunction(&F, I)
// executed after mana shield aura effect reduced damage to target - absorbAmount is real amount absorbed by aura
- // example: AfterEffectManaShield += AuraEffectAbsorbFn(class::function, EffectIndexSpecifier);
+ // example: AfterEffectManaShield += AuraEffectManaShieldFn(class::function, EffectIndexSpecifier);
// where function is: void function (AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount);
HookList<EffectManaShieldHandler> AfterEffectManaShield;
diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp
index 0357fcc4e15..6bcf3128427 100644
--- a/src/server/game/Tools/PlayerDump.cpp
+++ b/src/server/game/Tools/PlayerDump.cpp
@@ -936,7 +936,7 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s
if (!ChangeColumn(ts, line, "at_login", "1"))
return DUMP_FILE_BROKEN;
}
- else if (!ChangeColumn(ts, line, "name", name.c_str())) // characters.name
+ else if (!ChangeColumn(ts, line, "name", name)) // characters.name
return DUMP_FILE_BROKEN;
break;
}
diff --git a/src/server/game/Warden/WardenCheckMgr.cpp b/src/server/game/Warden/WardenCheckMgr.cpp
index 3b8387e3381..5a7a6bf49dd 100644
--- a/src/server/game/Warden/WardenCheckMgr.cpp
+++ b/src/server/game/Warden/WardenCheckMgr.cpp
@@ -38,6 +38,8 @@ WardenCheckMgr::~WardenCheckMgr()
void WardenCheckMgr::LoadWardenChecks()
{
+ uint32 oldMSTime = getMSTime();
+
// Check if Warden is enabled by config before loading anything
if (!sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED))
{
@@ -141,11 +143,13 @@ void WardenCheckMgr::LoadWardenChecks()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u warden checks.", count);
+ TC_LOG_INFO("server.loading", ">> Loaded %u warden checks in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
void WardenCheckMgr::LoadWardenOverrides()
{
+ uint32 oldMSTime = getMSTime();
+
// Check if Warden is enabled by config before loading anything
if (!sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED))
{
@@ -187,7 +191,7 @@ void WardenCheckMgr::LoadWardenOverrides()
}
while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u warden action overrides.", count);
+ TC_LOG_INFO("server.loading", ">> Loaded %u warden action overrides in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
WardenCheckMgr* WardenCheckMgr::instance()
diff --git a/src/server/game/Weather/WeatherMgr.cpp b/src/server/game/Weather/WeatherMgr.cpp
index 95728b7e8ae..98fbf897e27 100644
--- a/src/server/game/Weather/WeatherMgr.cpp
+++ b/src/server/game/Weather/WeatherMgr.cpp
@@ -95,7 +95,7 @@ void LoadWeatherData()
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 weather definitions. DB table `game_weather` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 weather definitions. DB table `game_weather` is empty.");
return;
}
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index ae5cc87d097..03081a113de 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -81,7 +81,7 @@
#include "VMapFactory.h"
#include "VMapManager2.h"
#include "WardenCheckMgr.h"
-#include "WaypointMovementGenerator.h"
+#include "WaypointManager.h"
#include "WeatherMgr.h"
#include "WhoListStorage.h"
#include "WorldSession.h"
@@ -134,6 +134,11 @@ World::World()
memset(m_int_configs, 0, sizeof(m_int_configs));
memset(m_bool_configs, 0, sizeof(m_bool_configs));
memset(m_float_configs, 0, sizeof(m_float_configs));
+
+ _guidWarn = false;
+ _guidAlert = false;
+ _warnDiff = 0;
+ _warnShutdownTime = time(NULL);
}
/// World destructor
@@ -196,6 +201,59 @@ void World::SetClosed(bool val)
sScriptMgr->OnOpenStateChange(!val);
}
+void World::TriggerGuidWarning()
+{
+ // Lock this only to prevent multiple maps triggering at the same time
+ std::lock_guard<std::mutex> lock(_guidAlertLock);
+
+ time_t gameTime = GameTime::GetGameTime();
+ time_t today = (gameTime / DAY) * DAY;
+
+ // Check if our window to restart today has passed. 5 mins until quiet time
+ while (gameTime >= (today + (getIntConfig(CONFIG_RESPAWN_RESTARTQUIETTIME) * HOUR) - 1810))
+ today += DAY;
+
+ // Schedule restart for 30 minutes before quiet time, or as long as we have
+ _warnShutdownTime = today + (getIntConfig(CONFIG_RESPAWN_RESTARTQUIETTIME) * HOUR) - 1800;
+
+ _guidWarn = true;
+ SendGuidWarning();
+}
+
+void World::TriggerGuidAlert()
+{
+ // Lock this only to prevent multiple maps triggering at the same time
+ std::lock_guard<std::mutex> lock(_guidAlertLock);
+
+ DoGuidAlertRestart();
+ _guidAlert = true;
+ _guidWarn = false;
+}
+
+void World::DoGuidWarningRestart()
+{
+ if (m_ShutdownTimer)
+ return;
+
+ ShutdownServ(1800, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE);
+ _warnShutdownTime += HOUR;
+}
+
+void World::DoGuidAlertRestart()
+{
+ if (m_ShutdownTimer)
+ return;
+
+ ShutdownServ(300, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE, _alertRestartReason);
+}
+
+void World::SendGuidWarning()
+{
+ if (!m_ShutdownTimer && _guidWarn && getIntConfig(CONFIG_RESPAWN_GUIDWARNING_FREQUENCY) > 0)
+ SendServerMessage(SERVER_MSG_STRING, _guidWarningMsg.c_str());
+ _warnDiff = 0;
+}
+
/// Find a session by its id
WorldSession* World::FindSession(uint32 id) const
{
@@ -670,6 +728,27 @@ void World::LoadConfigSettings(bool reload)
m_float_configs[CONFIG_GROUP_XP_DISTANCE] = sConfigMgr->GetFloatDefault("MaxGroupXPDistance", 74.0f);
m_float_configs[CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE] = sConfigMgr->GetFloatDefault("MaxRecruitAFriendBonusDistance", 100.0f);
+ m_int_configs[CONFIG_MIN_QUEST_SCALED_XP_RATIO] = sConfigMgr->GetIntDefault("MinQuestScaledXPRatio", 0);
+ if (m_int_configs[CONFIG_MIN_QUEST_SCALED_XP_RATIO] > 100)
+ {
+ TC_LOG_ERROR("server.loading", "MinQuestScaledXPRatio (%i) must be in range 0..100. Set to 0.", m_int_configs[CONFIG_MIN_QUEST_SCALED_XP_RATIO]);
+ m_int_configs[CONFIG_MIN_QUEST_SCALED_XP_RATIO] = 0;
+ }
+
+ m_int_configs[CONFIG_MIN_CREATURE_SCALED_XP_RATIO] = sConfigMgr->GetIntDefault("MinCreatureScaledXPRatio", 0);
+ if (m_int_configs[CONFIG_MIN_CREATURE_SCALED_XP_RATIO] > 100)
+ {
+ TC_LOG_ERROR("server.loading", "MinCreatureScaledXPRatio (%i) must be in range 0..100. Set to 0.", m_int_configs[CONFIG_MIN_CREATURE_SCALED_XP_RATIO]);
+ m_int_configs[CONFIG_MIN_CREATURE_SCALED_XP_RATIO] = 0;
+ }
+
+ m_int_configs[CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO] = sConfigMgr->GetIntDefault("MinDiscoveredScaledXPRatio", 0);
+ if (m_int_configs[CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO] > 100)
+ {
+ TC_LOG_ERROR("server.loading", "MinDiscoveredScaledXPRatio (%i) must be in range 0..100. Set to 0.", m_int_configs[CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO]);
+ m_int_configs[CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO] = 0;
+ }
+
/// @todo Add MonsterSight (with meaning) in worldserver.conf or put them as define
m_float_configs[CONFIG_SIGHT_MONSTER] = sConfigMgr->GetFloatDefault("MonsterSight", 50.0f);
@@ -916,6 +995,12 @@ void World::LoadConfigSettings(bool reload)
m_int_configs[CONFIG_GROUP_VISIBILITY] = sConfigMgr->GetIntDefault("Visibility.GroupMode", 1);
m_int_configs[CONFIG_MAIL_DELIVERY_DELAY] = sConfigMgr->GetIntDefault("MailDeliveryDelay", HOUR);
+ m_int_configs[CONFIG_CLEAN_OLD_MAIL_TIME] = sConfigMgr->GetIntDefault("CleanOldMailTime", 4);
+ if (m_int_configs[CONFIG_CLEAN_OLD_MAIL_TIME] > 23)
+ {
+ TC_LOG_ERROR("server.loading", "CleanOldMailTime (%u) must be an hour, between 0 and 23. Set to 4.", m_int_configs[CONFIG_CLEAN_OLD_MAIL_TIME]);
+ m_int_configs[CONFIG_CLEAN_OLD_MAIL_TIME] = 4;
+ }
m_int_configs[CONFIG_UPTIME_UPDATE] = sConfigMgr->GetIntDefault("UpdateUptimeInterval", 10);
if (int32(m_int_configs[CONFIG_UPTIME_UPDATE]) <= 0)
@@ -1192,6 +1277,50 @@ void World::LoadConfigSettings(bool reload)
m_int_configs[CONFIG_NO_GRAY_AGGRO_BELOW] = m_int_configs[CONFIG_NO_GRAY_AGGRO_ABOVE];
}
+ // Respawn Settings
+ m_int_configs[CONFIG_RESPAWN_MINCHECKINTERVALMS] = sConfigMgr->GetIntDefault("Respawn.MinCheckIntervalMS", 5000);
+ m_int_configs[CONFIG_RESPAWN_DYNAMICMODE] = sConfigMgr->GetIntDefault("Respawn.DynamicMode", 0);
+ if (m_int_configs[CONFIG_RESPAWN_DYNAMICMODE] > 1)
+ {
+ TC_LOG_ERROR("server.loading", "Invalid value for Respawn.DynamicMode (%u). Set to 0.", m_int_configs[CONFIG_RESPAWN_DYNAMICMODE]);
+ m_int_configs[CONFIG_RESPAWN_DYNAMICMODE] = 0;
+ }
+ m_bool_configs[CONFIG_RESPAWN_DYNAMIC_ESCORTNPC] = sConfigMgr->GetBoolDefault("Respawn.DynamicEscortNPC", false);
+ m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL] = sConfigMgr->GetIntDefault("Respawn.GuidWarnLevel", 12000000);
+ if (m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL] > 16777215)
+ {
+ TC_LOG_ERROR("server.loading", "Respawn.GuidWarnLevel (%u) cannot be greater than maximum GUID (16777215). Set to 12000000.", m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL]);
+ m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL] = 12000000;
+ }
+ m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL] = sConfigMgr->GetIntDefault("Respawn.GuidAlertLevel", 16000000);
+ if (m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL] > 16777215)
+ {
+ TC_LOG_ERROR("server.loading", "Respawn.GuidWarnLevel (%u) cannot be greater than maximum GUID (16777215). Set to 16000000.", m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL]);
+ m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL] = 16000000;
+ }
+ m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME] = sConfigMgr->GetIntDefault("Respawn.RestartQuietTime", 3);
+ if (m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME] > 23)
+ {
+ TC_LOG_ERROR("server.loading", "Respawn.RestartQuietTime (%u) must be an hour, between 0 and 23. Set to 3.", m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME]);
+ m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME] = 3;
+ }
+ m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE] = sConfigMgr->GetFloatDefault("Respawn.DynamicRateCreature", 10.0f);
+ if (m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE] < 0.0f)
+ {
+ TC_LOG_ERROR("server.loading", "Respawn.DynamicRateCreature (%f) must be positive. Set to 10.", m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE]);
+ m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE] = 10.0f;
+ }
+ m_int_configs[CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE] = sConfigMgr->GetIntDefault("Respawn.DynamicMinimumCreature", 10);
+ m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT] = sConfigMgr->GetFloatDefault("Respawn.DynamicRateGameObject", 10.0f);
+ if (m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT] < 0.0f)
+ {
+ TC_LOG_ERROR("server.loading", "Respawn.DynamicRateGameObject (%f) must be positive. Set to 10.", m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT]);
+ m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT] = 10.0f;
+ }
+ m_int_configs[CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT] = sConfigMgr->GetIntDefault("Respawn.DynamicMinimumGameObject", 10);
+ _guidWarningMsg = sConfigMgr->GetStringDefault("Respawn.WarningMessage", "There will be an unscheduled server restart at 03:00. The server will be available again shortly after.");
+ _alertRestartReason = sConfigMgr->GetStringDefault("Respawn.AlertRestartReason", "Urgent Maintenance");
+ m_int_configs[CONFIG_RESPAWN_GUIDWARNING_FREQUENCY] = sConfigMgr->GetIntDefault("Respawn.WarningFrequency", 1800);
///- Read the "Data" directory from the config file
std::string dataPath = sConfigMgr->GetStringDefault("DataDir", "./");
if (dataPath.empty() || (dataPath.at(dataPath.length()-1) != '/' && dataPath.at(dataPath.length()-1) != '\\'))
@@ -1504,6 +1633,7 @@ void World::SetInitialWorldSettings()
sObjectMgr->LoadPageTextLocales();
sObjectMgr->LoadGossipMenuItemsLocales();
sObjectMgr->LoadPointOfInterestLocales();
+ sObjectMgr->LoadQuestGreetingsLocales();
sObjectMgr->SetDBCLocaleIndex(GetDefaultDbcLocale()); // Get once for all the locale index of DBC language (console/broadcasts)
TC_LOG_INFO("server.loading", ">> Localization strings loaded in %u ms", GetMSTimeDiffToNow(oldMSTime));
@@ -1601,6 +1731,12 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading Creature Base Stats...");
sObjectMgr->LoadCreatureClassLevelStats();
+ TC_LOG_INFO("server.loading", "Loading Spawn Group Templates...");
+ sObjectMgr->LoadSpawnGroupTemplates();
+
+ TC_LOG_INFO("server.loading", "Loading instance spawn groups...");
+ sObjectMgr->LoadInstanceSpawnGroups();
+
TC_LOG_INFO("server.loading", "Loading Creature Data...");
sObjectMgr->LoadCreatures();
@@ -1617,10 +1753,13 @@ void World::SetInitialWorldSettings()
sObjectMgr->LoadCreatureAddons(); // must be after LoadCreatureTemplates() and LoadCreatures()
TC_LOG_INFO("server.loading", "Loading Gameobject Data...");
- sObjectMgr->LoadGameobjects();
+ sObjectMgr->LoadGameObjects();
+
+ TC_LOG_INFO("server.loading", "Loading Spawn Group Data...");
+ sObjectMgr->LoadSpawnGroups();
TC_LOG_INFO("server.loading", "Loading GameObject Addon Data...");
- sObjectMgr->LoadGameObjectAddons(); // must be after LoadGameObjectTemplate() and LoadGameobjects()
+ sObjectMgr->LoadGameObjectAddons(); // must be after LoadGameObjectTemplate() and LoadGameObjects()
TC_LOG_INFO("server.loading", "Loading GameObject Quest Items...");
sObjectMgr->LoadGameObjectQuestItems();
@@ -1646,11 +1785,15 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading Quests Starters and Enders...");
sObjectMgr->LoadQuestStartersAndEnders(); // must be after quest load
+ TC_LOG_INFO("server.loading", "Loading Quests Greetings...");
+ sObjectMgr->LoadQuestGreetings(); // must be loaded after creature_template, gameobject_template tables
+
TC_LOG_INFO("server.loading", "Loading Objects Pooling Data...");
sPoolMgr->LoadFromDB();
TC_LOG_INFO("server.loading", "Loading Game Event Data..."); // must be after loading pools fully
- sGameEventMgr->LoadFromDB();
+ sGameEventMgr->LoadHolidayDates(); // Must be after loading DBC
+ sGameEventMgr->LoadFromDB(); // Must be after loading holiday dates
TC_LOG_INFO("server.loading", "Loading UNIT_NPC_FLAG_SPELLCLICK Data..."); // must be after LoadQuests
sObjectMgr->LoadNPCSpellClickSpells();
@@ -1914,7 +2057,8 @@ void World::SetInitialWorldSettings()
tm localTm;
time_t gameTime = GameTime::GetGameTime();
localtime_r(&gameTime, &localTm);
- mail_timer = ((((localTm.tm_hour + 20) % 24)* HOUR * IN_MILLISECONDS) / m_timers[WUPDATE_AUCTIONS].GetInterval());
+ uint8 CleanOldMailsTime = getIntConfig(CONFIG_CLEAN_OLD_MAIL_TIME);
+ mail_timer = ((((localTm.tm_hour + (24 - CleanOldMailsTime)) % 24)* HOUR * IN_MILLISECONDS) / m_timers[WUPDATE_AUCTIONS].GetInterval());
//1440
mail_timer_expires = ((DAY * IN_MILLISECONDS) / (m_timers[WUPDATE_AUCTIONS].GetInterval()));
TC_LOG_INFO("server.loading", "Mail timer set to: " UI64FMTD ", mail return is called every " UI64FMTD " minutes", uint64(mail_timer), uint64(mail_timer_expires));
@@ -2289,6 +2433,16 @@ void World::Update(uint32 diff)
// update the instance reset times
sInstanceSaveMgr->Update();
+ // Check for shutdown warning
+ if (_guidWarn && !_guidAlert)
+ {
+ _warnDiff += diff;
+ if (GameTime::GetGameTime() >= _warnShutdownTime)
+ DoGuidWarningRestart();
+ else if (_warnDiff > getIntConfig(CONFIG_RESPAWN_GUIDWARNING_FREQUENCY) * IN_MILLISECONDS)
+ SendGuidWarning();
+ }
+
// And last, but not least handle the issued cli commands
ProcessCliCommands();
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 5254b32bbdf..8d06620641b 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -202,7 +202,6 @@ enum WorldFloatConfigs
CONFIG_ARENA_WIN_RATING_MODIFIER_2,
CONFIG_ARENA_LOSE_RATING_MODIFIER,
CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER,
- CONFIG_RESPAWN_DYNAMICRADIUS,
CONFIG_RESPAWN_DYNAMICRATE_CREATURE,
CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT,
FLOAT_CONFIG_VALUE_COUNT
@@ -251,6 +250,9 @@ enum WorldIntConfigs
CONFIG_DAILY_QUEST_RESET_TIME_HOUR,
CONFIG_MAX_PRIMARY_TRADE_SKILL,
CONFIG_MIN_PETITION_SIGNS,
+ CONFIG_MIN_QUEST_SCALED_XP_RATIO,
+ CONFIG_MIN_CREATURE_SCALED_XP_RATIO,
+ CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO,
CONFIG_GM_LOGIN_STATE,
CONFIG_GM_VISIBLE_STATE,
CONFIG_GM_ACCEPT_TICKETS,
@@ -263,6 +265,7 @@ enum WorldIntConfigs
CONFIG_FORCE_SHUTDOWN_THRESHOLD,
CONFIG_GROUP_VISIBILITY,
CONFIG_MAIL_DELIVERY_DELAY,
+ CONFIG_CLEAN_OLD_MAIL_TIME,
CONFIG_UPTIME_UPDATE,
CONFIG_SKILL_CHANCE_ORANGE,
CONFIG_SKILL_CHANCE_YELLOW,
@@ -379,13 +382,11 @@ enum WorldIntConfigs
CONFIG_AUCTION_GETALL_DELAY,
CONFIG_AUCTION_SEARCH_DELAY,
CONFIG_TALENTS_INSPECTING,
- CONFIG_RESPAWN_MINCELLCHECKMS,
+ CONFIG_RESPAWN_MINCHECKINTERVALMS,
CONFIG_RESPAWN_DYNAMICMODE,
CONFIG_RESPAWN_GUIDWARNLEVEL,
CONFIG_RESPAWN_GUIDALERTLEVEL,
CONFIG_RESPAWN_RESTARTQUIETTIME,
- CONFIG_RESPAWN_ACTIVITYSCOPECREATURE,
- CONFIG_RESPAWN_ACTIVITYSCOPEGAMEOBJECT,
CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE,
CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT,
CONFIG_RESPAWN_GUIDWARNING_FREQUENCY,
@@ -765,6 +766,10 @@ class TC_GAME_API World
void ReloadRBAC();
void RemoveOldCorpses();
+ void TriggerGuidWarning();
+ void TriggerGuidAlert();
+ bool IsGuidWarning() { return _guidWarn; }
+ bool IsGuidAlert() { return _guidAlert; }
protected:
void _UpdateGameTime();
@@ -859,7 +864,21 @@ class TC_GAME_API World
AutobroadcastsWeightMap m_AutobroadcastsWeights;
void ProcessQueryCallbacks();
+
+ void SendGuidWarning();
+ void DoGuidWarningRestart();
+ void DoGuidAlertRestart();
QueryCallbackProcessor _queryProcessor;
+
+ std::string _guidWarningMsg;
+ std::string _alertRestartReason;
+
+ std::mutex _guidAlertLock;
+
+ bool _guidWarn;
+ bool _guidAlert;
+ uint32 _warnDiff;
+ time_t _warnShutdownTime;
};
TC_GAME_API extern Realm realm;