diff --git a/Source/Framework/Constants/Language.cs b/Source/Framework/Constants/Language.cs
index f9228d46e..96168ad36 100644
--- a/Source/Framework/Constants/Language.cs
+++ b/Source/Framework/Constants/Language.cs
@@ -1079,11 +1079,26 @@ namespace Framework.Constants
DebugSceneObjectList = 5068,
DebugSceneObjectDetail = 5069,
- NpcinfoUnitFieldFlags2 = 5070,
- NpcinfoUnitFieldFlags3 = 5071,
- NpcinfoNpcFlags = 5072,
+ // Strings Added For DynamicSpawning
+ SpawninfoGroupId = 5070,
+ SpawninfoCompatibilityMode = 5071,
+ SpawninfoGuidinfo = 5072,
+ SpawninfoSpawnidLocation = 5073,
+ SpawninfoDistancefromplayer = 5074,
+ SpawngroupBadgroup = 5075,
+ SpawngroupSpawncount = 5076,
+ ListRespawnsRange = 5077,
+ ListRespawnsZone = 5078,
+ ListRespawnsListheader = 5079,
+ ListRespawnsOverdue = 5080,
+ ListRespawnsCreatures = 5081,
+ ListRespawnsGameobjects = 5082,
- // Room For More Trinity Strings 5073-9999
+ NpcinfoUnitFieldFlags2 = 5084,
+ NpcinfoUnitFieldFlags3 = 5085,
+ NpcinfoNpcFlags = 5086,
+
+ // Room For More Trinity Strings 5087-6603
// Level Requirement Notifications
SayReq = 6604,
diff --git a/Source/Framework/Constants/MapConst.cs b/Source/Framework/Constants/MapConst.cs
index 81f217e2b..3e571bc12 100644
--- a/Source/Framework/Constants/MapConst.cs
+++ b/Source/Framework/Constants/MapConst.cs
@@ -20,6 +20,8 @@ namespace Framework.Constants
{
public class MapConst
{
+ public const uint InvalidZone = 0xFFFFFFFF;
+
//Grids
public const int MaxGrids = 64;
public const float SizeofGrids = 533.33333f;
@@ -200,4 +202,33 @@ namespace Framework.Constants
All = Vmap | Gobject
}
+
+ public enum SpawnObjectType
+ {
+ Creature = 0,
+ GameObject = 1,
+
+ Max
+ }
+
+ public enum SpawnObjectTypeMask
+ {
+ Creature = (1 << SpawnObjectType.Creature),
+ GameObject = (1 << SpawnObjectType.GameObject),
+
+ All = (1 << SpawnObjectType.Max) - 1
+ }
+
+ [Flags]
+ public enum SpawnGroupFlags
+ {
+ None = 0x00,
+ System = 0x01,
+ CompatibilityMode = 0x02,
+ ManualSpawn = 0x04,
+ DynamicSpawnRate = 0x08,
+ EscortQuestNpc = 0x10,
+
+ All = (System | CompatibilityMode | ManualSpawn | DynamicSpawnRate | EscortQuestNpc)
+ }
}
diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs
index b7aa804be..605b3205a 100644
--- a/Source/Framework/Constants/SharedConst.cs
+++ b/Source/Framework/Constants/SharedConst.cs
@@ -1316,6 +1316,17 @@ namespace Framework.Constants
RealmZone,
ResetDuelCooldowns,
ResetDuelHealthMana,
+ RespawnDynamicEscortNpc,
+ RespawnDynamicMinimumCreature,
+ RespawnDynamicMinimumGameObject,
+ RespawnDynamicMode,
+ RespawnDynamicRateCreature,
+ RespawnDynamicRateGameobject,
+ RespawnGuidAlertLevel,
+ RespawnGuidWarnLevel,
+ RespawnGuidWarningFrequency,
+ RespawnMinCheckIntervalMs,
+ RespawnRestartQuietTime,
RestrictedLfgChannel,
SaveRespawnTimeImmediately,
SessionAddDelay,
diff --git a/Source/Framework/Constants/SmartAIConst.cs b/Source/Framework/Constants/SmartAIConst.cs
index c240bb7fa..9c9c05c95 100644
--- a/Source/Framework/Constants/SmartAIConst.cs
+++ b/Source/Framework/Constants/SmartAIConst.cs
@@ -13,7 +13,9 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
- */
+ */
+
+using System;
namespace Framework.Constants
{
@@ -114,6 +116,15 @@ namespace Framework.Constants
End = 3
}
+ [Flags]
+ public enum SmartAiSpawnFlags
+ {
+ None = 0x00,
+ IgnoreRespawn = 0x01,
+ ForceSpawn = 0x02,
+ NosaveRespawn = 0x04,
+ }
+
public enum SmartAITemplate
{
Basic = 0, //nothing is preset
@@ -125,6 +136,7 @@ namespace Framework.Constants
End = 6
}
+ [Flags]
public enum SmartCastFlags
{
InterruptPrevious = 0x01, //Interrupt any spell casting
@@ -361,6 +373,8 @@ namespace Framework.Constants
PlayAnimkit = 128,
ScenePlay = 129, // sceneId
SceneCancel = 130, // sceneId
+ SpawnSpawngroup = 131, // Group ID, min secs, max secs, spawnflags
+ DespawnSpawngroup = 132, // Group ID, min secs, max secs, spawnflags
// 131 - 134 : 3.3.5 reserved
PlayCinematic = 135, // reserved for future uses
SetMovementSpeed = 136, // movementType, speedInteger, speedFraction
diff --git a/Source/Framework/Database/Databases/WorldDatabase.cs b/Source/Framework/Database/Databases/WorldDatabase.cs
index 668d93f9f..c41fa12ae 100644
--- a/Source/Framework/Database/Databases/WorldDatabase.cs
+++ b/Source/Framework/Database/Databases/WorldDatabase.cs
@@ -89,6 +89,7 @@ namespace Framework.Database
PrepareStatement(WorldStatements.DEL_DISABLES, "DELETE FROM disables WHERE entry = ? AND sourceType = ?");
PrepareStatement(WorldStatements.UPD_CREATURE_ZONE_AREA_DATA, "UPDATE creature SET zoneId = ?, areaId = ? WHERE guid = ?");
PrepareStatement(WorldStatements.UPD_GAMEOBJECT_ZONE_AREA_DATA, "UPDATE gameobject SET zoneId = ?, areaId = ? WHERE guid = ?");
+ PrepareStatement(WorldStatements.DEL_SPAWNGROUP_MEMBER, "DELETE FROM spawn_group WHERE spawnType = ? AND spawnId = ?");
PrepareStatement(WorldStatements.SEL_GUILD_REWARDS_REQ_ACHIEVEMENTS, "SELECT AchievementRequired FROM guild_rewards_req_achievements WHERE ItemID = ?");
}
}
@@ -164,6 +165,7 @@ namespace Framework.Database
DEL_DISABLES,
UPD_CREATURE_ZONE_AREA_DATA,
UPD_GAMEOBJECT_ZONE_AREA_DATA,
+ DEL_SPAWNGROUP_MEMBER,
SEL_GUILD_REWARDS_REQ_ACHIEVEMENTS,
MAX_WORLDDATABASE_STATEMENTS
diff --git a/Source/Game/AI/CoreAI/CreatureAI.cs b/Source/Game/AI/CoreAI/CreatureAI.cs
index a9e6dd4f4..255fe7e5e 100644
--- a/Source/Game/AI/CoreAI/CreatureAI.cs
+++ b/Source/Game/AI/CoreAI/CreatureAI.cs
@@ -474,6 +474,15 @@ namespace Game.AI
// Object destruction is handled by Unit::RemoveCharmedBy
public virtual PlayerAI GetAIForCharmedPlayer(Player who) { return null; }
+
+ ///
+ /// 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
+ ///
+ ///
+ ///
+ public virtual bool IsEscortNPC(bool onlyIfActive) { return false; }
+
List GetBoundary() { return _boundary; }
bool MoveInLineOfSight_locked;
diff --git a/Source/Game/AI/ScriptedAI/ScriptedAI.cs b/Source/Game/AI/ScriptedAI/ScriptedAI.cs
index 557e5f681..a3a8bc46e 100644
--- a/Source/Game/AI/ScriptedAI/ScriptedAI.cs
+++ b/Source/Game/AI/ScriptedAI/ScriptedAI.cs
@@ -495,7 +495,7 @@ namespace Game.AI
_events.Reset();
summons.DespawnAll();
_scheduler.CancelAll();
- if (instance != null)
+ if (instance != null && instance.GetBossState(_bossId) != EncounterState.Done)
instance.SetBossState(_bossId, EncounterState.NotStarted);
}
@@ -576,12 +576,14 @@ namespace Game.AI
DoMeleeAttackIfReady();
}
- public void _DespawnAtEvade(uint delayToRespawn = 30, Creature who = null)
+ public void _DespawnAtEvade(uint delayToRespawn = 30, Creature who = null) { _DespawnAtEvade(TimeSpan.FromSeconds(delayToRespawn), who); }
+
+ public void _DespawnAtEvade(TimeSpan delayToRespawn, Creature who = null)
{
- if (delayToRespawn < 2)
+ if (delayToRespawn < TimeSpan.FromSeconds(2))
{
Log.outError(LogFilter.Scripts, "_DespawnAtEvade called with delay of {0} seconds, defaulting to 2.", delayToRespawn);
- delayToRespawn = 2;
+ delayToRespawn = TimeSpan.FromSeconds(2);
}
if (!who)
@@ -595,7 +597,7 @@ namespace Game.AI
return;
}
- who.DespawnOrUnsummon(0, TimeSpan.FromSeconds(delayToRespawn));
+ who.DespawnOrUnsummon(0, delayToRespawn);
if (instance != null && who == me)
instance.SetBossState(_bossId, EncounterState.Fail);
diff --git a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs
index 2efd91687..7da034a09 100644
--- a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs
+++ b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs
@@ -18,6 +18,7 @@
using Framework.Constants;
using Game.Entities;
using Game.Groups;
+using Game.Maps;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -234,13 +235,17 @@ namespace Game.AI
return;
}
- if (m_bCanInstantRespawn)
+ if (m_bCanInstantRespawn && !WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc))
{
me.SetDeathState(DeathState.JustDied);
me.Respawn();
}
else
+ {
+ if (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc))
+ me.GetMap().RemoveRespawnTime(SpawnObjectType.Creature, me.GetSpawnId(), true);
me.DespawnOrUnsummon();
+ }
return;
}
@@ -274,11 +279,18 @@ namespace Game.AI
{
Log.outDebug(LogFilter.Scripts, "EscortAI failed because player/group was to far away or not found");
- if (m_bCanInstantRespawn)
+ bool isEscort = false;
+ CreatureData cdata = me.GetCreatureData();
+ if (cdata != null)
+ isEscort = (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc) && cdata.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.EscortQuestNpc));
+
+ if (m_bCanInstantRespawn && !isEscort)
{
me.SetDeathState(DeathState.JustDied);
me.Respawn();
}
+ else if (m_bCanInstantRespawn && isEscort)
+ me.GetMap().RemoveRespawnTime(SpawnObjectType.Creature, me.GetSpawnId(), true);
else
me.DespawnOrUnsummon();
@@ -391,6 +403,25 @@ namespace Game.AI
/// todo get rid of this many variables passed in function.
public void Start(bool isActiveAttacker = true, bool run = false, ObjectGuid playerGUID = default, Quest quest = null, bool instantRespawn = false, bool canLoopPath = false, bool resetWaypoints = true)
{
+ // Queue respawn from the point it starts
+ Map map = me.GetMap();
+ if (map != null)
+ {
+ CreatureData cdata = me.GetCreatureData();
+ if (cdata != null)
+ {
+ SpawnGroupTemplateData groupdata = cdata.spawnGroupData;
+ if (groupdata != null)
+ {
+ if (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc) && groupdata.flags.HasAnyFlag(SpawnGroupFlags.EscortQuestNpc) && map.GetCreatureRespawnTime(me.GetSpawnId()) == 0)
+ {
+ me.SetRespawnTime(me.GetRespawnDelay());
+ me.SaveRespawnTime();
+ }
+ }
+ }
+ }
+
if (me.GetVictim())
{
Log.outError(LogFilter.Server, "TSCR ERROR: EscortAI (script: {0}, creature entry: {1}) attempts to Start while in combat", me.GetScriptName(), me.GetEntry());
@@ -536,6 +567,17 @@ namespace Game.AI
return false;
}
+ public override bool IsEscortNPC(bool onlyIfActive)
+ {
+ if (!onlyIfActive)
+ return true;
+
+ if (!GetEventStarterGUID().IsEmpty())
+ return true;
+
+ return false;
+ }
+
public virtual void WaypointReached(uint pointId) { }
public virtual void WaypointStart(uint pointId) { }
diff --git a/Source/Game/AI/SmartScripts/SmartAIManager.cs b/Source/Game/AI/SmartScripts/SmartAIManager.cs
index 1c4f4235e..99aba6ac5 100644
--- a/Source/Game/AI/SmartScripts/SmartAIManager.cs
+++ b/Source/Game/AI/SmartScripts/SmartAIManager.cs
@@ -129,39 +129,39 @@ namespace Game.AI
continue;
}
- CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(creature.id);
+ CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(creature.Id);
if (creatureInfo == null)
{
- Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: Creature entry ({creature.id}) guid ({-temp.entryOrGuid}) does not exist, skipped loading.");
+ Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: Creature entry ({creature.Id}) guid ({-temp.entryOrGuid}) does not exist, skipped loading.");
continue;
}
if (creatureInfo.AIName != "SmartAI")
{
- Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: Creature entry ({creature.id}) guid ({-temp.entryOrGuid}) is not using SmartAI, skipped loading.");
+ Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: Creature entry ({creature.Id}) guid ({-temp.entryOrGuid}) is not using SmartAI, skipped loading.");
continue;
}
break;
}
case SmartScriptType.GameObject:
{
- GameObjectData gameObject = Global.ObjectMgr.GetGOData((ulong)-temp.entryOrGuid);
+ GameObjectData gameObject = Global.ObjectMgr.GetGameObjectData((ulong)-temp.entryOrGuid);
if (gameObject == null)
{
Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject guid ({-temp.entryOrGuid}) does not exist, skipped loading.");
continue;
}
- GameObjectTemplate gameObjectInfo = Global.ObjectMgr.GetGameObjectTemplate(gameObject.id);
+ GameObjectTemplate gameObjectInfo = Global.ObjectMgr.GetGameObjectTemplate(gameObject.Id);
if (gameObjectInfo == null)
{
- Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject entry ({gameObject.id}) guid ({-temp.entryOrGuid}) does not exist, skipped loading.");
+ Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject entry ({gameObject.Id}) guid ({-temp.entryOrGuid}) does not exist, skipped loading.");
continue;
}
if (gameObjectInfo.AIName != "SmartGameObjectAI")
{
- Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject entry ({gameObject.id}) guid ({-temp.entryOrGuid}) is not using SmartGameObjectAI, skipped loading.");
+ Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject entry ({gameObject.Id}) guid ({-temp.entryOrGuid}) is not using SmartGameObjectAI, skipped loading.");
continue;
}
break;
@@ -758,7 +758,7 @@ namespace Game.AI
return false;
}
- if (e.Event.distance.guid != 0 && Global.ObjectMgr.GetGOData(e.Event.distance.guid) == null)
+ if (e.Event.distance.guid != 0 && Global.ObjectMgr.GetGameObjectData(e.Event.distance.guid) == null)
{
Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject guid {0}, skipped.", e.Event.distance.guid);
return false;
@@ -1376,6 +1376,8 @@ namespace Game.AI
case SmartActions.TriggerRandomTimedEvent:
case SmartActions.SetCounter:
case SmartActions.RemoveAllGameobjects:
+ case SmartActions.SpawnSpawngroup:
+ case SmartActions.DespawnSpawngroup:
break;
default:
Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Not handled action_type({0}), event_type({1}), Entry {2} SourceType {3} Event {4}, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
@@ -1423,7 +1425,7 @@ namespace Game.AI
return false;
}
else
- entry = data.id;
+ entry = data.Id;
}
else
entry = (uint)e.entryOrGuid;
@@ -2368,6 +2370,9 @@ namespace Game.AI
[FieldOffset(4)]
public DisableEvade disableEvade;
+ [FieldOffset(4)]
+ public GroupSpawn groupSpawn;
+
[FieldOffset(4)]
public AuraType auraType;
@@ -2854,6 +2859,13 @@ namespace Game.AI
{
public uint disable;
}
+ public struct GroupSpawn
+ {
+ public uint groupId;
+ public uint minDelay;
+ public uint maxDelay;
+ public uint spawnflags;
+ }
public struct AuraType
{
public uint type;
diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs
index dffc900ac..1e58628db 100644
--- a/Source/Game/AI/SmartScripts/SmartScript.cs
+++ b/Source/Game/AI/SmartScripts/SmartScript.cs
@@ -2051,6 +2051,91 @@ namespace Game.AI
target.ToCreature().SetCorpseDelay(e.Action.corpseDelay.timer);
break;
}
+ case SmartActions.SpawnSpawngroup:
+ {
+ if (e.Action.groupSpawn.minDelay == 0 && e.Action.groupSpawn.maxDelay == 0)
+ {
+ bool ignoreRespawn = ((e.Action.groupSpawn.spawnflags & (uint)SmartAiSpawnFlags.IgnoreRespawn) != 0);
+ bool force = ((e.Action.groupSpawn.spawnflags & (uint)SmartAiSpawnFlags.ForceSpawn) != 0);
+
+ // Instant spawn
+ Global.ObjectMgr.SpawnGroupSpawn(e.Action.groupSpawn.groupId, GetBaseObject().GetMap(), ignoreRespawn, force);
+ }
+ else
+ {
+ // Delayed spawn (use values from parameter to schedule event to call us back
+ SmartEvent ne = new SmartEvent();
+ ne.type = SmartEvents.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 |= SmartEventFlags.NotRepeatable;
+
+ SmartAction ac = new SmartAction();
+ ac.type = SmartActions.SpawnSpawngroup;
+ 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 = new SmartScriptHolder();
+ ev.Event = ne;
+ ev.event_id = e.event_id;
+ ev.Target = e.Target;
+ ev.Action = ac;
+ InitTimer(ev);
+ mStoredEvents.Add(ev);
+ }
+ break;
+ }
+ case SmartActions.DespawnSpawngroup:
+ {
+ if (e.Action.groupSpawn.minDelay == 0 && e.Action.groupSpawn.maxDelay == 0)
+ {
+ bool deleteRespawnTimes = ((e.Action.groupSpawn.spawnflags & (uint)SmartAiSpawnFlags.NosaveRespawn) != 0);
+
+ // Instant spawn
+ Global.ObjectMgr.SpawnGroupDespawn(e.Action.groupSpawn.groupId, GetBaseObject().GetMap(), deleteRespawnTimes);
+ }
+ else
+ {
+ // Delayed spawn (use values from parameter to schedule event to call us back
+ SmartEvent ne = new SmartEvent();
+ ne.type = SmartEvents.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 |= SmartEventFlags.NotRepeatable;
+
+ SmartAction ac = new SmartAction();
+ ac.type = SmartActions.DespawnSpawngroup;
+ 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 = new SmartScriptHolder();
+ ev.Event = ne;
+ ev.event_id = e.event_id;
+ ev.Target = e.Target;
+ ev.Action = ac;
+ InitTimer(ev);
+ mStoredEvents.Add(ev);
+ }
+ break;
+ }
case SmartActions.DisableEvade:
{
if (!IsSmart())
diff --git a/Source/Game/Chat/Commands/GameObjectCommands.cs b/Source/Game/Chat/Commands/GameObjectCommands.cs
index 46cb04771..84a414bfc 100644
--- a/Source/Game/Chat/Commands/GameObjectCommands.cs
+++ b/Source/Game/Chat/Commands/GameObjectCommands.cs
@@ -201,7 +201,7 @@ namespace Game.Chat
if (gameObjectInfo == null)
continue;
- handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gameObjectInfo.name, x, y, z, mapId);
+ handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gameObjectInfo.name, x, y, z, mapId, "", "");
++count;
} while (result.NextRow());
@@ -412,10 +412,10 @@ namespace Game.Chat
if (!ulong.TryParse(cValue, out ulong guidLow))
return false;
- GameObjectData data = Global.ObjectMgr.GetGOData(guidLow);
+ GameObjectData data = Global.ObjectMgr.GetGameObjectData(guidLow);
if (data == null)
return false;
- entry = data.id;
+ entry = data.Id;
}
else
{
@@ -427,15 +427,45 @@ namespace Game.Chat
if (gameObjectInfo == null)
return false;
+ GameObject thisGO = null;
+ if (handler.GetSession().GetPlayer())
+ thisGO = handler.GetSession().GetPlayer().FindNearestGameObject(entry, 30);
+ else if (handler.GetSelectedObject() != null && handler.GetSelectedObject().IsTypeId(TypeId.GameObject))
+ thisGO = handler.GetSelectedObject().ToGameObject();
+
GameObjectTypes type = gameObjectInfo.type;
uint displayId = gameObjectInfo.displayId;
string name = gameObjectInfo.name;
uint lootId = gameObjectInfo.GetLootId();
+ // If we have a real object, send some info about it
+ if (thisGO != null)
+ {
+ handler.SendSysMessage(CypherStrings.SpawninfoGuidinfo, thisGO.GetGUID().ToString());
+ handler.SendSysMessage(CypherStrings.SpawninfoSpawnidLocation, thisGO.GetSpawnId(), thisGO.GetPositionX(), thisGO.GetPositionY(), thisGO.GetPositionZ());
+ Player player = handler.GetSession().GetPlayer();
+ if (player != null)
+ {
+ Position playerPos = player.GetPosition();
+ float dist = thisGO.GetExactDist(playerPos);
+ handler.SendSysMessage(CypherStrings.SpawninfoDistancefromplayer, dist);
+ }
+ }
handler.SendSysMessage(CypherStrings.GoinfoEntry, entry);
handler.SendSysMessage(CypherStrings.GoinfoType, type);
handler.SendSysMessage(CypherStrings.GoinfoLootid, lootId);
handler.SendSysMessage(CypherStrings.GoinfoDisplayid, displayId);
+ WorldObject obj = handler.GetSelectedObject();
+ if (obj != null)
+ {
+ if (obj.IsGameObject() && obj.ToGameObject().GetGameObjectData() != null && obj.ToGameObject().GetGameObjectData().spawnGroupData.groupId != 0)
+ {
+ SpawnGroupTemplateData groupData = obj.ToGameObject().GetGameObjectData().spawnGroupData;
+ handler.SendSysMessage(CypherStrings.SpawninfoGroupId, groupData.name, groupData.groupId, groupData.flags, groupData.isActive);
+ }
+ if (obj.IsGameObject())
+ handler.SendSysMessage(CypherStrings.SpawninfoCompatibilityMode, obj.ToGameObject().GetRespawnCompatibilityMode());
+ }
handler.SendSysMessage(CypherStrings.GoinfoName, name);
handler.SendSysMessage(CypherStrings.GoinfoSize, gameObjectInfo.size);
@@ -508,7 +538,7 @@ namespace Game.Chat
return false;
// TODO: is it really necessary to add both the real and DB table guid here ?
- Global.ObjectMgr.AddGameObjectToGrid(spawnId, Global.ObjectMgr.GetGOData(spawnId));
+ Global.ObjectMgr.AddGameObjectToGrid(spawnId, Global.ObjectMgr.GetGameObjectData(spawnId));
handler.SendSysMessage(CypherStrings.GameobjectAdd, objectId, objectInfo.name, spawnId, player.GetPositionX(), player.GetPositionY(), player.GetPositionZ());
return true;
}
diff --git a/Source/Game/Chat/Commands/GoCommands.cs b/Source/Game/Chat/Commands/GoCommands.cs
index bdf379044..f6d0bda70 100644
--- a/Source/Game/Chat/Commands/GoCommands.cs
+++ b/Source/Game/Chat/Commands/GoCommands.cs
@@ -202,28 +202,17 @@ namespace Game.Chat.Commands
if (!ulong.TryParse(id, out ulong guidLow) || guidLow == 0)
return false;
- float x, y, z, o;
- uint mapId;
-
// by DB guid
- GameObjectData goData = Global.ObjectMgr.GetGOData(guidLow);
- if (goData != null)
- {
- x = goData.posX;
- y = goData.posY;
- z = goData.posZ;
- o = goData.orientation;
- mapId = goData.mapid;
- }
- else
+ GameObjectData goData = Global.ObjectMgr.GetGameObjectData(guidLow);
+ if (goData == null)
{
handler.SendSysMessage(CypherStrings.CommandGoobjnotfound);
return false;
}
- if (!GridDefines.IsValidMapCoord(mapId, x, y, z, o) || Global.ObjectMgr.IsTransportMap(mapId))
+ if (!GridDefines.IsValidMapCoord(goData.spawnPoint) || Global.ObjectMgr.IsTransportMap(goData.spawnPoint.GetMapId()))
{
- handler.SendSysMessage(CypherStrings.InvalidTargetCoord, x, y, mapId);
+ handler.SendSysMessage(CypherStrings.InvalidTargetCoord, goData.spawnPoint.GetPositionX(), goData.spawnPoint.GetPositionY(), goData.spawnPoint.GetMapId());
return false;
}
@@ -237,7 +226,7 @@ namespace Game.Chat.Commands
else
player.SaveRecallPosition();
- player.TeleportTo(mapId, x, y, z, o);
+ player.TeleportTo(goData.spawnPoint);
return true;
}
diff --git a/Source/Game/Chat/Commands/ListCommands.cs b/Source/Game/Chat/Commands/ListCommands.cs
index 62aeb087f..d5f4ef161 100644
--- a/Source/Game/Chat/Commands/ListCommands.cs
+++ b/Source/Game/Chat/Commands/ListCommands.cs
@@ -18,7 +18,9 @@
using Framework.Constants;
using Framework.Database;
using Framework.IO;
+using Game.DataStorage;
using Game.Entities;
+using Game.Maps;
using Game.Spells;
using System.Collections.Generic;
@@ -128,11 +130,39 @@ namespace Game.Chat.Commands
float y = result.Read(2);
float z = result.Read(3);
ushort mapId = result.Read(4);
+ bool liveFound = false;
+ // Get map (only support base map from console)
+ Map thisMap;
if (handler.GetSession() != null)
- handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, cInfo.Name, x, y, z, mapId);
+ thisMap = handler.GetSession().GetPlayer().GetMap();
else
- handler.SendSysMessage(CypherStrings.CreatureListConsole, guid, cInfo.Name, x, y, z, mapId);
+ thisMap = Global.MapMgr.FindBaseNonInstanceMap(mapId);
+
+ // If map found, try to find active version of this creature
+ if (thisMap)
+ {
+ var creBounds = thisMap.GetCreatureBySpawnIdStore().LookupByKey(guid);
+ if (!creBounds.Empty())
+ {
+ foreach (var creature in creBounds)
+ {
+ if (handler.GetSession())
+ handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, cInfo.Name, x, y, z, mapId, creature.GetGUID().ToString(), creature.IsAlive() ? "*" : " ");
+ else
+ handler.SendSysMessage(CypherStrings.CreatureListConsole, guid, cInfo.Name, x, y, z, mapId, creature.GetGUID().ToString(), creature.IsAlive() ? "*" : " ");
+ }
+ liveFound = true;
+ }
+ }
+
+ if (!liveFound)
+ {
+ if (handler.GetSession())
+ handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, cInfo.Name, x, y, z, mapId, "", "");
+ else
+ handler.SendSysMessage(CypherStrings.CreatureListConsole, guid, cInfo.Name, x, y, z, mapId, "", "");
+ }
}
while (result.NextRow());
}
@@ -495,11 +525,39 @@ namespace Game.Chat.Commands
float z = result.Read(3);
ushort mapId = result.Read(4);
uint entry = result.Read(5);
+ bool liveFound = false;
+ // Get map (only support base map from console)
+ Map thisMap;
if (handler.GetSession() != null)
- handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gInfo.name, x, y, z, mapId);
+ thisMap = handler.GetSession().GetPlayer().GetMap();
else
- handler.SendSysMessage(CypherStrings.GoListConsole, guid, gInfo.name, x, y, z, mapId);
+ thisMap = Global.MapMgr.FindBaseNonInstanceMap(mapId);
+
+ // If map found, try to find active version of this object
+ if (thisMap)
+ {
+ var goBounds = thisMap.GetGameObjectBySpawnIdStore().LookupByKey(guid);
+ if (!goBounds.Empty())
+ {
+ foreach (var go in goBounds)
+ {
+ if (handler.GetSession())
+ handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gInfo.name, x, y, z, mapId, go.GetGUID(), go.IsSpawned() ? "*" : " ");
+ else
+ handler.SendSysMessage(CypherStrings.GoListConsole, guid, gInfo.name, x, y, z, mapId, go.GetGUID(), go.IsSpawned() ? "*" : " ");
+ }
+ liveFound = true;
+ }
+ }
+
+ if (!liveFound)
+ {
+ if (handler.GetSession())
+ handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gInfo.name, x, y, z, mapId, "", "");
+ else
+ handler.SendSysMessage(CypherStrings.GoListConsole, guid, gInfo.name, x, y, z, mapId, "", "");
+ }
}
while (result.NextRow());
}
@@ -509,6 +567,77 @@ namespace Game.Chat.Commands
return true;
}
+ [Command("respawns", RBACPermissions.CommandListRespawns)]
+ static bool HandleListRespawnsCommand(StringArguments args, CommandHandler handler)
+ {
+ // We need a player
+ Player player = handler.GetSession().GetPlayer();
+ if (player == null)
+ return false;
+
+ // And we need a map
+ Map map = player.GetMap();
+ if (map == null)
+ return false;
+
+ uint range = 0;
+ if (!args.Empty())
+ range = args.NextUInt32();
+
+ List respawns = new List();
+ Locale locale = handler.GetSession().GetSessionDbcLocale();
+ string stringOverdue = Global.ObjectMgr.GetCypherString(CypherStrings.ListRespawnsOverdue, locale);
+ string stringCreature = Global.ObjectMgr.GetCypherString(CypherStrings.ListRespawnsCreatures, locale);
+ string stringGameobject = Global.ObjectMgr.GetCypherString(CypherStrings.ListRespawnsGameobjects, locale);
+
+ uint zoneId = player.GetZoneId();
+ if (range != 0)
+ handler.SendSysMessage(CypherStrings.ListRespawnsRange, stringCreature, range);
+ else
+ handler.SendSysMessage(CypherStrings.ListRespawnsZone, stringCreature, GetZoneName(zoneId, handler.GetSessionDbcLocale()), zoneId);
+ handler.SendSysMessage(CypherStrings.ListRespawnsListheader);
+ map.GetRespawnInfo(respawns, SpawnObjectTypeMask.Creature, range != 0 ? 0 : zoneId);
+ foreach (RespawnInfo ri in respawns)
+ {
+ CreatureData data = Global.ObjectMgr.GetCreatureData(ri.spawnId);
+ if (data == null)
+ continue;
+
+ if (range != 0 && !player.IsInDist(data.spawnPoint, range))
+ continue;
+
+ uint gridY = ri.gridId / MapConst.MaxGrids;
+ uint gridX = ri.gridId % MapConst.MaxGrids;
+
+ string respawnTime = ri.respawnTime > Time.UnixTime ? Time.secsToTimeString((ulong)(ri.respawnTime - Time.UnixTime), true) : stringOverdue;
+ handler.SendSysMessage($"{ri.spawnId} | {ri.entry} | [{gridX},{gridY}] | {GetZoneName(ri.zoneId, handler.GetSessionDbcLocale())} ({ri.zoneId}) | {(data.spawnGroupData.isActive ? respawnTime : "inactive")}");
+ }
+
+ respawns.Clear();
+ if (range != 0)
+ handler.SendSysMessage(CypherStrings.ListRespawnsRange, stringGameobject, range);
+ else
+ handler.SendSysMessage(CypherStrings.ListRespawnsZone, stringGameobject, GetZoneName(zoneId, handler.GetSessionDbcLocale()), zoneId);
+ handler.SendSysMessage(CypherStrings.ListRespawnsListheader);
+ map.GetRespawnInfo(respawns, SpawnObjectTypeMask.GameObject, range != 0 ? 0 : zoneId);
+ foreach (RespawnInfo ri in respawns)
+ {
+ GameObjectData data = Global.ObjectMgr.GetGameObjectData(ri.spawnId);
+ if (data == null)
+ continue;
+
+ if (range != 0 && !player.IsInDist(data.spawnPoint, range))
+ continue;
+
+ uint gridY = ri.gridId / MapConst.MaxGrids;
+ uint gridX = ri.gridId % MapConst.MaxGrids;
+
+ string respawnTime = ri.respawnTime > Time.UnixTime ? Time.secsToTimeString((ulong)(ri.respawnTime - Time.UnixTime), true) : stringOverdue;
+ handler.SendSysMessage($"{ri.spawnId} | {ri.entry} | [{gridX},{gridY}] | {GetZoneName(ri.zoneId, handler.GetSessionDbcLocale())} ({ri.zoneId}) | {(data.spawnGroupData.isActive ? respawnTime : "inactive")}");
+ }
+ return true;
+ }
+
[Command("scenes", RBACPermissions.CommandListScenes)]
static bool HandleListScenesCommand(StringArguments args, CommandHandler handler)
{
@@ -531,5 +660,11 @@ namespace Game.Chat.Commands
return true;
}
+
+ static string GetZoneName(uint zoneId, Locale locale)
+ {
+ AreaTableRecord zoneEntry = CliDB.AreaTableStorage.LookupByKey(zoneId);
+ return zoneEntry != null ? zoneEntry.AreaName[locale] : "";
+ }
}
}
diff --git a/Source/Game/Chat/Commands/MiscCommands.cs b/Source/Game/Chat/Commands/MiscCommands.cs
index b2e894457..a47ea389b 100644
--- a/Source/Game/Chat/Commands/MiscCommands.cs
+++ b/Source/Game/Chat/Commands/MiscCommands.cs
@@ -34,221 +34,8 @@ namespace Game.Chat
{
class MiscCommands
{
- [CommandNonGroup("commands", RBACPermissions.CommandCommands, true)]
- static bool Commands(StringArguments args, CommandHandler handler)
- {
- string list = "";
- foreach (var command in CommandManager.GetCommands())
- {
- if (handler.IsAvailable(command))
- {
- if (handler.GetSession() != null)
- list += "\n ";
- else
- list += "\n\r ";
-
- list += command.Name;
-
- if (!command.ChildCommands.Empty())
- list += " ...";
- }
- }
-
- if (list.IsEmpty())
- return false;
-
- handler.SendSysMessage(CypherStrings.AvailableCmd);
- handler.SendSysMessage(list);
- return true;
- }
-
- [CommandNonGroup("help", RBACPermissions.CommandHelp, true)]
- static bool Help(StringArguments args, CommandHandler handler)
- {
- string cmd = args.NextString("");
- if (cmd.IsEmpty())
- {
- handler.ShowHelpForCommand(CommandManager.GetCommands(), "help");
- handler.ShowHelpForCommand(CommandManager.GetCommands(), "");
- }
- else
- {
- if (!handler.ShowHelpForCommand(CommandManager.GetCommands(), cmd))
- handler.SendSysMessage(CypherStrings.NoHelpCmd);
- }
-
- return true;
- }
-
- [CommandNonGroup("revive", RBACPermissions.CommandRevive, true)]
- static bool Revive(StringArguments args, CommandHandler handler)
- {
- Player target;
- ObjectGuid targetGuid;
- if (!handler.ExtractPlayerTarget(args, out target, out targetGuid))
- return false;
-
- if (target != null)
- {
- target.ResurrectPlayer(0.5f);
- target.SpawnCorpseBones();
- target.SaveToDB();
- }
- else
- Player.OfflineResurrect(targetGuid, null);
-
- return true;
- }
-
- [CommandNonGroup("die", RBACPermissions.CommandDie)]
- static bool Die(StringArguments args, CommandHandler handler)
- {
- Unit target = handler.GetSelectedUnit();
-
- if (!target && handler.GetPlayer().GetTarget().IsEmpty())
- {
- handler.SendSysMessage(CypherStrings.SelectCharOrCreature);
- return false;
- }
-
- Player player = target.ToPlayer();
- if (player)
- if (handler.HasLowerSecurity(player, ObjectGuid.Empty, false))
- return false;
-
- if (target.IsAlive())
- handler.GetSession().GetPlayer().Kill(target);
-
- return true;
- }
-
- [CommandNonGroup("gps", RBACPermissions.CommandGps)]
- static bool HandleGPSCommand(StringArguments args, CommandHandler handler)
- {
- WorldObject obj;
- if (!args.Empty())
- {
- HighGuid guidHigh = 0;
- ulong guidLow = handler.ExtractLowGuidFromLink(args, ref guidHigh);
- if (guidLow == 0)
- return false;
- switch (guidHigh)
- {
- case HighGuid.Player:
- {
- obj = Global.ObjAccessor.FindPlayer(ObjectGuid.Create(HighGuid.Player, guidLow));
- if (!obj)
- {
- handler.SendSysMessage(CypherStrings.PlayerNotFound);
- }
- break;
- }
- case HighGuid.Creature:
- {
- obj = handler.GetCreatureFromPlayerMapByDbGuid(guidLow);
- if (!obj)
- {
- handler.SendSysMessage(CypherStrings.CommandNocreaturefound);
- }
- break;
- }
- case HighGuid.GameObject:
- {
- obj = handler.GetObjectFromPlayerMapByDbGuid(guidLow);
- if (!obj)
- {
- handler.SendSysMessage(CypherStrings.CommandNogameobjectfound);
- }
- break;
- }
- default:
- return false;
- }
- if (!obj)
- return false;
- }
- else
- {
- obj = handler.GetSelectedUnit();
-
- if (!obj)
- {
- handler.SendSysMessage(CypherStrings.SelectCharOrCreature);
- return false;
- }
- }
-
- CellCoord cellCoord = GridDefines.ComputeCellCoord(obj.GetPositionX(), obj.GetPositionY());
- Cell cell = new Cell(cellCoord);
-
- uint zoneId, areaId;
- obj.GetZoneAndAreaId(out zoneId, out areaId);
- uint mapId = obj.GetMapId();
-
- MapRecord mapEntry = CliDB.MapStorage.LookupByKey(mapId);
- AreaTableRecord zoneEntry = CliDB.AreaTableStorage.LookupByKey(zoneId);
- AreaTableRecord areaEntry = CliDB.AreaTableStorage.LookupByKey(areaId);
-
- float zoneX = obj.GetPositionX();
- float zoneY = obj.GetPositionY();
-
- Global.DB2Mgr.Map2ZoneCoordinates((int)zoneId, ref zoneX, ref zoneY);
-
- Map map = obj.GetMap();
- float groundZ = map.GetHeight(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), MapConst.MaxHeight);
- float floorZ = map.GetHeight(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ());
-
- GridCoord gridCoord = GridDefines.ComputeGridCoord(obj.GetPositionX(), obj.GetPositionY());
-
- // 63? WHY?
- uint gridX = (MapConst.MaxGrids - 1) - gridCoord.X_coord;
- uint gridY = (MapConst.MaxGrids - 1) - gridCoord.Y_coord;
-
- bool haveMap = Map.ExistMap(mapId, gridX, gridY);
- bool haveVMap = Map.ExistVMap(mapId, gridX, gridY);
- bool haveMMap = (Global.DisableMgr.IsPathfindingEnabled(mapId) && Global.MMapMgr.GetNavMesh(handler.GetSession().GetPlayer().GetMapId()) != null);
-
- if (haveVMap)
- {
- if (map.IsOutdoors(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ()))
- handler.SendSysMessage(CypherStrings.GpsPositionOutdoors);
- else
- handler.SendSysMessage(CypherStrings.GpsPositionIndoors);
- }
- else
- handler.SendSysMessage(CypherStrings.GpsNoVmap);
-
- string unknown = handler.GetCypherString(CypherStrings.Unknown);
-
- handler.SendSysMessage(CypherStrings.MapPosition,
- mapId, (mapEntry != null ? mapEntry.MapName[handler.GetSessionDbcLocale()] : unknown),
- zoneId, (zoneEntry != null ? zoneEntry.AreaName[handler.GetSessionDbcLocale()] : unknown),
- areaId, (areaEntry != null ? areaEntry.AreaName[handler.GetSessionDbcLocale()] : unknown),
- obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), obj.GetOrientation());
-
- Transport transport = obj.GetTransport();
- if (transport)
- {
- handler.SendSysMessage(CypherStrings.TransportPosition, transport.GetGoInfo().MoTransport.SpawnMap, obj.GetTransOffsetX(), obj.GetTransOffsetY(), obj.GetTransOffsetZ(), obj.GetTransOffsetO(),
- transport.GetEntry(), transport.GetName());
- }
-
- handler.SendSysMessage(CypherStrings.GridPosition, cell.GetGridX(), cell.GetGridY(), cell.GetCellX(), cell.GetCellY(), obj.GetInstanceId(),
- zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMap);
-
- LiquidData liquidStatus;
- ZLiquidStatus status = map.GetLiquidStatus(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), MapConst.MapAllLiquidTypes, out liquidStatus);
-
- if (liquidStatus != null)
- handler.SendSysMessage(CypherStrings.LiquidStatus, liquidStatus.level, liquidStatus.depth_level, liquidStatus.entry, liquidStatus.type_flags, status);
-
- PhasingHandler.PrintToChat(handler, obj.GetPhaseShift());
-
- return true;
- }
-
[CommandNonGroup("additem", RBACPermissions.CommandAdditem)]
- static bool AddItem(StringArguments args, CommandHandler handler)
+ static bool HandleAddItemCommand(StringArguments args, CommandHandler handler)
{
if (args.Empty())
return false;
@@ -372,7 +159,7 @@ namespace Game.Chat
}
[CommandNonGroup("additemset", RBACPermissions.CommandAdditemset)]
- static bool AddItemset(StringArguments args, CommandHandler handler)
+ static bool HandleAddItemSetCommand(StringArguments args, CommandHandler handler)
{
if (args.Empty())
return false;
@@ -446,41 +233,9 @@ namespace Game.Chat
return true;
}
- [CommandNonGroup("dev", RBACPermissions.CommandDev)]
- static bool HandleDevCommand(StringArguments args, CommandHandler handler)
- {
- if (args.Empty())
- {
- if (handler.GetSession().GetPlayer().HasPlayerFlag(PlayerFlags.Developer))
- handler.GetSession().SendNotification(CypherStrings.DevOn);
- else
- handler.GetSession().SendNotification(CypherStrings.DevOff);
- return true;
- }
-
- string argstr = args.NextString();
-
- if (argstr == "on")
- {
- handler.GetSession().GetPlayer().HasPlayerFlag(PlayerFlags.Developer);
- handler.GetSession().SendNotification(CypherStrings.DevOn);
- return true;
- }
-
- if (argstr == "off")
- {
- handler.GetSession().GetPlayer().RemovePlayerFlag(PlayerFlags.Developer);
- handler.GetSession().SendNotification(CypherStrings.DevOff);
- return true;
- }
-
- handler.SendSysMessage(CypherStrings.UseBol);
- return false;
- }
-
// Teleport to Player
[CommandNonGroup("appear", RBACPermissions.CommandAppear)]
- static bool Appear(StringArguments args, CommandHandler handler)
+ static bool HandleAppearCommand(StringArguments args, CommandHandler handler)
{
Player target;
ObjectGuid targetGuid;
@@ -624,125 +379,282 @@ namespace Game.Chat
return true;
}
- // Summon Player
- [CommandNonGroup("summon", RBACPermissions.CommandSummon)]
- static bool Summon(StringArguments args, CommandHandler handler)
+ [CommandNonGroup("bank", RBACPermissions.CommandBank)]
+ static bool HandleBankCommand(StringArguments args, CommandHandler handler)
{
- Player target;
- ObjectGuid targetGuid;
- string targetName;
- if (!handler.ExtractPlayerTarget(args, out target, out targetGuid, out targetName))
+ handler.GetSession().SendShowBank(handler.GetSession().GetPlayer().GetGUID());
+ return true;
+ }
+
+ [CommandNonGroup("bindsight", RBACPermissions.CommandBindsight)]
+ static bool HandleBindSightCommand(StringArguments args, CommandHandler handler)
+ {
+ Unit unit = handler.GetSelectedUnit();
+ if (!unit)
return false;
- Player _player = handler.GetSession().GetPlayer();
- if (target == _player || targetGuid == _player.GetGUID())
- {
- handler.SendSysMessage(CypherStrings.CantTeleportSelf);
+ handler.GetSession().GetPlayer().CastSpell(unit, 6277, true);
+ return true;
+ }
+ [CommandNonGroup("combatstop", RBACPermissions.CommandCombatstop, true)]
+ static bool HandleCombatStopCommand(StringArguments args, CommandHandler handler)
+ {
+ Player target = null;
+
+ if (!args.Empty())
+ {
+ target = Global.ObjAccessor.FindPlayerByName(args.NextString());
+ if (!target)
+ {
+ handler.SendSysMessage(CypherStrings.PlayerNotFound);
+ return false;
+ }
+ }
+
+ if (!target)
+ {
+ if (!handler.ExtractPlayerTarget(args, out target))
+ return false;
+ }
+
+ // check online security
+ if (handler.HasLowerSecurity(target, ObjectGuid.Empty))
+ return false;
+
+ target.CombatStop();
+ target.GetHostileRefManager().DeleteReferences();
+ return true;
+ }
+
+ [CommandNonGroup("cometome", RBACPermissions.CommandCometome)]
+ static bool HandleComeToMeCommand(StringArguments args, CommandHandler handler)
+ {
+ Creature caster = handler.GetSelectedCreature();
+ if (!caster)
+ {
+ handler.SendSysMessage(CypherStrings.SelectCreature);
return false;
}
- if (target)
+ Player player = handler.GetSession().GetPlayer();
+ caster.GetMotionMaster().MovePoint(0, player.GetPositionX(), player.GetPositionY(), player.GetPositionZ());
+
+ return true;
+ }
+
+ [CommandNonGroup("commands", RBACPermissions.CommandCommands, true)]
+ static bool HandleCommandsCommand(StringArguments args, CommandHandler handler)
+ {
+ string list = "";
+ foreach (var command in CommandManager.GetCommands())
{
- string nameLink = handler.PlayerLink(targetName);
- // check online security
- if (handler.HasLowerSecurity(target, ObjectGuid.Empty))
- return false;
-
- if (target.IsBeingTeleported())
+ if (handler.IsAvailable(command))
{
- handler.SendSysMessage(CypherStrings.IsTeleported, nameLink);
+ if (handler.GetSession() != null)
+ list += "\n ";
+ else
+ list += "\n\r ";
+ list += command.Name;
+
+ if (!command.ChildCommands.Empty())
+ list += " ...";
+ }
+ }
+
+ if (list.IsEmpty())
+ return false;
+
+ handler.SendSysMessage(CypherStrings.AvailableCmd);
+ handler.SendSysMessage(list);
+ return true;
+ }
+
+ [CommandNonGroup("damage", RBACPermissions.CommandDamage)]
+ static bool HandleDamageCommand(StringArguments args, CommandHandler handler)
+ {
+ if (args.Empty())
+ return false;
+
+ string str = args.NextString();
+
+ if (str == "go")
+ {
+ ulong guidLow = args.NextUInt64();
+ if (guidLow == 0)
+ {
+ handler.SendSysMessage(CypherStrings.BadValue);
return false;
}
- Map map = _player.GetMap();
- if (map.IsBattlegroundOrArena())
+ int damage = args.NextInt32();
+ if (damage == 0)
{
- // only allow if gm mode is on
- if (!_player.IsGameMaster())
+ handler.SendSysMessage(CypherStrings.BadValue);
+ return false;
+ }
+ Player player = handler.GetSession().GetPlayer();
+ if (player)
+ {
+ GameObject go = handler.GetObjectFromPlayerMapByDbGuid(guidLow);
+ if (!go)
{
- handler.SendSysMessage(CypherStrings.CannotGoToBgGm, nameLink);
+ handler.SendSysMessage(CypherStrings.CommandObjnotfound, guidLow);
return false;
}
- // if both players are in different bgs
- else if (target.GetBattlegroundId() != 0 && _player.GetBattlegroundId() != target.GetBattlegroundId())
- target.LeaveBattleground(false); // Note: should be changed so target gets no Deserter debuff
- // all's well, set bg id
- // when porting out from the bg, it will be reset to 0
- target.SetBattlegroundId(_player.GetBattlegroundId(), _player.GetBattlegroundTypeId());
- // remember current position as entry point for return at bg end teleportation
- if (!target.GetMap().IsBattlegroundOrArena())
- target.SetBattlegroundEntryPoint();
- }
- else if (map.Instanceable())
- {
- Map targetMap = target.GetMap();
-
- Player targetGroupLeader = null;
- Group targetGroup = target.GetGroup();
- if (targetGroup != null)
- targetGroupLeader = Global.ObjAccessor.GetPlayer(map, targetGroup.GetLeaderGUID());
-
- // check if far teleport is allowed
- if (targetGroupLeader == null || (targetGroupLeader.GetMapId() != map.GetId()) || (targetGroupLeader.GetInstanceId() != map.GetInstanceId()))
+ if (!go.IsDestructibleBuilding())
{
- if ((targetMap.GetId() != map.GetId()) || (targetMap.GetInstanceId() != map.GetInstanceId()))
- {
- handler.SendSysMessage(CypherStrings.CannotSummonToInst);
- return false;
- }
- }
-
- // check if we're already in a different instance of the same map
- if ((targetMap.GetId() == map.GetId()) && (targetMap.GetInstanceId() != map.GetInstanceId()))
- {
- handler.SendSysMessage(CypherStrings.CannotSummonInstInst, nameLink);
+ handler.SendSysMessage(CypherStrings.InvalidGameobjectType);
return false;
}
+
+ go.ModifyHealth(-damage, player);
+ handler.SendSysMessage(CypherStrings.GameobjectDamaged, go.GetName(), guidLow, -damage, go.GetGoValue().Building.Health);
}
- handler.SendSysMessage(CypherStrings.Summoning, nameLink, "");
- if (handler.NeedReportToTarget(target))
- target.SendSysMessage(CypherStrings.SummonedBy, handler.PlayerLink(_player.GetName()));
+ return true;
+ }
- // stop flight if need
- if (target.IsInFlight())
- {
- target.GetMotionMaster().MovementExpired();
- target.CleanupAfterTaxiFlight();
- }
- // save only in non-flight case
+ Unit target = handler.GetSelectedUnit();
+ if (!target || handler.GetSession().GetPlayer().GetTarget().IsEmpty())
+ {
+ handler.SendSysMessage(CypherStrings.SelectCharOrCreature);
+ return false;
+ }
+ Player player_ = target.ToPlayer();
+ if (player_)
+ if (handler.HasLowerSecurity(player_, ObjectGuid.Empty, false))
+ return false;
+
+ if (!target.IsAlive())
+ return true;
+
+ if (!int.TryParse(str, out int damage_int))
+ return false;
+
+ if (damage_int <= 0)
+ return true;
+
+ uint damage_ = (uint)damage_int;
+
+ string schoolStr = args.NextString();
+
+ Player attacker = handler.GetSession().GetPlayer();
+
+ // flat melee damage without resistence/etc reduction
+ if (string.IsNullOrEmpty(schoolStr))
+ {
+ attacker.DealDamage(target, damage_, null, DamageEffectType.Direct, SpellSchoolMask.Normal, null, false);
+ if (target != attacker)
+ attacker.SendAttackStateUpdate(HitInfo.AffectsVictim, target, SpellSchoolMask.Normal, damage_, 0, 0, VictimState.Hit, 0);
+ return true;
+ }
+
+ if (!int.TryParse(schoolStr, out int school) || school >= (int)SpellSchools.Max)
+ return false;
+
+ SpellSchoolMask schoolmask = (SpellSchoolMask)(1 << school);
+
+ if (attacker.IsDamageReducedByArmor(schoolmask))
+ damage_ = attacker.CalcArmorReducedDamage(handler.GetPlayer(), target, damage_, null, WeaponAttackType.BaseAttack);
+
+ string spellStr = args.NextString();
+
+ // melee damage by specific school
+ if (string.IsNullOrEmpty(spellStr))
+ {
+ DamageInfo dmgInfo = new DamageInfo(attacker, target, damage_, null, schoolmask, DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack);
+ attacker.CalcAbsorbResist(dmgInfo);
+
+ if (dmgInfo.GetDamage() == 0)
+ return true;
+
+ damage_ = dmgInfo.GetDamage();
+
+ uint absorb = dmgInfo.GetAbsorb();
+ uint resist = dmgInfo.GetResist();
+ attacker.DealDamageMods(target, ref damage_, ref absorb);
+ attacker.DealDamage(target, damage_, null, DamageEffectType.Direct, schoolmask, null, false);
+ attacker.SendAttackStateUpdate(HitInfo.AffectsVictim, target, schoolmask, damage_, absorb, resist, VictimState.Hit, 0);
+ return true;
+ }
+
+ // non-melee damage
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint spellid = handler.ExtractSpellIdFromLink(args);
+ if (spellid == 0)
+ return false;
+
+ SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellid, attacker.GetMap().GetDifficultyID());
+ if (spellInfo == null)
+ return false;
+
+ SpellNonMeleeDamage damageInfo = new SpellNonMeleeDamage(attacker, target, spellInfo, spellInfo.GetSpellXSpellVisualId(attacker), spellInfo.SchoolMask);
+ damageInfo.damage = damage_;
+ attacker.DealDamageMods(damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb);
+ target.DealSpellDamage(damageInfo, true);
+ target.SendSpellNonMeleeDamageLog(damageInfo);
+ return true;
+ }
+
+ [CommandNonGroup("dev", RBACPermissions.CommandDev)]
+ static bool HandleDevCommand(StringArguments args, CommandHandler handler)
+ {
+ if (args.Empty())
+ {
+ if (handler.GetSession().GetPlayer().HasPlayerFlag(PlayerFlags.Developer))
+ handler.GetSession().SendNotification(CypherStrings.DevOn);
else
- target.SaveRecallPosition();
-
- // before GM
- float x, y, z;
- _player.GetClosePoint(out x, out y, out z, target.GetCombatReach());
- target.TeleportTo(_player.GetMapId(), x, y, z, target.GetOrientation());
- PhasingHandler.InheritPhaseShift(target, _player);
- target.UpdateObjectVisibility();
+ handler.GetSession().SendNotification(CypherStrings.DevOff);
+ return true;
}
- else
+
+ string argstr = args.NextString();
+
+ if (argstr == "on")
{
- // check offline security
- if (handler.HasLowerSecurity(null, targetGuid))
+ handler.GetSession().GetPlayer().HasPlayerFlag(PlayerFlags.Developer);
+ handler.GetSession().SendNotification(CypherStrings.DevOn);
+ return true;
+ }
+
+ if (argstr == "off")
+ {
+ handler.GetSession().GetPlayer().RemovePlayerFlag(PlayerFlags.Developer);
+ handler.GetSession().SendNotification(CypherStrings.DevOff);
+ return true;
+ }
+
+ handler.SendSysMessage(CypherStrings.UseBol);
+ return false;
+ }
+
+ [CommandNonGroup("die", RBACPermissions.CommandDie)]
+ static bool HandleDieCommand(StringArguments args, CommandHandler handler)
+ {
+ Unit target = handler.GetSelectedUnit();
+
+ if (!target && handler.GetPlayer().GetTarget().IsEmpty())
+ {
+ handler.SendSysMessage(CypherStrings.SelectCharOrCreature);
+ return false;
+ }
+
+ Player player = target.ToPlayer();
+ if (player)
+ if (handler.HasLowerSecurity(player, ObjectGuid.Empty, false))
return false;
- string nameLink = handler.PlayerLink(targetName);
-
- handler.SendSysMessage(CypherStrings.Summoning, nameLink, handler.GetCypherString(CypherStrings.Offline));
-
- // in point where GM stay
- Player.SavePositionInDB(new WorldLocation(_player.GetMapId(), _player.GetPositionX(), _player.GetPositionY(), _player.GetPositionZ(), _player.GetOrientation()), _player.GetZoneId(), targetGuid);
- }
+ if (target.IsAlive())
+ handler.GetSession().GetPlayer().Kill(target);
return true;
}
[CommandNonGroup("dismount", RBACPermissions.CommandDismount)]
- static bool Dismount(StringArguments args, CommandHandler handler)
+ static bool HandleDismountCommand(StringArguments args, CommandHandler handler)
{
Player player = handler.GetSession().GetPlayer();
@@ -764,48 +676,6 @@ namespace Game.Chat
return true;
}
- [CommandNonGroup("guid", RBACPermissions.CommandGuid)]
- static bool Guid(StringArguments args, CommandHandler handler)
- {
- ObjectGuid guid = handler.GetSession().GetPlayer().GetTarget();
-
- if (guid.IsEmpty())
- {
- handler.SendSysMessage(CypherStrings.NoSelection);
- return false;
- }
-
- handler.SendSysMessage(CypherStrings.ObjectGuid, guid.ToString(), guid.GetHigh());
- return true;
- }
-
- // move item to other slot
- [CommandNonGroup("itemmove", RBACPermissions.CommandItemmove)]
- static bool ItemMove(StringArguments args, CommandHandler handler)
- {
- if (args.Empty())
- return false;
-
- byte srcSlot = args.NextByte();
- byte dstSlot = args.NextByte();
-
- if (srcSlot == dstSlot)
- return true;
-
- if (handler.GetSession().GetPlayer().IsValidPos(InventorySlots.Bag0, srcSlot, true))
- return false;
-
- if (handler.GetSession().GetPlayer().IsValidPos(InventorySlots.Bag0, dstSlot, false))
- return false;
-
- ushort src = (ushort)((InventorySlots.Bag0 << 8) | srcSlot);
- ushort dst = (ushort)((InventorySlots.Bag0 << 8) | dstSlot);
-
- handler.GetSession().GetPlayer().SwapItem(src, dst);
-
- return true;
- }
-
[CommandNonGroup("distance", RBACPermissions.CommandDistance)]
static bool HandleGetDistanceCommand(StringArguments args, CommandHandler handler)
{
@@ -867,73 +737,326 @@ namespace Game.Chat
return true;
}
- // Teleport player to last position
- [CommandNonGroup("recall", RBACPermissions.CommandRecall)]
- static bool Recall(StringArguments args, CommandHandler handler)
+ [CommandNonGroup("freeze", RBACPermissions.CommandFreeze)]
+ static bool HandleFreezeCommand(StringArguments args, CommandHandler handler)
{
- Player target;
- if (!handler.ExtractPlayerTarget(args, out target))
- return false;
+ Player player = handler.GetSelectedPlayer(); // Selected player, if any. Might be null.
+ int freezeDuration = 0; // Freeze Duration (in seconds)
+ bool canApplyFreeze = false; // Determines if every possible argument is set so Freeze can be applied
+ bool getDurationFromConfig = false; // If there's no given duration, we'll retrieve the world cfg value later
- // check online security
- if (handler.HasLowerSecurity(target, ObjectGuid.Empty))
- return false;
-
- if (target.IsBeingTeleported())
+ if (args.Empty())
{
- handler.SendSysMessage(CypherStrings.IsTeleported, handler.GetNameLink(target));
+ // Might have a selected player. We'll check it later
+ // Get the duration from world cfg
+ getDurationFromConfig = true;
+ }
+ else
+ {
+ // Get the args that we might have (up to 2)
+ string arg1 = args.NextString();
+ string arg2 = args.NextString();
- return false;
+ // Analyze them to see if we got either a playerName or duration or both
+ if (!arg1.IsEmpty())
+ {
+ if (arg1.IsNumber())
+ {
+ // case 2: .freeze duration
+ // We have a selected player. We'll check him later
+ if (!int.TryParse(arg1, out freezeDuration))
+ return false;
+ canApplyFreeze = true;
+ }
+ else
+ {
+ // case 3 or 4: .freeze player duration | .freeze player
+ // find the player
+ string name = arg1;
+ ObjectManager.NormalizePlayerName(ref name);
+ player = Global.ObjAccessor.FindPlayerByName(name);
+ // Check if we have duration set
+ if (!arg2.IsEmpty() && arg2.IsNumber())
+ {
+ if (!int.TryParse(arg2, out freezeDuration))
+ return false;
+ canApplyFreeze = true;
+ }
+ else
+ getDurationFromConfig = true;
+ }
+ }
}
- // stop flight if need
- if (target.IsInFlight())
+ // Check if duration needs to be retrieved from config
+ if (getDurationFromConfig)
{
- target.GetMotionMaster().MovementExpired();
- target.CleanupAfterTaxiFlight();
+ freezeDuration = WorldConfig.GetIntValue(WorldCfg.GmFreezeDuration);
+ canApplyFreeze = true;
}
- target.Recall();
- return true;
+ // Player and duration retrieval is over
+ if (canApplyFreeze)
+ {
+ if (!player) // can be null if some previous selection failed
+ {
+ handler.SendSysMessage(CypherStrings.CommandFreezeWrong);
+ return true;
+ }
+ else if (player == handler.GetSession().GetPlayer())
+ {
+ // Can't freeze himself
+ handler.SendSysMessage(CypherStrings.CommandFreezeError);
+ return true;
+ }
+ else // Apply the effect
+ {
+ // Add the freeze aura and set the proper duration
+ // Player combat status and flags are now handled
+ // in Freeze Spell AuraScript (OnApply)
+ Aura freeze = player.AddAura(9454, player);
+ if (freeze != null)
+ {
+ if (freezeDuration != 0)
+ freeze.SetDuration(freezeDuration * Time.InMilliseconds);
+ handler.SendSysMessage(CypherStrings.CommandFreeze, player.GetName());
+ // save player
+ player.SaveToDB();
+ return true;
+ }
+ }
+ }
+ return false;
}
- [CommandNonGroup("save", RBACPermissions.CommandSave)]
- static bool Save(StringArguments args, CommandHandler handler)
+ [CommandNonGroup("gps", RBACPermissions.CommandGps)]
+ static bool HandleGPSCommand(StringArguments args, CommandHandler handler)
{
- Player player = handler.GetSession().GetPlayer();
-
- // save GM account without delay and output message
- if (handler.GetSession().HasPermission(RBACPermissions.CommandsSaveWithoutDelay))
+ WorldObject obj;
+ if (!args.Empty())
{
- Player target = handler.GetSelectedPlayer();
- if (target)
- target.SaveToDB();
+ HighGuid guidHigh = 0;
+ ulong guidLow = handler.ExtractLowGuidFromLink(args, ref guidHigh);
+ if (guidLow == 0)
+ return false;
+ switch (guidHigh)
+ {
+ case HighGuid.Player:
+ {
+ obj = Global.ObjAccessor.FindPlayer(ObjectGuid.Create(HighGuid.Player, guidLow));
+ if (!obj)
+ {
+ handler.SendSysMessage(CypherStrings.PlayerNotFound);
+ }
+ break;
+ }
+ case HighGuid.Creature:
+ {
+ obj = handler.GetCreatureFromPlayerMapByDbGuid(guidLow);
+ if (!obj)
+ {
+ handler.SendSysMessage(CypherStrings.CommandNocreaturefound);
+ }
+ break;
+ }
+ case HighGuid.GameObject:
+ {
+ obj = handler.GetObjectFromPlayerMapByDbGuid(guidLow);
+ if (!obj)
+ {
+ handler.SendSysMessage(CypherStrings.CommandNogameobjectfound);
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ if (!obj)
+ return false;
+ }
+ else
+ {
+ obj = handler.GetSelectedUnit();
+
+ if (!obj)
+ {
+ handler.SendSysMessage(CypherStrings.SelectCharOrCreature);
+ return false;
+ }
+ }
+
+ CellCoord cellCoord = GridDefines.ComputeCellCoord(obj.GetPositionX(), obj.GetPositionY());
+ Cell cell = new Cell(cellCoord);
+
+ uint zoneId, areaId;
+ obj.GetZoneAndAreaId(out zoneId, out areaId);
+ uint mapId = obj.GetMapId();
+
+ MapRecord mapEntry = CliDB.MapStorage.LookupByKey(mapId);
+ AreaTableRecord zoneEntry = CliDB.AreaTableStorage.LookupByKey(zoneId);
+ AreaTableRecord areaEntry = CliDB.AreaTableStorage.LookupByKey(areaId);
+
+ float zoneX = obj.GetPositionX();
+ float zoneY = obj.GetPositionY();
+
+ Global.DB2Mgr.Map2ZoneCoordinates((int)zoneId, ref zoneX, ref zoneY);
+
+ Map map = obj.GetMap();
+ float groundZ = map.GetHeight(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), MapConst.MaxHeight);
+ float floorZ = map.GetHeight(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ());
+
+ GridCoord gridCoord = GridDefines.ComputeGridCoord(obj.GetPositionX(), obj.GetPositionY());
+
+ // 63? WHY?
+ uint gridX = (MapConst.MaxGrids - 1) - gridCoord.X_coord;
+ uint gridY = (MapConst.MaxGrids - 1) - gridCoord.Y_coord;
+
+ bool haveMap = Map.ExistMap(mapId, gridX, gridY);
+ bool haveVMap = Map.ExistVMap(mapId, gridX, gridY);
+ bool haveMMap = (Global.DisableMgr.IsPathfindingEnabled(mapId) && Global.MMapMgr.GetNavMesh(handler.GetSession().GetPlayer().GetMapId()) != null);
+
+ if (haveVMap)
+ {
+ if (map.IsOutdoors(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ()))
+ handler.SendSysMessage(CypherStrings.GpsPositionOutdoors);
else
- player.SaveToDB();
- handler.SendSysMessage(CypherStrings.PlayerSaved);
- return true;
+ handler.SendSysMessage(CypherStrings.GpsPositionIndoors);
+ }
+ else
+ handler.SendSysMessage(CypherStrings.GpsNoVmap);
+
+ string unknown = handler.GetCypherString(CypherStrings.Unknown);
+
+ handler.SendSysMessage(CypherStrings.MapPosition,
+ mapId, (mapEntry != null ? mapEntry.MapName[handler.GetSessionDbcLocale()] : unknown),
+ zoneId, (zoneEntry != null ? zoneEntry.AreaName[handler.GetSessionDbcLocale()] : unknown),
+ areaId, (areaEntry != null ? areaEntry.AreaName[handler.GetSessionDbcLocale()] : unknown),
+ obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), obj.GetOrientation());
+
+ Transport transport = obj.GetTransport();
+ if (transport)
+ {
+ handler.SendSysMessage(CypherStrings.TransportPosition, transport.GetGoInfo().MoTransport.SpawnMap, obj.GetTransOffsetX(), obj.GetTransOffsetY(), obj.GetTransOffsetZ(), obj.GetTransOffsetO(),
+ transport.GetEntry(), transport.GetName());
}
- // save if the player has last been saved over 20 seconds ago
- uint saveInterval = WorldConfig.GetUIntValue(WorldCfg.IntervalSave);
- if (saveInterval == 0 || (saveInterval > 20 * Time.InMilliseconds && player.GetSaveTimer() <= saveInterval - 20 * Time.InMilliseconds))
- player.SaveToDB();
+ handler.SendSysMessage(CypherStrings.GridPosition, cell.GetGridX(), cell.GetGridY(), cell.GetCellX(), cell.GetCellY(), obj.GetInstanceId(),
+ zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMap);
+
+ LiquidData liquidStatus;
+ ZLiquidStatus status = map.GetLiquidStatus(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), MapConst.MapAllLiquidTypes, out liquidStatus);
+
+ if (liquidStatus != null)
+ handler.SendSysMessage(CypherStrings.LiquidStatus, liquidStatus.level, liquidStatus.depth_level, liquidStatus.entry, liquidStatus.type_flags, status);
+
+ PhasingHandler.PrintToChat(handler, obj.GetPhaseShift());
return true;
}
- // Save all players in the world
- [CommandNonGroup("saveall", RBACPermissions.CommandSaveall, true)]
- static bool SaveAll(StringArguments args, CommandHandler handler)
+ [CommandNonGroup("guid", RBACPermissions.CommandGuid)]
+ static bool HandleGUIDCommand(StringArguments args, CommandHandler handler)
{
- Global.ObjAccessor.SaveAllPlayers();
- handler.SendSysMessage(CypherStrings.PlayersSaved);
+ ObjectGuid guid = handler.GetSession().GetPlayer().GetTarget();
+
+ if (guid.IsEmpty())
+ {
+ handler.SendSysMessage(CypherStrings.NoSelection);
+ return false;
+ }
+
+ handler.SendSysMessage(CypherStrings.ObjectGuid, guid.ToString(), guid.GetHigh());
+ return true;
+ }
+
+ [CommandNonGroup("help", RBACPermissions.CommandHelp, true)]
+ static bool HandleHelpCommand(StringArguments args, CommandHandler handler)
+ {
+ string cmd = args.NextString("");
+ if (cmd.IsEmpty())
+ {
+ handler.ShowHelpForCommand(CommandManager.GetCommands(), "help");
+ handler.ShowHelpForCommand(CommandManager.GetCommands(), "");
+ }
+ else
+ {
+ if (!handler.ShowHelpForCommand(CommandManager.GetCommands(), cmd))
+ handler.SendSysMessage(CypherStrings.NoHelpCmd);
+ }
+
+ return true;
+ }
+
+ [CommandNonGroup("hidearea", RBACPermissions.CommandHidearea)]
+ static bool HandleHideAreaCommand(StringArguments args, CommandHandler handler)
+ {
+ if (args.Empty())
+ return false;
+
+ Player playerTarget = handler.GetSelectedPlayer();
+ if (!playerTarget)
+ {
+ handler.SendSysMessage(CypherStrings.NoCharSelected);
+ return false;
+ }
+
+ AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(args.NextUInt32());
+ if (area == null)
+ {
+ handler.SendSysMessage(CypherStrings.BadValue);
+ return false;
+ }
+
+ if (area.AreaBit < 0)
+ {
+ handler.SendSysMessage(CypherStrings.BadValue);
+ return false;
+ }
+
+ uint offset = (uint)area.AreaBit / 64;
+ if (offset >= PlayerConst.ExploredZonesSize)
+ {
+ handler.SendSysMessage(CypherStrings.BadValue);
+ return false;
+ }
+
+ uint val = (1u << (area.AreaBit % 64));
+ playerTarget.RemoveExploredZones(offset, val);
+
+ handler.SendSysMessage(CypherStrings.UnexploreArea);
+ return true;
+ }
+
+ // move item to other slot
+ [CommandNonGroup("itemmove", RBACPermissions.CommandItemmove)]
+ static bool HandleItemMoveCommand(StringArguments args, CommandHandler handler)
+ {
+ if (args.Empty())
+ return false;
+
+ byte srcSlot = args.NextByte();
+ byte dstSlot = args.NextByte();
+
+ if (srcSlot == dstSlot)
+ return true;
+
+ if (handler.GetSession().GetPlayer().IsValidPos(InventorySlots.Bag0, srcSlot, true))
+ return false;
+
+ if (handler.GetSession().GetPlayer().IsValidPos(InventorySlots.Bag0, dstSlot, false))
+ return false;
+
+ ushort src = (ushort)((InventorySlots.Bag0 << 8) | srcSlot);
+ ushort dst = (ushort)((InventorySlots.Bag0 << 8) | dstSlot);
+
+ handler.GetSession().GetPlayer().SwapItem(src, dst);
+
return true;
}
// kick player
[CommandNonGroup("kick", RBACPermissions.CommandKick, true)]
- static bool Kick(StringArguments args, CommandHandler handler)
+ static bool HandleKickPlayerCommand(StringArguments args, CommandHandler handler)
{
Player target;
string playerName;
@@ -965,90 +1088,8 @@ namespace Game.Chat
return true;
}
- [CommandNonGroup("unstuck", RBACPermissions.CommandUnstuck, true)]
- static bool HandleUnstuckCommand(StringArguments args, CommandHandler handler)
- {
- uint SPELL_UNSTUCK_ID = 7355;
- uint SPELL_UNSTUCK_VISUAL = 2683;
-
- // No args required for players
- if (handler.GetSession() != null && handler.GetSession().HasPermission(RBACPermissions.CommandsUseUnstuckWithArgs))
- {
- // 7355: "Stuck"
- var player1 = handler.GetSession().GetPlayer();
- if (player1)
- player1.CastSpell(player1, SPELL_UNSTUCK_ID, false);
- return true;
- }
-
- if (args.Empty())
- return false;
-
- string location_str = "inn";
- string loc = args.NextString();
- if (string.IsNullOrEmpty(loc))
- location_str = loc;
-
- Player player;
- ObjectGuid targetGUID;
- if (!handler.ExtractPlayerTarget(args, out player, out targetGUID))
- return false;
-
- if (!player)
- {
- PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHAR_HOMEBIND);
- stmt.AddValue(0, targetGUID.GetCounter());
- SQLResult result = DB.Characters.Query(stmt);
- if (!result.IsEmpty())
- {
- Player.SavePositionInDB(new WorldLocation(result.Read(0), result.Read(2), result.Read(3), result.Read(4), 0.0f), result.Read(1), targetGUID);
- return true;
- }
-
- return false;
- }
-
- if (player.IsInFlight() || player.IsInCombat())
- {
- SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(SPELL_UNSTUCK_ID, Difficulty.None);
- if (spellInfo == null)
- return false;
-
- Player caster = handler.GetSession().GetPlayer();
- if (caster)
- {
- ObjectGuid castId = ObjectGuid.Create(HighGuid.Cast, SpellCastSource.Normal, player.GetMapId(), SPELL_UNSTUCK_ID, player.GetMap().GenerateLowGuid(HighGuid.Cast));
- Spell.SendCastResult(caster, spellInfo, SPELL_UNSTUCK_VISUAL, castId, SpellCastResult.CantDoThatRightNow);
- }
-
- return false;
- }
-
- if (location_str == "inn")
- {
- var home = player.GetHomebind();
- player.TeleportTo(home.GetMapId(), home.GetPositionX(), home.GetPositionY(), home.GetPositionZ(), player.GetOrientation());
- return true;
- }
-
- if (location_str == "graveyard")
- {
- player.RepopAtGraveyard();
- return true;
- }
-
- if (location_str == "startzone")
- {
- player.TeleportTo(player.GetStartPosition());
- return true;
- }
-
- //Not a supported argument
- return false;
- }
-
[CommandNonGroup("linkgrave", RBACPermissions.CommandLinkgrave)]
- static bool LinkGrave(StringArguments args, CommandHandler handler)
+ static bool HandleLinkGraveCommand(StringArguments args, CommandHandler handler)
{
if (args.Empty())
return false;
@@ -1094,8 +1135,285 @@ namespace Game.Chat
return true;
}
+ [CommandNonGroup("listfreeze", RBACPermissions.CommandListfreeze)]
+ static bool HandleListFreezeCommand(StringArguments args, CommandHandler handler)
+ {
+ // Get names from DB
+ PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_AURA_FROZEN);
+ SQLResult result = DB.Characters.Query(stmt);
+ if (result.IsEmpty())
+ {
+ handler.SendSysMessage(CypherStrings.CommandNoFrozenPlayers);
+ return true;
+ }
+
+ // Header of the names
+ handler.SendSysMessage(CypherStrings.CommandListFreeze);
+
+ // Output of the results
+ do
+ {
+ string player = result.Read(0);
+ int remaintime = result.Read(1);
+ // Save the frozen player to update remaining time in case of future .listfreeze uses
+ // before the frozen state expires
+ Player frozen = Global.ObjAccessor.FindPlayerByName(player);
+ if (frozen)
+ frozen.SaveToDB();
+ // Notify the freeze duration
+ if (remaintime == -1) // Permanent duration
+ handler.SendSysMessage(CypherStrings.CommandPermaFrozenPlayer, player);
+ else
+ // show time left (seconds)
+ handler.SendSysMessage(CypherStrings.CommandTempFrozenPlayer, player, remaintime / Time.InMilliseconds);
+ }
+ while (result.NextRow());
+
+ return true;
+ }
+
+ [CommandNonGroup("mailbox", RBACPermissions.CommandMailbox)]
+ static bool HandleMailBoxCommand(StringArguments args, CommandHandler handler)
+ {
+ Player player = handler.GetSession().GetPlayer();
+
+ handler.GetSession().SendShowMailBox(player.GetGUID());
+ return true;
+ }
+
+ [CommandNonGroup("movegens", RBACPermissions.CommandMovegens)]
+ static bool HandleMovegensCommand(StringArguments args, CommandHandler handler)
+ {
+ Unit unit = handler.GetSelectedUnit();
+ if (!unit)
+ {
+ handler.SendSysMessage(CypherStrings.SelectCharOrCreature);
+
+ return false;
+ }
+
+ handler.SendSysMessage(CypherStrings.MovegensList, (unit.IsTypeId(TypeId.Player) ? "Player" : "Creature"), unit.GetGUID().ToString());
+
+ MotionMaster motionMaster = unit.GetMotionMaster();
+ float x, y, z;
+ motionMaster.GetDestination(out x, out y, out z);
+
+ for (byte i = 0; i < (int)MovementSlot.Max; ++i)
+ {
+ IMovementGenerator movementGenerator = motionMaster.GetMotionSlot(i);
+ if (movementGenerator == null)
+ {
+ handler.SendSysMessage("Empty");
+ continue;
+ }
+
+ switch (movementGenerator.GetMovementGeneratorType())
+ {
+ case MovementGeneratorType.Idle:
+ handler.SendSysMessage(CypherStrings.MovegensIdle);
+ break;
+ case MovementGeneratorType.Random:
+ handler.SendSysMessage(CypherStrings.MovegensRandom);
+ break;
+ case MovementGeneratorType.Waypoint:
+ handler.SendSysMessage(CypherStrings.MovegensWaypoint);
+ break;
+ case MovementGeneratorType.Confused:
+ handler.SendSysMessage(CypherStrings.MovegensConfused);
+ break;
+ case MovementGeneratorType.Chase:
+ {
+ Unit target;
+ if (unit.IsTypeId(TypeId.Player))
+ target = ((ChaseMovementGenerator)movementGenerator).GetTarget();
+ else
+ target = ((ChaseMovementGenerator)movementGenerator).GetTarget();
+
+ if (!target)
+ handler.SendSysMessage(CypherStrings.MovegensChaseNull);
+ else if (target.IsTypeId(TypeId.Player))
+ handler.SendSysMessage(CypherStrings.MovegensChasePlayer, target.GetName(), target.GetGUID().ToString());
+ else
+ handler.SendSysMessage(CypherStrings.MovegensChaseCreature, target.GetName(), target.GetGUID().ToString());
+ break;
+ }
+ case MovementGeneratorType.Follow:
+ {
+ Unit target;
+ if (unit.IsTypeId(TypeId.Player))
+ target = ((FollowMovementGenerator)movementGenerator).GetTarget();
+ else
+ target = ((FollowMovementGenerator)movementGenerator).GetTarget();
+
+ if (!target)
+ handler.SendSysMessage(CypherStrings.MovegensFollowNull);
+ else if (target.IsTypeId(TypeId.Player))
+ handler.SendSysMessage(CypherStrings.MovegensFollowPlayer, target.GetName(), target.GetGUID().ToString());
+ else
+ handler.SendSysMessage(CypherStrings.MovegensFollowCreature, target.GetName(), target.GetGUID().ToString());
+ break;
+ }
+ case MovementGeneratorType.Home:
+ {
+ if (unit.IsTypeId(TypeId.Unit))
+ handler.SendSysMessage(CypherStrings.MovegensHomeCreature, x, y, z);
+ else
+ handler.SendSysMessage(CypherStrings.MovegensHomePlayer);
+ break;
+ }
+ case MovementGeneratorType.Flight:
+ handler.SendSysMessage(CypherStrings.MovegensFlight);
+ break;
+ case MovementGeneratorType.Point:
+ {
+ handler.SendSysMessage(CypherStrings.MovegensPoint, x, y, z);
+ break;
+ }
+ case MovementGeneratorType.Fleeing:
+ handler.SendSysMessage(CypherStrings.MovegensFear);
+ break;
+ case MovementGeneratorType.Distract:
+ handler.SendSysMessage(CypherStrings.MovegensDistract);
+ break;
+ case MovementGeneratorType.Effect:
+ handler.SendSysMessage(CypherStrings.MovegensEffect);
+ break;
+ default:
+ handler.SendSysMessage(CypherStrings.MovegensUnknown, movementGenerator.GetMovementGeneratorType());
+ break;
+ }
+ }
+ return true;
+ }
+
+ // mute player for some times
+ [CommandNonGroup("mute", RBACPermissions.CommandMute, true)]
+ static bool HandleMuteCommand(StringArguments args, CommandHandler handler)
+ {
+ string nameStr;
+ string delayStr;
+ handler.ExtractOptFirstArg(args, out nameStr, out delayStr);
+ if (string.IsNullOrEmpty(delayStr))
+ return false;
+
+ string muteReason = args.NextString();
+ string muteReasonStr = "No reason";
+ if (muteReason != null)
+ muteReasonStr = muteReason;
+
+ Player target;
+ ObjectGuid targetGuid;
+ string targetName;
+ if (!handler.ExtractPlayerTarget(new StringArguments(nameStr), out target, out targetGuid, out targetName))
+ return false;
+
+ uint accountId = target ? target.GetSession().GetAccountId() : Global.CharacterCacheStorage.GetCharacterAccountIdByGuid(targetGuid);
+
+ // find only player from same account if any
+ if (!target)
+ {
+ WorldSession session = Global.WorldMgr.FindSession(accountId);
+ if (session != null)
+ target = session.GetPlayer();
+ }
+
+ if (!uint.TryParse(delayStr, out uint notSpeakTime))
+ return false;
+
+ // must have strong lesser security level
+ if (handler.HasLowerSecurity(target, targetGuid, true))
+ return false;
+
+ PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_MUTE_TIME);
+ string muteBy;
+ if (handler.GetSession() != null)
+ muteBy = handler.GetSession().GetPlayerName();
+ else
+ muteBy = "Console";
+
+ if (target)
+ {
+ // Target is online, mute will be in effect right away.
+ long muteTime = Time.UnixTime + notSpeakTime * Time.Minute;
+ target.GetSession().m_muteTime = muteTime;
+ stmt.AddValue(0, muteTime);
+ string nameLink = handler.PlayerLink(targetName);
+
+ if (WorldConfig.GetBoolValue(WorldCfg.ShowMuteInWorld))
+ {
+ Global.WorldMgr.SendWorldText(CypherStrings.CommandMutemessageWorld, (handler.GetSession() != null ? handler.GetSession().GetPlayerName() : "Server"), nameLink, notSpeakTime, muteReasonStr);
+ target.SendSysMessage(CypherStrings.YourChatDisabled, notSpeakTime, muteBy, muteReasonStr);
+ }
+ else
+ {
+ target.SendSysMessage(CypherStrings.YourChatDisabled, notSpeakTime, muteBy, muteReasonStr);
+ }
+ }
+ else
+ {
+ // Target is offline, mute will be in effect starting from the next login.
+ int muteTime = -(int)(notSpeakTime * Time.Minute);
+ stmt.AddValue(0, muteTime);
+ }
+
+ stmt.AddValue(1, muteReasonStr);
+ stmt.AddValue(2, muteBy);
+ stmt.AddValue(3, accountId);
+ DB.Login.Execute(stmt);
+ string nameLink_ = handler.PlayerLink(targetName);
+
+ if (WorldConfig.GetBoolValue(WorldCfg.ShowMuteInWorld) && !target)
+ Global.WorldMgr.SendWorldText(CypherStrings.CommandMutemessageWorld, handler.GetSession().GetPlayerName(), nameLink_, notSpeakTime, muteReasonStr);
+ else
+ handler.SendSysMessage(target ? CypherStrings.YouDisableChat : CypherStrings.CommandDisableChatDelayed, nameLink_, notSpeakTime, muteReasonStr);
+ return true;
+ }
+
+ // mutehistory command
+ [CommandNonGroup("mutehistory", RBACPermissions.CommandMutehistory, true)]
+ static bool HandleMuteInfoCommand(StringArguments args, CommandHandler handler)
+ {
+ if (args.Empty())
+ return false;
+
+ string accountName = args.NextString("");
+ if (accountName.IsEmpty())
+ return false;
+
+ uint accountId = Global.AccountMgr.GetId(accountName);
+ if (accountId == 0)
+ {
+ handler.SendSysMessage(CypherStrings.AccountNotExist, accountName);
+ return false;
+ }
+
+ PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_ACCOUNT_MUTE_INFO);
+ stmt.AddValue(0, accountId);
+
+ SQLResult result = DB.Login.Query(stmt);
+ if (result.IsEmpty())
+ {
+ handler.SendSysMessage(CypherStrings.CommandMutehistoryEmpty, accountName);
+ return true;
+ }
+
+ handler.SendSysMessage(CypherStrings.CommandMutehistory, accountName);
+ do
+ {
+ // we have to manually set the string for mutedate
+ long sqlTime = result.Read(0);
+
+ // set it to string
+ string buffer = Time.UnixTimeToDateTime(sqlTime).ToShortTimeString();
+
+ handler.SendSysMessage(CypherStrings.CommandMutehistoryOutput, buffer, result.Read(1), result.Read(2), result.Read(3));
+ } while (result.NextRow());
+
+ return true;
+ }
+
[CommandNonGroup("neargrave", RBACPermissions.CommandNeargrave)]
- static bool NearGrave(StringArguments args, CommandHandler handler)
+ static bool HandleNearGraveCommand(StringArguments args, CommandHandler handler)
{
string px2 = args.NextString();
Team team;
@@ -1154,129 +1472,6 @@ namespace Game.Chat
return true;
}
- [CommandNonGroup("showarea", RBACPermissions.CommandShowarea)]
- static bool ShowArea(StringArguments args, CommandHandler handler)
- {
- if (args.Empty())
- return false;
-
- Player playerTarget = handler.GetSelectedPlayer();
- if (!playerTarget)
- {
- handler.SendSysMessage(CypherStrings.NoCharSelected);
- return false;
- }
-
- AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(args.NextUInt32());
- if (area == null)
- {
- handler.SendSysMessage(CypherStrings.BadValue);
- return false;
- }
-
- if (area.AreaBit < 0)
- {
- handler.SendSysMessage(CypherStrings.BadValue);
- return false;
- }
-
- uint offset = (uint)area.AreaBit / 64;
- if (offset >= PlayerConst.ExploredZonesSize)
- {
- handler.SendSysMessage(CypherStrings.BadValue);
- return false;
- }
-
- ulong val = 1ul << (area.AreaBit % 64);
- playerTarget.AddExploredZones(offset, val);
-
- handler.SendSysMessage(CypherStrings.ExploreArea);
- return true;
- }
-
- [CommandNonGroup("hidearea", RBACPermissions.CommandHidearea)]
- static bool HideArea(StringArguments args, CommandHandler handler)
- {
- if (args.Empty())
- return false;
-
- Player playerTarget = handler.GetSelectedPlayer();
- if (!playerTarget)
- {
- handler.SendSysMessage(CypherStrings.NoCharSelected);
- return false;
- }
-
- AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(args.NextUInt32());
- if (area == null)
- {
- handler.SendSysMessage(CypherStrings.BadValue);
- return false;
- }
-
- if (area.AreaBit < 0)
- {
- handler.SendSysMessage(CypherStrings.BadValue);
- return false;
- }
-
- uint offset = (uint)area.AreaBit / 64;
- if (offset >= PlayerConst.ExploredZonesSize)
- {
- handler.SendSysMessage(CypherStrings.BadValue);
- return false;
- }
-
- uint val = (1u << (area.AreaBit % 64));
- playerTarget.RemoveExploredZones(offset, val);
-
- handler.SendSysMessage(CypherStrings.UnexploreArea);
- return true;
- }
-
- [CommandNonGroup("bank", RBACPermissions.CommandBank)]
- static bool Bank(StringArguments args, CommandHandler handler)
- {
- handler.GetSession().SendShowBank(handler.GetSession().GetPlayer().GetGUID());
- return true;
- }
-
- [CommandNonGroup("wchange", RBACPermissions.CommandWchange)]
- static bool WChange(StringArguments args, CommandHandler handler)
- {
- if (args.Empty())
- return false;
-
- // Weather is OFF
- if (!WorldConfig.GetBoolValue(WorldCfg.Weather))
- {
- handler.SendSysMessage(CypherStrings.WeatherDisabled);
- return false;
- }
-
- // *Change the weather of a cell
- //0 to 3, 0: fine, 1: rain, 2: snow, 3: sand
- if (!uint.TryParse(args.NextString(), out uint type))
- return false;
-
- //0 to 1, sending -1 is instand good weather
- if (!float.TryParse(args.NextString(), out float grade))
- return false;
-
- Player player = handler.GetSession().GetPlayer();
- uint zoneid = player.GetZoneId();
-
- Weather weather = player.GetMap().GetOrGenerateZoneDefaultWeather(zoneid);
- if (weather == null)
- {
- handler.SendSysMessage(CypherStrings.NoWeather);
- return false;
- }
-
- weather.SetWeather((WeatherType)type, grade);
- return true;
- }
-
[CommandNonGroup("pinfo", RBACPermissions.CommandPinfo, true)]
static bool HandlePInfoCommand(StringArguments args, CommandHandler handler)
{
@@ -1625,491 +1820,97 @@ namespace Game.Chat
return true;
}
- [CommandNonGroup("respawn", RBACPermissions.CommandRespawn)]
- static bool Respawn(StringArguments args, CommandHandler handler)
- {
- Player player = handler.GetSession().GetPlayer();
-
- // accept only explicitly selected target (not implicitly self targeting case)
- Creature target = !player.GetTarget().IsEmpty() ? handler.GetSelectedCreature() : null;
- if (target)
- {
- if (target.IsPet())
- {
- handler.SendSysMessage(CypherStrings.SelectCreature);
- return false;
- }
-
- if (target.IsDead())
- target.Respawn();
- return true;
- }
-
- var worker = new WorldObjectWorker(player, new RespawnDo());
- Cell.VisitGridObjects(player, worker, player.GetGridActivationRange());
-
- return true;
- }
-
- // mute player for some times
- [CommandNonGroup("mute", RBACPermissions.CommandMute, true)]
- static bool Mute(StringArguments args, CommandHandler handler)
- {
- string nameStr;
- string delayStr;
- handler.ExtractOptFirstArg(args, out nameStr, out delayStr);
- if (string.IsNullOrEmpty(delayStr))
- return false;
-
- string muteReason = args.NextString();
- string muteReasonStr = "No reason";
- if (muteReason != null)
- muteReasonStr = muteReason;
-
- Player target;
- ObjectGuid targetGuid;
- string targetName;
- if (!handler.ExtractPlayerTarget(new StringArguments(nameStr), out target, out targetGuid, out targetName))
- return false;
-
- uint accountId = target ? target.GetSession().GetAccountId() : Global.CharacterCacheStorage.GetCharacterAccountIdByGuid(targetGuid);
-
- // find only player from same account if any
- if (!target)
- {
- WorldSession session = Global.WorldMgr.FindSession(accountId);
- if (session != null)
- target = session.GetPlayer();
- }
-
- if (!uint.TryParse(delayStr, out uint notSpeakTime))
- return false;
-
- // must have strong lesser security level
- if (handler.HasLowerSecurity(target, targetGuid, true))
- return false;
-
- PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_MUTE_TIME);
- string muteBy;
- if (handler.GetSession() != null)
- muteBy = handler.GetSession().GetPlayerName();
- else
- muteBy = "Console";
-
- if (target)
- {
- // Target is online, mute will be in effect right away.
- long muteTime = Time.UnixTime + notSpeakTime * Time.Minute;
- target.GetSession().m_muteTime = muteTime;
- stmt.AddValue(0, muteTime);
- string nameLink = handler.PlayerLink(targetName);
-
- if (WorldConfig.GetBoolValue(WorldCfg.ShowMuteInWorld))
- {
- Global.WorldMgr.SendWorldText(CypherStrings.CommandMutemessageWorld, (handler.GetSession() != null ? handler.GetSession().GetPlayerName() : "Server"), nameLink, notSpeakTime, muteReasonStr);
- target.SendSysMessage(CypherStrings.YourChatDisabled, notSpeakTime, muteBy, muteReasonStr);
- }
- else
- {
- target.SendSysMessage(CypherStrings.YourChatDisabled, notSpeakTime, muteBy, muteReasonStr);
- }
- }
- else
- {
- // Target is offline, mute will be in effect starting from the next login.
- int muteTime = -(int)(notSpeakTime * Time.Minute);
- stmt.AddValue(0, muteTime);
- }
-
- stmt.AddValue(1, muteReasonStr);
- stmt.AddValue(2, muteBy);
- stmt.AddValue(3, accountId);
- DB.Login.Execute(stmt);
- string nameLink_ = handler.PlayerLink(targetName);
-
- if (WorldConfig.GetBoolValue(WorldCfg.ShowMuteInWorld) && !target)
- Global.WorldMgr.SendWorldText(CypherStrings.CommandMutemessageWorld, handler.GetSession().GetPlayerName(), nameLink_, notSpeakTime, muteReasonStr);
- else
- handler.SendSysMessage(target ? CypherStrings.YouDisableChat : CypherStrings.CommandDisableChatDelayed, nameLink_, notSpeakTime, muteReasonStr);
- return true;
- }
-
- // unmute player
- [CommandNonGroup("unmute", RBACPermissions.CommandUnmute, true)]
- static bool UnMute(StringArguments args, CommandHandler handler)
- {
- Player target;
- ObjectGuid targetGuid;
- string targetName;
- if (!handler.ExtractPlayerTarget(args, out target, out targetGuid, out targetName))
- return false;
-
- uint accountId = target ? target.GetSession().GetAccountId() : Global.CharacterCacheStorage.GetCharacterAccountIdByGuid(targetGuid);
-
- // find only player from same account if any
- if (!target)
- {
- WorldSession session = Global.WorldMgr.FindSession(accountId);
- if (session != null)
- target = session.GetPlayer();
- }
-
- // must have strong lesser security level
- if (handler.HasLowerSecurity(target, targetGuid, true))
- return false;
-
- if (target)
- {
- if (target.GetSession().CanSpeak())
- {
- handler.SendSysMessage(CypherStrings.ChatAlreadyEnabled);
- return false;
- }
-
- target.GetSession().m_muteTime = 0;
- }
-
- PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_MUTE_TIME);
- stmt.AddValue(0, 0);
- stmt.AddValue(1, "");
- stmt.AddValue(2, "");
- stmt.AddValue(3, accountId);
- DB.Login.Execute(stmt);
-
- if (target)
- target.SendSysMessage(CypherStrings.YourChatEnabled);
-
- string nameLink = handler.PlayerLink(targetName);
-
- handler.SendSysMessage(CypherStrings.YouEnableChat, nameLink);
-
- return true;
- }
-
- // mutehistory command
- [CommandNonGroup("mutehistory", RBACPermissions.CommandMutehistory, true)]
- static bool HandleMuteInfoCommand(StringArguments args, CommandHandler handler)
+ [CommandNonGroup("playall", RBACPermissions.CommandPlayall)]
+ static bool HandlePlayAllCommand(StringArguments args, CommandHandler handler)
{
if (args.Empty())
return false;
- string accountName = args.NextString("");
- if (accountName.IsEmpty())
- return false;
+ uint soundId = args.NextUInt32();
- uint accountId = Global.AccountMgr.GetId(accountName);
- if (accountId == 0)
+ if (!CliDB.SoundKitStorage.ContainsKey(soundId))
{
- handler.SendSysMessage(CypherStrings.AccountNotExist, accountName);
+ handler.SendSysMessage(CypherStrings.SoundNotExist, soundId);
return false;
}
- PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_ACCOUNT_MUTE_INFO);
- stmt.AddValue(0, accountId);
-
- SQLResult result = DB.Login.Query(stmt);
- if (result.IsEmpty())
- {
- handler.SendSysMessage(CypherStrings.CommandMutehistoryEmpty, accountName);
- return true;
- }
-
- handler.SendSysMessage(CypherStrings.CommandMutehistory, accountName);
- do
- {
- // we have to manually set the string for mutedate
- long sqlTime = result.Read(0);
-
- // set it to string
- string buffer = Time.UnixTimeToDateTime(sqlTime).ToShortTimeString();
-
- handler.SendSysMessage(CypherStrings.CommandMutehistoryOutput, buffer, result.Read(1), result.Read(2), result.Read(3));
- } while (result.NextRow());
+ Global.WorldMgr.SendGlobalMessage(new PlaySound(handler.GetSession().GetPlayer().GetGUID(), soundId));
+ handler.SendSysMessage(CypherStrings.CommandPlayedToAll, soundId);
return true;
}
- [CommandNonGroup("movegens", RBACPermissions.CommandMovegens)]
- static bool MoveGens(StringArguments args, CommandHandler handler)
+ [CommandNonGroup("possess", RBACPermissions.CommandPossess)]
+ static bool HandlePossessCommand(StringArguments args, CommandHandler handler)
{
Unit unit = handler.GetSelectedUnit();
if (!unit)
- {
- handler.SendSysMessage(CypherStrings.SelectCharOrCreature);
-
return false;
- }
- handler.SendSysMessage(CypherStrings.MovegensList, (unit.IsTypeId(TypeId.Player) ? "Player" : "Creature"), unit.GetGUID().ToString());
-
- MotionMaster motionMaster = unit.GetMotionMaster();
- float x, y, z;
- motionMaster.GetDestination(out x, out y, out z);
-
- for (byte i = 0; i < (int)MovementSlot.Max; ++i)
- {
- IMovementGenerator movementGenerator = motionMaster.GetMotionSlot(i);
- if (movementGenerator == null)
- {
- handler.SendSysMessage("Empty");
- continue;
- }
-
- switch (movementGenerator.GetMovementGeneratorType())
- {
- case MovementGeneratorType.Idle:
- handler.SendSysMessage(CypherStrings.MovegensIdle);
- break;
- case MovementGeneratorType.Random:
- handler.SendSysMessage(CypherStrings.MovegensRandom);
- break;
- case MovementGeneratorType.Waypoint:
- handler.SendSysMessage(CypherStrings.MovegensWaypoint);
- break;
- case MovementGeneratorType.Confused:
- handler.SendSysMessage(CypherStrings.MovegensConfused);
- break;
- case MovementGeneratorType.Chase:
- {
- Unit target;
- if (unit.IsTypeId(TypeId.Player))
- target = ((ChaseMovementGenerator)movementGenerator).GetTarget();
- else
- target = ((ChaseMovementGenerator)movementGenerator).GetTarget();
-
- if (!target)
- handler.SendSysMessage(CypherStrings.MovegensChaseNull);
- else if (target.IsTypeId(TypeId.Player))
- handler.SendSysMessage(CypherStrings.MovegensChasePlayer, target.GetName(), target.GetGUID().ToString());
- else
- handler.SendSysMessage(CypherStrings.MovegensChaseCreature, target.GetName(), target.GetGUID().ToString());
- break;
- }
- case MovementGeneratorType.Follow:
- {
- Unit target;
- if (unit.IsTypeId(TypeId.Player))
- target = ((FollowMovementGenerator)movementGenerator).GetTarget();
- else
- target = ((FollowMovementGenerator)movementGenerator).GetTarget();
-
- if (!target)
- handler.SendSysMessage(CypherStrings.MovegensFollowNull);
- else if (target.IsTypeId(TypeId.Player))
- handler.SendSysMessage(CypherStrings.MovegensFollowPlayer, target.GetName(), target.GetGUID().ToString());
- else
- handler.SendSysMessage(CypherStrings.MovegensFollowCreature, target.GetName(), target.GetGUID().ToString());
- break;
- }
- case MovementGeneratorType.Home:
- {
- if (unit.IsTypeId(TypeId.Unit))
- handler.SendSysMessage(CypherStrings.MovegensHomeCreature, x, y, z);
- else
- handler.SendSysMessage(CypherStrings.MovegensHomePlayer);
- break;
- }
- case MovementGeneratorType.Flight:
- handler.SendSysMessage(CypherStrings.MovegensFlight);
- break;
- case MovementGeneratorType.Point:
- {
- handler.SendSysMessage(CypherStrings.MovegensPoint, x, y, z);
- break;
- }
- case MovementGeneratorType.Fleeing:
- handler.SendSysMessage(CypherStrings.MovegensFear);
- break;
- case MovementGeneratorType.Distract:
- handler.SendSysMessage(CypherStrings.MovegensDistract);
- break;
- case MovementGeneratorType.Effect:
- handler.SendSysMessage(CypherStrings.MovegensEffect);
- break;
- default:
- handler.SendSysMessage(CypherStrings.MovegensUnknown, movementGenerator.GetMovementGeneratorType());
- break;
- }
- }
+ handler.GetSession().GetPlayer().CastSpell(unit, 530, true);
return true;
}
- [CommandNonGroup("cometome", RBACPermissions.CommandCometome)]
- static bool HandleComeToMeCommand(StringArguments args, CommandHandler handler)
+ [CommandNonGroup("pvpstats", RBACPermissions.CommandPvpstats, true)]
+ static bool HandlePvPstatsCommand(StringArguments args, CommandHandler handler)
{
- Creature caster = handler.GetSelectedCreature();
- if (!caster)
+ if (WorldConfig.GetBoolValue(WorldCfg.BattlegroundStoreStatisticsEnable))
{
- handler.SendSysMessage(CypherStrings.SelectCreature);
- return false;
- }
+ PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_PVPSTATS_FACTIONS_OVERALL);
+ SQLResult result = DB.Characters.Query(stmt);
- Player player = handler.GetSession().GetPlayer();
- caster.GetMotionMaster().MovePoint(0, player.GetPositionX(), player.GetPositionY(), player.GetPositionZ());
-
- return true;
- }
-
- [CommandNonGroup("damage", RBACPermissions.CommandDamage)]
- static bool Damage(StringArguments args, CommandHandler handler)
- {
- if (args.Empty())
- return false;
-
- string str = args.NextString();
-
- if (str == "go")
- {
- ulong guidLow = args.NextUInt64();
- if (guidLow == 0)
+ if (!result.IsEmpty())
{
- handler.SendSysMessage(CypherStrings.BadValue);
- return false;
- }
+ uint horde_victories = result.Read(1);
- int damage = args.NextInt32();
- if (damage == 0)
- {
- handler.SendSysMessage(CypherStrings.BadValue);
- return false;
- }
- Player player = handler.GetSession().GetPlayer();
- if (player)
- {
- GameObject go = handler.GetObjectFromPlayerMapByDbGuid(guidLow);
- if (!go)
- {
- handler.SendSysMessage(CypherStrings.CommandObjnotfound, guidLow);
+ if (!(result.NextRow()))
return false;
- }
- if (!go.IsDestructibleBuilding())
- {
- handler.SendSysMessage(CypherStrings.InvalidGameobjectType);
- return false;
- }
+ uint alliance_victories = result.Read(1);
- go.ModifyHealth(-damage, player);
- handler.SendSysMessage(CypherStrings.GameobjectDamaged, go.GetName(), guidLow, -damage, go.GetGoValue().Building.Health);
+ handler.SendSysMessage(CypherStrings.Pvpstats, alliance_victories, horde_victories);
}
-
- return true;
- }
-
- Unit target = handler.GetSelectedUnit();
- if (!target || handler.GetSession().GetPlayer().GetTarget().IsEmpty())
- {
- handler.SendSysMessage(CypherStrings.SelectCharOrCreature);
- return false;
- }
- Player player_ = target.ToPlayer();
- if (player_)
- if (handler.HasLowerSecurity(player_, ObjectGuid.Empty, false))
+ else
return false;
-
- if (!target.IsAlive())
- return true;
-
- if (!int.TryParse(str, out int damage_int))
- return false;
-
- if (damage_int <= 0)
- return true;
-
- uint damage_ = (uint)damage_int;
-
- string schoolStr = args.NextString();
-
- Player attacker = handler.GetSession().GetPlayer();
-
- // flat melee damage without resistence/etc reduction
- if (string.IsNullOrEmpty(schoolStr))
- {
- attacker.DealDamage(target, damage_, null, DamageEffectType.Direct, SpellSchoolMask.Normal, null, false);
- if (target != attacker)
- attacker.SendAttackStateUpdate(HitInfo.AffectsVictim, target, SpellSchoolMask.Normal, damage_, 0, 0, VictimState.Hit, 0);
- return true;
}
+ else
+ handler.SendSysMessage(CypherStrings.PvpstatsDisabled);
- if (!int.TryParse(schoolStr, out int school) || school >= (int)SpellSchools.Max)
- return false;
-
- SpellSchoolMask schoolmask = (SpellSchoolMask)(1 << school);
-
- if (attacker.IsDamageReducedByArmor(schoolmask))
- damage_ = attacker.CalcArmorReducedDamage(handler.GetPlayer(), target, damage_, null, WeaponAttackType.BaseAttack);
-
- string spellStr = args.NextString();
-
- // melee damage by specific school
- if (string.IsNullOrEmpty(spellStr))
- {
- DamageInfo dmgInfo = new DamageInfo(attacker, target, damage_, null, schoolmask, DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack);
- attacker.CalcAbsorbResist(dmgInfo);
-
- if (dmgInfo.GetDamage() == 0)
- return true;
-
- damage_ = dmgInfo.GetDamage();
-
- uint absorb = dmgInfo.GetAbsorb();
- uint resist = dmgInfo.GetResist();
- attacker.DealDamageMods(target, ref damage_, ref absorb);
- attacker.DealDamage(target, damage_, null, DamageEffectType.Direct, schoolmask, null, false);
- attacker.SendAttackStateUpdate(HitInfo.AffectsVictim, target, schoolmask, damage_, absorb, resist, VictimState.Hit, 0);
- return true;
- }
-
- // non-melee damage
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
- uint spellid = handler.ExtractSpellIdFromLink(args);
- if (spellid == 0)
- return false;
-
- SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellid, attacker.GetMap().GetDifficultyID());
- if (spellInfo == null)
- return false;
-
- SpellNonMeleeDamage damageInfo = new SpellNonMeleeDamage(attacker, target, spellInfo, spellInfo.GetSpellXSpellVisualId(attacker), spellInfo.SchoolMask);
- damageInfo.damage = damage_;
- attacker.DealDamageMods(damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb);
- target.DealSpellDamage(damageInfo, true);
- target.SendSpellNonMeleeDamageLog(damageInfo);
return true;
}
- [CommandNonGroup("combatstop", RBACPermissions.CommandCombatstop, true)]
- static bool CombatStop(StringArguments args, CommandHandler handler)
+ // Teleport player to last position
+ [CommandNonGroup("recall", RBACPermissions.CommandRecall)]
+ static bool HandleRecallCommand(StringArguments args, CommandHandler handler)
{
- Player target = null;
-
- if (!args.Empty())
- {
- target = Global.ObjAccessor.FindPlayerByName(args.NextString());
- if (!target)
- {
- handler.SendSysMessage(CypherStrings.PlayerNotFound);
- return false;
- }
- }
-
- if (!target)
- {
- if (!handler.ExtractPlayerTarget(args, out target))
- return false;
- }
+ Player target;
+ if (!handler.ExtractPlayerTarget(args, out target))
+ return false;
// check online security
if (handler.HasLowerSecurity(target, ObjectGuid.Empty))
return false;
- target.CombatStop();
- target.GetHostileRefManager().DeleteReferences();
+ if (target.IsBeingTeleported())
+ {
+ handler.SendSysMessage(CypherStrings.IsTeleported, handler.GetNameLink(target));
+
+ return false;
+ }
+
+ // stop flight if need
+ if (target.IsInFlight())
+ {
+ target.GetMotionMaster().MovementExpired();
+ target.CleanupAfterTaxiFlight();
+ }
+
+ target.Recall();
return true;
}
[CommandNonGroup("repairitems", RBACPermissions.CommandRepairitems, true)]
- static bool RepairItems(StringArguments args, CommandHandler handler)
+ static bool HandleRepairitemsCommand(StringArguments args, CommandHandler handler)
{
Player target;
if (!handler.ExtractPlayerTarget(args, out target))
@@ -2129,96 +1930,265 @@ namespace Game.Chat
return true;
}
- [CommandNonGroup("freeze", RBACPermissions.CommandFreeze)]
- static bool HandleFreezeCommand(StringArguments args, CommandHandler handler)
+ [CommandNonGroup("respawn", RBACPermissions.CommandRespawn)]
+ static bool HandleRespawnCommand(StringArguments args, CommandHandler handler)
{
- Player player = handler.GetSelectedPlayer(); // Selected player, if any. Might be null.
- int freezeDuration = 0; // Freeze Duration (in seconds)
- bool canApplyFreeze = false; // Determines if every possible argument is set so Freeze can be applied
- bool getDurationFromConfig = false; // If there's no given duration, we'll retrieve the world cfg value later
+ Player player = handler.GetSession().GetPlayer();
- if (args.Empty())
+ // accept only explicitly selected target (not implicitly self targeting case)
+ Creature target = !player.GetTarget().IsEmpty() ? handler.GetSelectedCreature() : null;
+ if (target)
{
- // Might have a selected player. We'll check it later
- // Get the duration from world cfg
- getDurationFromConfig = true;
+ if (target.IsPet())
+ {
+ handler.SendSysMessage(CypherStrings.SelectCreature);
+ return false;
+ }
+
+ if (target.IsDead())
+ target.Respawn();
+ return true;
+ }
+
+ // First handle any creatures that still have a corpse around
+ var worker = new WorldObjectWorker(player, new RespawnDo());
+ Cell.VisitGridObjects(player, worker, player.GetGridActivationRange());
+
+ // Now handle any that had despawned, but had respawn time logged.
+ List data = new List();
+ player.GetMap().GetRespawnInfo(data, SpawnObjectTypeMask.All, 0);
+ if (!data.Empty())
+ {
+ uint gridId = GridDefines.ComputeGridCoord(player.GetPositionX(), player.GetPositionY()).GetId();
+ foreach (RespawnInfo info in data)
+ if (info.gridId == gridId)
+ player.GetMap().RemoveRespawnTime(info, true);
+ }
+
+ return true;
+ }
+
+ [CommandNonGroup("revive", RBACPermissions.CommandRevive, true)]
+ static bool HandleReviveCommand(StringArguments args, CommandHandler handler)
+ {
+ Player target;
+ ObjectGuid targetGuid;
+ if (!handler.ExtractPlayerTarget(args, out target, out targetGuid))
+ return false;
+
+ if (target != null)
+ {
+ target.ResurrectPlayer(0.5f);
+ target.SpawnCorpseBones();
+ target.SaveToDB();
+ }
+ else
+ Player.OfflineResurrect(targetGuid, null);
+
+ return true;
+ }
+
+ // Save all players in the world
+ [CommandNonGroup("saveall", RBACPermissions.CommandSaveall, true)]
+ static bool HandleSaveAllCommand(StringArguments args, CommandHandler handler)
+ {
+ Global.ObjAccessor.SaveAllPlayers();
+ handler.SendSysMessage(CypherStrings.PlayersSaved);
+ return true;
+ }
+
+ [CommandNonGroup("save", RBACPermissions.CommandSave)]
+ static bool HandleSaveCommand(StringArguments args, CommandHandler handler)
+ {
+ Player player = handler.GetSession().GetPlayer();
+
+ // save GM account without delay and output message
+ if (handler.GetSession().HasPermission(RBACPermissions.CommandsSaveWithoutDelay))
+ {
+ Player target = handler.GetSelectedPlayer();
+ if (target)
+ target.SaveToDB();
+ else
+ player.SaveToDB();
+ handler.SendSysMessage(CypherStrings.PlayerSaved);
+ return true;
+ }
+
+ // save if the player has last been saved over 20 seconds ago
+ uint saveInterval = WorldConfig.GetUIntValue(WorldCfg.IntervalSave);
+ if (saveInterval == 0 || (saveInterval > 20 * Time.InMilliseconds && player.GetSaveTimer() <= saveInterval - 20 * Time.InMilliseconds))
+ player.SaveToDB();
+
+ return true;
+ }
+
+ [CommandNonGroup("showarea", RBACPermissions.CommandShowarea)]
+ static bool HandleShowAreaCommand(StringArguments args, CommandHandler handler)
+ {
+ if (args.Empty())
+ return false;
+
+ Player playerTarget = handler.GetSelectedPlayer();
+ if (!playerTarget)
+ {
+ handler.SendSysMessage(CypherStrings.NoCharSelected);
+ return false;
+ }
+
+ AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(args.NextUInt32());
+ if (area == null)
+ {
+ handler.SendSysMessage(CypherStrings.BadValue);
+ return false;
+ }
+
+ if (area.AreaBit < 0)
+ {
+ handler.SendSysMessage(CypherStrings.BadValue);
+ return false;
+ }
+
+ uint offset = (uint)area.AreaBit / 64;
+ if (offset >= PlayerConst.ExploredZonesSize)
+ {
+ handler.SendSysMessage(CypherStrings.BadValue);
+ return false;
+ }
+
+ ulong val = 1ul << (area.AreaBit % 64);
+ playerTarget.AddExploredZones(offset, val);
+
+ handler.SendSysMessage(CypherStrings.ExploreArea);
+ return true;
+ }
+
+ // Summon Player
+ [CommandNonGroup("summon", RBACPermissions.CommandSummon)]
+ static bool HandleSummonCommand(StringArguments args, CommandHandler handler)
+ {
+ Player target;
+ ObjectGuid targetGuid;
+ string targetName;
+ if (!handler.ExtractPlayerTarget(args, out target, out targetGuid, out targetName))
+ return false;
+
+ Player _player = handler.GetSession().GetPlayer();
+ if (target == _player || targetGuid == _player.GetGUID())
+ {
+ handler.SendSysMessage(CypherStrings.CantTeleportSelf);
+
+ return false;
+ }
+
+ if (target)
+ {
+ string nameLink = handler.PlayerLink(targetName);
+ // check online security
+ if (handler.HasLowerSecurity(target, ObjectGuid.Empty))
+ return false;
+
+ if (target.IsBeingTeleported())
+ {
+ handler.SendSysMessage(CypherStrings.IsTeleported, nameLink);
+
+ return false;
+ }
+
+ Map map = _player.GetMap();
+ if (map.IsBattlegroundOrArena())
+ {
+ // only allow if gm mode is on
+ if (!_player.IsGameMaster())
+ {
+ handler.SendSysMessage(CypherStrings.CannotGoToBgGm, nameLink);
+ return false;
+ }
+ // if both players are in different bgs
+ else if (target.GetBattlegroundId() != 0 && _player.GetBattlegroundId() != target.GetBattlegroundId())
+ target.LeaveBattleground(false); // Note: should be changed so target gets no Deserter debuff
+
+ // all's well, set bg id
+ // when porting out from the bg, it will be reset to 0
+ target.SetBattlegroundId(_player.GetBattlegroundId(), _player.GetBattlegroundTypeId());
+ // remember current position as entry point for return at bg end teleportation
+ if (!target.GetMap().IsBattlegroundOrArena())
+ target.SetBattlegroundEntryPoint();
+ }
+ else if (map.Instanceable())
+ {
+ Map targetMap = target.GetMap();
+
+ Player targetGroupLeader = null;
+ Group targetGroup = target.GetGroup();
+ if (targetGroup != null)
+ targetGroupLeader = Global.ObjAccessor.GetPlayer(map, targetGroup.GetLeaderGUID());
+
+ // check if far teleport is allowed
+ if (targetGroupLeader == null || (targetGroupLeader.GetMapId() != map.GetId()) || (targetGroupLeader.GetInstanceId() != map.GetInstanceId()))
+ {
+ if ((targetMap.GetId() != map.GetId()) || (targetMap.GetInstanceId() != map.GetInstanceId()))
+ {
+ handler.SendSysMessage(CypherStrings.CannotSummonToInst);
+ return false;
+ }
+ }
+
+ // check if we're already in a different instance of the same map
+ if ((targetMap.GetId() == map.GetId()) && (targetMap.GetInstanceId() != map.GetInstanceId()))
+ {
+ handler.SendSysMessage(CypherStrings.CannotSummonInstInst, nameLink);
+ return false;
+ }
+ }
+
+ handler.SendSysMessage(CypherStrings.Summoning, nameLink, "");
+ if (handler.NeedReportToTarget(target))
+ target.SendSysMessage(CypherStrings.SummonedBy, handler.PlayerLink(_player.GetName()));
+
+ // stop flight if need
+ if (target.IsInFlight())
+ {
+ target.GetMotionMaster().MovementExpired();
+ target.CleanupAfterTaxiFlight();
+ }
+ // save only in non-flight case
+ else
+ target.SaveRecallPosition();
+
+ // before GM
+ float x, y, z;
+ _player.GetClosePoint(out x, out y, out z, target.GetCombatReach());
+ target.TeleportTo(_player.GetMapId(), x, y, z, target.GetOrientation());
+ PhasingHandler.InheritPhaseShift(target, _player);
+ target.UpdateObjectVisibility();
}
else
{
- // Get the args that we might have (up to 2)
- string arg1 = args.NextString();
- string arg2 = args.NextString();
+ // check offline security
+ if (handler.HasLowerSecurity(null, targetGuid))
+ return false;
- // Analyze them to see if we got either a playerName or duration or both
- if (!arg1.IsEmpty())
- {
- if (arg1.IsNumber())
- {
- // case 2: .freeze duration
- // We have a selected player. We'll check him later
- if (!int.TryParse(arg1, out freezeDuration))
- return false;
- canApplyFreeze = true;
- }
- else
- {
- // case 3 or 4: .freeze player duration | .freeze player
- // find the player
- string name = arg1;
- ObjectManager.NormalizePlayerName(ref name);
- player = Global.ObjAccessor.FindPlayerByName(name);
- // Check if we have duration set
- if (!arg2.IsEmpty() && arg2.IsNumber())
- {
- if (!int.TryParse(arg2, out freezeDuration))
- return false;
- canApplyFreeze = true;
- }
- else
- getDurationFromConfig = true;
- }
- }
+ string nameLink = handler.PlayerLink(targetName);
+
+ handler.SendSysMessage(CypherStrings.Summoning, nameLink, handler.GetCypherString(CypherStrings.Offline));
+
+ // in point where GM stay
+ Player.SavePositionInDB(new WorldLocation(_player.GetMapId(), _player.GetPositionX(), _player.GetPositionY(), _player.GetPositionZ(), _player.GetOrientation()), _player.GetZoneId(), targetGuid);
}
- // Check if duration needs to be retrieved from config
- if (getDurationFromConfig)
- {
- freezeDuration = WorldConfig.GetIntValue(WorldCfg.GmFreezeDuration);
- canApplyFreeze = true;
- }
+ return true;
+ }
- // Player and duration retrieval is over
- if (canApplyFreeze)
- {
- if (!player) // can be null if some previous selection failed
- {
- handler.SendSysMessage(CypherStrings.CommandFreezeWrong);
- return true;
- }
- else if (player == handler.GetSession().GetPlayer())
- {
- // Can't freeze himself
- handler.SendSysMessage(CypherStrings.CommandFreezeError);
- return true;
- }
- else // Apply the effect
- {
- // Add the freeze aura and set the proper duration
- // Player combat status and flags are now handled
- // in Freeze Spell AuraScript (OnApply)
- Aura freeze = player.AddAura(9454, player);
- if (freeze != null)
- {
- if (freezeDuration != 0)
- freeze.SetDuration(freezeDuration * Time.InMilliseconds);
- handler.SendSysMessage(CypherStrings.CommandFreeze, player.GetName());
- // save player
- player.SaveToDB();
- return true;
- }
- }
- }
- return false;
+ [CommandNonGroup("unbindsight", RBACPermissions.CommandUnbindsight)]
+ static bool HandleUnbindSightCommand(StringArguments args, CommandHandler handler)
+ {
+ Player player = handler.GetSession().GetPlayer();
+
+ if (player.IsPossessing())
+ return false;
+
+ player.StopCastingBindSight();
+ return true;
}
[CommandNonGroup("unfreeze", RBACPermissions.CommandUnfreeze)]
@@ -2280,76 +2250,60 @@ namespace Game.Chat
return true;
}
- [CommandNonGroup("listfreeze", RBACPermissions.CommandListfreeze)]
- static bool ListFreeze(StringArguments args, CommandHandler handler)
+ // unmute player
+ [CommandNonGroup("unmute", RBACPermissions.CommandUnmute, true)]
+ static bool HandleUnmuteCommand(StringArguments args, CommandHandler handler)
{
- // Get names from DB
- PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_AURA_FROZEN);
- SQLResult result = DB.Characters.Query(stmt);
- if (result.IsEmpty())
- {
- handler.SendSysMessage(CypherStrings.CommandNoFrozenPlayers);
- return true;
- }
-
- // Header of the names
- handler.SendSysMessage(CypherStrings.CommandListFreeze);
-
- // Output of the results
- do
- {
- string player = result.Read(0);
- int remaintime = result.Read(1);
- // Save the frozen player to update remaining time in case of future .listfreeze uses
- // before the frozen state expires
- Player frozen = Global.ObjAccessor.FindPlayerByName(player);
- if (frozen)
- frozen.SaveToDB();
- // Notify the freeze duration
- if (remaintime == -1) // Permanent duration
- handler.SendSysMessage(CypherStrings.CommandPermaFrozenPlayer, player);
- else
- // show time left (seconds)
- handler.SendSysMessage(CypherStrings.CommandTempFrozenPlayer, player, remaintime / Time.InMilliseconds);
- }
- while (result.NextRow());
-
- return true;
- }
-
- [CommandNonGroup("playall", RBACPermissions.CommandPlayall)]
- static bool PlayAll(StringArguments args, CommandHandler handler)
- {
- if (args.Empty())
+ Player target;
+ ObjectGuid targetGuid;
+ string targetName;
+ if (!handler.ExtractPlayerTarget(args, out target, out targetGuid, out targetName))
return false;
- uint soundId = args.NextUInt32();
+ uint accountId = target ? target.GetSession().GetAccountId() : Global.CharacterCacheStorage.GetCharacterAccountIdByGuid(targetGuid);
- if (!CliDB.SoundKitStorage.ContainsKey(soundId))
+ // find only player from same account if any
+ if (!target)
{
- handler.SendSysMessage(CypherStrings.SoundNotExist, soundId);
- return false;
+ WorldSession session = Global.WorldMgr.FindSession(accountId);
+ if (session != null)
+ target = session.GetPlayer();
}
- Global.WorldMgr.SendGlobalMessage(new PlaySound(handler.GetSession().GetPlayer().GetGUID(), soundId));
-
- handler.SendSysMessage(CypherStrings.CommandPlayedToAll, soundId);
- return true;
- }
-
- [CommandNonGroup("possess", RBACPermissions.CommandPossess)]
- static bool Possess(StringArguments args, CommandHandler handler)
- {
- Unit unit = handler.GetSelectedUnit();
- if (!unit)
+ // must have strong lesser security level
+ if (handler.HasLowerSecurity(target, targetGuid, true))
return false;
- handler.GetSession().GetPlayer().CastSpell(unit, 530, true);
+ if (target)
+ {
+ if (target.GetSession().CanSpeak())
+ {
+ handler.SendSysMessage(CypherStrings.ChatAlreadyEnabled);
+ return false;
+ }
+
+ target.GetSession().m_muteTime = 0;
+ }
+
+ PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_MUTE_TIME);
+ stmt.AddValue(0, 0);
+ stmt.AddValue(1, "");
+ stmt.AddValue(2, "");
+ stmt.AddValue(3, accountId);
+ DB.Login.Execute(stmt);
+
+ if (target)
+ target.SendSysMessage(CypherStrings.YourChatEnabled);
+
+ string nameLink = handler.PlayerLink(targetName);
+
+ handler.SendSysMessage(CypherStrings.YouEnableChat, nameLink);
+
return true;
}
[CommandNonGroup("unpossess", RBACPermissions.CommandUnpossess)]
- static bool UnPossess(StringArguments args, CommandHandler handler)
+ static bool HandleUnPossessCommand(StringArguments args, CommandHandler handler)
{
Unit unit = handler.GetSelectedUnit();
if (!unit)
@@ -2360,63 +2314,121 @@ namespace Game.Chat
return true;
}
- [CommandNonGroup("bindsight", RBACPermissions.CommandBindsight)]
- static bool BindSight(StringArguments args, CommandHandler handler)
+ [CommandNonGroup("unstuck", RBACPermissions.CommandUnstuck, true)]
+ static bool HandleUnstuckCommand(StringArguments args, CommandHandler handler)
{
- Unit unit = handler.GetSelectedUnit();
- if (!unit)
- return false;
+ uint SPELL_UNSTUCK_ID = 7355;
+ uint SPELL_UNSTUCK_VISUAL = 2683;
- handler.GetSession().GetPlayer().CastSpell(unit, 6277, true);
- return true;
- }
-
- [CommandNonGroup("unbindsight", RBACPermissions.CommandUnbindsight)]
- static bool UnbindSight(StringArguments args, CommandHandler handler)
- {
- Player player = handler.GetSession().GetPlayer();
-
- if (player.IsPossessing())
- return false;
-
- player.StopCastingBindSight();
- return true;
- }
-
- [CommandNonGroup("mailbox", RBACPermissions.CommandMailbox)]
- static bool MailBox(StringArguments args, CommandHandler handler)
- {
- Player player = handler.GetSession().GetPlayer();
-
- handler.GetSession().SendShowMailBox(player.GetGUID());
- return true;
- }
-
- [CommandNonGroup("pvpstats", RBACPermissions.CommandPvpstats, true)]
- static bool HandlePvPstatsCommand(StringArguments args, CommandHandler handler)
- {
- if (WorldConfig.GetBoolValue(WorldCfg.BattlegroundStoreStatisticsEnable))
+ // No args required for players
+ if (handler.GetSession() != null && handler.GetSession().HasPermission(RBACPermissions.CommandsUseUnstuckWithArgs))
{
- PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_PVPSTATS_FACTIONS_OVERALL);
- SQLResult result = DB.Characters.Query(stmt);
+ // 7355: "Stuck"
+ var player1 = handler.GetSession().GetPlayer();
+ if (player1)
+ player1.CastSpell(player1, SPELL_UNSTUCK_ID, false);
+ return true;
+ }
+ if (args.Empty())
+ return false;
+
+ string location_str = "inn";
+ string loc = args.NextString();
+ if (string.IsNullOrEmpty(loc))
+ location_str = loc;
+
+ Player player;
+ ObjectGuid targetGUID;
+ if (!handler.ExtractPlayerTarget(args, out player, out targetGUID))
+ return false;
+
+ if (!player)
+ {
+ PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHAR_HOMEBIND);
+ stmt.AddValue(0, targetGUID.GetCounter());
+ SQLResult result = DB.Characters.Query(stmt);
if (!result.IsEmpty())
{
- uint horde_victories = result.Read(1);
-
- if (!(result.NextRow()))
- return false;
-
- uint alliance_victories = result.Read(1);
-
- handler.SendSysMessage(CypherStrings.Pvpstats, alliance_victories, horde_victories);
+ Player.SavePositionInDB(new WorldLocation(result.Read(0), result.Read(2), result.Read(3), result.Read(4), 0.0f), result.Read(1), targetGUID);
+ return true;
}
- else
- return false;
- }
- else
- handler.SendSysMessage(CypherStrings.PvpstatsDisabled);
+ return false;
+ }
+
+ if (player.IsInFlight() || player.IsInCombat())
+ {
+ SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(SPELL_UNSTUCK_ID, Difficulty.None);
+ if (spellInfo == null)
+ return false;
+
+ Player caster = handler.GetSession().GetPlayer();
+ if (caster)
+ {
+ ObjectGuid castId = ObjectGuid.Create(HighGuid.Cast, SpellCastSource.Normal, player.GetMapId(), SPELL_UNSTUCK_ID, player.GetMap().GenerateLowGuid(HighGuid.Cast));
+ Spell.SendCastResult(caster, spellInfo, SPELL_UNSTUCK_VISUAL, castId, SpellCastResult.CantDoThatRightNow);
+ }
+
+ return false;
+ }
+
+ if (location_str == "inn")
+ {
+ var home = player.GetHomebind();
+ player.TeleportTo(home.GetMapId(), home.GetPositionX(), home.GetPositionY(), home.GetPositionZ(), player.GetOrientation());
+ return true;
+ }
+
+ if (location_str == "graveyard")
+ {
+ player.RepopAtGraveyard();
+ return true;
+ }
+
+ if (location_str == "startzone")
+ {
+ player.TeleportTo(player.GetStartPosition());
+ return true;
+ }
+
+ //Not a supported argument
+ return false;
+ }
+
+ [CommandNonGroup("wchange", RBACPermissions.CommandWchange)]
+ static bool HandleChangeWeather(StringArguments args, CommandHandler handler)
+ {
+ if (args.Empty())
+ return false;
+
+ // Weather is OFF
+ if (!WorldConfig.GetBoolValue(WorldCfg.Weather))
+ {
+ handler.SendSysMessage(CypherStrings.WeatherDisabled);
+ return false;
+ }
+
+ // *Change the weather of a cell
+ //0 to 3, 0: fine, 1: rain, 2: snow, 3: sand
+ if (!uint.TryParse(args.NextString(), out uint type))
+ return false;
+
+ //0 to 1, sending -1 is instand good weather
+ if (!float.TryParse(args.NextString(), out float grade))
+ return false;
+
+ Player player = handler.GetSession().GetPlayer();
+ uint zoneid = player.GetZoneId();
+
+ Weather weather = player.GetMap().GetOrGenerateZoneDefaultWeather(zoneid);
+ if (weather == null)
+ {
+ handler.SendSysMessage(CypherStrings.NoWeather);
+ return false;
+ }
+
+ weather.SetWeather((WeatherType)type, grade);
return true;
}
}
diff --git a/Source/Game/Chat/Commands/NPCCommands.cs b/Source/Game/Chat/Commands/NPCCommands.cs
index ece9f0cae..01731cc38 100644
--- a/Source/Game/Chat/Commands/NPCCommands.cs
+++ b/Source/Game/Chat/Commands/NPCCommands.cs
@@ -33,6 +33,41 @@ namespace Game.Chat
[CommandGroup("npc", RBACPermissions.CommandNpc)]
class NPCCommands
{
+ [Command("despawngroup", RBACPermissions.CommandNpcDespawngroup)]
+ static bool HandleNpcDespawnGroup(StringArguments args, CommandHandler handler)
+ {
+ if (args.Empty())
+ return false;
+
+ bool deleteRespawnTimes = false;
+ uint groupId = 0;
+
+ // Decode arguments
+ string arg = args.NextString();
+ while (!arg.IsEmpty())
+ {
+ string thisArg = arg.ToLower();
+ if (thisArg == "removerespawntime")
+ deleteRespawnTimes = true;
+ else if (thisArg.IsEmpty() || !thisArg.IsNumber())
+ return false;
+ else
+ groupId = uint.Parse(thisArg);
+
+ arg = args.NextString();
+ }
+
+ Player player = handler.GetSession().GetPlayer();
+
+ if (!Global.ObjectMgr.SpawnGroupDespawn(groupId, player.GetMap(), deleteRespawnTimes))
+ {
+ handler.SendSysMessage(CypherStrings.SpawngroupBadgroup, groupId);
+ return false;
+ }
+
+ return true;
+ }
+
[Command("evade", RBACPermissions.CommandNpcEvade)]
static bool HandleNpcEvadeCommand(StringArguments args, CommandHandler handler)
{
@@ -96,13 +131,21 @@ namespace Game.Chat
uint nativeid = target.GetNativeDisplayId();
uint Entry = target.GetEntry();
- long curRespawnDelay = target.GetRespawnTimeEx() - Time.UnixTime;
+ long curRespawnDelay = target.GetRespawnCompatibilityMode() ? target.GetRespawnTimeEx() - Time.UnixTime : target.GetMap().GetCreatureRespawnTime(target.GetSpawnId()) - Time.UnixTime;
if (curRespawnDelay < 0)
curRespawnDelay = 0;
+
string curRespawnDelayStr = Time.secsToTimeString((ulong)curRespawnDelay, true);
string defRespawnDelayStr = Time.secsToTimeString(target.GetRespawnDelay(), true);
handler.SendSysMessage(CypherStrings.NpcinfoChar, target.GetSpawnId(), target.GetGUID().ToString(), faction, npcflags, Entry, displayid, nativeid);
+ if (target.GetCreatureData() != null && target.GetCreatureData().spawnGroupData.groupId != 0)
+ {
+ SpawnGroupTemplateData groupData = target.GetCreatureData().spawnGroupData;
+ if (groupData != null)
+ handler.SendSysMessage(CypherStrings.SpawninfoGroupId, groupData.name, groupData.groupId, groupData.flags, groupData.isActive);
+ }
+ handler.SendSysMessage(CypherStrings.SpawninfoCompatibilityMode, target.GetRespawnCompatibilityMode());
handler.SendSysMessage(CypherStrings.NpcinfoLevel, target.GetLevel());
handler.SendSysMessage(CypherStrings.NpcinfoEquipment, target.GetCurrentEquipmentId(), target.GetOriginalEquipmentId());
handler.SendSysMessage(CypherStrings.NpcinfoHealth, target.GetCreateHealth(), target.GetMaxHealth(), target.GetHealth());
@@ -161,71 +204,60 @@ namespace Game.Chat
ulong lowguid;
Creature creature = handler.GetSelectedCreature();
- if (!creature)
+ Player player = handler.GetSession().GetPlayer();
+ if (player == null)
+ return false;
+
+ if (creature != null)
+ lowguid = creature.GetSpawnId();
+ else
{
// number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
string cId = handler.ExtractKeyFromLink(args, "Hcreature");
- if (string.IsNullOrEmpty(cId))
+ if (cId.IsEmpty())
return false;
if (!ulong.TryParse(cId, out lowguid))
return false;
+ }
+
// Attempting creature load from DB data
CreatureData data = Global.ObjectMgr.GetCreatureData(lowguid);
- if (data == null)
- {
- handler.SendSysMessage(CypherStrings.CommandCreatguidnotfound, lowguid);
- return false;
- }
-
- uint map_id = data.mapid;
- if (handler.GetSession().GetPlayer().GetMapId() != map_id)
- {
- handler.SendSysMessage(CypherStrings.CommandCreatureatsamemap, lowguid);
- return false;
- }
-
- }
- else
+ if (data == null)
{
- lowguid = creature.GetSpawnId();
+ handler.SendSysMessage(CypherStrings.CommandCreatguidnotfound, lowguid);
+ return false;
}
- float x = handler.GetPlayer().GetPositionX();
- float y = handler.GetPlayer().GetPositionY();
- float z = handler.GetPlayer().GetPositionZ();
- float o = handler.GetPlayer().GetOrientation();
-
- if (creature)
+ if (player.GetMapId() != data.spawnPoint.GetMapId())
{
- CreatureData data = Global.ObjectMgr.GetCreatureData(creature.GetSpawnId());
- if (data != null)
- {
- data.posX = x;
- data.posY = y;
- data.posZ = z;
- data.orientation = o;
- }
- creature.UpdatePosition(x, y, z, o);
- creature.GetMotionMaster().Initialize();
- if (creature.IsAlive()) // dead creature will reset movement generator at respawn
- {
- creature.SetDeathState(DeathState.JustDied);
- creature.Respawn();
- }
+ handler.SendSysMessage(CypherStrings.CommandCreatureatsamemap, lowguid);
+ return false;
}
+ data.spawnPoint.Relocate(player);
+
+ // update position in DB
PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_CREATURE_POSITION);
-
- stmt.AddValue(0, x);
- stmt.AddValue(1, y);
- stmt.AddValue(2, z);
- stmt.AddValue(3, o);
+ stmt.AddValue(0, player.GetPositionX());
+ stmt.AddValue(1, player.GetPositionY());
+ stmt.AddValue(2, player.GetPositionZ());
+ stmt.AddValue(3, player.GetOrientation());
stmt.AddValue(4, lowguid);
DB.World.Execute(stmt);
+ // respawn selected creature at the new location
+ if (creature)
+ {
+ if (creature.IsAlive())
+ creature.SetDeathState(DeathState.JustDied);
+ creature.Respawn(true);
+ if (!creature.GetRespawnCompatibilityMode())
+ creature.AddObjectToRemoveList();
+ }
+
handler.SendSysMessage(CypherStrings.CommandCreaturemoved);
return true;
}
@@ -264,7 +296,7 @@ namespace Game.Chat
if (creatureTemplate == null)
continue;
- handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, creatureTemplate.Name, x, y, z, mapId);
+ handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, creatureTemplate.Name, x, y, z, mapId, "", "");
++count;
}
@@ -386,6 +418,49 @@ namespace Game.Chat
return true;
}
+ [Command("spawngroup", RBACPermissions.CommandNpcSpawngroup)]
+ static bool HandleNpcSpawnGroup(StringArguments args, CommandHandler handler)
+ {
+ if (args.Empty())
+ return false;
+
+ bool ignoreRespawn = false;
+ bool force = false;
+ uint groupId = 0;
+
+ // Decode arguments
+ string arg = args.NextString();
+ while (!arg.IsEmpty())
+ {
+ string thisArg = arg.ToLower();
+ if (thisArg == "ignorerespawn")
+ ignoreRespawn = true;
+ else if (thisArg == "force")
+ force = true;
+ else if (thisArg.IsEmpty() || !thisArg.IsNumber())
+ return false;
+ else
+ groupId = uint.Parse(thisArg);
+
+ arg = args.NextString();
+ }
+
+ Player player = handler.GetSession().GetPlayer();
+
+ List creatureList = new List();
+ if (!Global.ObjectMgr.SpawnGroupSpawn(groupId, player.GetMap(), ignoreRespawn, force, creatureList))
+ {
+ handler.SendSysMessage(CypherStrings.SpawngroupBadgroup, groupId);
+ return false;
+ }
+
+ handler.SendSysMessage(CypherStrings.SpawngroupSpawncount, creatureList.Count);
+ foreach (WorldObject obj in creatureList)
+ handler.SendSysMessage($"{obj.GetName()} ({obj.GetGUID()})");
+
+ return true;
+ }
+
[Command("tame", RBACPermissions.CommandNpcTame)]
static bool HandleNpcTameCommand(StringArguments args, CommandHandler handler)
{
@@ -581,13 +656,11 @@ namespace Game.Chat
{
ulong guid = map.GenerateLowGuid(HighGuid.Creature);
CreatureData data = Global.ObjectMgr.NewOrExistCreatureData(guid);
- data.id = id;
- data.posX = chr.GetTransOffsetX();
- data.posY = chr.GetTransOffsetY();
- data.posZ = chr.GetTransOffsetZ();
- data.orientation = chr.GetTransOffsetO();
+ data.spawnId = guid;
+ data.Id = id;
+ data.spawnPoint.Relocate(chr.GetTransOffsetX(), chr.GetTransOffsetY(), chr.GetTransOffsetZ(), chr.GetTransOffsetO());
// @todo: add phases
-
+
Creature _creature = trans.CreateNPCPassenger(guid, data);
_creature.SaveToDB((uint)trans.GetGoInfo().MoTransport.SpawnMap, new List() { map.GetDifficultyID() });
@@ -607,7 +680,7 @@ namespace Game.Chat
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells()
// current "creature" variable is deleted and created fresh new, otherwise old values might trigger asserts or cause undefined behavior
creature.CleanupsBeforeDelete();
- creature = Creature.CreateCreatureFromDB(db_guid, map);
+ creature = Creature.CreateCreatureFromDB(db_guid, map, true, true);
if (!creature)
return false;
@@ -794,7 +867,7 @@ namespace Game.Chat
[Command("delete", RBACPermissions.CommandNpcDelete)]
static bool HandleNpcDeleteCommand(StringArguments args, CommandHandler handler)
{
- Creature unit = null;
+ Creature creature;
if (!args.Empty())
{
@@ -806,21 +879,27 @@ namespace Game.Chat
if (!ulong.TryParse(cId, out ulong guidLow) || guidLow == 0)
return false;
- unit = handler.GetCreatureFromPlayerMapByDbGuid(guidLow);
+ creature = handler.GetCreatureFromPlayerMapByDbGuid(guidLow);
}
else
- unit = handler.GetSelectedCreature();
+ creature = handler.GetSelectedCreature();
- if (!unit || unit.IsPet() || unit.IsTotem())
+ if (!creature || creature.IsPet() || creature.IsTotem())
{
handler.SendSysMessage(CypherStrings.SelectCreature);
return false;
}
- // Delete the creature
- unit.CombatStop();
- unit.DeleteFromDB();
- unit.AddObjectToRemoveList();
+ TempSummon summon = creature.ToTempSummon();
+ if (summon != null)
+ summon.UnSummon();
+ else
+ {
+ // Delete the creature
+ creature.CombatStop();
+ creature.DeleteFromDB();
+ creature.AddObjectToRemoveList();
+ }
handler.SendSysMessage(CypherStrings.CommandDelcreatmessage);
diff --git a/Source/Game/Chat/Commands/WPCommands.cs b/Source/Game/Chat/Commands/WPCommands.cs
index 76e82956b..182b71f88 100644
--- a/Source/Game/Chat/Commands/WPCommands.cs
+++ b/Source/Game/Chat/Commands/WPCommands.cs
@@ -559,7 +559,7 @@ namespace Game.Chat.Commands
creature.Dispose();
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
- creature = Creature.CreateCreatureFromDB(dbGuid, map);
+ creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
if (!creature)
{
handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1);
@@ -779,7 +779,7 @@ namespace Game.Chat.Commands
creature.Dispose();
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
- creature = Creature.CreateCreatureFromDB(dbGuid, map);
+ creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
if (!creature)
{
handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, id);
@@ -844,7 +844,7 @@ namespace Game.Chat.Commands
creature.CleanupsBeforeDelete();
creature.Dispose();
- creature = Creature.CreateCreatureFromDB(dbGuid, map);
+ creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
if (!creature)
{
handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1);
@@ -899,7 +899,7 @@ namespace Game.Chat.Commands
creature.CleanupsBeforeDelete();
creature.Dispose();
- creature = Creature.CreateCreatureFromDB(dbGuid, map);
+ creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
if (!creature)
{
handler.SendSysMessage(CypherStrings.WaypointNotcreated, 1);
diff --git a/Source/Game/Conditions/ConditionManager.cs b/Source/Game/Conditions/ConditionManager.cs
index faa6a7e3c..6fa742fbe 100644
--- a/Source/Game/Conditions/ConditionManager.cs
+++ b/Source/Game/Conditions/ConditionManager.cs
@@ -1328,7 +1328,7 @@ namespace Game
CreatureData creatureData = Global.ObjectMgr.GetCreatureData(cond.ConditionValue3);
if (creatureData != null)
{
- if (cond.ConditionValue2 != 0 && creatureData.id != cond.ConditionValue2)
+ if (cond.ConditionValue2 != 0 && creatureData.Id != cond.ConditionValue2)
{
Log.outError(LogFilter.Sql, "{0} has guid {1} set but does not match creature entry ({1}), skipped.", cond.ToString(true), cond.ConditionValue3, cond.ConditionValue2);
return false;
@@ -1349,10 +1349,10 @@ namespace Game
}
if (cond.ConditionValue3 != 0)
{
- GameObjectData goData = Global.ObjectMgr.GetGOData(cond.ConditionValue3);
+ GameObjectData goData = Global.ObjectMgr.GetGameObjectData(cond.ConditionValue3);
if (goData != null)
{
- if (cond.ConditionValue2 != 0 && goData.id != cond.ConditionValue2)
+ if (cond.ConditionValue2 != 0 && goData.Id != cond.ConditionValue2)
{
Log.outError(LogFilter.Sql, "{0} has guid {1} set but does not match gameobject entry ({1}), skipped.", cond.ToString(true), cond.ConditionValue3, cond.ConditionValue2);
return false;
diff --git a/Source/Game/Entities/Creature/Creature.Fields.cs b/Source/Game/Entities/Creature/Creature.Fields.cs
index 71319037c..949ff1a70 100644
--- a/Source/Game/Entities/Creature/Creature.Fields.cs
+++ b/Source/Game/Entities/Creature/Creature.Fields.cs
@@ -71,6 +71,7 @@ namespace Game.Entities
//Formation var
CreatureGroup m_formation;
bool TriggerJustRespawned;
+ bool m_respawnCompatibilityMode;
public uint[] m_spells = new uint[SharedConst.MaxCreatureSpells];
diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs
index ff9e4bf63..7b8db4187 100644
--- a/Source/Game/Entities/Creature/Creature.cs
+++ b/Source/Game/Entities/Creature/Creature.cs
@@ -123,45 +123,62 @@ namespace Game.Entities
if (GetDeathState() != DeathState.Corpse)
return;
- m_corpseRemoveTime = Time.UnixTime;
- SetDeathState(DeathState.Dead);
- RemoveAllAuras();
- DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
- loot.Clear();
- uint respawnDelay = m_respawnDelay;
- if (IsAIEnabled)
- GetAI().CorpseRemoved(respawnDelay);
-
- if (destroyForNearbyPlayers)
- DestroyForNearbyPlayers();
-
- // Should get removed later, just keep "compatibility" with scripts
- if (setSpawnTime)
- m_respawnTime = Math.Max(Time.UnixTime + respawnDelay, m_respawnTime);
-
- // 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(out x, out y, out z, out o);
-
- // We were spawned on transport, calculate real position
- if (IsSpawnedOnTransport())
+ if (m_respawnCompatibilityMode)
{
- Position pos = m_movementInfo.transport.pos;
- pos.posX = x;
- pos.posY = y;
- pos.posZ = z;
- pos.SetOrientation(o);
+ m_corpseRemoveTime = Time.UnixTime;
+ SetDeathState(DeathState.Dead);
+ RemoveAllAuras();
+ //DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
+ loot.Clear();
+ uint respawnDelay = m_respawnDelay;
+ if (IsAIEnabled)
+ GetAI().CorpseRemoved(respawnDelay);
- ITransport transport = GetDirectTransport();
- if (transport != null)
- transport.CalculatePassengerPosition(ref x, ref y, ref z, ref o);
+ if (destroyForNearbyPlayers)
+ DestroyForNearbyPlayers();
+
+ // Should get removed later, just keep "compatibility" with scripts
+ if (setSpawnTime)
+ m_respawnTime = Math.Max(Time.UnixTime + respawnDelay, m_respawnTime);
+
+ // 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(out x, out y, out z, out o);
+
+ // We were spawned on transport, calculate real position
+ if (IsSpawnedOnTransport())
+ {
+ Position pos = m_movementInfo.transport.pos;
+ pos.posX = x;
+ pos.posY = y;
+ pos.posZ = z;
+ pos.SetOrientation(o);
+
+ ITransport transport = GetDirectTransport();
+ if (transport != null)
+ transport.CalculatePassengerPosition(ref x, ref y, ref z, ref 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)
+ {
+ uint respawnDelay = m_respawnDelay;
+ m_respawnTime = Math.Max(Time.UnixTime + respawnDelay, m_respawnTime);
- SetHomePosition(x, y, z, o);
- GetMap().CreatureRelocation(this, x, y, z, o);
+ SaveRespawnTime(0, false);
+ }
+ AddObjectToRemoveList();
+ }
}
public bool InitEntry(uint entry, CreatureData data = null)
@@ -375,7 +392,7 @@ namespace Game.Entities
{
case DeathState.JustRespawned:
case DeathState.JustDied:
- Log.outError(LogFilter.Unit, "Creature ({0}) in wrong state: {2}", GetGUID().ToString(), m_deathState);
+ Log.outError(LogFilter.Unit, "Creature ({0}) in wrong state: {1}", GetGUID().ToString(), m_deathState);
break;
case DeathState.Dead:
{
@@ -383,22 +400,25 @@ namespace Game.Entities
if (m_respawnTime <= now)
{
// First check if there are any scripts that object to us respawning
- if (!Global.ScriptMgr.CanSpawn(GetSpawnId(), GetEntry(), GetCreatureTemplate(), GetCreatureData(), GetMap()))
- break; // Will be rechecked on next Update call
+ if (!Global.ScriptMgr.CanSpawn(GetSpawnId(), GetEntry(), GetCreatureData(), GetMap()))
+ {
+ m_respawnTime = now + RandomHelper.URand(4, 7);
+ break; // Will be rechecked on next Update call after delay expires
+ }
ObjectGuid dbtableHighGuid = ObjectGuid.Create(HighGuid.Creature, GetMapId(), GetEntry(), m_spawnId);
- long linkedRespawntime = GetMap().GetLinkedRespawnTime(dbtableHighGuid);
- if (linkedRespawntime == 0) // Can respawn
+ long linkedRespawnTime = GetMap().GetLinkedRespawnTime(dbtableHighGuid);
+ if (linkedRespawnTime == 0) // Can respawn
Respawn();
else // the master is dead
{
ObjectGuid targetGuid = Global.ObjectMgr.GetLinkedRespawnGuid(dbtableHighGuid);
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
- SetRespawnTime(Time.Day);
+ SetRespawnTime(Time.Week);
else
{
// else copy time from master and add a little
- long baseRespawnTime = Math.Max(linkedRespawntime, now);
+ long baseRespawnTime = Math.Max(linkedRespawnTime, now);
long offset = RandomHelper.URand(5, Time.Minute);
// linked guid can be a boss, uses std::numeric_limits::max to never respawn in that instance
@@ -731,7 +751,7 @@ namespace Game.Entities
lowGuid = map.GenerateLowGuid(HighGuid.Creature);
Creature creature = new Creature();
- if (!creature.Create(lowGuid, map, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), null, vehId))
+ if (!creature.Create(lowGuid, map, entry, pos, null, vehId))
return null;
return creature;
@@ -740,13 +760,13 @@ namespace Game.Entities
public static Creature CreateCreatureFromDB(ulong spawnId, Map map, bool addToMap = true, bool allowDuplicate = false)
{
Creature creature = new Creature();
- if (!creature.LoadCreatureFromDB(spawnId, map, addToMap, allowDuplicate))
+ if (!creature.LoadFromDB(spawnId, map, addToMap, allowDuplicate))
return null;
return creature;
}
- public bool Create(ulong guidlow, Map map, uint entry, float x, float y, float z, float ang, CreatureData data, uint vehId)
+ public bool Create(ulong guidlow, Map map, uint entry, Position pos, CreatureData data = null, uint vehId = 0, bool dynamic = false)
{
SetMap(map);
@@ -756,6 +776,10 @@ namespace Game.Entities
PhasingHandler.InitDbVisibleMapId(GetPhaseShift(), data.terrainSwapMap);
}
+ // Set if this creature can handle dynamic spawns
+ if (!dynamic)
+ SetRespawnCompatibilityMode();
+
CreatureTemplate cinfo = Global.ObjectMgr.GetCreatureTemplate(entry);
if (cinfo == null)
{
@@ -765,13 +789,13 @@ namespace Game.Entities
//! 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())
{
- Log.outError(LogFilter.Unit, "Creature.Create: given coordinates for creature (guidlow {0}, entry {1}) are not valid (X: {2}, Y: {3}, Z: {4}, O: {5})", guidlow, entry, x, y, z, ang);
+ Log.outError(LogFilter.Unit, $"Creature.Create: given coordinates for creature (guidlow {guidlow}, entry {entry}) are not valid ({pos})");
return false;
}
UpdatePositionData();
@@ -810,10 +834,8 @@ namespace Game.Entities
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
if (HasUnitMovementFlag(MovementFlag.Hover))
{
- z += m_unitData.HoverHeight;
-
//! Relocate again with updated Z coord
- Relocate(x, y, z, ang);
+ posZ += m_unitData.HoverHeight;
}
LastUsedScriptID = GetScriptId();
@@ -924,6 +946,14 @@ namespace Game.Entities
&& !GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoXpAtKill);
}
+ public bool IsEscortNPC(bool onlyIfActive = true)
+ {
+ if (!IsAIEnabled)
+ return false;
+
+ return GetAI().IsEscortNPC(onlyIfActive);
+ }
+
public override bool IsMovementPreventedByCasting()
{
// first check if currently a movement allowed channel is active and we're not casting
@@ -1068,16 +1098,20 @@ namespace Game.Entities
dynamicflags = 0;
}
- // data.guid = guid must not be updated at save
- data.id = GetEntry();
- data.mapid = (ushort)mapid;
+ if (data.spawnId == 0)
+ data.spawnId = m_spawnId;
+ Cypher.Assert(data.spawnId == m_spawnId);
+
+ data.Id = GetEntry();
data.displayid = displayId;
- data.equipmentId = GetCurrentEquipmentId();
- data.posX = GetPositionX();
- data.posY = GetPositionY();
- data.posZ = GetPositionZMinusOffset();
- data.orientation = GetOrientation();
- data.spawntimesecs = m_respawnDelay;
+ data.equipmentId = (sbyte)GetCurrentEquipmentId();
+
+ if (GetTransport() == null)
+ data.spawnPoint.WorldRelocate(this);
+ else
+ data.spawnPoint.WorldRelocate(mapid, GetTransOffsetX(), GetTransOffsetY(), GetTransOffsetZ(), GetTransOffsetO());
+
+ data.spawntimesecs = (int)m_respawnDelay;
// prevent add data integrity problems
data.spawndist = GetDefaultMovementType() == MovementGeneratorType.Idle ? 0.0f : m_respawnradius;
data.currentwaypoint = 0;
@@ -1092,6 +1126,8 @@ namespace Game.Entities
data.unit_flags2 = unitFlags2;
data.unit_flags3 = unitFlags3;
data.dynamicflags = (uint)dynamicflags;
+ if (data.spawnGroupData == null)
+ data.spawnGroupData = Global.ObjectMgr.GetDefaultSpawnGroup();
data.phaseId = GetDBPhase() > 0 ? (uint)GetDBPhase() : data.phaseId;
data.phaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.phaseGroup;
@@ -1414,7 +1450,7 @@ namespace Game.Entities
return;
}
- GetMap().RemoveCreatureRespawnTime(m_spawnId);
+ GetMap().RemoveRespawnTime(SpawnObjectType.Creature, m_spawnId);
Global.ObjectMgr.DeleteCreatureData(m_spawnId);
SQLTransaction trans = new SQLTransaction();
@@ -1423,6 +1459,11 @@ namespace Game.Entities
stmt.AddValue(0, m_spawnId);
trans.Append(stmt);
+ stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_SPAWNGROUP_MEMBER);
+ stmt.AddValue(0, (byte)SpawnObjectType.Creature);
+ stmt.AddValue(1, m_spawnId);
+ trans.Append(stmt);
+
stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_CREATURE_ADDON);
stmt.AddValue(0, m_spawnId);
trans.Append(stmt);
@@ -1549,14 +1590,31 @@ namespace Game.Entities
if (s == DeathState.JustDied)
{
m_corpseRemoveTime = Time.UnixTime + m_corpseDelay;
- if (IsDungeonBoss() && m_respawnDelay == 0)
- m_respawnTime = long.MaxValue; // never respawn in this instance
+ uint respawnDelay = m_respawnDelay;
+ uint scalingMode = WorldConfig.GetUIntValue(WorldCfg.RespawnDynamicMode);
+ if (scalingMode != 0)
+ GetMap().ApplyDynamicModeRespawnScaling(this, m_spawnId, ref 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 == 0)
+ m_respawnTime = long.MaxValue; // never respawn in this instance
+ else
+ m_respawnTime = Time.UnixTime + respawnDelay + m_corpseDelay;
+ }
else
- m_respawnTime = Time.UnixTime + m_respawnDelay + m_corpseDelay;
+ {
+ if (IsDungeonBoss() && m_respawnDelay == 0)
+ m_respawnTime = long.MaxValue; // never respawn in this instance
+ else
+ m_respawnTime = Time.UnixTime + respawnDelay;
+ }
// always save boss respawn time at death to prevent crash cheating
if (WorldConfig.GetBoolValue(WorldCfg.SaveRespawnTimeImmediately) || IsWorldBoss())
SaveRespawnTime();
+ else if (!m_respawnCompatibilityMode)
+ SaveRespawnTime(0, false);
ReleaseFocus(null, false); // remove spellcast focus
DoNotReacquireTarget(); // cancel delayed re-target
@@ -1632,8 +1690,6 @@ namespace Game.Entities
public void Respawn(bool force = false)
{
- DestroyForNearbyPlayers();
-
if (force)
{
if (IsAlive())
@@ -1642,49 +1698,60 @@ namespace Game.Entities
SetDeathState(DeathState.Corpse);
}
- RemoveCorpse(false, false);
-
- if (GetDeathState() == DeathState.Dead)
+ if (m_respawnCompatibilityMode)
{
- if (m_spawnId != 0)
- GetMap().RemoveCreatureRespawnTime(m_spawnId);
+ DestroyForNearbyPlayers();
+ RemoveCorpse(false, false);
- Log.outDebug(LogFilter.Unit, "Respawning creature {0} ({1})", GetName(), GetGUID().ToString());
- m_respawnTime = 0;
- ResetPickPocketRefillTimer();
- loot.Clear();
+ if (GetDeathState() == DeathState.Dead)
+ {
+ if (m_spawnId != 0)
+ GetMap().RemoveRespawnTime(SpawnObjectType.Creature, m_spawnId);
+
+ Log.outDebug(LogFilter.Unit, "Respawning creature {0} ({1})", GetName(), GetGUID().ToString());
+ m_respawnTime = 0;
+ ResetPickPocketRefillTimer();
+ loot.Clear();
+
+ if (m_originalEntry != GetEntry())
+ UpdateEntry(m_originalEntry);
- if (m_originalEntry != GetEntry())
- UpdateEntry(m_originalEntry);
- else
SelectLevel();
- SetDeathState(DeathState.JustRespawned);
+ SetDeathState(DeathState.JustRespawned);
- CreatureModel display = new CreatureModel(GetNativeDisplayId(), GetNativeDisplayScale(), 1.0f);
- if (Global.ObjectMgr.GetCreatureModelRandomGender(ref display, GetCreatureTemplate()) != null)
- {
- SetDisplayId(display.CreatureDisplayID, display.DisplayScale);
- SetNativeDisplayId(display.CreatureDisplayID, display.DisplayScale);
+ CreatureModel display = new CreatureModel(GetNativeDisplayId(), GetNativeDisplayScale(), 1.0f);
+ if (Global.ObjectMgr.GetCreatureModelRandomGender(ref display, GetCreatureTemplate()) != null)
+ {
+ SetDisplayId(display.CreatureDisplayID, display.DisplayScale);
+ SetNativeDisplayId(display.CreatureDisplayID, display.DisplayScale);
+ }
+
+ GetMotionMaster().InitDefault();
+ //Re-initialize reactstate that could be altered by movementgenerators
+ InitializeReactState();
+
+ //Call AI respawn virtual function//Call AI respawn virtual function
+ if (IsAIEnabled)
+ {
+ //reset the AI to be sure no dirty or uninitialized values will be used till next tick
+ GetAI().Reset();
+ TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
+ }
+
+ uint poolid = GetSpawnId() != 0 ? Global.PoolMgr.IsPartOfAPool(GetSpawnId()) : 0;
+ if (poolid != 0)
+ Global.PoolMgr.UpdatePool(poolid, GetSpawnId());
}
-
- GetMotionMaster().InitDefault();
- //Re-initialize reactstate that could be altered by movementgenerators
- InitializeReactState();
-
- //Call AI respawn virtual function
- if (IsAIEnabled)
- {
- GetAI().Reset();
- TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
- }
-
- uint poolid = GetSpawnId() != 0 ? Global.PoolMgr.IsPartOfAPool(GetSpawnId()) : 0;
- if (poolid != 0)
- Global.PoolMgr.UpdatePool(poolid, GetSpawnId());
+ UpdateObjectVisibility();
+ }
+ else
+ {
+ if (m_spawnId != 0)
+ GetMap().RemoveRespawnTime(SpawnObjectType.Creature, m_spawnId, true);
}
- UpdateObjectVisibility();
+ Log.outDebug(LogFilter.Unit, $"Respawning creature {GetName()} ({GetGUID()})");
}
public void ForcedDespawn(uint timeMSToDespawn = 0, TimeSpan forceRespawnTimer = default)
@@ -1695,30 +1762,49 @@ namespace Game.Entities
return;
}
- uint corpseDelay = GetCorpseDelay();
- uint respawnDelay = GetRespawnDelay();
-
- // do it before killing creature
- DestroyForNearbyPlayers();
-
- bool overrideRespawnTime = false;
- if (IsAlive())
+ if (m_respawnCompatibilityMode)
{
- if (forceRespawnTimer > TimeSpan.Zero)
+ uint corpseDelay = GetCorpseDelay();
+ uint respawnDelay = GetRespawnDelay();
+
+ // do it before killing creature
+ DestroyForNearbyPlayers();
+
+ bool overrideRespawnTime = false;
+ if (IsAlive())
{
- SetCorpseDelay(0);
- SetRespawnDelay((uint)forceRespawnTimer.TotalSeconds);
- overrideRespawnTime = false;
+ if (forceRespawnTimer > TimeSpan.Zero)
+ {
+ SetCorpseDelay(0);
+ SetRespawnDelay((uint)forceRespawnTimer.TotalSeconds);
+ overrideRespawnTime = false;
+ }
+
+ SetDeathState(DeathState.JustDied);
}
- SetDeathState(DeathState.JustDied);
+ // Skip corpse decay time
+ RemoveCorpse(overrideRespawnTime, false);
+
+ SetCorpseDelay(corpseDelay);
+ SetRespawnDelay(respawnDelay);
}
+ else
+ {
+ if (forceRespawnTimer > TimeSpan.Zero)
+ SaveRespawnTime((uint)forceRespawnTimer.TotalSeconds);
+ else
+ {
+ uint respawnDelay = m_respawnDelay;
+ uint scalingMode = WorldConfig.GetUIntValue(WorldCfg.RespawnDynamicMode);
+ if (scalingMode != 0)
+ GetMap().ApplyDynamicModeRespawnScaling(this, m_spawnId, ref respawnDelay, scalingMode);
+ m_respawnTime = Time.UnixTime + respawnDelay;
+ SaveRespawnTime();
+ }
- // Skip corpse decay time
- RemoveCorpse(overrideRespawnTime, false);
-
- SetCorpseDelay(corpseDelay);
- SetRespawnDelay(respawnDelay);
+ AddObjectToRemoveList();
+ }
}
public void DespawnOrUnsummon(TimeSpan time, TimeSpan forceRespawnTimer = default) { DespawnOrUnsummon((uint)time.TotalMilliseconds, forceRespawnTimer); }
@@ -2074,12 +2160,19 @@ namespace Game.Entities
return false;
}
- public override void SaveRespawnTime()
+ public override void SaveRespawnTime(uint forceDelay = 0, bool saveToDb = true)
{
if (IsSummon() || m_spawnId == 0 || (m_creatureData != null && !m_creatureData.dbData))
return;
- GetMap().SaveCreatureRespawnTime(m_spawnId, m_respawnTime);
+ if (m_respawnCompatibilityMode)
+ {
+ GetMap().SaveRespawnTimeDB(SpawnObjectType.Creature, m_spawnId, m_respawnTime);
+ return;
+ }
+
+ uint thisRespawnTime = (uint)(forceDelay != 0 ? Time.UnixTime + forceDelay : m_respawnTime);
+ GetMap().SaveRespawnTime(SpawnObjectType.Creature, m_spawnId, GetEntry(), thisRespawnTime, GetMap().GetZoneId(GetPhaseShift(), GetHomePosition()), GridDefines.ComputeGridCoord(GetHomePosition().GetPositionX(), GetHomePosition().GetPositionY()).GetId(), m_creatureData.dbData && saveToDb);
}
public bool CanCreatureAttack(Unit victim, bool force = true)
@@ -2293,32 +2386,20 @@ namespace Game.Entities
}
public void GetRespawnPosition(out float x, out float y, out float z, out float ori, out float dist)
{
- // for npcs on transport, this will return transport offset
- if (m_spawnId != 0)
+ if (m_creatureData != null)
{
- CreatureData data = Global.ObjectMgr.GetCreatureData(GetSpawnId());
- if (data != null)
- {
- x = data.posX;
- y = data.posY;
- z = data.posZ;
- ori = data.orientation;
- dist = data.spawndist;
-
- return;
- }
+ m_creatureData.spawnPoint.GetPosition(out x, out y, out z, out ori);
+ dist = m_creatureData.spawndist;
+ }
+ else
+ {
+ Position homePos = GetHomePosition();
+ homePos.GetPosition(out x, out y, out z, out ori);
+ 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();
- ori = homePos.GetOrientation();
- dist = 0;
}
- bool IsSpawnedOnTransport() { return m_creatureData != null && m_creatureData.mapid != GetMapId(); }
+ bool IsSpawnedOnTransport() { return m_creatureData != null && m_creatureData.spawnPoint.GetMapId() != GetMapId(); }
public void AllLootRemovedFromCorpse()
{
@@ -2934,12 +3015,7 @@ namespace Game.Entities
public CreatureTemplate GetCreatureTemplate() { return m_creatureInfo; }
public CreatureData GetCreatureData() { return m_creatureData; }
- public override bool LoadFromDB(ulong spawnId, Map map)
- {
- return LoadCreatureFromDB(spawnId, map, false, false);
- }
-
- public bool LoadCreatureFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate)
+ public override bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate)
{
if (!allowDuplicate)
{
@@ -2976,39 +3052,48 @@ namespace Game.Entities
}
m_spawnId = spawnId;
+ m_respawnCompatibilityMode = data.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.CompatibilityMode);
m_creatureData = data;
m_respawnradius = data.spawndist;
- m_respawnDelay = data.spawntimesecs;
- if (!Create(map.GenerateLowGuid(HighGuid.Creature), map, data.id, data.posX, data.posY, data.posZ, data.orientation, data, 0))
+ m_respawnDelay = (uint)data.spawntimesecs;
+ // 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 == 0 && !Global.ScriptMgr.CanSpawn(spawnId, data.Id, data, map))
+ {
+ SaveRespawnTime(RandomHelper.URand(4, 7));
+ return false;
+ }
+
+ if (!Create(map.GenerateLowGuid(HighGuid.Creature), map, data.Id, data.spawnPoint, data, 0, !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 = 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 == 0 && !Global.ScriptMgr.CanSpawn(spawnId, GetEntry(), GetCreatureTemplate(), GetCreatureData(), map))
- m_respawnTime = Time.UnixTime + 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 == 0 && !Global.ScriptMgr.CanSpawn(spawnId, GetEntry(), GetCreatureData(), map))
+ m_respawnTime = Time.UnixTime + RandomHelper.URand(4, 7);
- if (m_respawnTime != 0) // respawn on Update
+ if (m_respawnTime != 0) // respawn on UpdateLoadCreatureFromDB
{
m_deathState = DeathState.Dead;
if (CanFly())
{
- float tz = map.GetHeight(GetPhaseShift(), data.posX, data.posY, data.posZ, true, MapConst.MaxFallDistance);
- if (data.posZ - tz > 0.1f && GridDefines.IsValidMapCoord(tz))
- Relocate(data.posX, data.posY, tz);
+ float tz = map.GetHeight(GetPhaseShift(), data.spawnPoint, true, MapConst.MaxFallDistance);
+ if (data.spawnPoint.GetPositionZ() - tz > 0.1f && GridDefines.IsValidMapCoord(tz))
+ Relocate(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY(), tz);
}
}
SetSpawnHealth();
+ // checked at creature_template loading
DefaultMovementType = (MovementGeneratorType)data.movementType;
- loot.SetGUID(ObjectGuid.Create(HighGuid.LootObject, data.mapid, data.id, GetMap().GenerateLowGuid(HighGuid.LootObject)));
+ loot.SetGUID(ObjectGuid.Create(HighGuid.LootObject, GetMapId(), data.Id, GetMap().GenerateLowGuid(HighGuid.LootObject)));
if (addToMap && !GetMap().AddToMap(this))
return false;
@@ -3102,6 +3187,10 @@ namespace Game.Entities
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; }
+ public bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; }
+
public Unit SelectVictim()
{
// function provides main threat functionality
diff --git a/Source/Game/Entities/Creature/CreatureData.cs b/Source/Game/Entities/Creature/CreatureData.cs
index c3018285b..d2b501bf4 100644
--- a/Source/Game/Entities/Creature/CreatureData.cs
+++ b/Source/Game/Entities/Creature/CreatureData.cs
@@ -21,6 +21,7 @@ using System;
using System.Collections.Generic;
using Framework.Dynamic;
using Game.Networking.Packets;
+using Game.Maps;
namespace Game.Entities
{
@@ -327,34 +328,22 @@ namespace Game.Entities
public EquipmentItem[] Items = new EquipmentItem[SharedConst.MaxEquipmentItems];
}
- public class CreatureData
+ public class CreatureData : SpawnData
{
- public uint id; // entry in creature_template
- public ushort mapid;
public uint displayid;
- public int equipmentId;
- public float posX;
- public float posY;
- public float posZ;
- public float orientation;
- public uint spawntimesecs;
+ public sbyte equipmentId;
public float spawndist;
public uint currentwaypoint;
public uint curhealth;
public uint curmana;
public byte movementType;
- public List spawnDifficulties = new List();
public ulong npcflag;
- public uint unit_flags; // enum UnitFlags mask values
- public uint unit_flags2; // enum UnitFlags2 mask values
- public uint unit_flags3; // enum UnitFlags3 mask values
+ public uint unit_flags; // enum UnitFlags mask values
+ public uint unit_flags2; // enum UnitFlags2 mask values
+ public uint unit_flags3; // enum UnitFlags3 mask values
public uint dynamicflags;
- public PhaseUseFlagsValues phaseUseFlags;
- public uint phaseId;
- public uint phaseGroup;
- public int terrainSwapMap;
- public uint ScriptId;
- public bool dbData;
+
+ public CreatureData() : base(SpawnObjectType.Creature) { }
}
public class CreatureModelInfo
diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs
index 0e892bb24..3761a8374 100644
--- a/Source/Game/Entities/GameObject/GameObject.cs
+++ b/Source/Game/Entities/GameObject/GameObject.cs
@@ -166,7 +166,7 @@ namespace Game.Entities
return null;
GameObject go = new GameObject();
- if (!go.Create(entry, map, pos, rotation, animProgress, goState, artKit))
+ if (!go.Create(entry, map, pos, rotation, animProgress, goState, artKit, false, 0))
return null;
return go;
@@ -175,13 +175,13 @@ namespace Game.Entities
public static GameObject CreateGameObjectFromDB(ulong spawnId, Map map, bool addToMap = true)
{
GameObject go = new GameObject();
- if (!go.LoadGameObjectFromDB(spawnId, map, addToMap))
+ if (!go.LoadFromDB(spawnId, map, addToMap))
return null;
return go;
}
- bool Create(uint entry, Map map, Position pos, Quaternion rotation, uint animProgress, GameObjectState goState, uint artKit)
+ bool Create(uint entry, Map map, Position pos, Quaternion rotation, uint animProgress, GameObjectState goState, uint artKit, bool dynamic, ulong spawnid)
{
Cypher.Assert(map);
SetMap(map);
@@ -194,6 +194,10 @@ namespace Game.Entities
return false;
}
+ // Set if this object can handle dynamic spawns
+ if (!dynamic)
+ SetRespawnCompatibilityMode();
+
UpdatePositionData();
SetZoneScript();
@@ -373,6 +377,9 @@ namespace Game.Entities
if (map.Is25ManRaid())
loot.maxDuplicates = 3;
+ if (spawnid != 0)
+ m_spawnId = spawnid;
+
uint linkedEntry = GetGoInfo().GetLinkedGameObjectEntry();
if (linkedEntry != 0)
{
@@ -507,81 +514,88 @@ namespace Game.Entities
goto case LootState.Ready;
case LootState.Ready:
{
- if (m_respawnTime > 0) // timer on
+ if (m_respawnCompatibilityMode)
{
- long now = Time.UnixTime;
- if (m_respawnTime <= now) // timer expired
+ if (m_respawnTime > 0) // timer on
{
- ObjectGuid dbtableHighGuid = ObjectGuid.Create(HighGuid.GameObject, GetMapId(), GetEntry(), m_spawnId);
- long linkedRespawntime = GetMap().GetLinkedRespawnTime(dbtableHighGuid);
- if (linkedRespawntime != 0) // Can't respawn, the master is dead
+ long now = Time.UnixTime;
+ if (m_respawnTime <= now) // timer expired
{
- ObjectGuid targetGuid = Global.ObjectMgr.GetLinkedRespawnGuid(dbtableHighGuid);
- if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
- SetRespawnTime(Time.Day);
- else
- m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + RandomHelper.IRand(5, Time.Minute); // else copy time from master and add a little
- SaveRespawnTime(); // also save to DB immediately
- return;
- }
+ ObjectGuid dbtableHighGuid = ObjectGuid.Create(HighGuid.GameObject, GetMapId(), GetEntry(), m_spawnId);
+ long linkedRespawntime = GetMap().GetLinkedRespawnTime(dbtableHighGuid);
+ if (linkedRespawntime != 0) // Can't respawn, the master is dead
+ {
+ ObjectGuid targetGuid = Global.ObjectMgr.GetLinkedRespawnGuid(dbtableHighGuid);
+ if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
+ SetRespawnTime(Time.Week);
+ else
+ m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + RandomHelper.IRand(5, Time.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
- GameObject linkedTrap = GetLinkedTrap();
- if (linkedTrap)
- linkedTrap.SetLootState(LootState.Ready);
+ // If nearby linked trap exists, respawn it
+ GameObject linkedTrap = GetLinkedTrap();
+ if (linkedTrap)
+ linkedTrap.SetLootState(LootState.Ready);
- switch (GetGoType())
- {
- case GameObjectTypes.FishingNode: // can't fish now
- {
- Unit caster = GetOwner();
- if (caster != null && caster.IsTypeId(TypeId.Player))
+ switch (GetGoType())
+ {
+ case GameObjectTypes.FishingNode: // can't fish now
{
- caster.ToPlayer().RemoveGameObject(this, false);
- caster.ToPlayer().SendPacket(new FishEscaped());
+ Unit caster = GetOwner();
+ if (caster != null && caster.IsTypeId(TypeId.Player))
+ {
+ caster.ToPlayer().RemoveGameObject(this, false);
+ caster.ToPlayer().SendPacket(new FishEscaped());
+ }
+ // can be delete
+ m_lootState = LootState.JustDeactivated;
+ return;
}
- // can be delete
- m_lootState = LootState.JustDeactivated;
- return;
- }
- case GameObjectTypes.Door:
- case GameObjectTypes.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() != GameObjectState.Ready)
- ResetDoorOrButton();
- break;
- case GameObjectTypes.FishingHole:
- // Initialize a new max fish count on respawn
- m_goValue.FishingHole.MaxOpens = RandomHelper.URand(GetGoInfo().FishingHole.minRestock, GetGoInfo().FishingHole.maxRestock);
- break;
- default:
- break;
+ case GameObjectTypes.Door:
+ case GameObjectTypes.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() != GameObjectState.Ready)
+ ResetDoorOrButton();
+ break;
+ case GameObjectTypes.FishingHole:
+ // Initialize a new max fish count on respawn
+ m_goValue.FishingHole.MaxOpens = RandomHelper.URand(GetGoInfo().FishingHole.minRestock, GetGoInfo().FishingHole.maxRestock);
+ break;
+ default:
+ break;
+ }
+
+ if (!m_spawnedByDefault) // despawn timer
+ {
+ // can be despawned or destroyed
+ SetLootState(LootState.JustDeactivated);
+ return;
+ }
+
+ // Call AI Reset (required for example in SmartAI to clear one time events)
+ if (GetAI() != null)
+ GetAI().Reset();
+
+ // respawn timer
+ uint poolid = GetSpawnId() != 0 ? Global.PoolMgr.IsPartOfAPool(GetSpawnId()) : 0;
+ if (poolid != 0)
+ Global.PoolMgr.UpdatePool(poolid, GetSpawnId());
+ else
+ GetMap().AddToMap(this);
}
-
- if (!m_spawnedByDefault) // despawn timer
- {
- // can be despawned or destroyed
- SetLootState(LootState.JustDeactivated);
- return;
- }
-
- // Call AI Reset (required for example in SmartAI to clear one time events)
- if (GetAI() != null)
- GetAI().Reset();
-
- // respawn timer
- uint poolid = GetSpawnId() != 0 ? Global.PoolMgr.IsPartOfAPool(GetSpawnId()) : 0;
- if (poolid != 0)
- Global.PoolMgr.UpdatePool(poolid, GetSpawnId());
- else
- GetMap().AddToMap(this);
}
}
+ // Set respawn timer
+ if (!m_respawnCompatibilityMode && m_respawnTime > 0)
+ SaveRespawnTime(0, false);
+
if (IsSpawned())
{
GameObjectTemplate goInfo = GetGoInfo();
@@ -785,6 +799,7 @@ namespace Game.Entities
if (m_respawnDelayTime == 0)
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;
@@ -792,12 +807,29 @@ namespace Game.Entities
return;
}
- m_respawnTime = Time.UnixTime + m_respawnDelayTime;
+ uint respawnDelay = m_respawnDelayTime;
+ uint scalingMode = WorldConfig.GetUIntValue(WorldCfg.RespawnDynamicMode);
+ if (scalingMode != 0)
+ GetMap().ApplyDynamicModeRespawnScaling(this, m_spawnId, ref respawnDelay, scalingMode);
+ m_respawnTime = Time.UnixTime + respawnDelay;
// if option not set then object will be saved at grid unload
+ // Otherwise just save respawn time to map object memory
if (WorldConfig.GetBoolValue(WorldCfg.SaveRespawnTimeImmediately))
SaveRespawnTime();
+ if (!m_respawnCompatibilityMode)
+ {
+ // Respawn time was just saved if set to save to DB
+ // If not, we save only to map memory
+ if (!WorldConfig.GetBoolValue(WorldCfg.SaveRespawnTimeImmediately))
+ SaveRespawnTime(0, false);
+
+ // Then despawn
+ AddObjectToRemoveList();
+ return;
+ }
+
DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
break;
}
@@ -890,7 +922,7 @@ namespace Game.Entities
{
// 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 data = Global.ObjectMgr.GetGOData(m_spawnId);
+ GameObjectData data = Global.ObjectMgr.GetGameObjectData(m_spawnId);
if (data == null)
{
Log.outError(LogFilter.Maps, "GameObject.SaveToDB failed, cannot get gameobject data!");
@@ -911,22 +943,22 @@ namespace Game.Entities
m_spawnId = Global.ObjectMgr.GenerateGameObjectSpawnId();
// update in loaded data (changing data only in this place)
- GameObjectData data = new GameObjectData();
+ GameObjectData data = Global.ObjectMgr.NewOrExistGameObjectData(m_spawnId);
- // guid = guid must not be updated at save
- data.id = GetEntry();
- data.mapid = (ushort)mapid;
- data.posX = GetPositionX();
- data.posY = GetPositionY();
- data.posZ = GetPositionZ();
- data.orientation = GetOrientation();
+ if (data.spawnId == 0)
+ data.spawnId = m_spawnId;
+ Cypher.Assert(data.spawnId == m_spawnId);
+
+ data.Id = GetEntry();
+ data.spawnPoint.WorldRelocate(this);
data.rotation = m_worldRotation;
data.spawntimesecs = (int)(m_spawnedByDefault ? m_respawnDelayTime : -m_respawnDelayTime);
data.animprogress = GetGoAnimProgress();
- data.go_state = GetGoState();
+ data.goState = GetGoState();
data.spawnDifficulties = spawnDifficulties;
data.artKit = (byte)GetGoArtKit();
- Global.ObjectMgr.NewGOData(m_spawnId, data);
+ if (data.spawnGroupData == null)
+ data.spawnGroupData = Global.ObjectMgr.GetDefaultSpawnGroup();
data.phaseId = GetDBPhase() > 0 ? (uint)GetDBPhase() : data.phaseId;
data.phaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.phaseGroup;
@@ -958,24 +990,24 @@ namespace Game.Entities
DB.World.Execute(stmt);
}
- bool LoadGameObjectFromDB(ulong spawnId, Map map, bool addToMap)
+ public override bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool unused = true)
{
- GameObjectData data = Global.ObjectMgr.GetGOData(spawnId);
+ GameObjectData data = Global.ObjectMgr.GetGameObjectData(spawnId);
if (data == null)
{
Log.outError(LogFilter.Maps, "Gameobject (SpawnId: {0}) not found in table `gameobject`, can't load. ", spawnId);
return false;
}
- uint entry = data.id;
- Position pos = new Position(data.posX, data.posY, data.posZ, data.orientation);
+ uint entry = data.Id;
uint animprogress = data.animprogress;
- GameObjectState go_state = data.go_state;
+ GameObjectState go_state = data.goState;
uint artKit = data.artKit;
m_spawnId = spawnId;
- if (!Create(entry, map, pos, data.rotation, animprogress, go_state, artKit))
+ m_respawnCompatibilityMode = ((data.spawnGroupData.flags & SpawnGroupFlags.CompatibilityMode) != 0);
+ if (!Create(entry, map, data.spawnPoint, data.rotation, animprogress, go_state, artKit, !m_respawnCompatibilityMode, spawnId))
return false;
PhasingHandler.InitDbPhaseShift(GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup);
@@ -1000,7 +1032,7 @@ namespace Game.Entities
if (m_respawnTime != 0 && m_respawnTime <= Time.UnixTime)
{
m_respawnTime = 0;
- GetMap().RemoveGORespawnTime(m_spawnId);
+ GetMap().RemoveRespawnTime(SpawnObjectType.GameObject, m_spawnId);
}
}
}
@@ -1021,20 +1053,20 @@ namespace Game.Entities
public void DeleteFromDB()
{
- GetMap().RemoveGORespawnTime(m_spawnId);
- Global.ObjectMgr.DeleteGOData(m_spawnId);
+ GetMap().RemoveRespawnTime(SpawnObjectType.GameObject, m_spawnId);
+ Global.ObjectMgr.DeleteGameObjectData(m_spawnId);
+
+ SQLTransaction trans = new SQLTransaction();
PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_GAMEOBJECT);
-
stmt.AddValue(0, m_spawnId);
-
- DB.World.Execute(stmt);
+ trans.Append(stmt);
stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_EVENT_GAMEOBJECT);
-
stmt.AddValue(0, m_spawnId);
+ trans.Append(stmt);
- DB.World.Execute(stmt);
+ DB.World.CommitTransaction(trans);
}
public override bool HasQuest(uint quest_id)
@@ -1097,10 +1129,19 @@ namespace Game.Entities
return Global.ObjAccessor.GetUnit(this, GetOwnerGUID());
}
- public override void SaveRespawnTime()
+ public override void SaveRespawnTime(uint forceDelay = 0, bool savetodb = true)
{
- if (m_goData != null && m_goData.dbData && m_respawnTime > Time.UnixTime && m_spawnedByDefault)
- GetMap().SaveGORespawnTime(m_spawnId, m_respawnTime);
+ if (m_goData != null && m_respawnTime > Time.UnixTime && m_spawnedByDefault)
+ {
+ if (m_respawnCompatibilityMode)
+ {
+ GetMap().SaveRespawnTimeDB(SpawnObjectType.GameObject, m_spawnId, m_respawnTime);
+ return;
+ }
+
+ uint thisRespawnTime = (uint)(forceDelay != 0 ? Time.UnixTime + forceDelay : m_respawnTime);
+ GetMap().SaveRespawnTime(SpawnObjectType.GameObject, m_spawnId, GetEntry(), thisRespawnTime, GetZoneId(), GridDefines.ComputeGridCoord(GetPositionX(), GetPositionY()).GetId(), m_goData.dbData ? savetodb : false);
+ }
}
public override bool IsNeverVisibleFor(WorldObject seer)
@@ -1160,7 +1201,7 @@ namespace Game.Entities
if (m_spawnedByDefault && m_respawnTime > 0)
{
m_respawnTime = Time.UnixTime;
- GetMap().RemoveGORespawnTime(m_spawnId);
+ GetMap().RemoveRespawnTime(SpawnObjectType.GameObject, m_spawnId, true);
}
}
@@ -1262,7 +1303,7 @@ namespace Game.Entities
public void SetGoArtKit(byte kit)
{
SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.ArtKit), kit);
- GameObjectData data = Global.ObjectMgr.GetGOData(m_spawnId);
+ GameObjectData data = Global.ObjectMgr.GetGameObjectData(m_spawnId);
if (data != null)
data.artKit = kit;
}
@@ -1273,10 +1314,10 @@ namespace Game.Entities
if (go != null)
{
go.SetGoArtKit(artkit);
- data = go.GetGoData();
+ data = go.GetGameObjectData();
}
else if (lowguid != 0)
- data = Global.ObjectMgr.GetGOData(lowguid);
+ data = Global.ObjectMgr.GetGameObjectData(lowguid);
if (data != null)
data.artKit = artkit;
@@ -2075,7 +2116,7 @@ namespace Game.Entities
public uint GetScriptId()
{
- GameObjectData gameObjectData = GetGoData();
+ GameObjectData gameObjectData = GetGameObjectData();
if (gameObjectData != null)
{
uint scriptId = gameObjectData.ScriptId;
@@ -2548,23 +2589,10 @@ namespace Game.Entities
public void GetRespawnPosition(out float x, out float y, out float z, out float ori)
{
- if (m_spawnId != 0)
- {
- GameObjectData data = Global.ObjectMgr.GetGOData(GetSpawnId());
- if (data != null)
- {
- x = posX;
- y = posY;
- z = posZ;
- ori = data.orientation;
- return;
- }
- }
-
- x = GetPositionX();
- y = GetPositionY();
- z = GetPositionZ();
- ori = GetOrientation();
+ if (m_goData != null)
+ m_goData.spawnPoint.GetPosition(out x, out y, out z, out ori);
+ else
+ GetPosition(out x, out y, out z, out ori);
}
public float GetInteractionDistance()
@@ -2624,18 +2652,13 @@ namespace Game.Entities
public GameObjectTemplate GetGoInfo() { return m_goInfo; }
public GameObjectTemplateAddon GetTemplateAddon() { return m_goTemplateAddon; }
- public GameObjectData GetGoData() { return m_goData; }
+ public GameObjectData GetGameObjectData() { return m_goData; }
public GameObjectValue GetGoValue() { return m_goValue; }
public ulong GetSpawnId() { return m_spawnId; }
public long GetPackedWorldRotation() { return m_packedRotation; }
- public override bool LoadFromDB(ulong spawnId, Map map)
- {
- return LoadGameObjectFromDB(spawnId, map, false);
- }
-
public void SetOwnerGUID(ObjectGuid owner)
{
// Owner already found and different than expected owner - remove object from old owner
@@ -2768,6 +2791,10 @@ namespace Game.Entities
AddFlag(GameObjectFlags.MapObject);
}
+ // 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; }
+ public bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; }
+
#region Fields
protected GameObjectFieldData m_gameObjectData;
protected GameObjectValue m_goValue;
@@ -2799,6 +2826,7 @@ namespace Game.Entities
public Position StationaryPosition { get; set; }
GameObjectAI m_AI;
+ bool m_respawnCompatibilityMode;
ushort _animKitId;
uint _worldEffectID;
diff --git a/Source/Game/Entities/GameObject/GameObjectData.cs b/Source/Game/Entities/GameObject/GameObjectData.cs
index 41d3bb380..1b91e00d3 100644
--- a/Source/Game/Entities/GameObject/GameObjectData.cs
+++ b/Source/Game/Entities/GameObject/GameObjectData.cs
@@ -21,6 +21,7 @@ using Framework.GameMath;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Game.Networking.Packets;
+using Game.Maps;
namespace Game.Entities
{
@@ -1274,25 +1275,13 @@ namespace Game.Entities
public uint WorldEffectID;
}
- public class GameObjectData
+ public class GameObjectData : SpawnData
{
- public uint id; // entry in gamobject_template
- public ushort mapid;
- public float posX;
- public float posY;
- public float posZ;
- public float orientation;
public Quaternion rotation;
- public int spawntimesecs;
public uint animprogress;
- public GameObjectState go_state;
- public List spawnDifficulties = new List();
+ public GameObjectState goState;
public byte artKit;
- public PhaseUseFlagsValues phaseUseFlags;
- public uint phaseId;
- public uint phaseGroup;
- public int terrainSwapMap;
- public uint ScriptId;
- public bool dbData = true;
+
+ public GameObjectData() : base(SpawnObjectType.GameObject) { }
}
}
diff --git a/Source/Game/Entities/Object/ObjectGuid.cs b/Source/Game/Entities/Object/ObjectGuid.cs
index ff7c66f29..0bb1022f7 100644
--- a/Source/Game/Entities/Object/ObjectGuid.cs
+++ b/Source/Game/Entities/Object/ObjectGuid.cs
@@ -345,6 +345,10 @@ namespace Game.Entities
{
if (_nextGuid >= ObjectGuid.GetMaxCounter(_highGuid) - 1)
HandleCounterOverflow();
+
+ if (_highGuid == HighGuid.Creature || _highGuid == HighGuid.Vehicle || _highGuid == HighGuid.GameObject || _highGuid == HighGuid.Transport)
+ CheckGuidTrigger(_nextGuid);
+
return _nextGuid++;
}
@@ -355,5 +359,13 @@ namespace Game.Entities
Log.outFatal(LogFilter.Server, "{0} guid overflow!! Can't continue, shutting down server. ", _highGuid);
Global.WorldMgr.StopNow();
}
+
+ void CheckGuidTrigger(ulong guidlow)
+ {
+ if (!Global.WorldMgr.IsGuidAlert() && guidlow > WorldConfig.GetUInt64Value(WorldCfg.RespawnGuidAlertLevel))
+ Global.WorldMgr.TriggerGuidAlert();
+ else if (!Global.WorldMgr.IsGuidWarning() && guidlow > WorldConfig.GetUInt64Value(WorldCfg.RespawnGuidWarnLevel))
+ Global.WorldMgr.TriggerGuidWarning();
+ }
}
}
diff --git a/Source/Game/Entities/Object/Position.cs b/Source/Game/Entities/Object/Position.cs
index 88b2969f9..5cf5b5b94 100644
--- a/Source/Game/Entities/Object/Position.cs
+++ b/Source/Game/Entities/Object/Position.cs
@@ -364,6 +364,12 @@ namespace Game.Entities
Relocate(pos);
}
+ public void WorldRelocate(uint mapId, Position pos)
+ {
+ _mapId = mapId;
+ Relocate(pos);
+ }
+
public void WorldRelocate(WorldLocation loc)
{
_mapId = loc._mapId;
diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs
index 26e817f67..53a9dfcc9 100644
--- a/Source/Game/Entities/Object/WorldObject.cs
+++ b/Source/Game/Entities/Object/WorldObject.cs
@@ -1731,7 +1731,7 @@ namespace Game.Entities
public virtual uint GetLevelForTarget(WorldObject target) { return 1; }
- public virtual void SaveRespawnTime() { }
+ public virtual void SaveRespawnTime(uint forceDelay = 0, bool saveToDB = true) { }
public ZoneScript GetZoneScript() { return m_zoneScript; }
@@ -1770,7 +1770,7 @@ namespace Game.Entities
public virtual bool IsInvisibleDueToDespawn() { return false; }
public virtual bool IsAlwaysDetectableFor(WorldObject seer) { return false; }
- public virtual bool LoadFromDB(ulong guid, Map map) { return true; }
+ public virtual bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate) { return true; }
//Position
diff --git a/Source/Game/Entities/Player/Player.Map.cs b/Source/Game/Entities/Player/Player.Map.cs
index 1f2e13538..f0518800e 100644
--- a/Source/Game/Entities/Player/Player.Map.cs
+++ b/Source/Game/Entities/Player/Player.Map.cs
@@ -160,6 +160,8 @@ namespace Game.Entities
guild.UpdateMemberData(this, GuildMemberData.ZoneId, newZone);
}
+ GetMap().UpdatePlayerZoneStats(m_zoneUpdateId, newZone);
+
// group update
if (GetGroup())
{
diff --git a/Source/Game/Entities/Transport.cs b/Source/Game/Entities/Transport.cs
index 821708e49..1b450a330 100644
--- a/Source/Game/Entities/Transport.cs
+++ b/Source/Game/Entities/Transport.cs
@@ -318,14 +318,12 @@ namespace Game.Entities
{
Map map = GetMap();
- Creature creature = Creature.CreateCreatureFromDB(guid, map, false);
+ Creature creature = Creature.CreateCreatureFromDB(guid, map, false, true);
if (!creature)
return null;
- float x = data.posX;
- float y = data.posY;
- float z = data.posZ;
- float o = data.orientation;
+ float x, y, z, o;
+ data.spawnPoint.GetPosition(out x, out y, out z, out o);
creature.SetTransport(this);
creature.m_movementInfo.transport.guid = GetGUID();
@@ -365,10 +363,8 @@ namespace Game.Entities
if (!go)
return null;
- float x = data.posX;
- float y = data.posY;
- float z = data.posZ;
- float o = data.orientation;
+ float x, y, z, o;
+ data.spawnPoint.GetPosition(out x, out y, out z, out o);
go.SetTransport(this);
go.m_movementInfo.transport.guid = GetGUID();
@@ -472,7 +468,7 @@ namespace Game.Entities
pos.GetPosition(out x, out y, out z, out o);
CalculatePassengerPosition(ref x, ref y, ref z, ref o);
- if (!summon.Create(map.GenerateLowGuid(HighGuid.Creature), map, entry, x, y, z, o, null, vehId))
+ if (!summon.Create(map.GenerateLowGuid(HighGuid.Creature), map, entry, new Position(x, y, z, o), null, vehId))
return null;
PhasingHandler.InheritPhaseShift(summon, summoner ? (WorldObject)summoner : this);
@@ -553,7 +549,7 @@ namespace Game.Entities
// GameObjects on transport
foreach (var go in cell.Value.gameobjects)
- CreateGOPassenger(go, Global.ObjectMgr.GetGOData(go));
+ CreateGOPassenger(go, Global.ObjectMgr.GetGameObjectData(go));
}
}
diff --git a/Source/Game/Events/GameEventManager.cs b/Source/Game/Events/GameEventManager.cs
index b1ec2f436..7e14105df 100644
--- a/Source/Game/Events/GameEventManager.cs
+++ b/Source/Game/Events/GameEventManager.cs
@@ -412,7 +412,7 @@ namespace Game
int internal_event_id = mGameEvent.Length + event_id - 1;
- GameObjectData data = Global.ObjectMgr.GetGOData(guid);
+ GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
if (data == null)
{
Log.outError(LogFilter.Sql, "`game_event_gameobject` contains gameobject (GUID: {0}) not found in `gameobject` table.", guid);
@@ -773,7 +773,7 @@ namespace Game
uint entry = 0;
CreatureData data = Global.ObjectMgr.GetCreatureData(guid);
if (data != null)
- entry = data.id;
+ entry = data.Id;
VendorItem vItem = new VendorItem();
vItem.item = result.Read(2);
@@ -1103,7 +1103,7 @@ namespace Game
// get the creature data from the low guid to get the entry, to be able to find out the whole guid
CreatureData data = Global.ObjectMgr.GetCreatureData(guid);
if (data != null)
- creaturesByMap.Add(data.mapid, guid);
+ creaturesByMap.Add(data.spawnPoint.GetMapId(), guid);
}
foreach (var key in creaturesByMap.Keys)
@@ -1170,9 +1170,9 @@ namespace Game
Global.ObjectMgr.AddCreatureToGrid(guid, data);
// Spawn if necessary (loaded grids only)
- Map map = Global.MapMgr.FindMap(data.mapid, 0);
+ Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0);
// We use spawn coords to spawn
- if (map != null && !map.Instanceable() && map.IsGridLoaded(data.posX, data.posY))
+ if (map != null && !map.Instanceable() && map.IsGridLoaded(data.spawnPoint))
Creature.CreateCreatureFromDB(guid, map);
}
}
@@ -1187,15 +1187,15 @@ namespace Game
foreach (var guid in mGameEventGameobjectGuids[internal_event_id])
{
// Add to correct cell
- GameObjectData data = Global.ObjectMgr.GetGOData(guid);
+ GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
if (data != null)
{
Global.ObjectMgr.AddGameObjectToGrid(guid, data);
// Spawn if necessary (loaded grids only)
// this base map checked as non-instanced and then only existed
- Map map = Global.MapMgr.FindMap(data.mapid, 0);
+ Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0);
// We use current coords to unspawn, not spawn coords since creature can have changed grid
- if (map != null && !map.Instanceable() && map.IsGridLoaded(data.posX, data.posY))
+ if (map != null && !map.Instanceable() && map.IsGridLoaded(data.spawnPoint))
{
GameObject pGameobject = GameObject.CreateGameObjectFromDB(guid, map, false);
// @todo find out when it is add to map
@@ -1243,7 +1243,7 @@ namespace Game
{
Global.ObjectMgr.RemoveCreatureFromGrid(guid, data);
- Global.MapMgr.DoForAllMapsWithMapId(data.mapid, map =>
+ Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map =>
{
var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(guid);
foreach (var creature in creatureBounds)
@@ -1265,12 +1265,12 @@ namespace Game
if (event_id > 0 && HasGameObjectActiveEventExcept(guid, (ushort)event_id))
continue;
// Remove the gameobject from grid
- GameObjectData data = Global.ObjectMgr.GetGOData(guid);
+ GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
if (data != null)
{
Global.ObjectMgr.RemoveGameObjectFromGrid(guid, data);
- Global.MapMgr.DoForAllMapsWithMapId(data.mapid, map =>
+ Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map =>
{
var gameobjectBounds = map.GetGameObjectBySpawnIdStore().LookupByKey(guid);
foreach (var go in gameobjectBounds)
@@ -1300,7 +1300,7 @@ namespace Game
continue;
// Update if spawned
- Global.MapMgr.DoForAllMapsWithMapId(data.mapid, map =>
+ Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map =>
{
var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(tuple.Item1);
foreach (var creature in creatureBounds)
@@ -1337,12 +1337,12 @@ namespace Game
tuple.Item2.modelid_prev = data2.displayid;
tuple.Item2.equipement_id_prev = (byte)data2.equipmentId;
data2.displayid = tuple.Item2.modelid;
- data2.equipmentId = tuple.Item2.equipment_id;
+ data2.equipmentId = (sbyte)tuple.Item2.equipment_id;
}
else
{
data2.displayid = tuple.Item2.modelid_prev;
- data2.equipmentId = tuple.Item2.equipement_id_prev;
+ data2.equipmentId = (sbyte)tuple.Item2.equipement_id_prev;
}
}
}
diff --git a/Source/Game/Garrisons/Garrisons.cs b/Source/Game/Garrisons/Garrisons.cs
index d5b3c97e6..c5ada77fd 100644
--- a/Source/Game/Garrisons/Garrisons.cs
+++ b/Source/Game/Garrisons/Garrisons.cs
@@ -831,7 +831,7 @@ namespace Game.Garrisons
T BuildingSpawnHelper(GameObject building, ulong spawnId, Map map) where T : WorldObject, new()
{
T spawn = new T();
- if (!spawn.LoadFromDB(spawnId, map))
+ if (!spawn.LoadFromDB(spawnId, map, false, false))
return null;
float x = spawn.GetPositionX();
diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs
index 89edcfaa1..0f4c03f71 100644
--- a/Source/Game/Globals/ObjectManager.cs
+++ b/Source/Game/Globals/ObjectManager.cs
@@ -1310,7 +1310,7 @@ namespace Game
case ScriptCommands.RespawnGameobject:
{
- GameObjectData data = GetGOData(tmp.RespawnGameObject.GOGuid);
+ GameObjectData data = GetGameObjectData(tmp.RespawnGameObject.GOGuid);
if (data == null)
{
Log.outError(LogFilter.Sql, "Table `{0}` has invalid gameobject (GUID: {1}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {2}",
@@ -1318,11 +1318,11 @@ namespace Game
continue;
}
- GameObjectTemplate info = GetGameObjectTemplate(data.id);
+ GameObjectTemplate info = GetGameObjectTemplate(data.Id);
if (info == null)
{
Log.outError(LogFilter.Sql, "Table `{0}` has gameobject with invalid entry (GUID: {1} Entry: {2}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {3}",
- tableName, tmp.RespawnGameObject.GOGuid, data.id, tmp.id);
+ tableName, tmp.RespawnGameObject.GOGuid, data.Id, tmp.id);
continue;
}
@@ -1357,7 +1357,7 @@ namespace Game
case ScriptCommands.OpenDoor:
case ScriptCommands.CloseDoor:
{
- GameObjectData data = GetGOData(tmp.ToggleDoor.GOGuid);
+ GameObjectData data = GetGameObjectData(tmp.ToggleDoor.GOGuid);
if (data == null)
{
Log.outError(LogFilter.Sql, "Table `{0}` has invalid gameobject (GUID: {1}) in {2} for script id {3}",
@@ -1365,11 +1365,11 @@ namespace Game
continue;
}
- GameObjectTemplate info = GetGameObjectTemplate(data.id);
+ GameObjectTemplate info = GetGameObjectTemplate(data.Id);
if (info == null)
{
Log.outError(LogFilter.Sql, "Table `{0}` has gameobject with invalid entry (GUID: {1} Entry: {2}) in {3} for script id {4}",
- tableName, tmp.ToggleDoor.GOGuid, data.id, tmp.command, tmp.id);
+ tableName, tmp.ToggleDoor.GOGuid, data.Id, tmp.command, tmp.id);
continue;
}
@@ -2724,7 +2724,6 @@ namespace Game
linkedRespawnStorage.Clear();
// 0 1 2
SQLResult result = DB.World.Query("SELECT guid, linkedGuid, linkType FROM linked_respawn ORDER BY guid ASC");
-
if (result.IsEmpty())
{
Log.outError(LogFilter.ServerLoading, "Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
@@ -2760,8 +2759,8 @@ namespace Game
break;
}
- MapRecord map = CliDB.MapStorage.LookupByKey(master.mapid);
- if (map == null || !map.Instanceable() || (master.mapid != slave.mapid))
+ MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId());
+ if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId()))
{
Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -2776,8 +2775,8 @@ namespace Game
break;
}
- guid = ObjectGuid.Create(HighGuid.Creature, slave.mapid, slave.id, guidLow);
- linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.mapid, master.id, linkedGuidLow);
+ guid = ObjectGuid.Create(HighGuid.Creature, slave.spawnPoint.GetMapId(), slave.Id, guidLow);
+ linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow);
break;
}
case CreatureLinkedRespawnType.CreatureToGO:
@@ -2790,7 +2789,7 @@ namespace Game
break;
}
- GameObjectData master = GetGOData(linkedGuidLow);
+ GameObjectData master = GetGameObjectData(linkedGuidLow);
if (master == null)
{
Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", linkedGuidLow);
@@ -2798,8 +2797,8 @@ namespace Game
break;
}
- MapRecord map = CliDB.MapStorage.LookupByKey(master.mapid);
- if (map == null || !map.Instanceable() || (master.mapid != slave.mapid))
+ MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId());
+ if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId()))
{
Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -2814,13 +2813,13 @@ namespace Game
break;
}
- guid = ObjectGuid.Create(HighGuid.Creature, slave.mapid, slave.id, guidLow);
- linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.mapid, master.id, linkedGuidLow);
+ guid = ObjectGuid.Create(HighGuid.Creature, slave.spawnPoint.GetMapId(), slave.Id, guidLow);
+ linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow);
break;
}
case CreatureLinkedRespawnType.GOToGO:
{
- GameObjectData slave = GetGOData(guidLow);
+ GameObjectData slave = GetGameObjectData(guidLow);
if (slave == null)
{
Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", guidLow);
@@ -2828,7 +2827,7 @@ namespace Game
break;
}
- GameObjectData master = GetGOData(linkedGuidLow);
+ GameObjectData master = GetGameObjectData(linkedGuidLow);
if (master == null)
{
Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", linkedGuidLow);
@@ -2836,8 +2835,8 @@ namespace Game
break;
}
- MapRecord map = CliDB.MapStorage.LookupByKey(master.mapid);
- if (map == null || !map.Instanceable() || (master.mapid != slave.mapid))
+ MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId());
+ if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId()))
{
Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -2852,13 +2851,13 @@ namespace Game
break;
}
- guid = ObjectGuid.Create(HighGuid.GameObject, slave.mapid, slave.id, guidLow);
- linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.mapid, master.id, linkedGuidLow);
+ guid = ObjectGuid.Create(HighGuid.GameObject, slave.spawnPoint.GetMapId(), slave.Id, guidLow);
+ linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow);
break;
}
case CreatureLinkedRespawnType.GOToCreature:
{
- GameObjectData slave = GetGOData(guidLow);
+ GameObjectData slave = GetGameObjectData(guidLow);
if (slave == null)
{
Log.outError(LogFilter.Sql, "Couldn't get gameobject data for GUIDLow {0}", guidLow);
@@ -2874,8 +2873,8 @@ namespace Game
break;
}
- MapRecord map = CliDB.MapStorage.LookupByKey(master.mapid);
- if (map == null || !map.Instanceable() || (master.mapid != slave.mapid))
+ MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId());
+ if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId()))
{
Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow);
error = true;
@@ -2890,8 +2889,8 @@ namespace Game
break;
}
- guid = ObjectGuid.Create(HighGuid.GameObject, slave.mapid, slave.id, guidLow);
- linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.mapid, master.id, linkedGuidLow);
+ guid = ObjectGuid.Create(HighGuid.GameObject, slave.spawnPoint.GetMapId(), slave.Id, guidLow);
+ linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow);
break;
}
@@ -3231,8 +3230,8 @@ namespace Game
{
var time = Time.GetMSTime();
- // 0 1 2 3 4 5 6 7 8 9 10
- SQLResult result = DB.World.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
+ SQLResult result = DB.World.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, spawnDifficulties, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.unit_flags2, creature.unit_flags3, " +
//22 23 24 25 26 27
@@ -3274,21 +3273,18 @@ namespace Game
}
CreatureData data = new CreatureData();
- data.id = entry;
- data.mapid = result.Read(2);
- data.displayid = result.Read(3);
- data.equipmentId = result.Read(4);
- data.posX = result.Read(5);
- data.posY = result.Read(6);
- data.posZ = result.Read(7);
- data.orientation = result.Read(8);
- data.spawntimesecs = result.Read(9);
+ data.spawnId = guid;
+ data.Id = entry;
+ data.spawnPoint = new WorldLocation(result.Read(2), result.Read(3), result.Read(4), result.Read(5), result.Read(6));
+ data.displayid = result.Read(7);
+ data.equipmentId = result.Read(8);
+ data.spawntimesecs = result.Read(9);
data.spawndist = result.Read(10);
data.currentwaypoint = result.Read(11);
data.curhealth = result.Read(12);
data.curmana = result.Read(13);
data.movementType = result.Read(14);
- data.spawnDifficulties = ParseSpawnDifficulties(result.Read(15), "creature", guid, data.mapid, spawnMasks.LookupByKey(data.mapid));
+ data.spawnDifficulties = ParseSpawnDifficulties(result.Read(15), "creature", guid, data.spawnPoint.GetMapId(), spawnMasks.LookupByKey(data.spawnPoint.GetMapId()));
short gameEvent = result.Read(16);
uint PoolId = result.Read(17);
data.npcflag = result.Read(18);
@@ -3301,11 +3297,12 @@ namespace Game
data.phaseGroup = result.Read(25);
data.terrainSwapMap = result.Read(26);
data.ScriptId = GetScriptId(result.Read(27));
+ data.spawnGroupData = _spawnGroupDataStorage[0];
- var mapEntry = CliDB.MapStorage.LookupByKey(data.mapid);
+ var mapEntry = CliDB.MapStorage.LookupByKey(data.spawnPoint.GetMapId());
if (mapEntry == null)
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0}) that spawned at not existed map (Id: {1}), skipped.", guid, data.mapid);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0}) that spawned at not existed map (Id: {1}), skipped.", guid, data.spawnPoint.GetMapId());
continue;
}
@@ -3318,9 +3315,9 @@ namespace Game
bool ok = true;
for (uint diff = 0; diff < SharedConst.MaxCreatureDifficulties && ok; ++diff)
{
- if (_difficultyEntries[diff].Contains(data.id))
+ if (_difficultyEntries[diff].Contains(data.Id))
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0}) that listed as difficulty {1} template (entry: {2}) in `creaturetemplate`, skipped.", guid, diff + 1, data.id);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0}) that listed as difficulty {1} template (entry: {2}) in `creaturetemplate`, skipped.", guid, diff + 1, data.Id);
ok = false;
}
}
@@ -3330,9 +3327,9 @@ namespace Game
// -1 random, 0 no equipment,
if (data.equipmentId != 0)
{
- if (GetEquipmentInfo(data.id, data.equipmentId) == null)
+ if (GetEquipmentInfo(data.Id, data.equipmentId) == null)
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (Entry: {0}) with equipmentid {1} not found in table `creatureequiptemplate`, set to no equipment.", data.id, data.equipmentId);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (Entry: {0}) with equipmentid {1} not found in table `creatureequiptemplate`, set to no equipment.", data.Id, data.equipmentId);
data.equipmentId = 0;
}
}
@@ -3341,19 +3338,19 @@ namespace Game
{
if (!mapEntry.IsDungeon())
Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `creature_template`.`flagsextra` including CREATUREFLAGEXTRAINSTANCEBIND " +
- "but creature are not in instance.", guid, data.id);
+ "but creature are not in instance.", guid, data.Id);
}
if (data.spawndist < 0.0f)
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `spawndist`< 0, set to 0.", guid, data.id);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `spawndist`< 0, set to 0.", guid, data.Id);
data.spawndist = 0.0f;
}
else if (data.movementType == (byte)MovementGeneratorType.Random)
{
if (MathFunctions.fuzzyEq(data.spawndist, 0.0f))
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `MovementType`=1 (random movement) but with `spawndist`=0, replace by idle movement type (0).", guid, data.id);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `MovementType`=1 (random movement) but with `spawndist`=0, replace by idle movement type (0).", guid, data.Id);
data.movementType = (byte)MovementGeneratorType.Idle;
}
}
@@ -3361,33 +3358,27 @@ namespace Game
{
if (data.spawndist != 0.0f)
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.", guid, data.id);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.", guid, data.Id);
data.spawndist = 0.0f;
}
}
- if (Math.Abs(data.orientation) > 2 * MathFunctions.PI)
- {
- Log.outError(LogFilter.Sql, "Table `creature` has creature (GUID: {0} Entry: {1}) with abs(`orientation`) > 2*PI (orientation is expressed in radians), normalized.", guid, data.id);
- data.orientation = Position.NormalizeOrientation(data.orientation);
- }
-
if (Convert.ToBoolean(data.phaseUseFlags & ~PhaseUseFlagsValues.All))
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.id);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.Id);
data.phaseUseFlags &= PhaseUseFlagsValues.All;
}
if (data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.AlwaysVisible) && data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.Inverse))
{
Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE," +
- " removing PHASE_USE_FLAGS_INVERSE.", guid, data.id);
+ " removing PHASE_USE_FLAGS_INVERSE.", guid, data.Id);
data.phaseUseFlags &= ~PhaseUseFlagsValues.Inverse;
}
if (data.phaseGroup != 0 && data.phaseId != 0)
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.id);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.Id);
data.phaseGroup = 0;
}
@@ -3395,7 +3386,7 @@ namespace Game
{
if (!CliDB.PhaseStorage.ContainsKey(data.phaseId))
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `phaseid` {2} does not exist, set to 0", guid, data.id, data.phaseId);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `phaseid` {2} does not exist, set to 0", guid, data.Id, data.phaseId);
data.phaseId = 0;
}
}
@@ -3404,7 +3395,7 @@ namespace Game
{
if (Global.DB2Mgr.GetPhasesForGroup(data.phaseGroup).Empty())
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `phasegroup` {2} does not exist, set to 0", guid, data.id, data.phaseGroup);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `phasegroup` {2} does not exist, set to 0", guid, data.Id, data.phaseGroup);
data.phaseGroup = 0;
}
}
@@ -3414,12 +3405,12 @@ namespace Game
MapRecord terrainSwapEntry = CliDB.MapStorage.LookupByKey(data.terrainSwapMap);
if (terrainSwapEntry == null)
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} does not exist, set to -1", guid, data.id, data.terrainSwapMap);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} does not exist, set to -1", guid, data.Id, data.terrainSwapMap);
data.terrainSwapMap = -1;
}
- else if (terrainSwapEntry.ParentMapID != data.mapid)
+ else if (terrainSwapEntry.ParentMapID != data.spawnPoint.GetMapId())
{
- Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} which cannot be used on spawn map, set to -1", guid, data.id, data.terrainSwapMap);
+ Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} which cannot be used on spawn map, set to -1", guid, data.Id, data.terrainSwapMap);
data.terrainSwapMap = -1;
}
}
@@ -3427,7 +3418,7 @@ namespace Game
if (WorldConfig.GetBoolValue(WorldCfg.CalculateCreatureZoneAreaData))
{
PhasingHandler.InitDbVisibleMapId(phaseShift, data.terrainSwapMap);
- Global.MapMgr.GetZoneAndAreaId(phaseShift, out uint zoneId, out uint areaId, data.mapid, data.posX, data.posY, data.posZ);
+ Global.MapMgr.GetZoneAndAreaId(phaseShift, out uint zoneId, out uint areaId, data.spawnPoint);
PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_CREATURE_ZONE_AREA_DATA);
stmt.AddValue(0, zoneId);
@@ -3452,8 +3443,8 @@ namespace Game
{
foreach (Difficulty difficulty in data.spawnDifficulties)
{
- CellCoord cellCoord = GridDefines.ComputeCellCoord(data.posX, data.posY);
- var cellguids = CreateCellObjectGuids(data.mapid, difficulty, cellCoord.GetId());
+ CellCoord cellCoord = GridDefines.ComputeCellCoord(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY());
+ var cellguids = CreateCellObjectGuids(data.spawnPoint.GetMapId(), difficulty, cellCoord.GetId());
cellguids.creatures.Add(guid);
}
}
@@ -3461,8 +3452,8 @@ namespace Game
{
foreach (Difficulty difficulty in data.spawnDifficulties)
{
- CellCoord cellCoord = GridDefines.ComputeCellCoord(data.posX, data.posY);
- CellObjectGuids cellguids = GetCellObjectGuids(data.mapid, difficulty, cellCoord.GetId());
+ CellCoord cellCoord = GridDefines.ComputeCellCoord(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY());
+ CellObjectGuids cellguids = GetCellObjectGuids(data.spawnPoint.GetMapId(), difficulty, cellCoord.GetId());
if (cellguids == null)
return;
@@ -3483,14 +3474,15 @@ namespace Game
CreatureLevelScaling scaling = cInfo.GetLevelScaling(map.GetDifficultyID());
- ulong guid = GenerateCreatureSpawnId();
- CreatureData data = NewOrExistCreatureData(guid);
- data.id = entry;
- data.mapid = (ushort)mapId;
+ ulong spawnId = GenerateCreatureSpawnId();
+ CreatureData data = NewOrExistCreatureData(spawnId);
+ data.spawnId = spawnId;
+ data.Id = entry;
+ data.spawnPoint.WorldRelocate(mapId, pos);
data.displayid = 0;
data.equipmentId = 0;
- pos.GetPosition(out data.posX, out data.posY, out data.posZ, out data.orientation);
- data.spawntimesecs = spawntimedelay;
+
+ data.spawntimesecs = (int)spawntimedelay;
data.spawndist = 0;
data.currentwaypoint = 0;
data.curhealth = (uint)(Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, level, cInfo.HealthScalingExpansion, scaling.ContentTuningID, (Class)cInfo.UnitClass) * cInfo.ModHealth * cInfo.ModHealthExtra);
@@ -3501,13 +3493,14 @@ namespace Game
data.npcflag = (uint)cInfo.Npcflag;
data.unit_flags = (uint)cInfo.UnitFlags;
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 = Creature.CreateCreatureFromDB(guid, map);
+ Creature creature = Creature.CreateCreatureFromDB(spawnId, map, true, true);
if (!creature)
{
Log.outError(LogFilter.Server, "AddCreature: Cannot add creature entry {0} to map", entry);
@@ -3515,7 +3508,7 @@ namespace Game
}
}
- return guid;
+ return spawnId;
}
public List GetCreatureQuestItemList(uint id)
{
@@ -3562,7 +3555,7 @@ namespace Game
return false;
CreatureData master = GetCreatureData(guidLow);
- ObjectGuid guid = ObjectGuid.Create(HighGuid.Creature, master.mapid, master.id, guidLow);
+ ObjectGuid guid = ObjectGuid.Create(HighGuid.Creature, master.spawnPoint.GetMapId(), master.Id, guidLow);
PreparedStatement stmt;
if (linkedGuidLow == 0) // we're removing the linking
@@ -3581,8 +3574,8 @@ namespace Game
return false;
}
- MapRecord map = CliDB.MapStorage.LookupByKey(master.mapid);
- if (map == null || !map.Instanceable() || (master.mapid != slave.mapid))
+ MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId());
+ if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId()))
{
Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow);
return false;
@@ -3595,7 +3588,7 @@ namespace Game
return false;
}
- ObjectGuid linkedGuid = ObjectGuid.Create(HighGuid.Creature, slave.mapid, slave.id, linkedGuidLow);
+ ObjectGuid linkedGuid = ObjectGuid.Create(HighGuid.Creature, slave.spawnPoint.GetMapId(), slave.Id, linkedGuidLow);
linkedRespawnStorage[guid] = linkedGuid;
stmt = DB.World.GetPreparedStatement(WorldStatements.REP_CREATURE_LINKED_RESPAWN);
@@ -3614,8 +3607,11 @@ namespace Game
{
CreatureData data = GetCreatureData(guid);
if (data != null)
+ {
RemoveCreatureFromGrid(guid, data);
-
+ OnDeleteSpawnData(data);
+ }
+
creatureDataStorage.Remove(guid);
}
public CreatureBaseStats GetCreatureBaseStats(uint level, uint unitClass)
@@ -3919,14 +3915,14 @@ namespace Game
gameObjectAddon.WorldEffectID = 0;
}
- _gameObjectTemplateAddonStore[entry] = gameObjectAddon;
+ _gameObjectTemplateAddonStorage[entry] = gameObjectAddon;
++count;
}
while (result.NextRow());
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} game object template addons in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
- public void LoadGameobjects()
+ public void LoadGameObjects()
{
var time = Time.GetMSTime();
// 0 1 2 3 4 5 6
@@ -3993,28 +3989,26 @@ namespace Game
}
GameObjectData data = new GameObjectData();
- data.id = entry;
- data.mapid = result.Read(2);
- data.posX = result.Read(3);
- data.posY = result.Read(4);
- data.posZ = result.Read(5);
- data.orientation = result.Read(6);
+ data.spawnId = guid;
+ data.Id = entry;
+ data.spawnPoint = new WorldLocation(result.Read(2), result.Read(3), result.Read(4), result.Read(5), result.Read(6));
data.rotation.X = result.Read(7);
data.rotation.Y = result.Read(8);
data.rotation.Z = result.Read(9);
data.rotation.W = result.Read(10);
data.spawntimesecs = result.Read(11);
+ data.spawnGroupData = _spawnGroupDataStorage[0];
- var mapEntry = CliDB.MapStorage.LookupByKey(data.mapid);
+ var mapEntry = CliDB.MapStorage.LookupByKey(data.spawnPoint.GetMapId());
if (mapEntry == null)
{
- Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) spawned on a non-existed map (Id: {2}), skip", guid, data.id, data.mapid);
+ Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) spawned on a non-existed map (Id: {2}), skip", guid, data.Id, data.spawnPoint.GetMapId());
continue;
}
if (data.spawntimesecs == 0 && gInfo.IsDespawnAtAction())
{
- Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with `spawntimesecs` (0) value, but the gameobejct is marked as despawnable at action.", guid, data.id);
+ Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with `spawntimesecs` (0) value, but the gameobejct is marked as despawnable at action.", guid, data.Id);
}
data.animprogress = result.Read(12);
@@ -4025,13 +4019,13 @@ namespace Game
{
if (gInfo.type != GameObjectTypes.Transport || gostate > (int)GameObjectState.TransportActive + SharedConst.MaxTransportStopFrames)
{
- Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid `state` ({2}) value, skip", guid, data.id, gostate);
+ Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid `state` ({2}) value, skip", guid, data.Id, gostate);
continue;
}
}
- data.go_state = (GameObjectState)gostate;
+ data.goState = (GameObjectState)gostate;
- data.spawnDifficulties = ParseSpawnDifficulties(result.Read(14), "gameobject", guid, data.mapid, spawnMasks.LookupByKey(data.mapid));
+ data.spawnDifficulties = ParseSpawnDifficulties(result.Read(14), "gameobject", guid, data.spawnPoint.GetMapId(), spawnMasks.LookupByKey(data.spawnPoint.GetMapId()));
if (data.spawnDifficulties.Empty())
{
Log.outError(LogFilter.Sql, $"Table `creature` has creature (GUID: {guid}) that is not spawned in any difficulty, skipped.");
@@ -4046,20 +4040,20 @@ namespace Game
if (Convert.ToBoolean(data.phaseUseFlags & ~PhaseUseFlagsValues.All))
{
- Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.id);
+ Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.Id);
data.phaseUseFlags &= PhaseUseFlagsValues.All;
}
if (data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.AlwaysVisible) && data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.Inverse))
{
Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE," +
- " removing PHASE_USE_FLAGS_INVERSE.", guid, data.id);
+ " removing PHASE_USE_FLAGS_INVERSE.", guid, data.Id);
data.phaseUseFlags &= ~PhaseUseFlagsValues.Inverse;
}
if (data.phaseGroup != 0 && data.phaseId != 0)
{
- Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.id);
+ Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.Id);
data.phaseGroup = 0;
}
@@ -4067,7 +4061,7 @@ namespace Game
{
if (!CliDB.PhaseStorage.ContainsKey(data.phaseId))
{
- Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `phaseid` {2} does not exist, set to 0", guid, data.id, data.phaseId);
+ Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `phaseid` {2} does not exist, set to 0", guid, data.Id, data.phaseId);
data.phaseId = 0;
}
}
@@ -4076,7 +4070,7 @@ namespace Game
{
if (Global.DB2Mgr.GetPhasesForGroup(data.phaseGroup).Empty())
{
- Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `phaseGroup` {2} does not exist, set to 0", guid, data.id, data.phaseGroup);
+ Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `phaseGroup` {2} does not exist, set to 0", guid, data.Id, data.phaseGroup);
data.phaseGroup = 0;
}
}
@@ -4087,58 +4081,52 @@ namespace Game
MapRecord terrainSwapEntry = CliDB.MapStorage.LookupByKey(data.terrainSwapMap);
if (terrainSwapEntry == null)
{
- Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} does not exist, set to -1", guid, data.id, data.terrainSwapMap);
+ Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} does not exist, set to -1", guid, data.Id, data.terrainSwapMap);
data.terrainSwapMap = -1;
}
- else if (terrainSwapEntry.ParentMapID != data.mapid)
+ else if (terrainSwapEntry.ParentMapID != data.spawnPoint.GetMapId())
{
- Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} which cannot be used on spawn map, set to -1", guid, data.id, data.terrainSwapMap);
+ Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} which cannot be used on spawn map, set to -1", guid, data.Id, data.terrainSwapMap);
data.terrainSwapMap = -1;
}
}
data.ScriptId = GetScriptId(result.Read(21));
- if (Math.Abs(data.orientation) > 2 * MathFunctions.PI)
- {
- Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with abs(`orientation`) > 2*PI (orientation is expressed in radians), normalized.", guid, data.id);
- data.orientation = Position.NormalizeOrientation(data.orientation);
- }
-
if (data.rotation.X < -1.0f || data.rotation.X > 1.0f)
{
- Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid rotationX ({2}) value, skip", guid, data.id, data.rotation.X);
+ Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid rotationX ({2}) value, skip", guid, data.Id, data.rotation.X);
continue;
}
if (data.rotation.Y < -1.0f || data.rotation.Y > 1.0f)
{
- Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid rotationY ({2}) value, skip", guid, data.id, data.rotation.Y);
+ Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid rotationY ({2}) value, skip", guid, data.Id, data.rotation.Y);
continue;
}
if (data.rotation.Z < -1.0f || data.rotation.Z > 1.0f)
{
- Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid rotationZ ({2}) value, skip", guid, data.id, data.rotation.Z);
+ Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid rotationZ ({2}) value, skip", guid, data.Id, data.rotation.Z);
continue;
}
if (data.rotation.W < -1.0f || data.rotation.W > 1.0f)
{
- Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid rotationW ({2}) value, skip", guid, data.id, data.rotation.W);
+ Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid rotationW ({2}) value, skip", guid, data.Id, data.rotation.W);
continue;
}
- if (!GridDefines.IsValidMapCoord(data.mapid, data.posX, data.posY, data.posZ, data.orientation))
+ if (!GridDefines.IsValidMapCoord(data.spawnPoint))
{
- Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid coordinates, skip", guid, data.id);
+ Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid coordinates, skip", guid, data.Id);
continue;
}
if (WorldConfig.GetBoolValue(WorldCfg.CalculateGameobjectZoneAreaData))
{
PhasingHandler.InitDbVisibleMapId(phaseShift, data.terrainSwapMap);
- Global.MapMgr.GetZoneAndAreaId(phaseShift, out uint zoneId, out uint areaId, data.mapid, data.posX, data.posY, data.posZ);
+ Global.MapMgr.GetZoneAndAreaId(phaseShift, out uint zoneId, out uint areaId, data.spawnPoint);
PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_GAMEOBJECT_ZONE_AREA_DATA);
stmt.AddValue(0, zoneId);
@@ -4164,7 +4152,6 @@ namespace Game
// 0 1 2 3 4 5 6 7
SQLResult result = DB.World.Query("SELECT guid, parent_rotation0, parent_rotation1, parent_rotation2, parent_rotation3, invisibilityType, invisibilityValue, WorldEffectID FROM gameobject_addon");
-
if (result.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 gameobject addon definitions. DB table `gameobject_addon` is empty.");
@@ -4176,7 +4163,7 @@ namespace Game
{
ulong guid = result.Read(0);
- GameObjectData goData = GetGOData(guid);
+ GameObjectData goData = GetGameObjectData(guid);
if (goData == null)
{
Log.outError(LogFilter.Sql, $"GameObject (GUID: {guid}) does not exist but has a record in `gameobject_addon`");
@@ -4325,8 +4312,8 @@ namespace Game
{
foreach (Difficulty difficulty in data.spawnDifficulties)
{
- CellCoord cellCoord = GridDefines.ComputeCellCoord(data.posX, data.posY);
- var cellguids = CreateCellObjectGuids(data.mapid, difficulty, cellCoord.GetId());
+ CellCoord cellCoord = GridDefines.ComputeCellCoord(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY());
+ var cellguids = CreateCellObjectGuids(data.spawnPoint.GetMapId(), difficulty, cellCoord.GetId());
cellguids.gameobjects.Add(guid);
}
}
@@ -4334,15 +4321,15 @@ namespace Game
{
foreach (Difficulty difficulty in data.spawnDifficulties)
{
- CellCoord cellCoord = GridDefines.ComputeCellCoord(data.posX, data.posY);
- CellObjectGuids cellguids = GetCellObjectGuids(data.mapid, difficulty, cellCoord.GetId());
+ CellCoord cellCoord = GridDefines.ComputeCellCoord(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY());
+ CellObjectGuids cellguids = GetCellObjectGuids(data.spawnPoint.GetMapId(), difficulty, cellCoord.GetId());
if (cellguids == null)
return;
cellguids.gameobjects.Remove(guid);
}
}
- public ulong AddGOData(uint entry, uint mapId, Position pos, Quaternion rot, uint spawntimedelay)
+ public ulong AddGameObjectData(uint entry, uint mapId, Position pos, Quaternion rot, uint spawntimedelay)
{
GameObjectTemplate goinfo = GetGameObjectTemplate(entry);
if (goinfo == null)
@@ -4352,37 +4339,37 @@ namespace Game
if (map == null)
return 0;
- ulong guid = GenerateGameObjectSpawnId();
- GameObjectData data = new GameObjectData();
- data.id = entry;
- data.mapid = (ushort)mapId;
- pos.GetPosition(out data.posX, out data.posY, out data.posZ, out data.orientation);
+ ulong spawnId = GenerateGameObjectSpawnId();
+ GameObjectData data = NewOrExistGameObjectData(spawnId);
+ data.spawnId = spawnId;
+ data.Id = entry;
+ data.spawnPoint.WorldRelocate(mapId, pos);
data.rotation = rot;
data.spawntimesecs = (int)spawntimedelay;
data.animprogress = 100;
data.spawnDifficulties.Add(Difficulty.None);
- data.go_state = GameObjectState.Ready;
+ data.goState = GameObjectState.Ready;
data.artKit = (byte)(goinfo.type == GameObjectTypes.ControlZone ? 21 : 0);
data.dbData = false;
+ data.spawnGroupData = GetLegacySpawnGroup();
- NewGOData(guid, data);
- 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 = GameObject.CreateGameObjectFromDB(guid, map);
+ GameObject go = GameObject.CreateGameObjectFromDB(spawnId, map);
if (!go)
{
- Log.outError(LogFilter.Server, "AddGOData: cannot add gameobject entry {0} to map", entry);
+ Log.outError(LogFilter.Server, "AddGameObjectData: cannot add gameobject entry {0} to map", entry);
return 0;
}
}
- Log.outDebug(LogFilter.Maps, "AddGOData: dbguid:{0} entry:{1} map:{2} x:{3} y:{4} z:{5} o:{6}", guid, entry, mapId, data.posX, data.posY, data.posZ, data.orientation);
+ Log.outDebug(LogFilter.Maps, $"AddGameObjectData: dbguid:{spawnId} entry:{entry} map:{mapId} pos:{data.spawnPoint}");
- return guid;
+ return spawnId;
}
public GameObjectAddon GetGameObjectAddon(ulong lowguid)
@@ -4394,21 +4381,26 @@ namespace Game
return _gameObjectQuestItemStorage.LookupByKey(id);
}
MultiMap GetGameObjectQuestItemMap() { return _gameObjectQuestItemStorage; }
- public GameObjectData GetGOData(ulong guid)
+ public GameObjectData GetGameObjectData(ulong guid)
{
return gameObjectDataStorage.LookupByKey(guid);
}
- public void DeleteGOData(ulong guid)
+ public void DeleteGameObjectData(ulong guid)
{
- GameObjectData data = GetGOData(guid);
+ GameObjectData data = GetGameObjectData(guid);
if (data != null)
+ {
RemoveGameObjectFromGrid(guid, data);
+ OnDeleteSpawnData(data);
+ }
gameObjectDataStorage.Remove(guid);
}
- public void NewGOData(ulong guid, GameObjectData data)
+ public GameObjectData NewOrExistGameObjectData(ulong guid)
{
- gameObjectDataStorage.Add(guid, data);
+ if (!gameObjectDataStorage.ContainsKey(guid))
+ gameObjectDataStorage[guid] = new GameObjectData();
+ return gameObjectDataStorage[guid];
}
public GameObjectTemplate GetGameObjectTemplate(uint entry)
{
@@ -4416,7 +4408,7 @@ namespace Game
}
public GameObjectTemplateAddon GetGameObjectTemplateAddon(uint entry)
{
- return _gameObjectTemplateAddonStore.LookupByKey(entry);
+ return _gameObjectTemplateAddonStorage.LookupByKey(entry);
}
public Dictionary GetGameObjectTemplates()
{
@@ -5259,6 +5251,278 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} instance encounters in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
+ public void LoadSpawnGroupTemplates()
+ {
+ uint oldMSTime = Time.GetMSTime();
+
+ // 0 1 2
+ SQLResult result = DB.World.Query("SELECT groupId, groupName, groupFlags FROM spawn_group_template");
+ if (!result.IsEmpty())
+ {
+ do
+ {
+ uint groupId = result.Read(0);
+ SpawnGroupTemplateData group = new SpawnGroupTemplateData();
+ group.groupId = groupId;
+ group.name = result.Read(1);
+ group.mapId = 0xFFFFFFFF;
+ SpawnGroupFlags flags = (SpawnGroupFlags)result.Read(2);
+ if (flags.HasAnyFlag(~SpawnGroupFlags.All))
+ {
+ flags &= SpawnGroupFlags.All;
+ Log.outError(LogFilter.ServerLoading, $"Invalid spawn group flag {flags} on group ID {groupId} ({group.name}), reduced to valid flag {group.flags}.");
+ }
+ if (flags.HasAnyFlag(SpawnGroupFlags.System) && flags.HasAnyFlag(SpawnGroupFlags.ManualSpawn))
+ {
+ flags &= ~SpawnGroupFlags.ManualSpawn;
+ Log.outError(LogFilter.ServerLoading, $"System spawn group {groupId} ({group.name}) has invalid manual spawn flag. Ignored.");
+ }
+ group.flags = flags;
+ group.isActive = !group.flags.HasAnyFlag(SpawnGroupFlags.ManualSpawn);
+
+ _spawnGroupDataStorage[groupId] = group;
+ } while (result.NextRow());
+ }
+
+ if (!_spawnGroupDataStorage.ContainsKey(0))
+ {
+ Log.outError(LogFilter.ServerLoading, "Default spawn group (index 0) is missing from DB! Manually inserted.");
+ SpawnGroupTemplateData data = new SpawnGroupTemplateData();
+ data.groupId = 0;
+ data.name = "Default Group";
+ data.mapId = 0;
+ data.flags = SpawnGroupFlags.System;
+ data.isActive = true;
+ _spawnGroupDataStorage[0] = data;
+ }
+
+ if (!_spawnGroupDataStorage.ContainsKey(1))
+ {
+
+ Log.outError(LogFilter.ServerLoading, "Default legacy spawn group (index 1) is missing from DB! Manually inserted.");
+ SpawnGroupTemplateData data = new SpawnGroupTemplateData();
+ data.groupId = 1;
+ data.name = "Legacy Group";
+ data.mapId = 0;
+ data.flags = SpawnGroupFlags.System | SpawnGroupFlags.CompatibilityMode;
+ data.isActive = true;
+ _spawnGroupDataStorage[1] = data;
+ }
+
+ if (!result.IsEmpty())
+ Log.outInfo(LogFilter.ServerLoading, $"Loaded {_spawnGroupDataStorage.Count} spawn group templates in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
+ else
+ Log.outError(LogFilter.ServerLoading, "Loaded 0 spawn group templates. DB table `spawn_group_template` is empty.");
+ }
+ public void LoadSpawnGroups()
+ {
+ uint oldMSTime = Time.GetMSTime();
+
+ // 0 1 2
+ SQLResult result = DB.World.Query("SELECT groupId, spawnType, spawnId FROM spawn_group");
+ if (result.IsEmpty())
+ {
+ Log.outError(LogFilter.ServerLoading, "Loaded 0 spawn group members. DB table `spawn_group` is empty.");
+ return;
+ }
+
+ uint numMembers = 0;
+ do
+ {
+ uint groupId = result.Read(0);
+ SpawnObjectType spawnType = (SpawnObjectType)result.Read(1);
+ if (spawnType >= SpawnObjectType.Max)
+ {
+ Log.outError(LogFilter.ServerLoading, $"Spawn data with invalid type {spawnType} listed for spawn group {groupId}. Skipped.");
+ continue;
+ }
+ ulong spawnId = result.Read(2);
+
+ SpawnData data = GetSpawnData(spawnType, spawnId);
+ if (data == null)
+ {
+ Log.outError(LogFilter.ServerLoading, $"Spawn data with ID ({spawnType},{spawnId}) not found, but is listed as a member of spawn group {groupId}!");
+ continue;
+ }
+ else if (data.spawnGroupData.groupId != 0)
+ {
+ Log.outError(LogFilter.ServerLoading, $"Spawn with ID ({spawnType},{spawnId}) is listed as a member of spawn group {groupId}, but is already a member of spawn group {data.spawnGroupData.groupId}. Skipping.");
+ continue;
+ }
+ var groupTemplate = _spawnGroupDataStorage.LookupByKey(groupId);
+ if (groupTemplate == null)
+ {
+ Log.outError(LogFilter.ServerLoading, $"Spawn group {groupId} assigned to spawn ID ({spawnType},{spawnId}), but group is found!");
+ continue;
+ }
+ else
+ {
+ if (groupTemplate.mapId == 0xFFFFFFFF)
+ groupTemplate.mapId = data.spawnPoint.GetMapId();
+ else if (groupTemplate.mapId != data.spawnPoint.GetMapId() && !groupTemplate.flags.HasAnyFlag(SpawnGroupFlags.System))
+ {
+ Log.outError(LogFilter.ServerLoading, $"Spawn group {groupId} has map ID {groupTemplate.mapId}, but spawn ({spawnType},{spawnId}) has map id {data.spawnPoint.GetMapId()} - spawn NOT added to group!");
+ continue;
+ }
+ data.spawnGroupData = groupTemplate;
+ if (!groupTemplate.flags.HasAnyFlag(SpawnGroupFlags.System))
+ _spawnGroupMapStorage.Add(groupId, data);
+ ++numMembers;
+ }
+ } while (result.NextRow());
+
+ Log.outInfo(LogFilter.ServerLoading, $"Loaded {numMembers} spawn group members in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
+ }
+ void OnDeleteSpawnData(SpawnData data)
+ {
+ var templateIt = _spawnGroupDataStorage.LookupByKey(data.spawnGroupData.groupId);
+ Cypher.Assert(templateIt != null, $"Creature data for ({data.type},{data.spawnId}) is being deleted and has invalid spawn group index {data.spawnGroupData.groupId}!");
+
+ if (templateIt.flags.HasAnyFlag(SpawnGroupFlags.System)) // system groups don't store their members in the map
+ return;
+
+ var spawnDatas = _spawnGroupMapStorage.LookupByKey(data.spawnGroupData.groupId);
+ foreach (var it in spawnDatas)
+ {
+ if (it != data)
+ continue;
+ _spawnGroupMapStorage.Remove(data.spawnGroupData.groupId, it);
+ return;
+ }
+
+ Cypher.Assert(false, $"Spawn data ({data.type},{data.spawnId}) being removed is member of spawn group {data.spawnGroupData.groupId}, but not actually listed in the lookup table for that group!");
+ }
+ public bool SpawnGroupSpawn(uint groupId, Map map, bool ignoreRespawn, bool force = false, List spawnedObjects = null)
+ {
+ var spawnGroup = _spawnGroupDataStorage.LookupByKey(groupId);
+ if (spawnGroup == null || spawnGroup.flags.HasAnyFlag(SpawnGroupFlags.System))
+ {
+ Log.outError(LogFilter.Maps, $"Tried to despawn non-existing (or system) spawn group {groupId}. Blocked.");
+ return false;
+ }
+
+ if (!map)
+ {
+ Log.outError(LogFilter.Maps, $"Tried to despawn creature group {groupId}, but no map was supplied. Blocked.");
+ return false;
+ }
+
+ if (spawnGroup.mapId != map.GetId())
+ {
+ Log.outError(LogFilter.Maps, $"Tried to despawn creature group {groupId}, but supplied map is {map.GetId()}, creature group has map {spawnGroup.mapId}. Blocked.");
+ return false;
+ }
+
+ foreach (var data in _spawnGroupMapStorage.LookupByKey(groupId))
+ {
+ Cypher.Assert(spawnGroup.mapId == data.spawnPoint.GetMapId());
+ // Check if there's already an instance spawned
+ if (!force)
+ {
+ WorldObject obj = map.GetWorldObjectBySpawnId(data.type, data.spawnId);
+ if (obj != null)
+ if ((data.type != SpawnObjectType.Creature) || obj.ToCreature().IsAlive())
+ continue;
+ }
+
+ long respawnTime = map.GetRespawnTime(data.type, data.spawnId);
+ if (respawnTime != 0 && respawnTime > Time.UnixTime)
+ {
+ if (!force && !ignoreRespawn)
+ continue;
+
+ // we need to remove the respawn time, otherwise we'd end up double spawning
+ map.RemoveRespawnTime(data.type, data.spawnId, false);
+ }
+
+ // don't spawn if the grid isn't loaded (will be handled in grid loader)
+ if (!map.IsGridLoaded(data.spawnPoint))
+ continue;
+
+ // Everything OK, now do the actual (re)spawn
+ switch (data.type)
+ {
+ case SpawnObjectType.Creature:
+ {
+ Creature creature = new Creature();
+ if (!creature.LoadFromDB(data.spawnId, map, true, force))
+ creature.Dispose();
+ else if (spawnedObjects != null)
+ spawnedObjects.Add(creature);
+ break;
+ }
+ case SpawnObjectType.GameObject:
+ {
+ GameObject gameobject = new GameObject();
+ if (!gameobject.LoadFromDB(data.spawnId, map, true))
+ gameobject.Dispose();
+ else if (spawnedObjects != null)
+ spawnedObjects.Add(gameobject);
+ break;
+ }
+ default:
+ Cypher.Assert(false, $"Invalid spawn type {data.type} with spawnId {data.spawnId}");
+ return false;
+ }
+ }
+ spawnGroup.isActive = true; // start processing respawns for the group
+ return true;
+ }
+ public bool SpawnGroupDespawn(uint groupId, Map map, bool deleteRespawnTimes)
+ {
+ var spawnGroup = _spawnGroupDataStorage.LookupByKey(groupId);
+ if (spawnGroup == null || spawnGroup.flags.HasAnyFlag(SpawnGroupFlags.System))
+ {
+ Log.outError(LogFilter.Maps, $"Tried to despawn non-existing (or system) spawn group {groupId}. Blocked.");
+ return false;
+ }
+
+ if (!map)
+ {
+ Log.outError(LogFilter.Maps, $"Tried to despawn creature group {groupId}, but no map was supplied. Blocked.");
+ return false;
+ }
+
+ if (spawnGroup.mapId != map.GetId())
+ {
+ Log.outError(LogFilter.Maps, $"Tried to despawn creature group {groupId}, but supplied map is {map.GetId()}, creature group has map {spawnGroup.mapId}. Blocked.");
+ return false;
+ }
+
+ List toUnload = new List(); // unload after iterating, otherwise iterator invalidation
+ foreach (var data in _spawnGroupMapStorage.LookupByKey(groupId))
+ {
+ if (deleteRespawnTimes)
+ map.RemoveRespawnTime(data.type, data.spawnId);
+ switch (data.type)
+ {
+ case SpawnObjectType.Creature:
+ {
+ var bounds = map.GetCreatureBySpawnIdStore().LookupByKey(data.spawnId);
+ foreach (var creature in bounds)
+ toUnload.Add(creature);
+ break;
+ }
+ case SpawnObjectType.GameObject:
+ {
+ var bounds = map.GetGameObjectBySpawnIdStore().LookupByKey(data.spawnId);
+ foreach (var go in bounds)
+ toUnload.Add(go);
+ break;
+ }
+ default:
+ Cypher.Assert(false, $"Invalid spawn type {data.type} in spawn data with spawnId {data.spawnId}.");
+ return false;
+ }
+ }
+
+ // now do the actual despawning
+ foreach (WorldObject obj in toUnload)
+ obj.AddObjectToRemoveList();
+
+ spawnGroup.isActive = false; // stop processing respawns for the group, too
+ return true;
+ }
public InstanceTemplate GetInstanceTemplate(uint mapID)
{
@@ -5277,7 +5541,31 @@ namespace Game
return _dungeonEncounterStorage.LookupByKey(MathFunctions.MakePair64(mapId, (uint)difficulty));
}
public bool IsTransportMap(uint mapId) { return _transportMaps.Contains((ushort)mapId); }
+ void SetSpawnGroupActive(uint groupId, bool state)
+ {
+ var spawnGroup = _spawnGroupDataStorage.LookupByKey(groupId);
+ if (spawnGroup != null)
+ spawnGroup.isActive = state;
+ }
+ bool IsSpawnGroupActive(uint groupId)
+ {
+ var spawnGroup = _spawnGroupDataStorage.LookupByKey(groupId);
+ return (spawnGroup != null) && spawnGroup.isActive;
+ }
+ public SpawnGroupTemplateData GetDefaultSpawnGroup() { return _spawnGroupDataStorage.ElementAt(0).Value; }
+ SpawnGroupTemplateData GetLegacySpawnGroup() { return _spawnGroupDataStorage.ElementAt(1).Value; }
+ public SpawnData GetSpawnData(SpawnObjectType type, ulong guid)
+ {
+ if (type == SpawnObjectType.Creature)
+ return GetCreatureData(guid);
+ else if (type == SpawnObjectType.GameObject)
+ return GetGameObjectData(guid);
+ else
+ Cypher.Assert(false, $"Invalid spawn object type {type}");
+ return null;
+ }
+
//Player
public void LoadPlayerInfo()
{
@@ -9391,7 +9679,7 @@ namespace Game
{
if (_gameObjectSpawnId >= 0xFFFFFFFFFFFFFFFE)
{
- Log.outFatal(LogFilter.Server, "Gameobject spawn id overflow!! Can't continue, shutting down server. ");
+ Log.outFatal(LogFilter.Server, "GameObject spawn id overflow!! Can't continue, shutting down server. ");
Global.WorldMgr.StopNow();
}
return _gameObjectSpawnId++;
@@ -9806,6 +10094,8 @@ namespace Game
Dictionary instanceTemplateStorage = new Dictionary();
public MultiMap GraveYardStorage = new MultiMap();
List _transportMaps = new List();
+ Dictionary _spawnGroupDataStorage = new Dictionary();
+ MultiMap _spawnGroupMapStorage = new MultiMap();
//Spells /Skills / Phases
Dictionary _phaseInfoById = new Dictionary();
@@ -9841,7 +10131,7 @@ namespace Game
//GameObject
Dictionary _gameObjectTemplateStorage = new Dictionary();
Dictionary gameObjectDataStorage = new Dictionary();
- Dictionary _gameObjectTemplateAddonStore = new Dictionary();
+ Dictionary _gameObjectTemplateAddonStorage = new Dictionary();
Dictionary _gameObjectAddonStorage = new Dictionary();
MultiMap _gameObjectQuestItemStorage = new MultiMap();
List _gameObjectForQuestStorage = new List();
@@ -9893,9 +10183,9 @@ namespace Game
ulong _equipmentSetGuid;
uint _mailId;
uint _hiPetNumber;
- ulong _voidItemId;
ulong _creatureSpawnId;
ulong _gameObjectSpawnId;
+ ulong _voidItemId;
uint[] _playerXPperLevel;
Dictionary _baseXPTable = new Dictionary();
#endregion
diff --git a/Source/Game/Maps/GridNotifiers.cs b/Source/Game/Maps/GridNotifiers.cs
index a46b57789..f7ba5d339 100644
--- a/Source/Game/Maps/GridNotifiers.cs
+++ b/Source/Game/Maps/GridNotifiers.cs
@@ -2010,6 +2010,31 @@ namespace Game.Maps
bool _reqAlive;
}
+ class AnyPlayerInPositionRangeCheck : ICheck
+ {
+ public AnyPlayerInPositionRangeCheck(Position pos, float range, bool reqAlive = true)
+ {
+ _pos = pos;
+ _range = range;
+ _reqAlive = reqAlive;
+ }
+
+ public bool Invoke(Player u)
+ {
+ if (_reqAlive && !u.IsAlive())
+ return false;
+
+ if (!u.IsWithinDist3d(_pos, _range))
+ return false;
+
+ return true;
+ }
+
+ Position _pos;
+ float _range;
+ bool _reqAlive;
+ }
+
class NearestPlayerInObjectRangeCheck : ICheck
{
public NearestPlayerInObjectRangeCheck(WorldObject obj, float range)
diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs
index de1c3c0d3..910e15c7d 100644
--- a/Source/Game/Maps/Map.cs
+++ b/Source/Game/Maps/Map.cs
@@ -74,6 +74,8 @@ namespace Game.Maps
}
}
+ _zonePlayerCountMap.Clear();
+
//lets initialize visibility distance for map
InitVisibilityDistance();
_weatherUpdateTimer = new IntervalTimer();
@@ -90,6 +92,10 @@ namespace Game.Maps
{
Global.ScriptMgr.OnDestroyMap(this);
+ // Delete all waiting spawns
+ // This doesn't delete from database.
+ DeleteRespawnInfo();
+
for (var i = 0; i < i_worldObjects.Count; ++i)
{
WorldObject obj = i_worldObjects[i];
@@ -279,7 +285,7 @@ namespace Game.Maps
m_VisibilityNotifyPeriod = Global.WorldMgr.GetVisibilityNotifyPeriodOnContinents();
}
- public void AddToGrid(T obj, Cell cell)where T : WorldObject
+ public void AddToGrid(T obj, Cell cell) where T : WorldObject
{
Grid grid = GetGrid(cell.GetGridX(), cell.GetGridY());
switch (obj.GetTypeId())
@@ -429,7 +435,7 @@ namespace Game.Maps
GridMaps[gx][gy] = m_parentMap.GridMaps[gx][gy];
++rootParentTerrainMap.GridMapReference[gx][gy];
}
- }
+ }
}
}
@@ -475,6 +481,29 @@ namespace Game.Maps
loader.LoadN();
}
+ void GridMarkNoUnload(uint x, uint y)
+ {
+ // First make sure this grid is loaded
+ float gX = (((float)x - 0.5f - MapConst.CenterGridId) * MapConst.SizeofGrids) + (MapConst.CenterGridOffset * 2);
+ float gY = (((float)y - 0.5f - MapConst.CenterGridId) * MapConst.SizeofGrids) + (MapConst.CenterGridOffset * 2);
+ Cell cell = new Cell(gX, gY);
+ EnsureGridLoaded(cell);
+
+ // Mark as don't unload
+ var grid = GetGrid(x, y);
+ grid.SetUnloadExplicitLock(true);
+ }
+
+ void GridUnmarkNoUnload(uint x, uint y)
+ {
+ // If grid is loaded, clear unload lock
+ if (IsGridLoaded(new GridCoord(x, y)))
+ {
+ var grid = GetGrid(x, y);
+ grid.SetUnloadExplicitLock(false);
+ }
+ }
+
public void LoadGrid(float x, float y)
{
EnsureGridLoaded(new Cell(x, y));
@@ -598,10 +627,11 @@ namespace Game.Maps
return true;
}
- public bool IsGridLoaded(float x, float y)
- {
- return IsGridLoaded(GridDefines.ComputeGridCoord(x, y));
- }
+ public bool IsGridLoaded(uint gridId) { return IsGridLoaded(new GridCoord(gridId % MapConst.MaxGrids, gridId / MapConst.MaxGrids)); }
+
+ public bool IsGridLoaded(float x, float y) { return IsGridLoaded(GridDefines.ComputeGridCoord(x, y)); }
+
+ public bool IsGridLoaded(Position pos) { return IsGridLoaded(pos.GetPositionX(), pos.GetPositionY()); }
public bool IsGridLoaded(GridCoord p)
{
@@ -637,6 +667,20 @@ namespace Game.Maps
}
}
+ public void UpdatePlayerZoneStats(uint oldZone, uint newZone)
+ {
+ // Nothing to do if no change
+ if (oldZone == newZone)
+ return;
+
+ if (oldZone != MapConst.InvalidZone)
+ {
+ Cypher.Assert(_zonePlayerCountMap[oldZone] != 0, $"A player left zone {oldZone} (went to {newZone}) - but there were no players in the zone!");
+ --_zonePlayerCountMap[oldZone];
+ }
+ ++_zonePlayerCountMap[newZone];
+ }
+
public virtual void Update(uint diff)
{
_dynamicTree.Update(diff);
@@ -653,9 +697,18 @@ namespace Game.Maps
}
}
+ /// process any due respawns
+ if (_respawnCheckTimer <= diff)
+ {
+ ProcessRespawns();
+ _respawnCheckTimer = WorldConfig.GetUIntValue(WorldCfg.RespawnMinCheckIntervalMs);
+ }
+ else
+ _respawnCheckTimer -= diff;
+
// update active cells around players and active objects
ResetMarkedCells();
-
+
var update = new UpdaterNotifier(diff);
var grid_object_update = new Visitor(update, GridMapTypeMask.AllGrid);
@@ -666,10 +719,10 @@ namespace Game.Maps
Player player = m_activePlayers[i];
if (!player.IsInWorld)
continue;
-
+
// update players at tick
player.Update(diff);
-
+
VisitNearbyCellsOf(player, grid_object_update, world_object_update);
// If player is using far sight or mind vision, visit that object too
@@ -698,7 +751,7 @@ namespace Game.Maps
VisitNearbyCellsOf(c, grid_object_update, world_object_update);
}
}
-
+
for (var i = 0; i < m_activeNonPlayers.Count; ++i)
{
WorldObject obj = m_activeNonPlayers[i];
@@ -707,7 +760,7 @@ namespace Game.Maps
VisitNearbyCellsOf(obj, grid_object_update, world_object_update);
}
-
+
for (var i = 0; i < _transports.Count; ++i)
{
Transport transport = _transports[i];
@@ -716,7 +769,7 @@ namespace Game.Maps
transport.Update(diff);
}
-
+
SendObjectUpdates();
// Process necessary scripts
@@ -841,6 +894,8 @@ namespace Game.Maps
public virtual void RemovePlayerFromMap(Player player, bool remove)
{
+ // Before leaving map, update zone/area for stats
+ player.UpdateZone(MapConst.InvalidZone, 0);
Global.ScriptMgr.OnPlayerLeaveMap(this, player);
player.GetHostileRefManager().DeleteReferences(); // multithreading crashfix
@@ -1167,7 +1222,7 @@ namespace Game.Maps
{
_creatureToMoveLock = true;
- for (var i = 0; i< creaturesToMove.Count; ++i)
+ for (var i = 0; i < creaturesToMove.Count; ++i)
{
Creature creature = creaturesToMove[i];
if (creature.GetMap() != this) //pet is teleported to another map
@@ -1493,7 +1548,7 @@ namespace Game.Maps
// if in same cell then none do
if (old_cell.DiffCell(new_cell))
{
- Log.outDebug(LogFilter.Maps, "AreaTrigger ({0}) moved in grid[{0}, {1}] from cell[{2}, {3}] to cell[{4}, {5}].", at.GetGUID().ToString(), old_cell.GetGridX(), old_cell.GetGridY(),
+ Log.outDebug(LogFilter.Maps, "AreaTrigger ({0}) moved in grid[{0}, {1}] from cell[{2}, {3}] to cell[{4}, {5}].", at.GetGUID().ToString(), old_cell.GetGridX(), old_cell.GetGridY(),
old_cell.GetCellX(), old_cell.GetCellY(), new_cell.GetCellX(), new_cell.GetCellY());
RemoveFromGrid(at, old_cell);
@@ -1810,6 +1865,16 @@ namespace Game.Maps
return mapHeight; // explicitly use map data
}
+ public float GetHeight(PhaseShift phaseShift, float x, float y, float z, bool vmap = true, float maxSearchDist = MapConst.DefaultHeightSearch)
+ {
+ return Math.Max(GetStaticHeight(phaseShift, x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phaseShift, x, y, z, maxSearchDist));
+ }
+
+ public float GetHeight(PhaseShift phaseShift, Position pos, bool vmap = true, float maxSearchDist = MapConst.DefaultHeightSearch)
+ {
+ return GetHeight(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist);
+ }
+
public float GetMinHeight(PhaseShift phaseShift, float x, float y)
{
GridMap grid = GetGridMap(PhasingHandler.GetTerrainMapId(phaseShift, this, x, y), x, y);
@@ -1974,6 +2039,8 @@ namespace Game.Maps
return areaId;
}
+ public uint GetAreaId(PhaseShift phaseShift, Position pos) { return GetAreaId(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+
public uint GetZoneId(PhaseShift phaseShift, float x, float y, float z)
{
uint areaId = GetAreaId(phaseShift, x, y, z);
@@ -1985,6 +2052,8 @@ namespace Game.Maps
return areaId;
}
+ public uint GetZoneId(PhaseShift phaseShift, Position pos) { return GetZoneId(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+
public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, float x, float y, float z)
{
areaid = zoneid = GetAreaId(phaseShift, x, y, z);
@@ -1994,6 +2063,8 @@ namespace Game.Maps
zoneid = area.ParentAreaID;
}
+ public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, Position pos) { GetZoneAndAreaId(phaseShift, out zoneid, out areaid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
+
private byte GetTerrainType(PhaseShift phaseShift, float x, float y)
{
GridMap gmap = GetGridMap(PhasingHandler.GetTerrainMapId(phaseShift, this, x, y), x, y);
@@ -2229,11 +2300,6 @@ namespace Game.Maps
return result;
}
- public float GetHeight(PhaseShift phaseShift, float x, float y, float z, bool vmap = true, float maxSearchDist = MapConst.DefaultHeightSearch)
- {
- return Math.Max(GetStaticHeight(phaseShift, x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phaseShift, x, y, z, maxSearchDist));
- }
-
public bool IsInWater(PhaseShift phaseShift, float x, float y, float pZ)
{
return Convert.ToBoolean(GetLiquidStatus(phaseShift, x, y, pZ, MapConst.MapAllLiquidTypes) & (ZLiquidStatus.InWater | ZLiquidStatus.UnderWater));
@@ -2379,6 +2445,349 @@ namespace Game.Maps
}
}
+ bool CheckRespawn(RespawnInfo info)
+ {
+ uint poolId = info.spawnId != 0 ? Global.PoolMgr.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 == 0)
+ {
+ bool doDelete = false;
+ switch (info.type)
+ {
+ case SpawnObjectType.Creature:
+ {
+ // escort check for creatures only (if the world config boolean is set)
+ bool isEscort = false;
+ if (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc) && info.type == SpawnObjectType.Creature)
+ {
+ CreatureData cdata = Global.ObjectMgr.GetCreatureData(info.spawnId);
+ if (cdata != null)
+ if (cdata.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.EscortQuestNpc))
+ isEscort = true;
+ }
+
+ var range = _creatureBySpawnIdStore.LookupByKey(info.spawnId);
+ foreach (var creature in range)
+ {
+ 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 SpawnObjectType.GameObject:
+ // gameobject check is simpler - they cannot be dead or escorting
+ if (_gameobjectBySpawnIdStore.ContainsKey(info.spawnId))
+ doDelete = true;
+ break;
+ default:
+ Cypher.Assert(false, $"Invalid spawn type {info.type} with spawnId {info.spawnId} on map {GetId()}");
+ return true;
+ }
+ if (doDelete)
+ {
+ info.respawnTime = 0;
+ return false;
+ }
+ }
+
+ // next, check linked respawn time
+ ObjectGuid thisGUID = info.type == SpawnObjectType.GameObject ? ObjectGuid.Create(HighGuid.GameObject, GetId(), info.entry, info.spawnId) : ObjectGuid.Create(HighGuid.Creature, GetId(), info.entry, info.spawnId);
+ long linkedTime = GetLinkedRespawnTime(thisGUID);
+ if (linkedTime != 0)
+ {
+ long now = Time.UnixTime;
+ long respawnTime;
+ if (Global.ObjectMgr.GetLinkedRespawnGuid(thisGUID) == thisGUID) // never respawn, save "something" in DB
+ respawnTime = now + Time.Week;
+ else // set us to check again shortly after linked unit
+ respawnTime = Math.Max(now, linkedTime) + RandomHelper.URand(5, 15);
+ info.respawnTime = respawnTime;
+ return false;
+ }
+
+ // now, check if we're part of a pool
+ if (poolId != 0)
+ {
+ // 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 == SpawnObjectType.GameObject)
+ Global.PoolMgr.UpdatePool(poolId, info.spawnId);
+ else if (info.type == SpawnObjectType.Creature)
+ Global.PoolMgr.UpdatePool(poolId, info.spawnId);
+ else
+ Cypher.Assert(false, $"Invalid spawn type {info.type} (spawnid {info.spawnId}) on map {GetId()}");
+ info.respawnTime = 0;
+ return false;
+ }
+
+ // if we're a creature, see if the script objects to us spawning
+ if (info.type == SpawnObjectType.Creature)
+ {
+ if (!Global.ScriptMgr.CanSpawn(info.spawnId, info.entry, Global.ObjectMgr.GetCreatureData(info.spawnId), this))
+ { // if a script blocks our respawn, schedule next check in a little bit
+ info.respawnTime = Time.UnixTime + RandomHelper.URand(4, 7);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void DoRespawn(SpawnObjectType type, ulong spawnId, uint gridId)
+ {
+ if (!IsGridLoaded(gridId)) // if grid isn't loaded, this will be processed in grid load handler
+ return;
+
+ switch (type)
+ {
+ case SpawnObjectType.Creature:
+ {
+ Creature obj = new Creature();
+ if (!obj.LoadFromDB(spawnId, this, true, true))
+ obj.Dispose();
+ break;
+ }
+ case SpawnObjectType.GameObject:
+ {
+ GameObject obj = new GameObject();
+ if (!obj.LoadFromDB(spawnId, this, true))
+ obj.Dispose();
+ break;
+ }
+ default:
+ Cypher.Assert(false, $"Invalid spawn type {type} (spawnid {spawnId}) on map {GetId()}");
+ break;
+ }
+ }
+
+ void Respawn(RespawnInfo info, bool force = false, SQLTransaction dbTrans = null)
+ {
+ if (!force && !CheckRespawn(info))
+ {
+ if (info.respawnTime != 0)
+ 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 type = info.type;
+ uint gridId = info.gridId;
+ ulong spawnId = info.spawnId;
+ RemoveRespawnTime(info);
+ DoRespawn(type, spawnId, gridId);
+ }
+
+ void Respawn(List respawnData, bool force, SQLTransaction dbTrans)
+ {
+ SQLTransaction trans = dbTrans != null ? dbTrans : new SQLTransaction();
+ foreach (RespawnInfo info in respawnData)
+ Respawn(info, force, trans);
+
+ if (dbTrans == null)
+ DB.Characters.CommitTransaction(trans);
+ }
+
+ void AddRespawnInfo(RespawnInfo info, bool replace)
+ {
+ if (info.spawnId == 0)
+ return;
+
+ var bySpawnIdMap = GetRespawnMapForType(info.type);
+
+ var existing = bySpawnIdMap.LookupByKey(info.spawnId);
+ if (existing != null) // spawnid already has a respawn scheduled
+ {
+ 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);
+ _respawnTimes.Add(ri);
+ bySpawnIdMap.Add(ri.spawnId, ri);
+ }
+
+ static void PushRespawnInfoFrom(List data, Dictionary map, uint zoneId)
+ {
+ foreach (var pair in map)
+ if (zoneId == 0 || pair.Value.zoneId == zoneId)
+ data.Add(pair.Value);
+ }
+
+ public void GetRespawnInfo(List respawnData, SpawnObjectTypeMask types, uint zoneId)
+ {
+ if (types.HasAnyFlag(SpawnObjectTypeMask.Creature))
+ PushRespawnInfoFrom(respawnData, _creatureRespawnTimesBySpawnId, zoneId);
+ if (types.HasAnyFlag(SpawnObjectTypeMask.GameObject))
+ PushRespawnInfoFrom(respawnData, _gameObjectRespawnTimesBySpawnId, zoneId);
+ }
+
+ RespawnInfo GetRespawnInfo(SpawnObjectType type, ulong spawnId)
+ {
+ var map = GetRespawnMapForType(type);
+ var respawnInfo = map.LookupByKey(spawnId);
+ if (respawnInfo == null)
+ return null;
+
+ return respawnInfo;
+ }
+
+ Dictionary GetRespawnMapForType(SpawnObjectType type) { return (type == SpawnObjectType.GameObject) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; }
+
+ void DeleteRespawnInfo() // delete everything
+ {
+ _respawnTimes.Clear();
+ _creatureRespawnTimesBySpawnId.Clear();
+ _gameObjectRespawnTimesBySpawnId.Clear();
+ }
+
+ void DeleteRespawnInfo(RespawnInfo info)
+ {
+ // Delete from all relevant containers to ensure consistency
+ Cypher.Assert(info != null);
+
+ // spawnid store
+ GetRespawnMapForType(info.type).Remove(info.spawnId);
+ Cypher.Assert(GetRespawnMapForType(info.type).Count == 1, $"Respawn stores inconsistent for map {GetId()}, spawnid {info.spawnId} (type {info.type})");
+
+ //respawn heap
+ _respawnTimes.Remove(info);
+ }
+
+ public void RemoveRespawnTime(RespawnInfo info, bool doRespawn = false, SQLTransaction dbTrans = null)
+ {
+ PreparedStatement stmt;
+ switch (info.type)
+ {
+ case SpawnObjectType.Creature:
+ stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CREATURE_RESPAWN);
+ break;
+ case SpawnObjectType.GameObject:
+ stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_GO_RESPAWN);
+ break;
+ default:
+ Cypher.Assert(false, $"Invalid respawninfo type {info.type} for spawnid {info.spawnId} map {GetId()}");
+ return;
+ }
+
+ stmt.AddValue(0, info.spawnId);
+ stmt.AddValue(1, GetId());
+ stmt.AddValue(2, GetInstanceId());
+ DB.Characters.ExecuteOrAppend(dbTrans, stmt);
+
+ if (doRespawn)
+ Respawn(info);
+ else
+ DeleteRespawnInfo(info);
+ }
+
+ public void RemoveRespawnTime(List respawnData, bool doRespawn, SQLTransaction dbTrans = null)
+ {
+ SQLTransaction trans = dbTrans != null ? dbTrans : new SQLTransaction();
+ foreach (RespawnInfo info in respawnData)
+ RemoveRespawnTime(info, doRespawn, trans);
+
+ if (dbTrans == null)
+ DB.Characters.CommitTransaction(trans);
+ }
+
+ public void RemoveRespawnTime(SpawnObjectTypeMask types = SpawnObjectTypeMask.All, uint zoneId = 0, bool doRespawn = false, SQLTransaction dbTrans = null)
+ {
+ List v = new List();
+ GetRespawnInfo(v, types, zoneId);
+ if (!v.Empty())
+ RemoveRespawnTime(v, doRespawn, dbTrans);
+ }
+
+ public void RemoveRespawnTime(SpawnObjectType type, ulong spawnId, bool doRespawn = false, SQLTransaction dbTrans = null)
+ {
+ RespawnInfo info = GetRespawnInfo(type, spawnId);
+ if (info != null)
+ RemoveRespawnTime(info, doRespawn, dbTrans);
+ }
+
+ void ProcessRespawns()
+ {
+ long now = Time.UnixTime;
+ while (!_respawnTimes.Empty())
+ {
+ RespawnInfo next = _respawnTimes.First();
+ if (now < next.respawnTime) // done for this tick
+ break;
+
+ if (CheckRespawn(next)) // see if we're allowed to respawn
+ {
+ // ok, respawn
+ _respawnTimes.Remove(next);
+ GetRespawnMapForType(next.type).Remove(next.spawnId);
+ DoRespawn(next.type, next.spawnId, next.gridId);
+ }
+ else if (next.respawnTime == 0) // just remove respawn entry without rescheduling
+ {
+ _respawnTimes.Remove(next);
+ GetRespawnMapForType(next.type).Remove(next.spawnId);
+ }
+ else // value changed, update heap position
+ {
+ Cypher.Assert(now < next.respawnTime); // infinite loop guard
+ //_respawnTimes.decrease(next->handle);
+ }
+ }
+ }
+
+ public void ApplyDynamicModeRespawnScaling(WorldObject obj, ulong spawnId, ref uint respawnDelay, uint mode)
+ {
+ Cypher.Assert(mode == 1);
+ Cypher.Assert(obj.GetMap() == this);
+ SpawnObjectType type;
+ switch (obj.GetTypeId())
+ {
+ case TypeId.Unit:
+ type = SpawnObjectType.Creature;
+ break;
+ case TypeId.GameObject:
+ type = SpawnObjectType.GameObject;
+ break;
+ default:
+ return;
+ }
+
+ SpawnData data = Global.ObjectMgr.GetSpawnData(type, spawnId);
+ if (data == null || !data.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.DynamicSpawnRate))
+ return;
+
+ if (!_zonePlayerCountMap.ContainsKey(obj.GetZoneId()))
+ return;
+
+ uint playerCount = _zonePlayerCountMap[obj.GetZoneId()];
+ if (playerCount == 0)
+ return;
+
+ double adjustFactor = WorldConfig.GetFloatValue(type == SpawnObjectType.GameObject ? WorldCfg.RespawnDynamicRateGameobject : WorldCfg.RespawnDynamicRateCreature) / playerCount;
+ if (adjustFactor >= 1.0) // nothing to do here
+ return;
+
+ uint timeMinimum = WorldConfig.GetUIntValue(type == SpawnObjectType.GameObject ? WorldCfg.RespawnDynamicMinimumGameObject : WorldCfg.RespawnDynamicMinimumCreature);
+ if (respawnDelay <= timeMinimum)
+ return;
+
+ respawnDelay = (uint)Math.Max(Math.Ceiling(respawnDelay * adjustFactor), timeMinimum);
+ }
+
public virtual void DelayedUpdate(uint diff)
{
for (var i = 0; i < _transports.Count; ++i)
@@ -2617,64 +3026,37 @@ namespace Game.Maps
m_activeNonPlayers.Remove(obj);
}
- public void SaveCreatureRespawnTime(ulong dbGuid, long respawnTime)
+ public void SaveRespawnTime(SpawnObjectType type, ulong spawnId, uint entry, long respawnTime, uint zoneId, uint gridId = 0, bool writeDB = true, bool replace = false, SQLTransaction dbTrans = null)
{
if (respawnTime == 0)
{
// Delete only
- RemoveCreatureRespawnTime(dbGuid);
+ RemoveRespawnTime(type, spawnId, false, dbTrans);
return;
}
- _creatureRespawnTimes[dbGuid] = respawnTime;
+ RespawnInfo ri = new RespawnInfo();
+ ri.type = type;
+ ri.spawnId = spawnId;
+ ri.entry = entry;
+ ri.respawnTime = respawnTime;
+ ri.gridId = gridId;
+ ri.zoneId = zoneId;
+ AddRespawnInfo(ri, replace);
- PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.REP_CREATURE_RESPAWN);
- stmt.AddValue(0, dbGuid);
+ if (writeDB)
+ SaveRespawnTimeDB(type, spawnId, ri.respawnTime, dbTrans); // might be different from original respawn time if we didn't replace
+ }
+
+ public void SaveRespawnTimeDB(SpawnObjectType type, ulong spawnId, long respawnTime, SQLTransaction dbTrans = null)
+ {
+ // Just here for support of compatibility mode
+ PreparedStatement stmt = DB.Characters.GetPreparedStatement((type == SpawnObjectType.GameObject) ? CharStatements.REP_GO_RESPAWN : CharStatements.REP_CREATURE_RESPAWN);
+ stmt.AddValue(0, spawnId);
stmt.AddValue(1, respawnTime);
stmt.AddValue(2, GetId());
stmt.AddValue(3, GetInstanceId());
- DB.Characters.Execute(stmt);
- }
-
- public void RemoveCreatureRespawnTime(ulong dbGuid)
- {
- _creatureRespawnTimes.Remove(dbGuid);
-
- PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CREATURE_RESPAWN);
- stmt.AddValue(0, dbGuid);
- stmt.AddValue(1, GetId());
- stmt.AddValue(2, GetInstanceId());
- DB.Characters.Execute(stmt);
- }
-
- public void SaveGORespawnTime(ulong dbGuid, long respawnTime)
- {
- if (respawnTime == 0)
- {
- // Delete only
- RemoveGORespawnTime(dbGuid);
- return;
- }
-
- _goRespawnTimes[dbGuid] = respawnTime;
-
- PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.REP_GO_RESPAWN);
- stmt.AddValue(0, dbGuid);
- stmt.AddValue(1, respawnTime);
- stmt.AddValue(2, GetId());
- stmt.AddValue(3, GetInstanceId());
- DB.Characters.Execute(stmt);
- }
-
- public void RemoveGORespawnTime(ulong dbGuid)
- {
- _goRespawnTimes.Remove(dbGuid);
-
- PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_GO_RESPAWN);
- stmt.AddValue(0, dbGuid);
- stmt.AddValue(1, GetId());
- stmt.AddValue(2, GetInstanceId());
- DB.Characters.Execute(stmt);
+ DB.Characters.ExecuteOrAppend(dbTrans, stmt);
}
public void LoadRespawnTimes()
@@ -2690,7 +3072,10 @@ namespace Game.Maps
var loguid = result.Read(0);
var respawnTime = result.Read(1);
- _creatureRespawnTimes[loguid] = respawnTime;
+ CreatureData cdata = Global.ObjectMgr.GetCreatureData(loguid);
+ if (cdata != null)
+ SaveRespawnTime(SpawnObjectType.Creature, loguid, cdata.Id, respawnTime, GetZoneId(PhasingHandler.EmptyPhaseShift, cdata.spawnPoint), GridDefines.ComputeGridCoord(cdata.spawnPoint.GetPositionX(), cdata.spawnPoint.GetPositionY()).GetId(), false);
+
} while (result.NextRow());
}
@@ -2705,16 +3090,19 @@ namespace Game.Maps
var loguid = result.Read(0);
var respawnTime = result.Read(1);
- _goRespawnTimes[loguid] = respawnTime;
+ GameObjectData godata = Global.ObjectMgr.GetGameObjectData(loguid);
+ if (godata != null)
+ SaveRespawnTime(SpawnObjectType.GameObject, loguid, godata.Id, respawnTime, GetZoneId(PhasingHandler.EmptyPhaseShift, godata.spawnPoint), GridDefines.ComputeGridCoord(godata.spawnPoint.GetPositionX(), godata.spawnPoint.GetPositionY()).GetId(), false);
+
} while (result.NextRow());
}
}
+ public long GetRespawnTime(SpawnObjectType type, ulong spawnId) { return (type == SpawnObjectType.GameObject) ? GetGORespawnTime(spawnId) : GetCreatureRespawnTime(spawnId); }
+
public void DeleteRespawnTimes()
{
- _creatureRespawnTimes.Clear();
- _goRespawnTimes.Clear();
-
+ DeleteRespawnInfo();
DeleteRespawnTimesInDB(GetId(), GetInstanceId());
}
@@ -3106,7 +3494,8 @@ namespace Game.Maps
return GetGrid(p.X_coord, p.Y_coord) == null ||
GetGrid(p.X_coord, p.Y_coord).GetGridState() == GridState.Removal;
}
-
+ public bool IsRemovalGrid(Position pos) { return IsRemovalGrid(pos.GetPositionX(), pos.GetPositionY()); }
+
private bool GetUnloadLock(GridCoord p)
{
return GetGrid(p.X_coord, p.Y_coord).GetUnloadLock();
@@ -3320,7 +3709,7 @@ namespace Game.Maps
{
return _dynamicTree.GetHeight(x, y, z, maxSearchDist, phaseShift);
}
-
+
public virtual uint GetOwnerGuildId(Team team = Team.Other)
{
return 0;
@@ -3328,12 +3717,12 @@ namespace Game.Maps
public long GetCreatureRespawnTime(ulong dbGuid)
{
- return _creatureRespawnTimes.LookupByKey(dbGuid);
+ return _creatureRespawnTimesBySpawnId.ContainsKey(dbGuid) ? _creatureRespawnTimesBySpawnId[dbGuid].respawnTime : 0;
}
public long GetGORespawnTime(ulong dbGuid)
{
- return _goRespawnTimes.LookupByKey(dbGuid);
+ return _gameObjectRespawnTimesBySpawnId.ContainsKey(dbGuid) ? _gameObjectRespawnTimesBySpawnId[dbGuid].respawnTime : 0;
}
void SetTimer(uint t)
@@ -3421,6 +3810,33 @@ namespace Game.Maps
return go ? go.ToTransport() : null;
}
+ Creature GetCreatureBySpawnId(ulong spawnId)
+ {
+ var bounds = GetCreatureBySpawnIdStore().LookupByKey(spawnId);
+ if (bounds.Empty())
+ return null;
+
+ var foundCreature = bounds.Find(creature => creature.IsAlive());
+
+ return foundCreature != null ? foundCreature : bounds[0];
+ }
+
+ GameObject GetGameObjectBySpawnId(ulong spawnId)
+ {
+ var bounds = GetGameObjectBySpawnIdStore().LookupByKey(spawnId);
+ if (bounds.Empty())
+ return null;
+
+ var foundGameObject = bounds.Find(gameobject => gameobject.IsSpawned());
+
+ return foundGameObject != null ? foundGameObject : bounds[0];
+ }
+
+ public WorldObject GetWorldObjectBySpawnId(SpawnObjectType type, ulong spawnId)
+ {
+ return type == SpawnObjectType.GameObject ? (WorldObject)GetGameObjectBySpawnId(spawnId) : (WorldObject)GetCreatureBySpawnId(spawnId);
+ }
+
public void Visit(Cell cell, Visitor visitor)
{
uint x = cell.GetGridX();
@@ -3507,7 +3923,7 @@ namespace Game.Maps
return null;
}
- if (!summon.Create(GenerateLowGuid(HighGuid.Creature), this, entry, pos.posX, pos.posY, pos.posZ, pos.Orientation, null, vehId))
+ if (!summon.Create(GenerateLowGuid(HighGuid.Creature), this, entry, pos, null, vehId))
return null;
// Set the summon to the summoner's phase
@@ -3554,7 +3970,7 @@ namespace Game.Maps
_updateObjects.Remove(obj);
}
- public static implicit operator bool (Map map)
+ public static implicit operator bool(Map map)
{
return map != null;
}
@@ -4178,7 +4594,7 @@ namespace Game.Maps
uTarget = target?.ToUnit();
break;
case eScriptFlags.CastspellSourceToSource: // source . source
- uSource =source?.ToUnit();
+ uSource = source?.ToUnit();
uTarget = uSource;
break;
case eScriptFlags.CastspellTargetToTarget: // target . target
@@ -4442,10 +4858,14 @@ namespace Game.Maps
GridMap[][] GridMaps = new GridMap[MapConst.MaxGrids][];
ushort[][] GridMapReference = new ushort[MapConst.MaxGrids][];
- Dictionary _creatureRespawnTimes = new Dictionary();
DynamicMapTree _dynamicTree = new DynamicMapTree();
- Dictionary _goRespawnTimes = new Dictionary();
+ SortedSet _respawnTimes = new SortedSet(new CompareRespawnInfo());
+ Dictionary _creatureRespawnTimesBySpawnId = new Dictionary();
+ Dictionary _gameObjectRespawnTimesBySpawnId = new Dictionary();
+ uint _respawnCheckTimer;
+ Dictionary _zonePlayerCountMap = new Dictionary();
+
List _transports = new List();
Grid[][] i_grids = new Grid[MapConst.MaxGrids][];
MapRecord i_mapRecord;
@@ -5050,4 +5470,39 @@ namespace Game.Maps
public Optional areaInfo;
public Optional LiquidInfo;
}
+
+ public class RespawnInfo
+ {
+ public SpawnObjectType type;
+ public ulong spawnId;
+ public uint entry;
+ public long respawnTime;
+ public uint gridId;
+ public uint zoneId;
+
+ public RespawnInfo() { }
+ public RespawnInfo(RespawnInfo info)
+ {
+ type = info.type;
+ spawnId = info.spawnId;
+ entry = info.entry;
+ respawnTime = info.respawnTime;
+ gridId = info.gridId;
+ zoneId = info.zoneId;
+ }
+ }
+
+ struct CompareRespawnInfo : IComparer
+ {
+ public int Compare(RespawnInfo a, RespawnInfo b)
+ {
+ if (a.respawnTime != b.respawnTime)
+ return a.respawnTime.CompareTo(b.respawnTime);
+ if (a.spawnId != b.spawnId)
+ return a.spawnId.CompareTo(b.spawnId);
+
+ Cypher.Assert(a.type != b.type, $"Duplicate respawn entry for spawnId ({a.type},{a.spawnId}) found!");
+ return a.type.CompareTo(b.type);
+ }
+ }
}
\ No newline at end of file
diff --git a/Source/Game/Maps/MapManager.cs b/Source/Game/Maps/MapManager.cs
index 859fe27e0..34635a4a0 100644
--- a/Source/Game/Maps/MapManager.cs
+++ b/Source/Game/Maps/MapManager.cs
@@ -430,6 +430,16 @@ namespace Game.Entities
m.GetZoneAndAreaId(phaseShift, out zoneid, out areaid, x, y, z);
}
+ public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, uint mapid, Position pos)
+ {
+ GetZoneAndAreaId(phaseShift, out zoneid, out areaid, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
+ }
+
+ public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, WorldLocation loc)
+ {
+ GetZoneAndAreaId(phaseShift, out zoneid, out areaid, loc.GetMapId(), loc);
+ }
+
public void DoForAllMaps(Action