Dynamic Creature/Go spawning

Port From (https://github.com/TrinityCore/TrinityCore/commit/03b125e6d1947258316c931499746696a95aded2)
This commit is contained in:
hondacrx
2020-08-23 21:52:32 -04:00
parent 8fc9c45d50
commit 15ae7a7c66
45 changed files with 3925 additions and 1963 deletions
+19 -4
View File
@@ -1079,11 +1079,26 @@ namespace Framework.Constants
DebugSceneObjectList = 5068, DebugSceneObjectList = 5068,
DebugSceneObjectDetail = 5069, DebugSceneObjectDetail = 5069,
NpcinfoUnitFieldFlags2 = 5070, // Strings Added For DynamicSpawning
NpcinfoUnitFieldFlags3 = 5071, SpawninfoGroupId = 5070,
NpcinfoNpcFlags = 5072, 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 // Level Requirement Notifications
SayReq = 6604, SayReq = 6604,
+31
View File
@@ -20,6 +20,8 @@ namespace Framework.Constants
{ {
public class MapConst public class MapConst
{ {
public const uint InvalidZone = 0xFFFFFFFF;
//Grids //Grids
public const int MaxGrids = 64; public const int MaxGrids = 64;
public const float SizeofGrids = 533.33333f; public const float SizeofGrids = 533.33333f;
@@ -200,4 +202,33 @@ namespace Framework.Constants
All = Vmap | Gobject 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)
}
} }
+11
View File
@@ -1316,6 +1316,17 @@ namespace Framework.Constants
RealmZone, RealmZone,
ResetDuelCooldowns, ResetDuelCooldowns,
ResetDuelHealthMana, ResetDuelHealthMana,
RespawnDynamicEscortNpc,
RespawnDynamicMinimumCreature,
RespawnDynamicMinimumGameObject,
RespawnDynamicMode,
RespawnDynamicRateCreature,
RespawnDynamicRateGameobject,
RespawnGuidAlertLevel,
RespawnGuidWarnLevel,
RespawnGuidWarningFrequency,
RespawnMinCheckIntervalMs,
RespawnRestartQuietTime,
RestrictedLfgChannel, RestrictedLfgChannel,
SaveRespawnTimeImmediately, SaveRespawnTimeImmediately,
SessionAddDelay, SessionAddDelay,
+15 -1
View File
@@ -13,7 +13,9 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
using System;
namespace Framework.Constants namespace Framework.Constants
{ {
@@ -114,6 +116,15 @@ namespace Framework.Constants
End = 3 End = 3
} }
[Flags]
public enum SmartAiSpawnFlags
{
None = 0x00,
IgnoreRespawn = 0x01,
ForceSpawn = 0x02,
NosaveRespawn = 0x04,
}
public enum SmartAITemplate public enum SmartAITemplate
{ {
Basic = 0, //nothing is preset Basic = 0, //nothing is preset
@@ -125,6 +136,7 @@ namespace Framework.Constants
End = 6 End = 6
} }
[Flags]
public enum SmartCastFlags public enum SmartCastFlags
{ {
InterruptPrevious = 0x01, //Interrupt any spell casting InterruptPrevious = 0x01, //Interrupt any spell casting
@@ -361,6 +373,8 @@ namespace Framework.Constants
PlayAnimkit = 128, PlayAnimkit = 128,
ScenePlay = 129, // sceneId ScenePlay = 129, // sceneId
SceneCancel = 130, // 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 // 131 - 134 : 3.3.5 reserved
PlayCinematic = 135, // reserved for future uses PlayCinematic = 135, // reserved for future uses
SetMovementSpeed = 136, // movementType, speedInteger, speedFraction SetMovementSpeed = 136, // movementType, speedInteger, speedFraction
@@ -89,6 +89,7 @@ namespace Framework.Database
PrepareStatement(WorldStatements.DEL_DISABLES, "DELETE FROM disables WHERE entry = ? AND sourceType = ?"); 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_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.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 = ?"); 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, DEL_DISABLES,
UPD_CREATURE_ZONE_AREA_DATA, UPD_CREATURE_ZONE_AREA_DATA,
UPD_GAMEOBJECT_ZONE_AREA_DATA, UPD_GAMEOBJECT_ZONE_AREA_DATA,
DEL_SPAWNGROUP_MEMBER,
SEL_GUILD_REWARDS_REQ_ACHIEVEMENTS, SEL_GUILD_REWARDS_REQ_ACHIEVEMENTS,
MAX_WORLDDATABASE_STATEMENTS MAX_WORLDDATABASE_STATEMENTS
+9
View File
@@ -474,6 +474,15 @@ namespace Game.AI
// Object destruction is handled by Unit::RemoveCharmedBy // Object destruction is handled by Unit::RemoveCharmedBy
public virtual PlayerAI GetAIForCharmedPlayer(Player who) { return null; } public virtual PlayerAI GetAIForCharmedPlayer(Player who) { return null; }
/// <summary>
/// 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
/// </summary>
/// <param name="onlyIfActive"></param>
/// <returns></returns>
public virtual bool IsEscortNPC(bool onlyIfActive) { return false; }
List<AreaBoundary> GetBoundary() { return _boundary; } List<AreaBoundary> GetBoundary() { return _boundary; }
bool MoveInLineOfSight_locked; bool MoveInLineOfSight_locked;
+7 -5
View File
@@ -495,7 +495,7 @@ namespace Game.AI
_events.Reset(); _events.Reset();
summons.DespawnAll(); summons.DespawnAll();
_scheduler.CancelAll(); _scheduler.CancelAll();
if (instance != null) if (instance != null && instance.GetBossState(_bossId) != EncounterState.Done)
instance.SetBossState(_bossId, EncounterState.NotStarted); instance.SetBossState(_bossId, EncounterState.NotStarted);
} }
@@ -576,12 +576,14 @@ namespace Game.AI
DoMeleeAttackIfReady(); 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); Log.outError(LogFilter.Scripts, "_DespawnAtEvade called with delay of {0} seconds, defaulting to 2.", delayToRespawn);
delayToRespawn = 2; delayToRespawn = TimeSpan.FromSeconds(2);
} }
if (!who) if (!who)
@@ -595,7 +597,7 @@ namespace Game.AI
return; return;
} }
who.DespawnOrUnsummon(0, TimeSpan.FromSeconds(delayToRespawn)); who.DespawnOrUnsummon(0, delayToRespawn);
if (instance != null && who == me) if (instance != null && who == me)
instance.SetBossState(_bossId, EncounterState.Fail); instance.SetBossState(_bossId, EncounterState.Fail);
+44 -2
View File
@@ -18,6 +18,7 @@
using Framework.Constants; using Framework.Constants;
using Game.Entities; using Game.Entities;
using Game.Groups; using Game.Groups;
using Game.Maps;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -234,13 +235,17 @@ namespace Game.AI
return; return;
} }
if (m_bCanInstantRespawn) if (m_bCanInstantRespawn && !WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc))
{ {
me.SetDeathState(DeathState.JustDied); me.SetDeathState(DeathState.JustDied);
me.Respawn(); me.Respawn();
} }
else else
{
if (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc))
me.GetMap().RemoveRespawnTime(SpawnObjectType.Creature, me.GetSpawnId(), true);
me.DespawnOrUnsummon(); me.DespawnOrUnsummon();
}
return; return;
} }
@@ -274,11 +279,18 @@ namespace Game.AI
{ {
Log.outDebug(LogFilter.Scripts, "EscortAI failed because player/group was to far away or not found"); 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.SetDeathState(DeathState.JustDied);
me.Respawn(); me.Respawn();
} }
else if (m_bCanInstantRespawn && isEscort)
me.GetMap().RemoveRespawnTime(SpawnObjectType.Creature, me.GetSpawnId(), true);
else else
me.DespawnOrUnsummon(); me.DespawnOrUnsummon();
@@ -391,6 +403,25 @@ namespace Game.AI
/// todo get rid of this many variables passed in function. /// 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) 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()) 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()); 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; 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 WaypointReached(uint pointId) { }
public virtual void WaypointStart(uint pointId) { } public virtual void WaypointStart(uint pointId) { }
+21 -9
View File
@@ -129,39 +129,39 @@ namespace Game.AI
continue; continue;
} }
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(creature.id); CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(creature.Id);
if (creatureInfo == null) 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; continue;
} }
if (creatureInfo.AIName != "SmartAI") 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; continue;
} }
break; break;
} }
case SmartScriptType.GameObject: case SmartScriptType.GameObject:
{ {
GameObjectData gameObject = Global.ObjectMgr.GetGOData((ulong)-temp.entryOrGuid); GameObjectData gameObject = Global.ObjectMgr.GetGameObjectData((ulong)-temp.entryOrGuid);
if (gameObject == null) if (gameObject == null)
{ {
Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject guid ({-temp.entryOrGuid}) does not exist, skipped loading."); Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject guid ({-temp.entryOrGuid}) does not exist, skipped loading.");
continue; continue;
} }
GameObjectTemplate gameObjectInfo = Global.ObjectMgr.GetGameObjectTemplate(gameObject.id); GameObjectTemplate gameObjectInfo = Global.ObjectMgr.GetGameObjectTemplate(gameObject.Id);
if (gameObjectInfo == null) 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; continue;
} }
if (gameObjectInfo.AIName != "SmartGameObjectAI") 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; continue;
} }
break; break;
@@ -758,7 +758,7 @@ namespace Game.AI
return false; 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); Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject guid {0}, skipped.", e.Event.distance.guid);
return false; return false;
@@ -1376,6 +1376,8 @@ namespace Game.AI
case SmartActions.TriggerRandomTimedEvent: case SmartActions.TriggerRandomTimedEvent:
case SmartActions.SetCounter: case SmartActions.SetCounter:
case SmartActions.RemoveAllGameobjects: case SmartActions.RemoveAllGameobjects:
case SmartActions.SpawnSpawngroup:
case SmartActions.DespawnSpawngroup:
break; break;
default: 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); 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; return false;
} }
else else
entry = data.id; entry = data.Id;
} }
else else
entry = (uint)e.entryOrGuid; entry = (uint)e.entryOrGuid;
@@ -2368,6 +2370,9 @@ namespace Game.AI
[FieldOffset(4)] [FieldOffset(4)]
public DisableEvade disableEvade; public DisableEvade disableEvade;
[FieldOffset(4)]
public GroupSpawn groupSpawn;
[FieldOffset(4)] [FieldOffset(4)]
public AuraType auraType; public AuraType auraType;
@@ -2854,6 +2859,13 @@ namespace Game.AI
{ {
public uint disable; public uint disable;
} }
public struct GroupSpawn
{
public uint groupId;
public uint minDelay;
public uint maxDelay;
public uint spawnflags;
}
public struct AuraType public struct AuraType
{ {
public uint type; public uint type;
@@ -2051,6 +2051,91 @@ namespace Game.AI
target.ToCreature().SetCorpseDelay(e.Action.corpseDelay.timer); target.ToCreature().SetCorpseDelay(e.Action.corpseDelay.timer);
break; 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: case SmartActions.DisableEvade:
{ {
if (!IsSmart()) if (!IsSmart())
@@ -201,7 +201,7 @@ namespace Game.Chat
if (gameObjectInfo == null) if (gameObjectInfo == null)
continue; 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; ++count;
} while (result.NextRow()); } while (result.NextRow());
@@ -412,10 +412,10 @@ namespace Game.Chat
if (!ulong.TryParse(cValue, out ulong guidLow)) if (!ulong.TryParse(cValue, out ulong guidLow))
return false; return false;
GameObjectData data = Global.ObjectMgr.GetGOData(guidLow); GameObjectData data = Global.ObjectMgr.GetGameObjectData(guidLow);
if (data == null) if (data == null)
return false; return false;
entry = data.id; entry = data.Id;
} }
else else
{ {
@@ -427,15 +427,45 @@ namespace Game.Chat
if (gameObjectInfo == null) if (gameObjectInfo == null)
return false; 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; GameObjectTypes type = gameObjectInfo.type;
uint displayId = gameObjectInfo.displayId; uint displayId = gameObjectInfo.displayId;
string name = gameObjectInfo.name; string name = gameObjectInfo.name;
uint lootId = gameObjectInfo.GetLootId(); 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.GoinfoEntry, entry);
handler.SendSysMessage(CypherStrings.GoinfoType, type); handler.SendSysMessage(CypherStrings.GoinfoType, type);
handler.SendSysMessage(CypherStrings.GoinfoLootid, lootId); handler.SendSysMessage(CypherStrings.GoinfoLootid, lootId);
handler.SendSysMessage(CypherStrings.GoinfoDisplayid, displayId); 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.GoinfoName, name);
handler.SendSysMessage(CypherStrings.GoinfoSize, gameObjectInfo.size); handler.SendSysMessage(CypherStrings.GoinfoSize, gameObjectInfo.size);
@@ -508,7 +538,7 @@ namespace Game.Chat
return false; return false;
// TODO: is it really necessary to add both the real and DB table guid here ? // 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()); handler.SendSysMessage(CypherStrings.GameobjectAdd, objectId, objectInfo.name, spawnId, player.GetPositionX(), player.GetPositionY(), player.GetPositionZ());
return true; return true;
} }
+5 -16
View File
@@ -202,28 +202,17 @@ namespace Game.Chat.Commands
if (!ulong.TryParse(id, out ulong guidLow) || guidLow == 0) if (!ulong.TryParse(id, out ulong guidLow) || guidLow == 0)
return false; return false;
float x, y, z, o;
uint mapId;
// by DB guid // by DB guid
GameObjectData goData = Global.ObjectMgr.GetGOData(guidLow); GameObjectData goData = Global.ObjectMgr.GetGameObjectData(guidLow);
if (goData != null) if (goData == null)
{
x = goData.posX;
y = goData.posY;
z = goData.posZ;
o = goData.orientation;
mapId = goData.mapid;
}
else
{ {
handler.SendSysMessage(CypherStrings.CommandGoobjnotfound); handler.SendSysMessage(CypherStrings.CommandGoobjnotfound);
return false; 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; return false;
} }
@@ -237,7 +226,7 @@ namespace Game.Chat.Commands
else else
player.SaveRecallPosition(); player.SaveRecallPosition();
player.TeleportTo(mapId, x, y, z, o); player.TeleportTo(goData.spawnPoint);
return true; return true;
} }
+139 -4
View File
@@ -18,7 +18,9 @@
using Framework.Constants; using Framework.Constants;
using Framework.Database; using Framework.Database;
using Framework.IO; using Framework.IO;
using Game.DataStorage;
using Game.Entities; using Game.Entities;
using Game.Maps;
using Game.Spells; using Game.Spells;
using System.Collections.Generic; using System.Collections.Generic;
@@ -128,11 +130,39 @@ namespace Game.Chat.Commands
float y = result.Read<float>(2); float y = result.Read<float>(2);
float z = result.Read<float>(3); float z = result.Read<float>(3);
ushort mapId = result.Read<ushort>(4); ushort mapId = result.Read<ushort>(4);
bool liveFound = false;
// Get map (only support base map from console)
Map thisMap;
if (handler.GetSession() != null) if (handler.GetSession() != null)
handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, cInfo.Name, x, y, z, mapId); thisMap = handler.GetSession().GetPlayer().GetMap();
else 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()); while (result.NextRow());
} }
@@ -495,11 +525,39 @@ namespace Game.Chat.Commands
float z = result.Read<float>(3); float z = result.Read<float>(3);
ushort mapId = result.Read<ushort>(4); ushort mapId = result.Read<ushort>(4);
uint entry = result.Read<uint>(5); uint entry = result.Read<uint>(5);
bool liveFound = false;
// Get map (only support base map from console)
Map thisMap;
if (handler.GetSession() != null) if (handler.GetSession() != null)
handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gInfo.name, x, y, z, mapId); thisMap = handler.GetSession().GetPlayer().GetMap();
else 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()); while (result.NextRow());
} }
@@ -509,6 +567,77 @@ namespace Game.Chat.Commands
return true; 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<RespawnInfo> respawns = new List<RespawnInfo>();
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)] [Command("scenes", RBACPermissions.CommandListScenes)]
static bool HandleListScenesCommand(StringArguments args, CommandHandler handler) static bool HandleListScenesCommand(StringArguments args, CommandHandler handler)
{ {
@@ -531,5 +660,11 @@ namespace Game.Chat.Commands
return true; return true;
} }
static string GetZoneName(uint zoneId, Locale locale)
{
AreaTableRecord zoneEntry = CliDB.AreaTableStorage.LookupByKey(zoneId);
return zoneEntry != null ? zoneEntry.AreaName[locale] : "<unknown zone>";
}
} }
} }
File diff suppressed because it is too large Load Diff
+140 -61
View File
@@ -33,6 +33,41 @@ namespace Game.Chat
[CommandGroup("npc", RBACPermissions.CommandNpc)] [CommandGroup("npc", RBACPermissions.CommandNpc)]
class NPCCommands 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)] [Command("evade", RBACPermissions.CommandNpcEvade)]
static bool HandleNpcEvadeCommand(StringArguments args, CommandHandler handler) static bool HandleNpcEvadeCommand(StringArguments args, CommandHandler handler)
{ {
@@ -96,13 +131,21 @@ namespace Game.Chat
uint nativeid = target.GetNativeDisplayId(); uint nativeid = target.GetNativeDisplayId();
uint Entry = target.GetEntry(); 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) if (curRespawnDelay < 0)
curRespawnDelay = 0; curRespawnDelay = 0;
string curRespawnDelayStr = Time.secsToTimeString((ulong)curRespawnDelay, true); string curRespawnDelayStr = Time.secsToTimeString((ulong)curRespawnDelay, true);
string defRespawnDelayStr = Time.secsToTimeString(target.GetRespawnDelay(), true); string defRespawnDelayStr = Time.secsToTimeString(target.GetRespawnDelay(), true);
handler.SendSysMessage(CypherStrings.NpcinfoChar, target.GetSpawnId(), target.GetGUID().ToString(), faction, npcflags, Entry, displayid, nativeid); 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.NpcinfoLevel, target.GetLevel());
handler.SendSysMessage(CypherStrings.NpcinfoEquipment, target.GetCurrentEquipmentId(), target.GetOriginalEquipmentId()); handler.SendSysMessage(CypherStrings.NpcinfoEquipment, target.GetCurrentEquipmentId(), target.GetOriginalEquipmentId());
handler.SendSysMessage(CypherStrings.NpcinfoHealth, target.GetCreateHealth(), target.GetMaxHealth(), target.GetHealth()); handler.SendSysMessage(CypherStrings.NpcinfoHealth, target.GetCreateHealth(), target.GetMaxHealth(), target.GetHealth());
@@ -161,71 +204,60 @@ namespace Game.Chat
ulong lowguid; ulong lowguid;
Creature creature = handler.GetSelectedCreature(); 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 // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
string cId = handler.ExtractKeyFromLink(args, "Hcreature"); string cId = handler.ExtractKeyFromLink(args, "Hcreature");
if (string.IsNullOrEmpty(cId)) if (cId.IsEmpty())
return false; return false;
if (!ulong.TryParse(cId, out lowguid)) if (!ulong.TryParse(cId, out lowguid))
return false; return false;
}
// Attempting creature load from DB data // Attempting creature load from DB data
CreatureData data = Global.ObjectMgr.GetCreatureData(lowguid); CreatureData data = Global.ObjectMgr.GetCreatureData(lowguid);
if (data == null) 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
{ {
lowguid = creature.GetSpawnId(); handler.SendSysMessage(CypherStrings.CommandCreatguidnotfound, lowguid);
return false;
} }
float x = handler.GetPlayer().GetPositionX(); if (player.GetMapId() != data.spawnPoint.GetMapId())
float y = handler.GetPlayer().GetPositionY();
float z = handler.GetPlayer().GetPositionZ();
float o = handler.GetPlayer().GetOrientation();
if (creature)
{ {
CreatureData data = Global.ObjectMgr.GetCreatureData(creature.GetSpawnId()); handler.SendSysMessage(CypherStrings.CommandCreatureatsamemap, lowguid);
if (data != null) return false;
{
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();
}
} }
data.spawnPoint.Relocate(player);
// update position in DB
PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_CREATURE_POSITION); PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_CREATURE_POSITION);
stmt.AddValue(0, player.GetPositionX());
stmt.AddValue(0, x); stmt.AddValue(1, player.GetPositionY());
stmt.AddValue(1, y); stmt.AddValue(2, player.GetPositionZ());
stmt.AddValue(2, z); stmt.AddValue(3, player.GetOrientation());
stmt.AddValue(3, o);
stmt.AddValue(4, lowguid); stmt.AddValue(4, lowguid);
DB.World.Execute(stmt); 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); handler.SendSysMessage(CypherStrings.CommandCreaturemoved);
return true; return true;
} }
@@ -264,7 +296,7 @@ namespace Game.Chat
if (creatureTemplate == null) if (creatureTemplate == null)
continue; 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; ++count;
} }
@@ -386,6 +418,49 @@ namespace Game.Chat
return true; 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<WorldObject> creatureList = new List<WorldObject>();
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)] [Command("tame", RBACPermissions.CommandNpcTame)]
static bool HandleNpcTameCommand(StringArguments args, CommandHandler handler) static bool HandleNpcTameCommand(StringArguments args, CommandHandler handler)
{ {
@@ -581,13 +656,11 @@ namespace Game.Chat
{ {
ulong guid = map.GenerateLowGuid(HighGuid.Creature); ulong guid = map.GenerateLowGuid(HighGuid.Creature);
CreatureData data = Global.ObjectMgr.NewOrExistCreatureData(guid); CreatureData data = Global.ObjectMgr.NewOrExistCreatureData(guid);
data.id = id; data.spawnId = guid;
data.posX = chr.GetTransOffsetX(); data.Id = id;
data.posY = chr.GetTransOffsetY(); data.spawnPoint.Relocate(chr.GetTransOffsetX(), chr.GetTransOffsetY(), chr.GetTransOffsetZ(), chr.GetTransOffsetO());
data.posZ = chr.GetTransOffsetZ();
data.orientation = chr.GetTransOffsetO();
// @todo: add phases // @todo: add phases
Creature _creature = trans.CreateNPCPassenger(guid, data); Creature _creature = trans.CreateNPCPassenger(guid, data);
_creature.SaveToDB((uint)trans.GetGoInfo().MoTransport.SpawnMap, new List<Difficulty>() { map.GetDifficultyID() }); _creature.SaveToDB((uint)trans.GetGoInfo().MoTransport.SpawnMap, new List<Difficulty>() { map.GetDifficultyID() });
@@ -607,7 +680,7 @@ namespace Game.Chat
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells() // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells()
// current "creature" variable is deleted and created fresh new, otherwise old values might trigger asserts or cause undefined behavior // current "creature" variable is deleted and created fresh new, otherwise old values might trigger asserts or cause undefined behavior
creature.CleanupsBeforeDelete(); creature.CleanupsBeforeDelete();
creature = Creature.CreateCreatureFromDB(db_guid, map); creature = Creature.CreateCreatureFromDB(db_guid, map, true, true);
if (!creature) if (!creature)
return false; return false;
@@ -794,7 +867,7 @@ namespace Game.Chat
[Command("delete", RBACPermissions.CommandNpcDelete)] [Command("delete", RBACPermissions.CommandNpcDelete)]
static bool HandleNpcDeleteCommand(StringArguments args, CommandHandler handler) static bool HandleNpcDeleteCommand(StringArguments args, CommandHandler handler)
{ {
Creature unit = null; Creature creature;
if (!args.Empty()) if (!args.Empty())
{ {
@@ -806,21 +879,27 @@ namespace Game.Chat
if (!ulong.TryParse(cId, out ulong guidLow) || guidLow == 0) if (!ulong.TryParse(cId, out ulong guidLow) || guidLow == 0)
return false; return false;
unit = handler.GetCreatureFromPlayerMapByDbGuid(guidLow); creature = handler.GetCreatureFromPlayerMapByDbGuid(guidLow);
} }
else else
unit = handler.GetSelectedCreature(); creature = handler.GetSelectedCreature();
if (!unit || unit.IsPet() || unit.IsTotem()) if (!creature || creature.IsPet() || creature.IsTotem())
{ {
handler.SendSysMessage(CypherStrings.SelectCreature); handler.SendSysMessage(CypherStrings.SelectCreature);
return false; return false;
} }
// Delete the creature TempSummon summon = creature.ToTempSummon();
unit.CombatStop(); if (summon != null)
unit.DeleteFromDB(); summon.UnSummon();
unit.AddObjectToRemoveList(); else
{
// Delete the creature
creature.CombatStop();
creature.DeleteFromDB();
creature.AddObjectToRemoveList();
}
handler.SendSysMessage(CypherStrings.CommandDelcreatmessage); handler.SendSysMessage(CypherStrings.CommandDelcreatmessage);
+4 -4
View File
@@ -559,7 +559,7 @@ namespace Game.Chat.Commands
creature.Dispose(); creature.Dispose();
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
creature = Creature.CreateCreatureFromDB(dbGuid, map); creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
if (!creature) if (!creature)
{ {
handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1); handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1);
@@ -779,7 +779,7 @@ namespace Game.Chat.Commands
creature.Dispose(); creature.Dispose();
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
creature = Creature.CreateCreatureFromDB(dbGuid, map); creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
if (!creature) if (!creature)
{ {
handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, id); handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, id);
@@ -844,7 +844,7 @@ namespace Game.Chat.Commands
creature.CleanupsBeforeDelete(); creature.CleanupsBeforeDelete();
creature.Dispose(); creature.Dispose();
creature = Creature.CreateCreatureFromDB(dbGuid, map); creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
if (!creature) if (!creature)
{ {
handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1); handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1);
@@ -899,7 +899,7 @@ namespace Game.Chat.Commands
creature.CleanupsBeforeDelete(); creature.CleanupsBeforeDelete();
creature.Dispose(); creature.Dispose();
creature = Creature.CreateCreatureFromDB(dbGuid, map); creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
if (!creature) if (!creature)
{ {
handler.SendSysMessage(CypherStrings.WaypointNotcreated, 1); handler.SendSysMessage(CypherStrings.WaypointNotcreated, 1);
+3 -3
View File
@@ -1328,7 +1328,7 @@ namespace Game
CreatureData creatureData = Global.ObjectMgr.GetCreatureData(cond.ConditionValue3); CreatureData creatureData = Global.ObjectMgr.GetCreatureData(cond.ConditionValue3);
if (creatureData != null) 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); 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; return false;
@@ -1349,10 +1349,10 @@ namespace Game
} }
if (cond.ConditionValue3 != 0) if (cond.ConditionValue3 != 0)
{ {
GameObjectData goData = Global.ObjectMgr.GetGOData(cond.ConditionValue3); GameObjectData goData = Global.ObjectMgr.GetGameObjectData(cond.ConditionValue3);
if (goData != null) 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); 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; return false;
@@ -71,6 +71,7 @@ namespace Game.Entities
//Formation var //Formation var
CreatureGroup m_formation; CreatureGroup m_formation;
bool TriggerJustRespawned; bool TriggerJustRespawned;
bool m_respawnCompatibilityMode;
public uint[] m_spells = new uint[SharedConst.MaxCreatureSpells]; public uint[] m_spells = new uint[SharedConst.MaxCreatureSpells];
+247 -158
View File
@@ -123,45 +123,62 @@ namespace Game.Entities
if (GetDeathState() != DeathState.Corpse) if (GetDeathState() != DeathState.Corpse)
return; return;
m_corpseRemoveTime = Time.UnixTime; if (m_respawnCompatibilityMode)
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())
{ {
Position pos = m_movementInfo.transport.pos; m_corpseRemoveTime = Time.UnixTime;
pos.posX = x; SetDeathState(DeathState.Dead);
pos.posY = y; RemoveAllAuras();
pos.posZ = z; //DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
pos.SetOrientation(o); loot.Clear();
uint respawnDelay = m_respawnDelay;
if (IsAIEnabled)
GetAI().CorpseRemoved(respawnDelay);
ITransport transport = GetDirectTransport(); if (destroyForNearbyPlayers)
if (transport != null) DestroyForNearbyPlayers();
transport.CalculatePassengerPosition(ref x, ref y, ref z, ref o);
// 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); SaveRespawnTime(0, false);
GetMap().CreatureRelocation(this, x, y, z, o); }
AddObjectToRemoveList();
}
} }
public bool InitEntry(uint entry, CreatureData data = null) public bool InitEntry(uint entry, CreatureData data = null)
@@ -375,7 +392,7 @@ namespace Game.Entities
{ {
case DeathState.JustRespawned: case DeathState.JustRespawned:
case DeathState.JustDied: 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; break;
case DeathState.Dead: case DeathState.Dead:
{ {
@@ -383,22 +400,25 @@ namespace Game.Entities
if (m_respawnTime <= now) if (m_respawnTime <= now)
{ {
// First check if there are any scripts that object to us respawning // First check if there are any scripts that object to us respawning
if (!Global.ScriptMgr.CanSpawn(GetSpawnId(), GetEntry(), GetCreatureTemplate(), GetCreatureData(), GetMap())) if (!Global.ScriptMgr.CanSpawn(GetSpawnId(), GetEntry(), GetCreatureData(), GetMap()))
break; // Will be rechecked on next Update call {
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); ObjectGuid dbtableHighGuid = ObjectGuid.Create(HighGuid.Creature, GetMapId(), GetEntry(), m_spawnId);
long linkedRespawntime = GetMap().GetLinkedRespawnTime(dbtableHighGuid); long linkedRespawnTime = GetMap().GetLinkedRespawnTime(dbtableHighGuid);
if (linkedRespawntime == 0) // Can respawn if (linkedRespawnTime == 0) // Can respawn
Respawn(); Respawn();
else // the master is dead else // the master is dead
{ {
ObjectGuid targetGuid = Global.ObjectMgr.GetLinkedRespawnGuid(dbtableHighGuid); ObjectGuid targetGuid = Global.ObjectMgr.GetLinkedRespawnGuid(dbtableHighGuid);
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day) if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
SetRespawnTime(Time.Day); SetRespawnTime(Time.Week);
else else
{ {
// else copy time from master and add a little // 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); long offset = RandomHelper.URand(5, Time.Minute);
// linked guid can be a boss, uses std::numeric_limits<time_t>::max to never respawn in that instance // linked guid can be a boss, uses std::numeric_limits<time_t>::max to never respawn in that instance
@@ -731,7 +751,7 @@ namespace Game.Entities
lowGuid = map.GenerateLowGuid(HighGuid.Creature); lowGuid = map.GenerateLowGuid(HighGuid.Creature);
Creature creature = new 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 null;
return creature; return creature;
@@ -740,13 +760,13 @@ namespace Game.Entities
public static Creature CreateCreatureFromDB(ulong spawnId, Map map, bool addToMap = true, bool allowDuplicate = false) public static Creature CreateCreatureFromDB(ulong spawnId, Map map, bool addToMap = true, bool allowDuplicate = false)
{ {
Creature creature = new Creature(); Creature creature = new Creature();
if (!creature.LoadCreatureFromDB(spawnId, map, addToMap, allowDuplicate)) if (!creature.LoadFromDB(spawnId, map, addToMap, allowDuplicate))
return null; return null;
return creature; 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); SetMap(map);
@@ -756,6 +776,10 @@ namespace Game.Entities
PhasingHandler.InitDbVisibleMapId(GetPhaseShift(), data.terrainSwapMap); PhasingHandler.InitDbVisibleMapId(GetPhaseShift(), data.terrainSwapMap);
} }
// Set if this creature can handle dynamic spawns
if (!dynamic)
SetRespawnCompatibilityMode();
CreatureTemplate cinfo = Global.ObjectMgr.GetCreatureTemplate(entry); CreatureTemplate cinfo = Global.ObjectMgr.GetCreatureTemplate(entry);
if (cinfo == null) if (cinfo == null)
{ {
@@ -765,13 +789,13 @@ namespace Game.Entities
//! Relocate before CreateFromProto, to initialize coords and allow //! Relocate before CreateFromProto, to initialize coords and allow
//! returning correct zone id for selecting OutdoorPvP/Battlefield script //! 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 // 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 // invalid position, triggering a crash about Auras not removed in the destructor
if (!IsPositionValid()) 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; return false;
} }
UpdatePositionData(); UpdatePositionData();
@@ -810,10 +834,8 @@ namespace Game.Entities
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there //! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
if (HasUnitMovementFlag(MovementFlag.Hover)) if (HasUnitMovementFlag(MovementFlag.Hover))
{ {
z += m_unitData.HoverHeight;
//! Relocate again with updated Z coord //! Relocate again with updated Z coord
Relocate(x, y, z, ang); posZ += m_unitData.HoverHeight;
} }
LastUsedScriptID = GetScriptId(); LastUsedScriptID = GetScriptId();
@@ -924,6 +946,14 @@ namespace Game.Entities
&& !GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoXpAtKill); && !GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoXpAtKill);
} }
public bool IsEscortNPC(bool onlyIfActive = true)
{
if (!IsAIEnabled)
return false;
return GetAI().IsEscortNPC(onlyIfActive);
}
public override bool IsMovementPreventedByCasting() public override bool IsMovementPreventedByCasting()
{ {
// first check if currently a movement allowed channel is active and we're not casting // first check if currently a movement allowed channel is active and we're not casting
@@ -1068,16 +1098,20 @@ namespace Game.Entities
dynamicflags = 0; dynamicflags = 0;
} }
// data.guid = guid must not be updated at save if (data.spawnId == 0)
data.id = GetEntry(); data.spawnId = m_spawnId;
data.mapid = (ushort)mapid; Cypher.Assert(data.spawnId == m_spawnId);
data.Id = GetEntry();
data.displayid = displayId; data.displayid = displayId;
data.equipmentId = GetCurrentEquipmentId(); data.equipmentId = (sbyte)GetCurrentEquipmentId();
data.posX = GetPositionX();
data.posY = GetPositionY(); if (GetTransport() == null)
data.posZ = GetPositionZMinusOffset(); data.spawnPoint.WorldRelocate(this);
data.orientation = GetOrientation(); else
data.spawntimesecs = m_respawnDelay; data.spawnPoint.WorldRelocate(mapid, GetTransOffsetX(), GetTransOffsetY(), GetTransOffsetZ(), GetTransOffsetO());
data.spawntimesecs = (int)m_respawnDelay;
// prevent add data integrity problems // prevent add data integrity problems
data.spawndist = GetDefaultMovementType() == MovementGeneratorType.Idle ? 0.0f : m_respawnradius; data.spawndist = GetDefaultMovementType() == MovementGeneratorType.Idle ? 0.0f : m_respawnradius;
data.currentwaypoint = 0; data.currentwaypoint = 0;
@@ -1092,6 +1126,8 @@ namespace Game.Entities
data.unit_flags2 = unitFlags2; data.unit_flags2 = unitFlags2;
data.unit_flags3 = unitFlags3; data.unit_flags3 = unitFlags3;
data.dynamicflags = (uint)dynamicflags; data.dynamicflags = (uint)dynamicflags;
if (data.spawnGroupData == null)
data.spawnGroupData = Global.ObjectMgr.GetDefaultSpawnGroup();
data.phaseId = GetDBPhase() > 0 ? (uint)GetDBPhase() : data.phaseId; data.phaseId = GetDBPhase() > 0 ? (uint)GetDBPhase() : data.phaseId;
data.phaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.phaseGroup; data.phaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.phaseGroup;
@@ -1414,7 +1450,7 @@ namespace Game.Entities
return; return;
} }
GetMap().RemoveCreatureRespawnTime(m_spawnId); GetMap().RemoveRespawnTime(SpawnObjectType.Creature, m_spawnId);
Global.ObjectMgr.DeleteCreatureData(m_spawnId); Global.ObjectMgr.DeleteCreatureData(m_spawnId);
SQLTransaction trans = new SQLTransaction(); SQLTransaction trans = new SQLTransaction();
@@ -1423,6 +1459,11 @@ namespace Game.Entities
stmt.AddValue(0, m_spawnId); stmt.AddValue(0, m_spawnId);
trans.Append(stmt); 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 = DB.World.GetPreparedStatement(WorldStatements.DEL_CREATURE_ADDON);
stmt.AddValue(0, m_spawnId); stmt.AddValue(0, m_spawnId);
trans.Append(stmt); trans.Append(stmt);
@@ -1549,14 +1590,31 @@ namespace Game.Entities
if (s == DeathState.JustDied) if (s == DeathState.JustDied)
{ {
m_corpseRemoveTime = Time.UnixTime + m_corpseDelay; m_corpseRemoveTime = Time.UnixTime + m_corpseDelay;
if (IsDungeonBoss() && m_respawnDelay == 0) uint respawnDelay = m_respawnDelay;
m_respawnTime = long.MaxValue; // never respawn in this instance 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 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 // always save boss respawn time at death to prevent crash cheating
if (WorldConfig.GetBoolValue(WorldCfg.SaveRespawnTimeImmediately) || IsWorldBoss()) if (WorldConfig.GetBoolValue(WorldCfg.SaveRespawnTimeImmediately) || IsWorldBoss())
SaveRespawnTime(); SaveRespawnTime();
else if (!m_respawnCompatibilityMode)
SaveRespawnTime(0, false);
ReleaseFocus(null, false); // remove spellcast focus ReleaseFocus(null, false); // remove spellcast focus
DoNotReacquireTarget(); // cancel delayed re-target DoNotReacquireTarget(); // cancel delayed re-target
@@ -1632,8 +1690,6 @@ namespace Game.Entities
public void Respawn(bool force = false) public void Respawn(bool force = false)
{ {
DestroyForNearbyPlayers();
if (force) if (force)
{ {
if (IsAlive()) if (IsAlive())
@@ -1642,49 +1698,60 @@ namespace Game.Entities
SetDeathState(DeathState.Corpse); SetDeathState(DeathState.Corpse);
} }
RemoveCorpse(false, false); if (m_respawnCompatibilityMode)
if (GetDeathState() == DeathState.Dead)
{ {
if (m_spawnId != 0) DestroyForNearbyPlayers();
GetMap().RemoveCreatureRespawnTime(m_spawnId); RemoveCorpse(false, false);
Log.outDebug(LogFilter.Unit, "Respawning creature {0} ({1})", GetName(), GetGUID().ToString()); if (GetDeathState() == DeathState.Dead)
m_respawnTime = 0; {
ResetPickPocketRefillTimer(); if (m_spawnId != 0)
loot.Clear(); 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(); SelectLevel();
SetDeathState(DeathState.JustRespawned); SetDeathState(DeathState.JustRespawned);
CreatureModel display = new CreatureModel(GetNativeDisplayId(), GetNativeDisplayScale(), 1.0f); CreatureModel display = new CreatureModel(GetNativeDisplayId(), GetNativeDisplayScale(), 1.0f);
if (Global.ObjectMgr.GetCreatureModelRandomGender(ref display, GetCreatureTemplate()) != null) if (Global.ObjectMgr.GetCreatureModelRandomGender(ref display, GetCreatureTemplate()) != null)
{ {
SetDisplayId(display.CreatureDisplayID, display.DisplayScale); SetDisplayId(display.CreatureDisplayID, display.DisplayScale);
SetNativeDisplayId(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<Creature>(GetSpawnId()) : 0;
if (poolid != 0)
Global.PoolMgr.UpdatePool<Creature>(poolid, GetSpawnId());
} }
UpdateObjectVisibility();
GetMotionMaster().InitDefault(); }
//Re-initialize reactstate that could be altered by movementgenerators else
InitializeReactState(); {
if (m_spawnId != 0)
//Call AI respawn virtual function GetMap().RemoveRespawnTime(SpawnObjectType.Creature, m_spawnId, true);
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<Creature>(GetSpawnId()) : 0;
if (poolid != 0)
Global.PoolMgr.UpdatePool<Creature>(poolid, GetSpawnId());
} }
UpdateObjectVisibility(); Log.outDebug(LogFilter.Unit, $"Respawning creature {GetName()} ({GetGUID()})");
} }
public void ForcedDespawn(uint timeMSToDespawn = 0, TimeSpan forceRespawnTimer = default) public void ForcedDespawn(uint timeMSToDespawn = 0, TimeSpan forceRespawnTimer = default)
@@ -1695,30 +1762,49 @@ namespace Game.Entities
return; return;
} }
uint corpseDelay = GetCorpseDelay(); if (m_respawnCompatibilityMode)
uint respawnDelay = GetRespawnDelay();
// do it before killing creature
DestroyForNearbyPlayers();
bool overrideRespawnTime = false;
if (IsAlive())
{ {
if (forceRespawnTimer > TimeSpan.Zero) uint corpseDelay = GetCorpseDelay();
uint respawnDelay = GetRespawnDelay();
// do it before killing creature
DestroyForNearbyPlayers();
bool overrideRespawnTime = false;
if (IsAlive())
{ {
SetCorpseDelay(0); if (forceRespawnTimer > TimeSpan.Zero)
SetRespawnDelay((uint)forceRespawnTimer.TotalSeconds); {
overrideRespawnTime = false; 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 AddObjectToRemoveList();
RemoveCorpse(overrideRespawnTime, false); }
SetCorpseDelay(corpseDelay);
SetRespawnDelay(respawnDelay);
} }
public void DespawnOrUnsummon(TimeSpan time, TimeSpan forceRespawnTimer = default) { DespawnOrUnsummon((uint)time.TotalMilliseconds, forceRespawnTimer); } public void DespawnOrUnsummon(TimeSpan time, TimeSpan forceRespawnTimer = default) { DespawnOrUnsummon((uint)time.TotalMilliseconds, forceRespawnTimer); }
@@ -2074,12 +2160,19 @@ namespace Game.Entities
return false; 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)) if (IsSummon() || m_spawnId == 0 || (m_creatureData != null && !m_creatureData.dbData))
return; 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) 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) 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_creatureData != null)
if (m_spawnId != 0)
{ {
CreatureData data = Global.ObjectMgr.GetCreatureData(GetSpawnId()); m_creatureData.spawnPoint.GetPosition(out x, out y, out z, out ori);
if (data != null) dist = m_creatureData.spawndist;
{ }
x = data.posX; else
y = data.posY; {
z = data.posZ; Position homePos = GetHomePosition();
ori = data.orientation; homePos.GetPosition(out x, out y, out z, out ori);
dist = data.spawndist; dist = 0;
return;
}
} }
// 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() public void AllLootRemovedFromCorpse()
{ {
@@ -2934,12 +3015,7 @@ namespace Game.Entities
public CreatureTemplate GetCreatureTemplate() { return m_creatureInfo; } public CreatureTemplate GetCreatureTemplate() { return m_creatureInfo; }
public CreatureData GetCreatureData() { return m_creatureData; } public CreatureData GetCreatureData() { return m_creatureData; }
public override bool LoadFromDB(ulong spawnId, Map map) public override bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate)
{
return LoadCreatureFromDB(spawnId, map, false, false);
}
public bool LoadCreatureFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate)
{ {
if (!allowDuplicate) if (!allowDuplicate)
{ {
@@ -2976,39 +3052,48 @@ namespace Game.Entities
} }
m_spawnId = spawnId; m_spawnId = spawnId;
m_respawnCompatibilityMode = data.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.CompatibilityMode);
m_creatureData = data; m_creatureData = data;
m_respawnradius = data.spawndist; m_respawnradius = data.spawndist;
m_respawnDelay = data.spawntimesecs; m_respawnDelay = (uint)data.spawntimesecs;
if (!Create(map.GenerateLowGuid(HighGuid.Creature), map, data.id, data.posX, data.posY, data.posZ, data.orientation, data, 0)) // 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; return false;
//We should set first home position, because then AI calls home movement //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_deathState = DeathState.Alive;
m_respawnTime = GetMap().GetCreatureRespawnTime(m_spawnId); 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) // Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update)
if (m_respawnTime == 0 && !Global.ScriptMgr.CanSpawn(spawnId, GetEntry(), GetCreatureTemplate(), GetCreatureData(), map)) if (m_respawnCompatibilityMode && m_respawnTime == 0 && !Global.ScriptMgr.CanSpawn(spawnId, GetEntry(), GetCreatureData(), map))
m_respawnTime = Time.UnixTime + 1; 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; m_deathState = DeathState.Dead;
if (CanFly()) if (CanFly())
{ {
float tz = map.GetHeight(GetPhaseShift(), data.posX, data.posY, data.posZ, true, MapConst.MaxFallDistance); float tz = map.GetHeight(GetPhaseShift(), data.spawnPoint, true, MapConst.MaxFallDistance);
if (data.posZ - tz > 0.1f && GridDefines.IsValidMapCoord(tz)) if (data.spawnPoint.GetPositionZ() - tz > 0.1f && GridDefines.IsValidMapCoord(tz))
Relocate(data.posX, data.posY, tz); Relocate(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY(), tz);
} }
} }
SetSpawnHealth(); SetSpawnHealth();
// checked at creature_template loading
DefaultMovementType = (MovementGeneratorType)data.movementType; 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)) if (addToMap && !GetMap().AddToMap(this))
return false; return false;
@@ -3102,6 +3187,10 @@ namespace Game.Entities
m_originalEntry = entry; m_originalEntry = entry;
} }
// There's many places not ready for dynamic spawns. This allows them to live on for now.
void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
public bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; }
public Unit SelectVictim() public Unit SelectVictim()
{ {
// function provides main threat functionality // function provides main threat functionality
+8 -19
View File
@@ -21,6 +21,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Framework.Dynamic; using Framework.Dynamic;
using Game.Networking.Packets; using Game.Networking.Packets;
using Game.Maps;
namespace Game.Entities namespace Game.Entities
{ {
@@ -327,34 +328,22 @@ namespace Game.Entities
public EquipmentItem[] Items = new EquipmentItem[SharedConst.MaxEquipmentItems]; 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 uint displayid;
public int equipmentId; public sbyte equipmentId;
public float posX;
public float posY;
public float posZ;
public float orientation;
public uint spawntimesecs;
public float spawndist; public float spawndist;
public uint currentwaypoint; public uint currentwaypoint;
public uint curhealth; public uint curhealth;
public uint curmana; public uint curmana;
public byte movementType; public byte movementType;
public List<Difficulty> spawnDifficulties = new List<Difficulty>();
public ulong npcflag; public ulong npcflag;
public uint unit_flags; // enum UnitFlags mask values public uint unit_flags; // enum UnitFlags mask values
public uint unit_flags2; // enum UnitFlags2 mask values public uint unit_flags2; // enum UnitFlags2 mask values
public uint unit_flags3; // enum UnitFlags3 mask values public uint unit_flags3; // enum UnitFlags3 mask values
public uint dynamicflags; public uint dynamicflags;
public PhaseUseFlagsValues phaseUseFlags;
public uint phaseId; public CreatureData() : base(SpawnObjectType.Creature) { }
public uint phaseGroup;
public int terrainSwapMap;
public uint ScriptId;
public bool dbData;
} }
public class CreatureModelInfo public class CreatureModelInfo
+151 -123
View File
@@ -166,7 +166,7 @@ namespace Game.Entities
return null; return null;
GameObject go = new GameObject(); 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 null;
return go; return go;
@@ -175,13 +175,13 @@ namespace Game.Entities
public static GameObject CreateGameObjectFromDB(ulong spawnId, Map map, bool addToMap = true) public static GameObject CreateGameObjectFromDB(ulong spawnId, Map map, bool addToMap = true)
{ {
GameObject go = new GameObject(); GameObject go = new GameObject();
if (!go.LoadGameObjectFromDB(spawnId, map, addToMap)) if (!go.LoadFromDB(spawnId, map, addToMap))
return null; return null;
return go; 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); Cypher.Assert(map);
SetMap(map); SetMap(map);
@@ -194,6 +194,10 @@ namespace Game.Entities
return false; return false;
} }
// Set if this object can handle dynamic spawns
if (!dynamic)
SetRespawnCompatibilityMode();
UpdatePositionData(); UpdatePositionData();
SetZoneScript(); SetZoneScript();
@@ -373,6 +377,9 @@ namespace Game.Entities
if (map.Is25ManRaid()) if (map.Is25ManRaid())
loot.maxDuplicates = 3; loot.maxDuplicates = 3;
if (spawnid != 0)
m_spawnId = spawnid;
uint linkedEntry = GetGoInfo().GetLinkedGameObjectEntry(); uint linkedEntry = GetGoInfo().GetLinkedGameObjectEntry();
if (linkedEntry != 0) if (linkedEntry != 0)
{ {
@@ -507,81 +514,88 @@ namespace Game.Entities
goto case LootState.Ready; goto case LootState.Ready;
case LootState.Ready: case LootState.Ready:
{ {
if (m_respawnTime > 0) // timer on if (m_respawnCompatibilityMode)
{ {
long now = Time.UnixTime; if (m_respawnTime > 0) // timer on
if (m_respawnTime <= now) // timer expired
{ {
ObjectGuid dbtableHighGuid = ObjectGuid.Create(HighGuid.GameObject, GetMapId(), GetEntry(), m_spawnId); long now = Time.UnixTime;
long linkedRespawntime = GetMap().GetLinkedRespawnTime(dbtableHighGuid); if (m_respawnTime <= now) // timer expired
if (linkedRespawntime != 0) // Can't respawn, the master is dead
{ {
ObjectGuid targetGuid = Global.ObjectMgr.GetLinkedRespawnGuid(dbtableHighGuid); ObjectGuid dbtableHighGuid = ObjectGuid.Create(HighGuid.GameObject, GetMapId(), GetEntry(), m_spawnId);
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day) long linkedRespawntime = GetMap().GetLinkedRespawnTime(dbtableHighGuid);
SetRespawnTime(Time.Day); if (linkedRespawntime != 0) // Can't respawn, the master is dead
else {
m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + RandomHelper.IRand(5, Time.Minute); // else copy time from master and add a little ObjectGuid targetGuid = Global.ObjectMgr.GetLinkedRespawnGuid(dbtableHighGuid);
SaveRespawnTime(); // also save to DB immediately if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
return; 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_respawnTime = 0;
m_SkillupList.Clear(); m_SkillupList.Clear();
m_usetimes = 0; m_usetimes = 0;
// If nearby linked trap exists, respawn it // If nearby linked trap exists, respawn it
GameObject linkedTrap = GetLinkedTrap(); GameObject linkedTrap = GetLinkedTrap();
if (linkedTrap) if (linkedTrap)
linkedTrap.SetLootState(LootState.Ready); linkedTrap.SetLootState(LootState.Ready);
switch (GetGoType()) switch (GetGoType())
{ {
case GameObjectTypes.FishingNode: // can't fish now case GameObjectTypes.FishingNode: // can't fish now
{
Unit caster = GetOwner();
if (caster != null && caster.IsTypeId(TypeId.Player))
{ {
caster.ToPlayer().RemoveGameObject(this, false); Unit caster = GetOwner();
caster.ToPlayer().SendPacket(new FishEscaped()); 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 case GameObjectTypes.Door:
m_lootState = LootState.JustDeactivated; case GameObjectTypes.Button:
return; //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)
case GameObjectTypes.Door: ResetDoorOrButton();
case GameObjectTypes.Button: break;
//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) case GameObjectTypes.FishingHole:
if (GetGoState() != GameObjectState.Ready) // Initialize a new max fish count on respawn
ResetDoorOrButton(); m_goValue.FishingHole.MaxOpens = RandomHelper.URand(GetGoInfo().FishingHole.minRestock, GetGoInfo().FishingHole.maxRestock);
break; break;
case GameObjectTypes.FishingHole: default:
// Initialize a new max fish count on respawn break;
m_goValue.FishingHole.MaxOpens = RandomHelper.URand(GetGoInfo().FishingHole.minRestock, GetGoInfo().FishingHole.maxRestock); }
break;
default: if (!m_spawnedByDefault) // despawn timer
break; {
// 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<GameObject>(GetSpawnId()) : 0;
if (poolid != 0)
Global.PoolMgr.UpdatePool<GameObject>(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<GameObject>(GetSpawnId()) : 0;
if (poolid != 0)
Global.PoolMgr.UpdatePool<GameObject>(poolid, GetSpawnId());
else
GetMap().AddToMap(this);
} }
} }
// Set respawn timer
if (!m_respawnCompatibilityMode && m_respawnTime > 0)
SaveRespawnTime(0, false);
if (IsSpawned()) if (IsSpawned())
{ {
GameObjectTemplate goInfo = GetGoInfo(); GameObjectTemplate goInfo = GetGoInfo();
@@ -785,6 +799,7 @@ namespace Game.Entities
if (m_respawnDelayTime == 0) if (m_respawnDelayTime == 0)
return; return;
// ToDo: Decide if we should properly despawn these. Maybe they expect to be able to manually respawn from script?
if (!m_spawnedByDefault) if (!m_spawnedByDefault)
{ {
m_respawnTime = 0; m_respawnTime = 0;
@@ -792,12 +807,29 @@ namespace Game.Entities
return; 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 // 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)) if (WorldConfig.GetBoolValue(WorldCfg.SaveRespawnTimeImmediately))
SaveRespawnTime(); 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() DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
break; break;
} }
@@ -890,7 +922,7 @@ namespace Game.Entities
{ {
// this should only be used when the gameobject has already been loaded // this should only be used when the gameobject has already been loaded
// preferably after adding to map, because mapid may not be valid otherwise // 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) if (data == null)
{ {
Log.outError(LogFilter.Maps, "GameObject.SaveToDB failed, cannot get gameobject data!"); Log.outError(LogFilter.Maps, "GameObject.SaveToDB failed, cannot get gameobject data!");
@@ -911,22 +943,22 @@ namespace Game.Entities
m_spawnId = Global.ObjectMgr.GenerateGameObjectSpawnId(); m_spawnId = Global.ObjectMgr.GenerateGameObjectSpawnId();
// update in loaded data (changing data only in this place) // 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 if (data.spawnId == 0)
data.id = GetEntry(); data.spawnId = m_spawnId;
data.mapid = (ushort)mapid; Cypher.Assert(data.spawnId == m_spawnId);
data.posX = GetPositionX();
data.posY = GetPositionY(); data.Id = GetEntry();
data.posZ = GetPositionZ(); data.spawnPoint.WorldRelocate(this);
data.orientation = GetOrientation();
data.rotation = m_worldRotation; data.rotation = m_worldRotation;
data.spawntimesecs = (int)(m_spawnedByDefault ? m_respawnDelayTime : -m_respawnDelayTime); data.spawntimesecs = (int)(m_spawnedByDefault ? m_respawnDelayTime : -m_respawnDelayTime);
data.animprogress = GetGoAnimProgress(); data.animprogress = GetGoAnimProgress();
data.go_state = GetGoState(); data.goState = GetGoState();
data.spawnDifficulties = spawnDifficulties; data.spawnDifficulties = spawnDifficulties;
data.artKit = (byte)GetGoArtKit(); 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.phaseId = GetDBPhase() > 0 ? (uint)GetDBPhase() : data.phaseId;
data.phaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.phaseGroup; data.phaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.phaseGroup;
@@ -958,24 +990,24 @@ namespace Game.Entities
DB.World.Execute(stmt); 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) if (data == null)
{ {
Log.outError(LogFilter.Maps, "Gameobject (SpawnId: {0}) not found in table `gameobject`, can't load. ", spawnId); Log.outError(LogFilter.Maps, "Gameobject (SpawnId: {0}) not found in table `gameobject`, can't load. ", spawnId);
return false; return false;
} }
uint entry = data.id; uint entry = data.Id;
Position pos = new Position(data.posX, data.posY, data.posZ, data.orientation);
uint animprogress = data.animprogress; uint animprogress = data.animprogress;
GameObjectState go_state = data.go_state; GameObjectState go_state = data.goState;
uint artKit = data.artKit; uint artKit = data.artKit;
m_spawnId = spawnId; 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; return false;
PhasingHandler.InitDbPhaseShift(GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup); PhasingHandler.InitDbPhaseShift(GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup);
@@ -1000,7 +1032,7 @@ namespace Game.Entities
if (m_respawnTime != 0 && m_respawnTime <= Time.UnixTime) if (m_respawnTime != 0 && m_respawnTime <= Time.UnixTime)
{ {
m_respawnTime = 0; m_respawnTime = 0;
GetMap().RemoveGORespawnTime(m_spawnId); GetMap().RemoveRespawnTime(SpawnObjectType.GameObject, m_spawnId);
} }
} }
} }
@@ -1021,20 +1053,20 @@ namespace Game.Entities
public void DeleteFromDB() public void DeleteFromDB()
{ {
GetMap().RemoveGORespawnTime(m_spawnId); GetMap().RemoveRespawnTime(SpawnObjectType.GameObject, m_spawnId);
Global.ObjectMgr.DeleteGOData(m_spawnId); Global.ObjectMgr.DeleteGameObjectData(m_spawnId);
SQLTransaction trans = new SQLTransaction();
PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_GAMEOBJECT); PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_GAMEOBJECT);
stmt.AddValue(0, m_spawnId); stmt.AddValue(0, m_spawnId);
trans.Append(stmt);
DB.World.Execute(stmt);
stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_EVENT_GAMEOBJECT); stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_EVENT_GAMEOBJECT);
stmt.AddValue(0, m_spawnId); stmt.AddValue(0, m_spawnId);
trans.Append(stmt);
DB.World.Execute(stmt); DB.World.CommitTransaction(trans);
} }
public override bool HasQuest(uint quest_id) public override bool HasQuest(uint quest_id)
@@ -1097,10 +1129,19 @@ namespace Game.Entities
return Global.ObjAccessor.GetUnit(this, GetOwnerGUID()); 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) if (m_goData != null && m_respawnTime > Time.UnixTime && m_spawnedByDefault)
GetMap().SaveGORespawnTime(m_spawnId, m_respawnTime); {
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) public override bool IsNeverVisibleFor(WorldObject seer)
@@ -1160,7 +1201,7 @@ namespace Game.Entities
if (m_spawnedByDefault && m_respawnTime > 0) if (m_spawnedByDefault && m_respawnTime > 0)
{ {
m_respawnTime = Time.UnixTime; 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) public void SetGoArtKit(byte kit)
{ {
SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.ArtKit), 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) if (data != null)
data.artKit = kit; data.artKit = kit;
} }
@@ -1273,10 +1314,10 @@ namespace Game.Entities
if (go != null) if (go != null)
{ {
go.SetGoArtKit(artkit); go.SetGoArtKit(artkit);
data = go.GetGoData(); data = go.GetGameObjectData();
} }
else if (lowguid != 0) else if (lowguid != 0)
data = Global.ObjectMgr.GetGOData(lowguid); data = Global.ObjectMgr.GetGameObjectData(lowguid);
if (data != null) if (data != null)
data.artKit = artkit; data.artKit = artkit;
@@ -2075,7 +2116,7 @@ namespace Game.Entities
public uint GetScriptId() public uint GetScriptId()
{ {
GameObjectData gameObjectData = GetGoData(); GameObjectData gameObjectData = GetGameObjectData();
if (gameObjectData != null) if (gameObjectData != null)
{ {
uint scriptId = gameObjectData.ScriptId; 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) public void GetRespawnPosition(out float x, out float y, out float z, out float ori)
{ {
if (m_spawnId != 0) if (m_goData != null)
{ m_goData.spawnPoint.GetPosition(out x, out y, out z, out ori);
GameObjectData data = Global.ObjectMgr.GetGOData(GetSpawnId()); else
if (data != null) GetPosition(out x, out y, out z, out ori);
{
x = posX;
y = posY;
z = posZ;
ori = data.orientation;
return;
}
}
x = GetPositionX();
y = GetPositionY();
z = GetPositionZ();
ori = GetOrientation();
} }
public float GetInteractionDistance() public float GetInteractionDistance()
@@ -2624,18 +2652,13 @@ namespace Game.Entities
public GameObjectTemplate GetGoInfo() { return m_goInfo; } public GameObjectTemplate GetGoInfo() { return m_goInfo; }
public GameObjectTemplateAddon GetTemplateAddon() { return m_goTemplateAddon; } 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 GameObjectValue GetGoValue() { return m_goValue; }
public ulong GetSpawnId() { return m_spawnId; } public ulong GetSpawnId() { return m_spawnId; }
public long GetPackedWorldRotation() { return m_packedRotation; } public long GetPackedWorldRotation() { return m_packedRotation; }
public override bool LoadFromDB(ulong spawnId, Map map)
{
return LoadGameObjectFromDB(spawnId, map, false);
}
public void SetOwnerGUID(ObjectGuid owner) public void SetOwnerGUID(ObjectGuid owner)
{ {
// Owner already found and different than expected owner - remove object from old owner // Owner already found and different than expected owner - remove object from old owner
@@ -2768,6 +2791,10 @@ namespace Game.Entities
AddFlag(GameObjectFlags.MapObject); 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 #region Fields
protected GameObjectFieldData m_gameObjectData; protected GameObjectFieldData m_gameObjectData;
protected GameObjectValue m_goValue; protected GameObjectValue m_goValue;
@@ -2799,6 +2826,7 @@ namespace Game.Entities
public Position StationaryPosition { get; set; } public Position StationaryPosition { get; set; }
GameObjectAI m_AI; GameObjectAI m_AI;
bool m_respawnCompatibilityMode;
ushort _animKitId; ushort _animKitId;
uint _worldEffectID; uint _worldEffectID;
@@ -21,6 +21,7 @@ using Framework.GameMath;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Game.Networking.Packets; using Game.Networking.Packets;
using Game.Maps;
namespace Game.Entities namespace Game.Entities
{ {
@@ -1274,25 +1275,13 @@ namespace Game.Entities
public uint WorldEffectID; 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 Quaternion rotation;
public int spawntimesecs;
public uint animprogress; public uint animprogress;
public GameObjectState go_state; public GameObjectState goState;
public List<Difficulty> spawnDifficulties = new List<Difficulty>();
public byte artKit; public byte artKit;
public PhaseUseFlagsValues phaseUseFlags;
public uint phaseId; public GameObjectData() : base(SpawnObjectType.GameObject) { }
public uint phaseGroup;
public int terrainSwapMap;
public uint ScriptId;
public bool dbData = true;
} }
} }
+12
View File
@@ -345,6 +345,10 @@ namespace Game.Entities
{ {
if (_nextGuid >= ObjectGuid.GetMaxCounter(_highGuid) - 1) if (_nextGuid >= ObjectGuid.GetMaxCounter(_highGuid) - 1)
HandleCounterOverflow(); HandleCounterOverflow();
if (_highGuid == HighGuid.Creature || _highGuid == HighGuid.Vehicle || _highGuid == HighGuid.GameObject || _highGuid == HighGuid.Transport)
CheckGuidTrigger(_nextGuid);
return _nextGuid++; return _nextGuid++;
} }
@@ -355,5 +359,13 @@ namespace Game.Entities
Log.outFatal(LogFilter.Server, "{0} guid overflow!! Can't continue, shutting down server. ", _highGuid); Log.outFatal(LogFilter.Server, "{0} guid overflow!! Can't continue, shutting down server. ", _highGuid);
Global.WorldMgr.StopNow(); 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();
}
} }
} }
+6
View File
@@ -364,6 +364,12 @@ namespace Game.Entities
Relocate(pos); Relocate(pos);
} }
public void WorldRelocate(uint mapId, Position pos)
{
_mapId = mapId;
Relocate(pos);
}
public void WorldRelocate(WorldLocation loc) public void WorldRelocate(WorldLocation loc)
{ {
_mapId = loc._mapId; _mapId = loc._mapId;
+2 -2
View File
@@ -1731,7 +1731,7 @@ namespace Game.Entities
public virtual uint GetLevelForTarget(WorldObject target) { return 1; } 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; } public ZoneScript GetZoneScript() { return m_zoneScript; }
@@ -1770,7 +1770,7 @@ namespace Game.Entities
public virtual bool IsInvisibleDueToDespawn() { return false; } public virtual bool IsInvisibleDueToDespawn() { return false; }
public virtual bool IsAlwaysDetectableFor(WorldObject seer) { 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 //Position
@@ -160,6 +160,8 @@ namespace Game.Entities
guild.UpdateMemberData(this, GuildMemberData.ZoneId, newZone); guild.UpdateMemberData(this, GuildMemberData.ZoneId, newZone);
} }
GetMap().UpdatePlayerZoneStats(m_zoneUpdateId, newZone);
// group update // group update
if (GetGroup()) if (GetGroup())
{ {
+7 -11
View File
@@ -318,14 +318,12 @@ namespace Game.Entities
{ {
Map map = GetMap(); Map map = GetMap();
Creature creature = Creature.CreateCreatureFromDB(guid, map, false); Creature creature = Creature.CreateCreatureFromDB(guid, map, false, true);
if (!creature) if (!creature)
return null; return null;
float x = data.posX; float x, y, z, o;
float y = data.posY; data.spawnPoint.GetPosition(out x, out y, out z, out o);
float z = data.posZ;
float o = data.orientation;
creature.SetTransport(this); creature.SetTransport(this);
creature.m_movementInfo.transport.guid = GetGUID(); creature.m_movementInfo.transport.guid = GetGUID();
@@ -365,10 +363,8 @@ namespace Game.Entities
if (!go) if (!go)
return null; return null;
float x = data.posX; float x, y, z, o;
float y = data.posY; data.spawnPoint.GetPosition(out x, out y, out z, out o);
float z = data.posZ;
float o = data.orientation;
go.SetTransport(this); go.SetTransport(this);
go.m_movementInfo.transport.guid = GetGUID(); go.m_movementInfo.transport.guid = GetGUID();
@@ -472,7 +468,7 @@ namespace Game.Entities
pos.GetPosition(out x, out y, out z, out o); pos.GetPosition(out x, out y, out z, out o);
CalculatePassengerPosition(ref x, ref y, ref z, ref 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; return null;
PhasingHandler.InheritPhaseShift(summon, summoner ? (WorldObject)summoner : this); PhasingHandler.InheritPhaseShift(summon, summoner ? (WorldObject)summoner : this);
@@ -553,7 +549,7 @@ namespace Game.Entities
// GameObjects on transport // GameObjects on transport
foreach (var go in cell.Value.gameobjects) foreach (var go in cell.Value.gameobjects)
CreateGOPassenger(go, Global.ObjectMgr.GetGOData(go)); CreateGOPassenger(go, Global.ObjectMgr.GetGameObjectData(go));
} }
} }
+14 -14
View File
@@ -412,7 +412,7 @@ namespace Game
int internal_event_id = mGameEvent.Length + event_id - 1; int internal_event_id = mGameEvent.Length + event_id - 1;
GameObjectData data = Global.ObjectMgr.GetGOData(guid); GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
if (data == null) if (data == null)
{ {
Log.outError(LogFilter.Sql, "`game_event_gameobject` contains gameobject (GUID: {0}) not found in `gameobject` table.", guid); 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; uint entry = 0;
CreatureData data = Global.ObjectMgr.GetCreatureData(guid); CreatureData data = Global.ObjectMgr.GetCreatureData(guid);
if (data != null) if (data != null)
entry = data.id; entry = data.Id;
VendorItem vItem = new VendorItem(); VendorItem vItem = new VendorItem();
vItem.item = result.Read<uint>(2); vItem.item = result.Read<uint>(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 // 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); CreatureData data = Global.ObjectMgr.GetCreatureData(guid);
if (data != null) if (data != null)
creaturesByMap.Add(data.mapid, guid); creaturesByMap.Add(data.spawnPoint.GetMapId(), guid);
} }
foreach (var key in creaturesByMap.Keys) foreach (var key in creaturesByMap.Keys)
@@ -1170,9 +1170,9 @@ namespace Game
Global.ObjectMgr.AddCreatureToGrid(guid, data); Global.ObjectMgr.AddCreatureToGrid(guid, data);
// Spawn if necessary (loaded grids only) // 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 // 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); Creature.CreateCreatureFromDB(guid, map);
} }
} }
@@ -1187,15 +1187,15 @@ namespace Game
foreach (var guid in mGameEventGameobjectGuids[internal_event_id]) foreach (var guid in mGameEventGameobjectGuids[internal_event_id])
{ {
// Add to correct cell // Add to correct cell
GameObjectData data = Global.ObjectMgr.GetGOData(guid); GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
if (data != null) if (data != null)
{ {
Global.ObjectMgr.AddGameObjectToGrid(guid, data); Global.ObjectMgr.AddGameObjectToGrid(guid, data);
// Spawn if necessary (loaded grids only) // Spawn if necessary (loaded grids only)
// this base map checked as non-instanced and then only existed // 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 // 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); GameObject pGameobject = GameObject.CreateGameObjectFromDB(guid, map, false);
// @todo find out when it is add to map // @todo find out when it is add to map
@@ -1243,7 +1243,7 @@ namespace Game
{ {
Global.ObjectMgr.RemoveCreatureFromGrid(guid, data); Global.ObjectMgr.RemoveCreatureFromGrid(guid, data);
Global.MapMgr.DoForAllMapsWithMapId(data.mapid, map => Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map =>
{ {
var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(guid); var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(guid);
foreach (var creature in creatureBounds) foreach (var creature in creatureBounds)
@@ -1265,12 +1265,12 @@ namespace Game
if (event_id > 0 && HasGameObjectActiveEventExcept(guid, (ushort)event_id)) if (event_id > 0 && HasGameObjectActiveEventExcept(guid, (ushort)event_id))
continue; continue;
// Remove the gameobject from grid // Remove the gameobject from grid
GameObjectData data = Global.ObjectMgr.GetGOData(guid); GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
if (data != null) if (data != null)
{ {
Global.ObjectMgr.RemoveGameObjectFromGrid(guid, data); Global.ObjectMgr.RemoveGameObjectFromGrid(guid, data);
Global.MapMgr.DoForAllMapsWithMapId(data.mapid, map => Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map =>
{ {
var gameobjectBounds = map.GetGameObjectBySpawnIdStore().LookupByKey(guid); var gameobjectBounds = map.GetGameObjectBySpawnIdStore().LookupByKey(guid);
foreach (var go in gameobjectBounds) foreach (var go in gameobjectBounds)
@@ -1300,7 +1300,7 @@ namespace Game
continue; continue;
// Update if spawned // Update if spawned
Global.MapMgr.DoForAllMapsWithMapId(data.mapid, map => Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map =>
{ {
var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(tuple.Item1); var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(tuple.Item1);
foreach (var creature in creatureBounds) foreach (var creature in creatureBounds)
@@ -1337,12 +1337,12 @@ namespace Game
tuple.Item2.modelid_prev = data2.displayid; tuple.Item2.modelid_prev = data2.displayid;
tuple.Item2.equipement_id_prev = (byte)data2.equipmentId; tuple.Item2.equipement_id_prev = (byte)data2.equipmentId;
data2.displayid = tuple.Item2.modelid; data2.displayid = tuple.Item2.modelid;
data2.equipmentId = tuple.Item2.equipment_id; data2.equipmentId = (sbyte)tuple.Item2.equipment_id;
} }
else else
{ {
data2.displayid = tuple.Item2.modelid_prev; data2.displayid = tuple.Item2.modelid_prev;
data2.equipmentId = tuple.Item2.equipement_id_prev; data2.equipmentId = (sbyte)tuple.Item2.equipement_id_prev;
} }
} }
} }
+1 -1
View File
@@ -831,7 +831,7 @@ namespace Game.Garrisons
T BuildingSpawnHelper<T>(GameObject building, ulong spawnId, Map map) where T : WorldObject, new() T BuildingSpawnHelper<T>(GameObject building, ulong spawnId, Map map) where T : WorldObject, new()
{ {
T spawn = new T(); T spawn = new T();
if (!spawn.LoadFromDB(spawnId, map)) if (!spawn.LoadFromDB(spawnId, map, false, false))
return null; return null;
float x = spawn.GetPositionX(); float x = spawn.GetPositionX();
File diff suppressed because it is too large Load Diff
+25
View File
@@ -2010,6 +2010,31 @@ namespace Game.Maps
bool _reqAlive; bool _reqAlive;
} }
class AnyPlayerInPositionRangeCheck : ICheck<Player>
{
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<Player> class NearestPlayerInObjectRangeCheck : ICheck<Player>
{ {
public NearestPlayerInObjectRangeCheck(WorldObject obj, float range) public NearestPlayerInObjectRangeCheck(WorldObject obj, float range)
+535 -80
View File
@@ -74,6 +74,8 @@ namespace Game.Maps
} }
} }
_zonePlayerCountMap.Clear();
//lets initialize visibility distance for map //lets initialize visibility distance for map
InitVisibilityDistance(); InitVisibilityDistance();
_weatherUpdateTimer = new IntervalTimer(); _weatherUpdateTimer = new IntervalTimer();
@@ -90,6 +92,10 @@ namespace Game.Maps
{ {
Global.ScriptMgr.OnDestroyMap(this); Global.ScriptMgr.OnDestroyMap(this);
// Delete all waiting spawns
// This doesn't delete from database.
DeleteRespawnInfo();
for (var i = 0; i < i_worldObjects.Count; ++i) for (var i = 0; i < i_worldObjects.Count; ++i)
{ {
WorldObject obj = i_worldObjects[i]; WorldObject obj = i_worldObjects[i];
@@ -279,7 +285,7 @@ namespace Game.Maps
m_VisibilityNotifyPeriod = Global.WorldMgr.GetVisibilityNotifyPeriodOnContinents(); m_VisibilityNotifyPeriod = Global.WorldMgr.GetVisibilityNotifyPeriodOnContinents();
} }
public void AddToGrid<T>(T obj, Cell cell)where T : WorldObject public void AddToGrid<T>(T obj, Cell cell) where T : WorldObject
{ {
Grid grid = GetGrid(cell.GetGridX(), cell.GetGridY()); Grid grid = GetGrid(cell.GetGridX(), cell.GetGridY());
switch (obj.GetTypeId()) switch (obj.GetTypeId())
@@ -429,7 +435,7 @@ namespace Game.Maps
GridMaps[gx][gy] = m_parentMap.GridMaps[gx][gy]; GridMaps[gx][gy] = m_parentMap.GridMaps[gx][gy];
++rootParentTerrainMap.GridMapReference[gx][gy]; ++rootParentTerrainMap.GridMapReference[gx][gy];
} }
} }
} }
} }
@@ -475,6 +481,29 @@ namespace Game.Maps
loader.LoadN(); 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) public void LoadGrid(float x, float y)
{ {
EnsureGridLoaded(new Cell(x, y)); EnsureGridLoaded(new Cell(x, y));
@@ -598,10 +627,11 @@ namespace Game.Maps
return true; return true;
} }
public bool IsGridLoaded(float x, float y) public bool IsGridLoaded(uint gridId) { return IsGridLoaded(new GridCoord(gridId % MapConst.MaxGrids, gridId / MapConst.MaxGrids)); }
{
return IsGridLoaded(GridDefines.ComputeGridCoord(x, y)); 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) 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) public virtual void Update(uint diff)
{ {
_dynamicTree.Update(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 // update active cells around players and active objects
ResetMarkedCells(); ResetMarkedCells();
var update = new UpdaterNotifier(diff); var update = new UpdaterNotifier(diff);
var grid_object_update = new Visitor(update, GridMapTypeMask.AllGrid); var grid_object_update = new Visitor(update, GridMapTypeMask.AllGrid);
@@ -666,10 +719,10 @@ namespace Game.Maps
Player player = m_activePlayers[i]; Player player = m_activePlayers[i];
if (!player.IsInWorld) if (!player.IsInWorld)
continue; continue;
// update players at tick // update players at tick
player.Update(diff); player.Update(diff);
VisitNearbyCellsOf(player, grid_object_update, world_object_update); VisitNearbyCellsOf(player, grid_object_update, world_object_update);
// If player is using far sight or mind vision, visit that object too // 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); VisitNearbyCellsOf(c, grid_object_update, world_object_update);
} }
} }
for (var i = 0; i < m_activeNonPlayers.Count; ++i) for (var i = 0; i < m_activeNonPlayers.Count; ++i)
{ {
WorldObject obj = m_activeNonPlayers[i]; WorldObject obj = m_activeNonPlayers[i];
@@ -707,7 +760,7 @@ namespace Game.Maps
VisitNearbyCellsOf(obj, grid_object_update, world_object_update); VisitNearbyCellsOf(obj, grid_object_update, world_object_update);
} }
for (var i = 0; i < _transports.Count; ++i) for (var i = 0; i < _transports.Count; ++i)
{ {
Transport transport = _transports[i]; Transport transport = _transports[i];
@@ -716,7 +769,7 @@ namespace Game.Maps
transport.Update(diff); transport.Update(diff);
} }
SendObjectUpdates(); SendObjectUpdates();
// Process necessary scripts // Process necessary scripts
@@ -841,6 +894,8 @@ namespace Game.Maps
public virtual void RemovePlayerFromMap(Player player, bool remove) 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); Global.ScriptMgr.OnPlayerLeaveMap(this, player);
player.GetHostileRefManager().DeleteReferences(); // multithreading crashfix player.GetHostileRefManager().DeleteReferences(); // multithreading crashfix
@@ -1167,7 +1222,7 @@ namespace Game.Maps
{ {
_creatureToMoveLock = true; _creatureToMoveLock = true;
for (var i = 0; i< creaturesToMove.Count; ++i) for (var i = 0; i < creaturesToMove.Count; ++i)
{ {
Creature creature = creaturesToMove[i]; Creature creature = creaturesToMove[i];
if (creature.GetMap() != this) //pet is teleported to another map 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 in same cell then none do
if (old_cell.DiffCell(new_cell)) 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()); old_cell.GetCellX(), old_cell.GetCellY(), new_cell.GetCellX(), new_cell.GetCellY());
RemoveFromGrid(at, old_cell); RemoveFromGrid(at, old_cell);
@@ -1810,6 +1865,16 @@ namespace Game.Maps
return mapHeight; // explicitly use map data 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) public float GetMinHeight(PhaseShift phaseShift, float x, float y)
{ {
GridMap grid = GetGridMap(PhasingHandler.GetTerrainMapId(phaseShift, this, x, y), x, y); GridMap grid = GetGridMap(PhasingHandler.GetTerrainMapId(phaseShift, this, x, y), x, y);
@@ -1974,6 +2039,8 @@ namespace Game.Maps
return areaId; 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) public uint GetZoneId(PhaseShift phaseShift, float x, float y, float z)
{ {
uint areaId = GetAreaId(phaseShift, x, y, z); uint areaId = GetAreaId(phaseShift, x, y, z);
@@ -1985,6 +2052,8 @@ namespace Game.Maps
return areaId; 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) public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, float x, float y, float z)
{ {
areaid = zoneid = GetAreaId(phaseShift, x, y, z); areaid = zoneid = GetAreaId(phaseShift, x, y, z);
@@ -1994,6 +2063,8 @@ namespace Game.Maps
zoneid = area.ParentAreaID; 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) private byte GetTerrainType(PhaseShift phaseShift, float x, float y)
{ {
GridMap gmap = GetGridMap(PhasingHandler.GetTerrainMapId(phaseShift, this, x, y), x, y); GridMap gmap = GetGridMap(PhasingHandler.GetTerrainMapId(phaseShift, this, x, y), x, y);
@@ -2229,11 +2300,6 @@ namespace Game.Maps
return result; 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) 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)); 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<GameObject>(poolId, info.spawnId);
else if (info.type == SpawnObjectType.Creature)
Global.PoolMgr.UpdatePool<Creature>(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<RespawnInfo> 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<RespawnInfo> data, Dictionary<ulong, RespawnInfo> map, uint zoneId)
{
foreach (var pair in map)
if (zoneId == 0 || pair.Value.zoneId == zoneId)
data.Add(pair.Value);
}
public void GetRespawnInfo(List<RespawnInfo> 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<ulong, RespawnInfo> 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<RespawnInfo> 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<RespawnInfo> v = new List<RespawnInfo>();
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) public virtual void DelayedUpdate(uint diff)
{ {
for (var i = 0; i < _transports.Count; ++i) for (var i = 0; i < _transports.Count; ++i)
@@ -2617,64 +3026,37 @@ namespace Game.Maps
m_activeNonPlayers.Remove(obj); 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) if (respawnTime == 0)
{ {
// Delete only // Delete only
RemoveCreatureRespawnTime(dbGuid); RemoveRespawnTime(type, spawnId, false, dbTrans);
return; 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); if (writeDB)
stmt.AddValue(0, dbGuid); 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(1, respawnTime);
stmt.AddValue(2, GetId()); stmt.AddValue(2, GetId());
stmt.AddValue(3, GetInstanceId()); stmt.AddValue(3, GetInstanceId());
DB.Characters.Execute(stmt); DB.Characters.ExecuteOrAppend(dbTrans, 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);
} }
public void LoadRespawnTimes() public void LoadRespawnTimes()
@@ -2690,7 +3072,10 @@ namespace Game.Maps
var loguid = result.Read<uint>(0); var loguid = result.Read<uint>(0);
var respawnTime = result.Read<long>(1); var respawnTime = result.Read<long>(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()); } while (result.NextRow());
} }
@@ -2705,16 +3090,19 @@ namespace Game.Maps
var loguid = result.Read<uint>(0); var loguid = result.Read<uint>(0);
var respawnTime = result.Read<long>(1); var respawnTime = result.Read<long>(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()); } while (result.NextRow());
} }
} }
public long GetRespawnTime(SpawnObjectType type, ulong spawnId) { return (type == SpawnObjectType.GameObject) ? GetGORespawnTime(spawnId) : GetCreatureRespawnTime(spawnId); }
public void DeleteRespawnTimes() public void DeleteRespawnTimes()
{ {
_creatureRespawnTimes.Clear(); DeleteRespawnInfo();
_goRespawnTimes.Clear();
DeleteRespawnTimesInDB(GetId(), GetInstanceId()); DeleteRespawnTimesInDB(GetId(), GetInstanceId());
} }
@@ -3106,7 +3494,8 @@ namespace Game.Maps
return GetGrid(p.X_coord, p.Y_coord) == null || return GetGrid(p.X_coord, p.Y_coord) == null ||
GetGrid(p.X_coord, p.Y_coord).GetGridState() == GridState.Removal; 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) private bool GetUnloadLock(GridCoord p)
{ {
return GetGrid(p.X_coord, p.Y_coord).GetUnloadLock(); return GetGrid(p.X_coord, p.Y_coord).GetUnloadLock();
@@ -3320,7 +3709,7 @@ namespace Game.Maps
{ {
return _dynamicTree.GetHeight(x, y, z, maxSearchDist, phaseShift); return _dynamicTree.GetHeight(x, y, z, maxSearchDist, phaseShift);
} }
public virtual uint GetOwnerGuildId(Team team = Team.Other) public virtual uint GetOwnerGuildId(Team team = Team.Other)
{ {
return 0; return 0;
@@ -3328,12 +3717,12 @@ namespace Game.Maps
public long GetCreatureRespawnTime(ulong dbGuid) public long GetCreatureRespawnTime(ulong dbGuid)
{ {
return _creatureRespawnTimes.LookupByKey(dbGuid); return _creatureRespawnTimesBySpawnId.ContainsKey(dbGuid) ? _creatureRespawnTimesBySpawnId[dbGuid].respawnTime : 0;
} }
public long GetGORespawnTime(ulong dbGuid) public long GetGORespawnTime(ulong dbGuid)
{ {
return _goRespawnTimes.LookupByKey(dbGuid); return _gameObjectRespawnTimesBySpawnId.ContainsKey(dbGuid) ? _gameObjectRespawnTimesBySpawnId[dbGuid].respawnTime : 0;
} }
void SetTimer(uint t) void SetTimer(uint t)
@@ -3421,6 +3810,33 @@ namespace Game.Maps
return go ? go.ToTransport() : null; 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) public void Visit(Cell cell, Visitor visitor)
{ {
uint x = cell.GetGridX(); uint x = cell.GetGridX();
@@ -3507,7 +3923,7 @@ namespace Game.Maps
return null; 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; return null;
// Set the summon to the summoner's phase // Set the summon to the summoner's phase
@@ -3554,7 +3970,7 @@ namespace Game.Maps
_updateObjects.Remove(obj); _updateObjects.Remove(obj);
} }
public static implicit operator bool (Map map) public static implicit operator bool(Map map)
{ {
return map != null; return map != null;
} }
@@ -4178,7 +4594,7 @@ namespace Game.Maps
uTarget = target?.ToUnit(); uTarget = target?.ToUnit();
break; break;
case eScriptFlags.CastspellSourceToSource: // source . source case eScriptFlags.CastspellSourceToSource: // source . source
uSource =source?.ToUnit(); uSource = source?.ToUnit();
uTarget = uSource; uTarget = uSource;
break; break;
case eScriptFlags.CastspellTargetToTarget: // target . target case eScriptFlags.CastspellTargetToTarget: // target . target
@@ -4442,10 +4858,14 @@ namespace Game.Maps
GridMap[][] GridMaps = new GridMap[MapConst.MaxGrids][]; GridMap[][] GridMaps = new GridMap[MapConst.MaxGrids][];
ushort[][] GridMapReference = new ushort[MapConst.MaxGrids][]; ushort[][] GridMapReference = new ushort[MapConst.MaxGrids][];
Dictionary<ulong, long> _creatureRespawnTimes = new Dictionary<ulong, long>();
DynamicMapTree _dynamicTree = new DynamicMapTree(); DynamicMapTree _dynamicTree = new DynamicMapTree();
Dictionary<ulong, long> _goRespawnTimes = new Dictionary<ulong, long>(); SortedSet<RespawnInfo> _respawnTimes = new SortedSet<RespawnInfo>(new CompareRespawnInfo());
Dictionary<ulong, RespawnInfo> _creatureRespawnTimesBySpawnId = new Dictionary<ulong, RespawnInfo>();
Dictionary<ulong, RespawnInfo> _gameObjectRespawnTimesBySpawnId = new Dictionary<ulong, RespawnInfo>();
uint _respawnCheckTimer;
Dictionary<uint, uint> _zonePlayerCountMap = new Dictionary<uint, uint>();
List<Transport> _transports = new List<Transport>(); List<Transport> _transports = new List<Transport>();
Grid[][] i_grids = new Grid[MapConst.MaxGrids][]; Grid[][] i_grids = new Grid[MapConst.MaxGrids][];
MapRecord i_mapRecord; MapRecord i_mapRecord;
@@ -5050,4 +5470,39 @@ namespace Game.Maps
public Optional<AreaInfo> areaInfo; public Optional<AreaInfo> areaInfo;
public Optional<LiquidData> LiquidInfo; public Optional<LiquidData> 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<RespawnInfo>
{
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);
}
}
} }
+10
View File
@@ -430,6 +430,16 @@ namespace Game.Entities
m.GetZoneAndAreaId(phaseShift, out zoneid, out areaid, x, y, z); 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<Map> worker) public void DoForAllMaps(Action<Map> worker)
{ {
lock (_mapsLock) lock (_mapsLock)
+39 -3
View File
@@ -80,10 +80,46 @@ namespace Game.Maps
foreach (var guid in guid_set) foreach (var guid in guid_set)
{ {
T obj = new T(); T obj = new T();
if (!obj.LoadFromDB(guid, map)) // Don't spawn at all if there's a respawn time
continue; if ((obj.IsTypeId(TypeId.Unit) && map.GetCreatureRespawnTime(guid) == 0) || (obj.IsTypeId(TypeId.GameObject) && map.GetGORespawnTime(guid) == 0))
{
//TC_LOG_INFO("misc", "DEBUG: LoadHelper from table: %s for (guid: %u) Loading", table, guid);
if (obj.IsTypeId(TypeId.Unit))
{
CreatureData cdata = Global.ObjectMgr.GetCreatureData(guid);
Cypher.Assert(cdata != null, $"Tried to load creature with spawnId {guid}, but no such creature exists.");
AddObjectHelper(cell, ref count, map, obj); SpawnGroupTemplateData group = cdata.spawnGroupData;
// If creature in manual spawn group, don't spawn here, unless group is already active.
if (group.flags.HasFlag(SpawnGroupFlags.ManualSpawn) && !group.isActive)
continue;
// If script is blocking spawn, don't spawn but queue for a re-check in a little bit
if (!group.flags.HasFlag(SpawnGroupFlags.CompatibilityMode) && !Global.ScriptMgr.CanSpawn(guid, cdata.Id, cdata, map))
{
map.SaveRespawnTime(SpawnObjectType.Creature, guid, cdata.Id, Time.UnixTime + RandomHelper.URand(4, 7), map.GetZoneId(PhasingHandler.EmptyPhaseShift, cdata.spawnPoint), GridDefines.ComputeGridCoord(cdata.spawnPoint.GetPositionX(), cdata.spawnPoint.GetPositionY()).GetId(), false);
continue;
}
}
else if (obj.IsTypeId(TypeId.GameObject))
{
// If gameobject in manual spawn group, don't spawn here, unless group is already active.
GameObjectData godata = Global.ObjectMgr.GetGameObjectData(guid);
Cypher.Assert(godata != null, $"Tried to load gameobject with spawnId {guid}, but no such object exists.");
if (godata.spawnGroupData.flags.HasFlag(SpawnGroupFlags.ManualSpawn) && !godata.spawnGroupData.isActive)
continue;
}
if (!obj.LoadFromDB(guid, map, false, false))
{
obj.Dispose();
continue;
}
AddObjectHelper(cell, ref count, map, obj);
}
else
obj.Dispose();
} }
} }
+55
View File
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.Entities;
using System.Collections.Generic;
namespace Game.Maps
{
public class SpawnGroupTemplateData
{
public uint groupId;
public string name;
public uint mapId;
public SpawnGroupFlags flags;
public bool isActive;
}
public class SpawnData
{
public SpawnObjectType type;
public ulong spawnId;
public uint Id; // entry in respective _template table
public WorldLocation spawnPoint;
public PhaseUseFlagsValues phaseUseFlags;
public uint phaseId;
public uint phaseGroup;
public int terrainSwapMap = -1;
public int spawntimesecs;
public List<Difficulty> spawnDifficulties;
public SpawnGroupTemplateData spawnGroupData;
public uint ScriptId;
public bool dbData = true;
public SpawnData(SpawnObjectType t)
{
type = t;
}
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ namespace Game.Maps
{ {
public class ZoneScript public class ZoneScript
{ {
public virtual uint GetCreatureEntry(ulong guidlow, CreatureData data) { return data.id; } public virtual uint GetCreatureEntry(ulong guidlow, CreatureData data) { return data.Id; }
public virtual uint GetGameObjectEntry(ulong spawnId, uint entry) { return entry; } public virtual uint GetGameObjectEntry(ulong spawnId, uint entry) { return entry; }
public virtual void OnCreatureCreate(Creature creature) { } public virtual void OnCreatureCreate(Creature creature) { }
+5 -5
View File
@@ -358,7 +358,7 @@ namespace Game.PvP
void AddGO(uint type, ulong guid) void AddGO(uint type, ulong guid)
{ {
GameObjectData data = Global.ObjectMgr.GetGOData(guid); GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
if (data == null) if (data == null)
return; return;
@@ -378,7 +378,7 @@ namespace Game.PvP
public bool AddObject(uint type, uint entry, uint map, Position pos, Quaternion rot) public bool AddObject(uint type, uint entry, uint map, Position pos, Quaternion rot)
{ {
ulong guid = Global.ObjectMgr.AddGOData(entry, map, pos, rot, 0); ulong guid = Global.ObjectMgr.AddGameObjectData(entry, map, pos, rot, 0);
if (guid != 0) if (guid != 0)
{ {
AddGO(type, guid); AddGO(type, guid);
@@ -412,7 +412,7 @@ namespace Game.PvP
return false; return false;
} }
m_capturePointSpawnId = Global.ObjectMgr.AddGOData(entry, map, pos, rot, 0); m_capturePointSpawnId = Global.ObjectMgr.AddGameObjectData(entry, map, pos, rot, 0);
if (m_capturePointSpawnId == 0) if (m_capturePointSpawnId == 0)
return false; return false;
@@ -472,7 +472,7 @@ namespace Game.PvP
gameobject.Delete(); gameobject.Delete();
} }
Global.ObjectMgr.DeleteGOData(spawnId); Global.ObjectMgr.DeleteGameObjectData(spawnId);
m_ObjectTypes.Remove(spawnId); m_ObjectTypes.Remove(spawnId);
m_Objects.Remove(type); m_Objects.Remove(type);
return true; return true;
@@ -480,7 +480,7 @@ namespace Game.PvP
bool DelCapturePoint() bool DelCapturePoint()
{ {
Global.ObjectMgr.DeleteGOData(m_capturePointSpawnId); Global.ObjectMgr.DeleteGameObjectData(m_capturePointSpawnId);
m_capturePointSpawnId = 0; m_capturePointSpawnId = 0;
if (m_capturePoint) if (m_capturePoint)
+37 -10
View File
@@ -144,14 +144,14 @@ namespace Game
uint pool_id = result.Read<uint>(1); uint pool_id = result.Read<uint>(1);
float chance = result.Read<float>(2); float chance = result.Read<float>(2);
GameObjectData data = Global.ObjectMgr.GetGOData(guid); GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
if (data == null) if (data == null)
{ {
Log.outError(LogFilter.Sql, "`pool_gameobject` has a non existing gameobject spawn (GUID: {0}) defined for pool id ({1}), skipped.", guid, pool_id); Log.outError(LogFilter.Sql, "`pool_gameobject` has a non existing gameobject spawn (GUID: {0}) defined for pool id ({1}), skipped.", guid, pool_id);
continue; continue;
} }
GameObjectTemplate goinfo = Global.ObjectMgr.GetGameObjectTemplate(data.id); GameObjectTemplate goinfo = Global.ObjectMgr.GetGameObjectTemplate(data.Id);
if (goinfo.type != GameObjectTypes.Chest && if (goinfo.type != GameObjectTypes.Chest &&
goinfo.type != GameObjectTypes.FishingHole && goinfo.type != GameObjectTypes.FishingHole &&
goinfo.type != GameObjectTypes.GatheringNode && goinfo.type != GameObjectTypes.GatheringNode &&
@@ -534,6 +534,21 @@ namespace Game
return 0; return 0;
} }
// Selects proper template overload to call based on passed type
public uint IsPartOfAPool(SpawnObjectType type, ulong spawnId)
{
switch (type)
{
case SpawnObjectType.Creature:
return IsPartOfAPool<Creature>(spawnId);
case SpawnObjectType.GameObject:
return IsPartOfAPool<GameObject>(spawnId);
default:
Cypher.Assert(false, $"Invalid spawn type {type} passed to PoolMgr.IsPartOfPool (with spawnId {spawnId})");
return 0;
}
}
public enum QuestTypes public enum QuestTypes
{ {
None = 0, None = 0,
@@ -655,28 +670,40 @@ namespace Game
if (data != null) if (data != null)
{ {
Global.ObjectMgr.RemoveCreatureFromGrid(guid, data); Global.ObjectMgr.RemoveCreatureFromGrid(guid, data);
Map map = Global.MapMgr.FindMap(data.mapid, 0); Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0);
if (map != null && !map.Instanceable()) if (map != null && !map.Instanceable())
{ {
var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(guid); var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(guid);
foreach (var creature in creatureBounds) foreach (var creature in creatureBounds)
{
// For dynamic spawns, save respawn time here
if (!creature.GetRespawnCompatibilityMode())
creature.SaveRespawnTime(0, false);
creature.AddObjectToRemoveList(); creature.AddObjectToRemoveList();
}
} }
} }
break; break;
} }
case "GameObject": case "GameObject":
{ {
var data = Global.ObjectMgr.GetGOData(guid); var data = Global.ObjectMgr.GetGameObjectData(guid);
if (data != null) if (data != null)
{ {
Global.ObjectMgr.RemoveGameObjectFromGrid(guid, data); Global.ObjectMgr.RemoveGameObjectFromGrid(guid, data);
Map map = Global.MapMgr.FindMap(data.mapid, 0); Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0);
if (map != null && !map.Instanceable()) if (map != null && !map.Instanceable())
{ {
var gameobjectBounds = map.GetGameObjectBySpawnIdStore().LookupByKey(guid); var gameobjectBounds = map.GetGameObjectBySpawnIdStore().LookupByKey(guid);
foreach (var go in gameobjectBounds) foreach (var go in gameobjectBounds)
{
// For dynamic spawns, save respawn time here
if (!go.GetRespawnCompatibilityMode())
go.SaveRespawnTime(0, false);
go.AddObjectToRemoveList(); go.AddObjectToRemoveList();
}
} }
} }
break; break;
@@ -863,24 +890,24 @@ namespace Game
Global.ObjectMgr.AddCreatureToGrid(obj.guid, data); Global.ObjectMgr.AddCreatureToGrid(obj.guid, data);
// Spawn if necessary (loaded grids only) // 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 // 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(obj.guid, map); Creature.CreateCreatureFromDB(obj.guid, map);
} }
} }
break; break;
case "GameObject": case "GameObject":
{ {
GameObjectData data = Global.ObjectMgr.GetGOData(obj.guid); GameObjectData data = Global.ObjectMgr.GetGameObjectData(obj.guid);
if (data != null) if (data != null)
{ {
Global.ObjectMgr.AddGameObjectToGrid(obj.guid, data); Global.ObjectMgr.AddGameObjectToGrid(obj.guid, data);
// Spawn if necessary (loaded grids only) // Spawn if necessary (loaded grids only)
// this base map checked as non-instanced and then only existed // 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 // 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 go = GameObject.CreateGameObjectFromDB(obj.guid, map, false); GameObject go = GameObject.CreateGameObjectFromDB(obj.guid, map, false);
if (go) if (go)
+27 -4
View File
@@ -682,13 +682,36 @@ namespace Game.Scripting
} }
//CreatureScript //CreatureScript
public bool CanSpawn(ulong spawnId, uint entry, CreatureTemplate actTemplate, CreatureData cData, Map map) public bool CanSpawn(ulong spawnId, uint entry, CreatureData cData, Map map)
{ {
Cypher.Assert(actTemplate != null); Cypher.Assert(map != null);
CreatureTemplate baseTemplate = Global.ObjectMgr.GetCreatureTemplate(entry); CreatureTemplate baseTemplate = Global.ObjectMgr.GetCreatureTemplate(entry);
if (baseTemplate == null) Cypher.Assert(baseTemplate != null);
baseTemplate = actTemplate;
// find out which template we'd be using
CreatureTemplate actTemplate = null;
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(map.GetDifficultyID());
while (actTemplate == null && difficultyEntry != null)
{
int idx = CreatureTemplate.DifficultyIDToDifficultyEntryIndex(difficultyEntry.Id);
if (idx == -1)
break;
if (baseTemplate.DifficultyEntry[idx] != 0)
{
actTemplate = Global.ObjectMgr.GetCreatureTemplate(baseTemplate.DifficultyEntry[idx]);
break;
}
if (difficultyEntry.FallbackDifficultyID == 0)
break;
difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficultyEntry.FallbackDifficultyID);
}
if (actTemplate == null)
actTemplate = baseTemplate;
uint scriptId = baseTemplate.ScriptID; uint scriptId = baseTemplate.ScriptID;
if (cData != null && cData.ScriptId != 0) if (cData != null && cData.ScriptId != 0)
+43
View File
@@ -776,6 +776,49 @@ namespace Game
Values[WorldCfg.NoGrayAggroBelow] = Values[WorldCfg.NoGrayAggroAbove]; Values[WorldCfg.NoGrayAggroBelow] = Values[WorldCfg.NoGrayAggroAbove];
} }
// Respawn Settings
Values[WorldCfg.RespawnMinCheckIntervalMs] = GetDefaultValue("Respawn.MinCheckIntervalMS", 5000);
Values[WorldCfg.RespawnDynamicMode] = GetDefaultValue("Respawn.DynamicMode", 0);
if ((int)Values[WorldCfg.RespawnDynamicMode] > 1)
{
Log.outError(LogFilter.ServerLoading, $"Invalid value for Respawn.DynamicMode ({Values[WorldCfg.RespawnDynamicMode]}). Set to 0.");
Values[WorldCfg.RespawnDynamicMode] = 0;
}
Values[WorldCfg.RespawnDynamicEscortNpc] = GetDefaultValue("Respawn.DynamicEscortNPC", true);
Values[WorldCfg.RespawnGuidWarnLevel] = GetDefaultValue("Respawn.GuidWarnLevel", 12000000);
if ((int)Values[WorldCfg.RespawnGuidWarnLevel] > 16777215)
{
Log.outError(LogFilter.ServerLoading, $"Respawn.GuidWarnLevel ({Values[WorldCfg.RespawnGuidWarnLevel]}) cannot be greater than maximum GUID (16777215). Set to 12000000.");
Values[WorldCfg.RespawnGuidWarnLevel] = 12000000;
}
Values[WorldCfg.RespawnGuidAlertLevel] = GetDefaultValue("Respawn.GuidAlertLevel", 16000000);
if ((int)Values[WorldCfg.RespawnGuidAlertLevel] > 16777215)
{
Log.outError(LogFilter.ServerLoading, $"Respawn.GuidWarnLevel ({Values[WorldCfg.RespawnGuidAlertLevel]}) cannot be greater than maximum GUID (16777215). Set to 16000000.");
Values[WorldCfg.RespawnGuidAlertLevel] = 16000000;
}
Values[WorldCfg.RespawnRestartQuietTime] = GetDefaultValue("Respawn.RestartQuietTime", 3);
if ((int)Values[WorldCfg.RespawnRestartQuietTime] > 23)
{
Log.outError(LogFilter.ServerLoading, $"Respawn.RestartQuietTime ({Values[WorldCfg.RespawnRestartQuietTime]}) must be an hour, between 0 and 23. Set to 3.");
Values[WorldCfg.RespawnRestartQuietTime] = 3;
}
Values[WorldCfg.RespawnDynamicRateCreature] = GetDefaultValue("Respawn.DynamicRateCreature", 10.0f);
if ((float)Values[WorldCfg.RespawnDynamicRateCreature] < 0.0f)
{
Log.outError(LogFilter.ServerLoading, $"Respawn.DynamicRateCreature ({Values[WorldCfg.RespawnDynamicRateCreature]}) must be positive. Set to 10.");
Values[WorldCfg.RespawnDynamicRateCreature] = 10.0f;
}
Values[WorldCfg.RespawnDynamicMinimumCreature] = GetDefaultValue("Respawn.DynamicMinimumCreature", 10);
Values[WorldCfg.RespawnDynamicRateGameobject] = GetDefaultValue("Respawn.DynamicRateGameObject", 10.0f);
if ((float)Values[WorldCfg.RespawnDynamicRateGameobject] < 0.0f)
{
Log.outError(LogFilter.ServerLoading, $"Respawn.DynamicRateGameObject ({Values[WorldCfg.RespawnDynamicRateGameobject]}) must be positive. Set to 10.");
Values[WorldCfg.RespawnDynamicRateGameobject] = 10.0f;
}
Values[WorldCfg.RespawnDynamicMinimumGameObject] = GetDefaultValue("Respawn.DynamicMinimumGameObject", 10);
Values[WorldCfg.RespawnGuidWarningFrequency] = GetDefaultValue("Respawn.WarningFrequency", 1800);
Values[WorldCfg.EnableMmaps] = GetDefaultValue("mmap.EnablePathFinding", false); Values[WorldCfg.EnableMmaps] = GetDefaultValue("mmap.EnablePathFinding", false);
Values[WorldCfg.VmapIndoorCheck] = GetDefaultValue("vmap.EnableIndoorCheck", false); Values[WorldCfg.VmapIndoorCheck] = GetDefaultValue("vmap.EnableIndoorCheck", false);
+89 -1
View File
@@ -49,6 +49,7 @@ namespace Game
_realm = new Realm(); _realm = new Realm();
_worldUpdateTime = new WorldUpdateTime(); _worldUpdateTime = new WorldUpdateTime();
_warnShutdownTime = Time.UnixTime;
} }
public Player FindPlayerInZone(uint zone) public Player FindPlayerInZone(uint zone)
@@ -92,6 +93,61 @@ namespace Game
return m_motd; return m_motd;
} }
public void TriggerGuidWarning()
{
// Lock this only to prevent multiple maps triggering at the same time
lock (_guidAlertLock)
{
long gameTime = GameTime.GetGameTime();
long today = (gameTime / Time.Day) * Time.Day;
// Check if our window to restart today has passed. 5 mins until quiet time
while (gameTime >= (today + (WorldConfig.GetIntValue(WorldCfg.RespawnRestartQuietTime) * Time.Hour) - 1810))
today += Time.Day;
// Schedule restart for 30 minutes before quiet time, or as long as we have
_warnShutdownTime = today + (WorldConfig.GetIntValue(WorldCfg.RespawnRestartQuietTime) * Time.Hour) - 1800;
_guidWarn = true;
SendGuidWarning();
}
}
public void TriggerGuidAlert()
{
// Lock this only to prevent multiple maps triggering at the same time
lock (_guidAlertLock)
{
DoGuidAlertRestart();
_guidAlert = true;
_guidWarn = false;
}
}
void DoGuidWarningRestart()
{
if (m_ShutdownTimer != 0)
return;
ShutdownServ(1800, ShutdownMask.Restart, ShutdownExitCode.Restart);
_warnShutdownTime += Time.Hour;
}
void DoGuidAlertRestart()
{
if (m_ShutdownTimer != 0)
return;
ShutdownServ(300, ShutdownMask.Restart, ShutdownExitCode.Restart, _alertRestartReason);
}
void SendGuidWarning()
{
if (m_ShutdownTimer == 0 && _guidWarn && WorldConfig.GetIntValue(WorldCfg.RespawnGuidWarningFrequency) > 0)
SendServerMessage(ServerMessageType.String, _guidWarningMsg);
_warnDiff = 0;
}
public WorldSession FindSession(uint id) public WorldSession FindSession(uint id)
{ {
return m_sessions.LookupByKey(id); return m_sessions.LookupByKey(id);
@@ -527,6 +583,9 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Base Stats..."); Log.outInfo(LogFilter.ServerLoading, "Loading Creature Base Stats...");
Global.ObjectMgr.LoadCreatureClassLevelStats(); Global.ObjectMgr.LoadCreatureClassLevelStats();
Log.outInfo(LogFilter.ServerLoading, "Loading Spawn Group Templates...");
Global.ObjectMgr.LoadSpawnGroupTemplates();
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Data..."); Log.outInfo(LogFilter.ServerLoading, "Loading Creature Data...");
Global.ObjectMgr.LoadCreatures(); Global.ObjectMgr.LoadCreatures();
@@ -543,7 +602,10 @@ namespace Game
Global.ObjectMgr.LoadCreatureAddons(); Global.ObjectMgr.LoadCreatureAddons();
Log.outInfo(LogFilter.ServerLoading, "Loading GameObjects..."); Log.outInfo(LogFilter.ServerLoading, "Loading GameObjects...");
Global.ObjectMgr.LoadGameobjects(); Global.ObjectMgr.LoadGameObjects();
Log.outInfo(LogFilter.ServerLoading, "Loading Spawn Group Data...");
Global.ObjectMgr.LoadSpawnGroups();
Log.outInfo(LogFilter.ServerLoading, "Loading GameObject Addon Data..."); Log.outInfo(LogFilter.ServerLoading, "Loading GameObject Addon Data...");
Global.ObjectMgr.LoadGameObjectAddons(); // must be after LoadGameObjectTemplate() and LoadGameobjects() Global.ObjectMgr.LoadGameObjectAddons(); // must be after LoadGameObjectTemplate() and LoadGameobjects()
@@ -1083,6 +1145,9 @@ namespace Game
m_visibility_notify_periodInInstances = ConfigMgr.GetDefaultValue("Visibility.Notify.Period.InInstances", SharedConst.DefaultVisibilityNotifyPeriod); m_visibility_notify_periodInInstances = ConfigMgr.GetDefaultValue("Visibility.Notify.Period.InInstances", SharedConst.DefaultVisibilityNotifyPeriod);
m_visibility_notify_periodInBGArenas = ConfigMgr.GetDefaultValue("Visibility.Notify.Period.InBGArenas", SharedConst.DefaultVisibilityNotifyPeriod); m_visibility_notify_periodInBGArenas = ConfigMgr.GetDefaultValue("Visibility.Notify.Period.InBGArenas", SharedConst.DefaultVisibilityNotifyPeriod);
_guidWarningMsg = WorldConfig.GetDefaultValue("Respawn.WarningMessage", "There will be an unscheduled server restart at 03:00. The server will be available again shortly after.");
_alertRestartReason = WorldConfig.GetDefaultValue("Respawn.AlertRestartReason", "Urgent Maintenance");
string dataPath = ConfigMgr.GetDefaultValue("DataDir", "./"); string dataPath = ConfigMgr.GetDefaultValue("DataDir", "./");
if (reload) if (reload)
{ {
@@ -1332,6 +1397,16 @@ namespace Game
// update the instance reset times // update the instance reset times
Global.InstanceSaveMgr.Update(); Global.InstanceSaveMgr.Update();
// Check for shutdown warning
if (_guidWarn && !_guidAlert)
{
_warnDiff += diff;
if (GameTime.GetGameTime() >= _warnShutdownTime)
DoGuidWarningRestart();
else if (_warnDiff > WorldConfig.GetIntValue(WorldCfg.RespawnGuidWarningFrequency) * Time.InMilliseconds)
SendGuidWarning();
}
Global.ScriptMgr.OnWorldUpdate(diff); Global.ScriptMgr.OnWorldUpdate(diff);
} }
@@ -2288,6 +2363,9 @@ namespace Game
public CleaningFlags GetCleaningFlags() { return m_CleaningFlags; } public CleaningFlags GetCleaningFlags() { return m_CleaningFlags; }
public void SetCleaningFlags(CleaningFlags flags) { m_CleaningFlags = flags; } public void SetCleaningFlags(CleaningFlags flags) { m_CleaningFlags = flags; }
public bool IsGuidWarning() { return _guidWarn; }
public bool IsGuidAlert() { return _guidAlert; }
public WorldUpdateTime GetWorldUpdateTime() { return _worldUpdateTime; } public WorldUpdateTime GetWorldUpdateTime() { return _worldUpdateTime; }
#region Fields #region Fields
@@ -2349,6 +2427,16 @@ namespace Game
string _dataPath; string _dataPath;
WorldUpdateTime _worldUpdateTime; WorldUpdateTime _worldUpdateTime;
string _guidWarningMsg;
string _alertRestartReason;
object _guidAlertLock = new object();
bool _guidWarn;
bool _guidAlert;
uint _warnDiff;
long _warnShutdownTime;
#endregion #endregion
} }
@@ -96,7 +96,7 @@ namespace Scripts.Northrend.IcecrownCitadel
CreatureData data = creature.GetCreatureData(); CreatureData data = creature.GetCreatureData();
if (data != null) if (data != null)
creature.UpdatePosition(data.posX, data.posY, data.posZ, data.orientation); creature.UpdatePosition(data.spawnPoint);
creature.DespawnOrUnsummon(); creature.DespawnOrUnsummon();
creature.SetCorpseDelay(corpseDelay); creature.SetCorpseDelay(corpseDelay);
@@ -496,7 +496,7 @@ namespace Scripts.Northrend.IcecrownCitadel
Creature crusader = ObjectAccessor.GetCreature(me, instance.GetGuidData(DataTypes.CaptainArnath + i)); Creature crusader = ObjectAccessor.GetCreature(me, instance.GetGuidData(DataTypes.CaptainArnath + i));
if (crusader) if (crusader)
{ {
if (crusader.IsAlive() && crusader.GetEntry() == crusader.GetCreatureData().id) if (crusader.IsAlive() && crusader.GetEntry() == crusader.GetCreatureData().Id)
{ {
crusader.m_Events.AddEvent(new CaptainSurviveTalk(crusader), crusader.m_Events.CalculateTime(delay)); crusader.m_Events.AddEvent(new CaptainSurviveTalk(crusader), crusader.m_Events.CalculateTime(delay));
delay += 6000; delay += 6000;
@@ -346,7 +346,7 @@ namespace Scripts.Northrend.IcecrownCitadel
// Weekly quest spawn prevention // Weekly quest spawn prevention
public override uint GetCreatureEntry(ulong guidLow, CreatureData data) public override uint GetCreatureEntry(ulong guidLow, CreatureData data)
{ {
uint entry = data.id; uint entry = data.Id;
switch (entry) switch (entry)
{ {
case CreatureIds.InfiltratorMinchar: case CreatureIds.InfiltratorMinchar:
@@ -384,13 +384,13 @@ namespace Scripts.Northrend.IcecrownCitadel
case CreatureIds.ZafodBoombox: case CreatureIds.ZafodBoombox:
GameObjectTemplate go = Global.ObjectMgr.GetGameObjectTemplate(GameObjectIds.TheSkybreaker_A); GameObjectTemplate go = Global.ObjectMgr.GetGameObjectTemplate(GameObjectIds.TheSkybreaker_A);
if (go != null) if (go != null)
if ((TeamInInstance == Team.Alliance && data.mapid == go.MoTransport.SpawnMap) || if ((TeamInInstance == Team.Alliance && data.spawnPoint.GetMapId() == go.MoTransport.SpawnMap) ||
(TeamInInstance == Team.Horde && data.mapid != go.MoTransport.SpawnMap)) (TeamInInstance == Team.Horde && data.spawnPoint.GetMapId() != go.MoTransport.SpawnMap))
return entry; return entry;
return 0; return 0;
case CreatureIds.IGBMuradinBrozebeard: case CreatureIds.IGBMuradinBrozebeard:
if ((TeamInInstance == Team.Alliance && data.posX > 10.0f) || if ((TeamInInstance == Team.Alliance && data.spawnPoint.GetPositionX() > 10.0f) ||
(TeamInInstance == Team.Horde && data.posX < 10.0f)) (TeamInInstance == Team.Horde && data.spawnPoint.GetPositionX() < 10.0f))
return entry; return entry;
return 0; return 0;
default: default:
+116
View File
@@ -1661,6 +1661,122 @@ MonsterSight = 50.000000
# #
################################################################################################### ###################################################################################################
###################################################################################################
# SPAWN/RESPAWN SETTINGS
#
# Respawn.MinCheckIntervalMS
# Description: Minimum time that needs to pass between respawn checks for any given map.
# Default: 5000 - 5 seconds
Respawn.MinCheckIntervalMS = 5000
#
# Respawn.GuidWarnLevel
# Description: The point at which the highest guid for creatures or gameobjects in any map must reach
# before the warning logic is enabled. A restart will then be queued at the next quiet time
# The maximum guid per map is 16,777,216. So, it must be less than this value.
# Default: 12000000 - 12 million
Respawn.GuidWarnLevel = 12000000
#
# Respawn.WarningMessage
# Description: This message will be periodically shown (Frequency specified by Respawn.WarningFrequency) to
# all users of the server, once the Respawn.GuidWarnLevel has been passed, and a restart scheduled.
# It's used to warn users that there will be an out of schedule server restart soon.
# Default: "There will be an unscheduled server restart at 03:00 server time. The server will be available again shortly after."
Respawn.WarningMessage = "There will be an unscheduled server restart at 03:00. The server will be available again shortly after."
#
# Respawn.WarningFrequency
# Description: The frequency (in seconds) that the warning message will be sent to users after a quiet time restart is triggered.
# The message will repeat each time this many seconds passed until the server is restarted.
# If set to 0, no warnings will be sent.
# Default: 1800 - (30 minutes)
Respawn.WarningFrequency = 1800
#
# Respawn.GuidAlertLevel
# Description: The point at which the highest guid for creatures or gameobjects in any map must reach
# before the alert logic is enabled. A restart will then be triggered for 30 mins from that
# point. The maximum guid per map is 16,777,216. So, it must be less than this value.
# Default: 16000000 - 16 million
Respawn.GuidAlertLevel = 16000000
#
# Respawn.AlertRestartReason
# Description: The shutdown reason given when the alert level is reached. The server will use a fixed time of
# 5 minutes and the reason for shutdown will be this message
# Default: "Urgent Maintenance"
Respawn.AlertRestartReason = "Urgent Maintenance"
#
# Respawn.RestartQuietTime
# Description: The hour at which the server will be restarted after the Respawn.GuidWarnLevel
# threshold has been reached. This can be between 0 and 23. 20 will be 8pm server time
# Default: 3 - 3am
Respawn.RestartQuietTime = 3
#
# Respawn.DynamicMode
# Description: Select which mode (if any) should be used to adjust respawn of creatures.
# This will only affect creatures that have dynamic spawn rate scaling enabled in
# the spawn group table (by default, gathering nodes and quest targets with respawn time <30min
# 1 - Use number of players in zone
# Default: 0 - No dynamic respawn function
Respawn.DynamicMode = 0
#
# Respawn.DynamicEscortNPC
# Description: This switch controls the dynamic respawn system for escort NPCs not in instancable maps (base maps only).
# This will cause the respawn timer to begin when an escort event begins, and potentially
# allow multiple instances of the NPC to be alive at the same time (when combined with Respawn.DynamicMode > 0)
# 1 - Enabled
# Default: 0 - Disabled
Respawn.DynamicEscortNPC = 1
#
# Respawn.DynamicRateCreature
# Description: The rate at which the respawn time is adjusted for high player counts in a zone (for creatures).
# Up to this number of players, the respawn rate is unchanged.
# At double this number in players, you get twice as many respawns, at three times this number, three times the respawns, and so forth.
# Default: 10
Respawn.DynamicRateCreature = 10
#
# Respawn.DynamicMinimumCreature
# Description: The minimum respawn time for a creature under dynamic scaling.
# Default: 10 - (10 seconds)
Respawn.DynamicMinimumCreature = 10
#
# Respawn.DynamicRateGameObject
# Description: The rate at which the respawn time is adjusted for high player counts in a zone (for gameobjects).
# Up to this number of players, the respawn rate is unchanged.
# At double this number in players, you get twice as many respawns, at three times this number, three times the respawns, and so forth.
# Default: 10
Respawn.DynamicRateGameObject = 10
#
# Respawn.DynamicMinimumGameObject
# Description: The minimum respawn time for a gameobject under dynamic scaling.
# Default: 10 - (10 seconds)
Respawn.DynamicMinimumGameObject = 10
#
###################################################################################################
################################################################################################### ###################################################################################################
# CHAT SETTINGS # CHAT SETTINGS
# #
@@ -0,0 +1,214 @@
-- Create databases for spawn group template, and spawn group membership
-- Current flags
-- 0x01 Legacy Spawn Mode (spawn using legacy spawn system)
-- 0x02 Manual Spawn (don't automatically spawn, instead spawned by core as part of script)
DROP TABLE IF EXISTS `spawn_group_template`;
CREATE TABLE `spawn_group_template` (
`groupId` int(10) unsigned NOT NULL,
`groupName` varchar(100) NOT NULL,
`groupFlags` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`groupId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `spawn_group`;
CREATE TABLE `spawn_group` (
`groupId` int(10) unsigned NOT NULL,
`spawnType` tinyint(10) unsigned NOT NULL,
`spawnId` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`groupId`,`spawnType`,`spawnId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- Create the default groups
INSERT INTO `spawn_group_template` (`groupId`, `groupName`, `groupFlags`) VALUES
(0, 'Default Group', 0x01),
(1, 'Legacy Group', (0x01|0x02)),
(2, 'Dynamic Scaling (Quest objectives)', (0x01|0x08)),
(3, 'Dynamic Scaling (Escort NPCs)', (0x01|0x08|0x10)),
(4, 'Dynamic Scaling (Gathering nodes)', (0x01|0x08));
-- Create creature dynamic spawns group (creatures with quest items, or subjects of quests with less than 30min spawn time)
DROP TABLE IF EXISTS `creature_temp_group`;
CREATE TEMPORARY TABLE `creature_temp_group`
(
`creatureId` int(10) unsigned NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `creature_temp_group`
SELECT `guid`
FROM `creature` C
INNER JOIN `creature_questitem` ON `CreatureEntry` = C.`id`
WHERE `spawntimesecs` < 1800
AND `map` IN (0, 1, 530, 571);
INSERT INTO `creature_temp_group`
SELECT `guid`
FROM `creature` C
INNER JOIN `quest_objectives` QO ON QO.`ObjectID` = C.`id` AND QO.`Type` = 0
WHERE `spawntimesecs` < 1800
AND `map` IN (0, 1, 530, 571);
INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`)
SELECT DISTINCT 2, 0, `creatureId`
FROM `creature_temp_group`;
DROP TABLE `creature_temp_group`;
-- Create gameobject dynamic spawns group (gameobjects with quest items, or subjects of quests with less than 30min spawn time)
DROP TABLE IF EXISTS `gameobject_temp_group`;
CREATE TEMPORARY TABLE `gameobject_temp_group`
(
`gameobjectId` int(10) unsigned NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `gameobject_temp_group_ids`;
CREATE TEMPORARY TABLE `gameobject_temp_group_ids`
(
`entryid` int(10) NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `gameobject_temp_group_ids` ADD INDEX (`entryid`);
INSERT INTO `gameobject_temp_group`
SELECT `guid`
FROM `gameobject` G
INNER JOIN `gameobject_questitem` ON `GameObjectEntry` = G.`id`
WHERE `spawntimesecs` < 1800
AND `map` IN (0, 1, 530, 571);
INSERT INTO `gameobject_temp_group_ids` (`entryid`)
SELECT DISTINCT `ObjectID`
FROM `quest_objectives`
WHERE `Type`=2;
INSERT INTO `gameobject_temp_group`
SELECT `guid`
FROM `gameobject` G
INNER JOIN `gameobject_temp_group_ids` ON `entryid` = G.`id`
WHERE `spawntimesecs` < 1800
AND `map` IN (0, 1, 530, 571);
INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`)
SELECT DISTINCT 2, 1, `gameobjectId`
FROM `gameobject_temp_group`;
DROP TABLE `gameobject_temp_group`;
ALTER TABLE `gameobject_temp_group_ids` DROP INDEX `entryid`;
DROP TABLE `gameobject_temp_group_ids`;
-- Add mining nodes/herb nodes to profession node group
INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`)
SELECT 4, 1, `guid`
FROM `gameobject` g
INNER JOIN `gameobject_template` gt
ON gt.`entry` = g.`id`
WHERE `type` IN (3, 50)
AND `Data0` IN (2, 8, 9, 10, 11, 18, 19, 20, 21, 22, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 47, 48, 49, 50, 51, 379, 380, 399, 400, 439, 440, 441, 442, 443, 444, 519, 521, 719, 939, 1119, 1120,
1121, 1122, 1123, 1124, 1632, 1639, 1641, 1642, 1643, 1644, 1645, 1646, 1649, 1650, 1651, 1652, 1782, 1783, 1784, 1785, 1786, 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1800, 1860);
-- Add Escort NPCs
INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`) VALUES
(3, 0, 10873),
(3, 0, 17874),
(3, 0, 40210),
(3, 0, 11348),
(3, 0, 93301),
(3, 0, 93194),
(3, 0, 19107),
(3, 0, 21692),
(3, 0, 21584),
(3, 0, 23229),
(3, 0, 24268),
(3, 0, 21594),
(3, 0, 14387),
(3, 0, 50381),
(3, 0, 15031),
(3, 0, 26987),
(3, 0, 29241),
(3, 0, 32333),
(3, 0, 33115),
(3, 0, 37085),
(3, 0, 41759),
(3, 0, 84459),
(3, 0, 78685),
(3, 0, 62090),
(3, 0, 72388),
(3, 0, 86832),
(3, 0, 67040),
(3, 0, 78781),
(3, 0, 65108),
(3, 0, 63688),
(3, 0, 59383),
(3, 0, 63625),
(3, 0, 70021),
(3, 0, 82071),
(3, 0, 117903),
(3, 0, 111075),
(3, 0, 101136),
(3, 0, 101303),
(3, 0, 122686),
(3, 0, 117065),
(3, 0, 202337),
(3, 0, 2017),
(3, 0, 132683);
-- remove potential duplicates
DELETE FROM `spawn_group` WHERE `groupId` != 3 AND `spawnType`=0 AND `spawnId` IN (SELECT `spawnId` FROM (SELECT `spawnId` FROM `spawn_group` WHERE `groupId`=3 AND `spawnType`=0) as `temp`);
DELETE FROM `spawn_group` WHERE `groupId` != 4 AND `spawnType`=1 AND `spawnId` IN (SELECT `spawnId` FROM (SELECT `spawnId` FROM `spawn_group` WHERE `groupId`=4 AND `spawnType`=1) as `temp`);
-- Update trinity strings for various cs_list strings, to support showing spawn ID and guid.
UPDATE `trinity_string`
SET `content_default` = '%s (Entry: %d) - |cffffffff|Hgameobject:%s|h[%s X:%f Y:%f Z:%f MapId:%d]|h|r %s %s'
WHERE `entry` = 517;
UPDATE `trinity_string`
SET `content_default` = '%s - |cffffffff|Hcreature:%s|h[%s X:%f Y:%f Z:%f MapId:%d]|h|r %s %s'
WHERE `entry` = 515;
UPDATE `trinity_string`
SET `content_default` = '%s - %s X:%f Y:%f Z:%f MapId:%d %s %s'
WHERE `entry` = 1111;
UPDATE `trinity_string`
SET `content_default` = '%s - %s X:%f Y:%f Z:%f MapId:%d %s %s'
WHERE `entry` = 1110;
UPDATE `trinity_string`
SET `entry`=5084
WHERE `entry`=5070;
UPDATE `trinity_string`
SET `entry`=5085
WHERE `entry`=5071;
UPDATE `trinity_string`
SET `entry`=5086
WHERE `entry`=5072;
-- Add new trinity strings for extra npc/gobject info lines
DELETE FROM `trinity_string` WHERE `entry` BETWEEN 5070 AND 5082;
INSERT INTO `trinity_string` (`entry`, `content_default`) VALUES
(5070, 'Spawn group: %s (ID: %u, Flags: %u, Active: %u)'),
(5071, 'Compatibility Mode: %u'),
(5072, 'GUID: %s'),
(5073, 'SpawnID: %s, location (%f, %f, %f)'),
(5074, 'Distance from player %f'),
(5075, 'Spawn group %u not found'),
(5076, 'Spawned a total of %zu objects:'),
(5077, 'Listing %s respawns within %uyd'),
(5078, 'Listing %s respawns for %s (zone %u)'),
(5079, 'SpawnID | Entry | GridXY| Zone | Respawn time (Full)'),
(5080, 'overdue'),
(5081, 'creatures'),
(5082, 'gameobjects');
-- Add new NPC/Gameobject commands
DELETE FROM `command` WHERE `name` IN ('npc spawngroup', 'npc despawngroup', 'gobject spawngroup', 'gobject despawngroup', 'list respawns');
INSERT INTO `command` (`name`, `permission`, `help`) VALUES
('npc spawngroup', 856, 'Syntax: .npc spawngroup $groupId [ignorerespawn] [force]'),
('npc despawngroup', 857, 'Syntax: .npc despawngroup $groupId [removerespawntime]'),
('gobject spawngroup', 858, 'Syntax: .gobject spawngroup $groupId [ignorerespawn] [force]'),
('gobject despawngroup', 859, 'Syntax: .gobject despawngroup $groupId [removerespawntime]'),
('list respawns', 860, 'Syntax: .list respawns [distance]
Lists all pending respawns within <distance> yards, or within current zone if not specified.');