Dynamic Creature/Go spawning
Port From (https://github.com/TrinityCore/TrinityCore/commit/03b125e6d1947258316c931499746696a95aded2)
This commit is contained in:
@@ -1079,11 +1079,26 @@ namespace Framework.Constants
|
||||
DebugSceneObjectList = 5068,
|
||||
DebugSceneObjectDetail = 5069,
|
||||
|
||||
NpcinfoUnitFieldFlags2 = 5070,
|
||||
NpcinfoUnitFieldFlags3 = 5071,
|
||||
NpcinfoNpcFlags = 5072,
|
||||
// Strings Added For DynamicSpawning
|
||||
SpawninfoGroupId = 5070,
|
||||
SpawninfoCompatibilityMode = 5071,
|
||||
SpawninfoGuidinfo = 5072,
|
||||
SpawninfoSpawnidLocation = 5073,
|
||||
SpawninfoDistancefromplayer = 5074,
|
||||
SpawngroupBadgroup = 5075,
|
||||
SpawngroupSpawncount = 5076,
|
||||
ListRespawnsRange = 5077,
|
||||
ListRespawnsZone = 5078,
|
||||
ListRespawnsListheader = 5079,
|
||||
ListRespawnsOverdue = 5080,
|
||||
ListRespawnsCreatures = 5081,
|
||||
ListRespawnsGameobjects = 5082,
|
||||
|
||||
// Room For More Trinity Strings 5073-9999
|
||||
NpcinfoUnitFieldFlags2 = 5084,
|
||||
NpcinfoUnitFieldFlags3 = 5085,
|
||||
NpcinfoNpcFlags = 5086,
|
||||
|
||||
// Room For More Trinity Strings 5087-6603
|
||||
|
||||
// Level Requirement Notifications
|
||||
SayReq = 6604,
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace Framework.Constants
|
||||
{
|
||||
public class MapConst
|
||||
{
|
||||
public const uint InvalidZone = 0xFFFFFFFF;
|
||||
|
||||
//Grids
|
||||
public const int MaxGrids = 64;
|
||||
public const float SizeofGrids = 533.33333f;
|
||||
@@ -200,4 +202,33 @@ namespace Framework.Constants
|
||||
|
||||
All = Vmap | Gobject
|
||||
}
|
||||
|
||||
public enum SpawnObjectType
|
||||
{
|
||||
Creature = 0,
|
||||
GameObject = 1,
|
||||
|
||||
Max
|
||||
}
|
||||
|
||||
public enum SpawnObjectTypeMask
|
||||
{
|
||||
Creature = (1 << SpawnObjectType.Creature),
|
||||
GameObject = (1 << SpawnObjectType.GameObject),
|
||||
|
||||
All = (1 << SpawnObjectType.Max) - 1
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum SpawnGroupFlags
|
||||
{
|
||||
None = 0x00,
|
||||
System = 0x01,
|
||||
CompatibilityMode = 0x02,
|
||||
ManualSpawn = 0x04,
|
||||
DynamicSpawnRate = 0x08,
|
||||
EscortQuestNpc = 0x10,
|
||||
|
||||
All = (System | CompatibilityMode | ManualSpawn | DynamicSpawnRate | EscortQuestNpc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1316,6 +1316,17 @@ namespace Framework.Constants
|
||||
RealmZone,
|
||||
ResetDuelCooldowns,
|
||||
ResetDuelHealthMana,
|
||||
RespawnDynamicEscortNpc,
|
||||
RespawnDynamicMinimumCreature,
|
||||
RespawnDynamicMinimumGameObject,
|
||||
RespawnDynamicMode,
|
||||
RespawnDynamicRateCreature,
|
||||
RespawnDynamicRateGameobject,
|
||||
RespawnGuidAlertLevel,
|
||||
RespawnGuidWarnLevel,
|
||||
RespawnGuidWarningFrequency,
|
||||
RespawnMinCheckIntervalMs,
|
||||
RespawnRestartQuietTime,
|
||||
RestrictedLfgChannel,
|
||||
SaveRespawnTimeImmediately,
|
||||
SessionAddDelay,
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
*
|
||||
* 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 System;
|
||||
|
||||
namespace Framework.Constants
|
||||
{
|
||||
@@ -114,6 +116,15 @@ namespace Framework.Constants
|
||||
End = 3
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum SmartAiSpawnFlags
|
||||
{
|
||||
None = 0x00,
|
||||
IgnoreRespawn = 0x01,
|
||||
ForceSpawn = 0x02,
|
||||
NosaveRespawn = 0x04,
|
||||
}
|
||||
|
||||
public enum SmartAITemplate
|
||||
{
|
||||
Basic = 0, //nothing is preset
|
||||
@@ -125,6 +136,7 @@ namespace Framework.Constants
|
||||
End = 6
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum SmartCastFlags
|
||||
{
|
||||
InterruptPrevious = 0x01, //Interrupt any spell casting
|
||||
@@ -361,6 +373,8 @@ namespace Framework.Constants
|
||||
PlayAnimkit = 128,
|
||||
ScenePlay = 129, // sceneId
|
||||
SceneCancel = 130, // sceneId
|
||||
SpawnSpawngroup = 131, // Group ID, min secs, max secs, spawnflags
|
||||
DespawnSpawngroup = 132, // Group ID, min secs, max secs, spawnflags
|
||||
// 131 - 134 : 3.3.5 reserved
|
||||
PlayCinematic = 135, // reserved for future uses
|
||||
SetMovementSpeed = 136, // movementType, speedInteger, speedFraction
|
||||
|
||||
@@ -89,6 +89,7 @@ namespace Framework.Database
|
||||
PrepareStatement(WorldStatements.DEL_DISABLES, "DELETE FROM disables WHERE entry = ? AND sourceType = ?");
|
||||
PrepareStatement(WorldStatements.UPD_CREATURE_ZONE_AREA_DATA, "UPDATE creature SET zoneId = ?, areaId = ? WHERE guid = ?");
|
||||
PrepareStatement(WorldStatements.UPD_GAMEOBJECT_ZONE_AREA_DATA, "UPDATE gameobject SET zoneId = ?, areaId = ? WHERE guid = ?");
|
||||
PrepareStatement(WorldStatements.DEL_SPAWNGROUP_MEMBER, "DELETE FROM spawn_group WHERE spawnType = ? AND spawnId = ?");
|
||||
PrepareStatement(WorldStatements.SEL_GUILD_REWARDS_REQ_ACHIEVEMENTS, "SELECT AchievementRequired FROM guild_rewards_req_achievements WHERE ItemID = ?");
|
||||
}
|
||||
}
|
||||
@@ -164,6 +165,7 @@ namespace Framework.Database
|
||||
DEL_DISABLES,
|
||||
UPD_CREATURE_ZONE_AREA_DATA,
|
||||
UPD_GAMEOBJECT_ZONE_AREA_DATA,
|
||||
DEL_SPAWNGROUP_MEMBER,
|
||||
SEL_GUILD_REWARDS_REQ_ACHIEVEMENTS,
|
||||
|
||||
MAX_WORLDDATABASE_STATEMENTS
|
||||
|
||||
@@ -474,6 +474,15 @@ namespace Game.AI
|
||||
// Object destruction is handled by Unit::RemoveCharmedBy
|
||||
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; }
|
||||
|
||||
bool MoveInLineOfSight_locked;
|
||||
|
||||
@@ -495,7 +495,7 @@ namespace Game.AI
|
||||
_events.Reset();
|
||||
summons.DespawnAll();
|
||||
_scheduler.CancelAll();
|
||||
if (instance != null)
|
||||
if (instance != null && instance.GetBossState(_bossId) != EncounterState.Done)
|
||||
instance.SetBossState(_bossId, EncounterState.NotStarted);
|
||||
}
|
||||
|
||||
@@ -576,12 +576,14 @@ namespace Game.AI
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
public void _DespawnAtEvade(uint delayToRespawn = 30, Creature who = null)
|
||||
public void _DespawnAtEvade(uint delayToRespawn = 30, Creature who = null) { _DespawnAtEvade(TimeSpan.FromSeconds(delayToRespawn), who); }
|
||||
|
||||
public void _DespawnAtEvade(TimeSpan delayToRespawn, Creature who = null)
|
||||
{
|
||||
if (delayToRespawn < 2)
|
||||
if (delayToRespawn < TimeSpan.FromSeconds(2))
|
||||
{
|
||||
Log.outError(LogFilter.Scripts, "_DespawnAtEvade called with delay of {0} seconds, defaulting to 2.", delayToRespawn);
|
||||
delayToRespawn = 2;
|
||||
delayToRespawn = TimeSpan.FromSeconds(2);
|
||||
}
|
||||
|
||||
if (!who)
|
||||
@@ -595,7 +597,7 @@ namespace Game.AI
|
||||
return;
|
||||
}
|
||||
|
||||
who.DespawnOrUnsummon(0, TimeSpan.FromSeconds(delayToRespawn));
|
||||
who.DespawnOrUnsummon(0, delayToRespawn);
|
||||
|
||||
if (instance != null && who == me)
|
||||
instance.SetBossState(_bossId, EncounterState.Fail);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
using Framework.Constants;
|
||||
using Game.Entities;
|
||||
using Game.Groups;
|
||||
using Game.Maps;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -234,13 +235,17 @@ namespace Game.AI
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_bCanInstantRespawn)
|
||||
if (m_bCanInstantRespawn && !WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc))
|
||||
{
|
||||
me.SetDeathState(DeathState.JustDied);
|
||||
me.Respawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc))
|
||||
me.GetMap().RemoveRespawnTime(SpawnObjectType.Creature, me.GetSpawnId(), true);
|
||||
me.DespawnOrUnsummon();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -274,11 +279,18 @@ namespace Game.AI
|
||||
{
|
||||
Log.outDebug(LogFilter.Scripts, "EscortAI failed because player/group was to far away or not found");
|
||||
|
||||
if (m_bCanInstantRespawn)
|
||||
bool isEscort = false;
|
||||
CreatureData cdata = me.GetCreatureData();
|
||||
if (cdata != null)
|
||||
isEscort = (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc) && cdata.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.EscortQuestNpc));
|
||||
|
||||
if (m_bCanInstantRespawn && !isEscort)
|
||||
{
|
||||
me.SetDeathState(DeathState.JustDied);
|
||||
me.Respawn();
|
||||
}
|
||||
else if (m_bCanInstantRespawn && isEscort)
|
||||
me.GetMap().RemoveRespawnTime(SpawnObjectType.Creature, me.GetSpawnId(), true);
|
||||
else
|
||||
me.DespawnOrUnsummon();
|
||||
|
||||
@@ -391,6 +403,25 @@ namespace Game.AI
|
||||
/// todo get rid of this many variables passed in function.
|
||||
public void Start(bool isActiveAttacker = true, bool run = false, ObjectGuid playerGUID = default, Quest quest = null, bool instantRespawn = false, bool canLoopPath = false, bool resetWaypoints = true)
|
||||
{
|
||||
// Queue respawn from the point it starts
|
||||
Map map = me.GetMap();
|
||||
if (map != null)
|
||||
{
|
||||
CreatureData cdata = me.GetCreatureData();
|
||||
if (cdata != null)
|
||||
{
|
||||
SpawnGroupTemplateData groupdata = cdata.spawnGroupData;
|
||||
if (groupdata != null)
|
||||
{
|
||||
if (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc) && groupdata.flags.HasAnyFlag(SpawnGroupFlags.EscortQuestNpc) && map.GetCreatureRespawnTime(me.GetSpawnId()) == 0)
|
||||
{
|
||||
me.SetRespawnTime(me.GetRespawnDelay());
|
||||
me.SaveRespawnTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (me.GetVictim())
|
||||
{
|
||||
Log.outError(LogFilter.Server, "TSCR ERROR: EscortAI (script: {0}, creature entry: {1}) attempts to Start while in combat", me.GetScriptName(), me.GetEntry());
|
||||
@@ -536,6 +567,17 @@ namespace Game.AI
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool IsEscortNPC(bool onlyIfActive)
|
||||
{
|
||||
if (!onlyIfActive)
|
||||
return true;
|
||||
|
||||
if (!GetEventStarterGUID().IsEmpty())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void WaypointReached(uint pointId) { }
|
||||
public virtual void WaypointStart(uint pointId) { }
|
||||
|
||||
|
||||
@@ -129,39 +129,39 @@ namespace Game.AI
|
||||
continue;
|
||||
}
|
||||
|
||||
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(creature.id);
|
||||
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(creature.Id);
|
||||
if (creatureInfo == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: Creature entry ({creature.id}) guid ({-temp.entryOrGuid}) does not exist, skipped loading.");
|
||||
Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: Creature entry ({creature.Id}) guid ({-temp.entryOrGuid}) does not exist, skipped loading.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (creatureInfo.AIName != "SmartAI")
|
||||
{
|
||||
Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: Creature entry ({creature.id}) guid ({-temp.entryOrGuid}) is not using SmartAI, skipped loading.");
|
||||
Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: Creature entry ({creature.Id}) guid ({-temp.entryOrGuid}) is not using SmartAI, skipped loading.");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SmartScriptType.GameObject:
|
||||
{
|
||||
GameObjectData gameObject = Global.ObjectMgr.GetGOData((ulong)-temp.entryOrGuid);
|
||||
GameObjectData gameObject = Global.ObjectMgr.GetGameObjectData((ulong)-temp.entryOrGuid);
|
||||
if (gameObject == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject guid ({-temp.entryOrGuid}) does not exist, skipped loading.");
|
||||
continue;
|
||||
}
|
||||
|
||||
GameObjectTemplate gameObjectInfo = Global.ObjectMgr.GetGameObjectTemplate(gameObject.id);
|
||||
GameObjectTemplate gameObjectInfo = Global.ObjectMgr.GetGameObjectTemplate(gameObject.Id);
|
||||
if (gameObjectInfo == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject entry ({gameObject.id}) guid ({-temp.entryOrGuid}) does not exist, skipped loading.");
|
||||
Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject entry ({gameObject.Id}) guid ({-temp.entryOrGuid}) does not exist, skipped loading.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (gameObjectInfo.AIName != "SmartGameObjectAI")
|
||||
{
|
||||
Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject entry ({gameObject.id}) guid ({-temp.entryOrGuid}) is not using SmartGameObjectAI, skipped loading.");
|
||||
Log.outError(LogFilter.Sql, $"SmartAIMgr.LoadSmartAIFromDB: GameObject entry ({gameObject.Id}) guid ({-temp.entryOrGuid}) is not using SmartGameObjectAI, skipped loading.");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
@@ -758,7 +758,7 @@ namespace Game.AI
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e.Event.distance.guid != 0 && Global.ObjectMgr.GetGOData(e.Event.distance.guid) == null)
|
||||
if (e.Event.distance.guid != 0 && Global.ObjectMgr.GetGameObjectData(e.Event.distance.guid) == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject guid {0}, skipped.", e.Event.distance.guid);
|
||||
return false;
|
||||
@@ -1376,6 +1376,8 @@ namespace Game.AI
|
||||
case SmartActions.TriggerRandomTimedEvent:
|
||||
case SmartActions.SetCounter:
|
||||
case SmartActions.RemoveAllGameobjects:
|
||||
case SmartActions.SpawnSpawngroup:
|
||||
case SmartActions.DespawnSpawngroup:
|
||||
break;
|
||||
default:
|
||||
Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Not handled action_type({0}), event_type({1}), Entry {2} SourceType {3} Event {4}, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
|
||||
@@ -1423,7 +1425,7 @@ namespace Game.AI
|
||||
return false;
|
||||
}
|
||||
else
|
||||
entry = data.id;
|
||||
entry = data.Id;
|
||||
}
|
||||
else
|
||||
entry = (uint)e.entryOrGuid;
|
||||
@@ -2368,6 +2370,9 @@ namespace Game.AI
|
||||
[FieldOffset(4)]
|
||||
public DisableEvade disableEvade;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public GroupSpawn groupSpawn;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public AuraType auraType;
|
||||
|
||||
@@ -2854,6 +2859,13 @@ namespace Game.AI
|
||||
{
|
||||
public uint disable;
|
||||
}
|
||||
public struct GroupSpawn
|
||||
{
|
||||
public uint groupId;
|
||||
public uint minDelay;
|
||||
public uint maxDelay;
|
||||
public uint spawnflags;
|
||||
}
|
||||
public struct AuraType
|
||||
{
|
||||
public uint type;
|
||||
|
||||
@@ -2051,6 +2051,91 @@ namespace Game.AI
|
||||
target.ToCreature().SetCorpseDelay(e.Action.corpseDelay.timer);
|
||||
break;
|
||||
}
|
||||
case SmartActions.SpawnSpawngroup:
|
||||
{
|
||||
if (e.Action.groupSpawn.minDelay == 0 && e.Action.groupSpawn.maxDelay == 0)
|
||||
{
|
||||
bool ignoreRespawn = ((e.Action.groupSpawn.spawnflags & (uint)SmartAiSpawnFlags.IgnoreRespawn) != 0);
|
||||
bool force = ((e.Action.groupSpawn.spawnflags & (uint)SmartAiSpawnFlags.ForceSpawn) != 0);
|
||||
|
||||
// Instant spawn
|
||||
Global.ObjectMgr.SpawnGroupSpawn(e.Action.groupSpawn.groupId, GetBaseObject().GetMap(), ignoreRespawn, force);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Delayed spawn (use values from parameter to schedule event to call us back
|
||||
SmartEvent ne = new SmartEvent();
|
||||
ne.type = SmartEvents.Update;
|
||||
ne.event_chance = 100;
|
||||
|
||||
ne.minMaxRepeat.min = e.Action.groupSpawn.minDelay;
|
||||
ne.minMaxRepeat.max = e.Action.groupSpawn.maxDelay;
|
||||
ne.minMaxRepeat.repeatMin = 0;
|
||||
ne.minMaxRepeat.repeatMax = 0;
|
||||
|
||||
ne.event_flags = 0;
|
||||
ne.event_flags |= SmartEventFlags.NotRepeatable;
|
||||
|
||||
SmartAction ac = new SmartAction();
|
||||
ac.type = SmartActions.SpawnSpawngroup;
|
||||
ac.groupSpawn.groupId = e.Action.groupSpawn.groupId;
|
||||
ac.groupSpawn.minDelay = 0;
|
||||
ac.groupSpawn.maxDelay = 0;
|
||||
ac.groupSpawn.spawnflags = e.Action.groupSpawn.spawnflags;
|
||||
ac.timeEvent.id = e.Action.timeEvent.id;
|
||||
|
||||
SmartScriptHolder ev = new SmartScriptHolder();
|
||||
ev.Event = ne;
|
||||
ev.event_id = e.event_id;
|
||||
ev.Target = e.Target;
|
||||
ev.Action = ac;
|
||||
InitTimer(ev);
|
||||
mStoredEvents.Add(ev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SmartActions.DespawnSpawngroup:
|
||||
{
|
||||
if (e.Action.groupSpawn.minDelay == 0 && e.Action.groupSpawn.maxDelay == 0)
|
||||
{
|
||||
bool deleteRespawnTimes = ((e.Action.groupSpawn.spawnflags & (uint)SmartAiSpawnFlags.NosaveRespawn) != 0);
|
||||
|
||||
// Instant spawn
|
||||
Global.ObjectMgr.SpawnGroupDespawn(e.Action.groupSpawn.groupId, GetBaseObject().GetMap(), deleteRespawnTimes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Delayed spawn (use values from parameter to schedule event to call us back
|
||||
SmartEvent ne = new SmartEvent();
|
||||
ne.type = SmartEvents.Update;
|
||||
ne.event_chance = 100;
|
||||
|
||||
ne.minMaxRepeat.min = e.Action.groupSpawn.minDelay;
|
||||
ne.minMaxRepeat.max = e.Action.groupSpawn.maxDelay;
|
||||
ne.minMaxRepeat.repeatMin = 0;
|
||||
ne.minMaxRepeat.repeatMax = 0;
|
||||
|
||||
ne.event_flags = 0;
|
||||
ne.event_flags |= SmartEventFlags.NotRepeatable;
|
||||
|
||||
SmartAction ac = new SmartAction();
|
||||
ac.type = SmartActions.DespawnSpawngroup;
|
||||
ac.groupSpawn.groupId = e.Action.groupSpawn.groupId;
|
||||
ac.groupSpawn.minDelay = 0;
|
||||
ac.groupSpawn.maxDelay = 0;
|
||||
ac.groupSpawn.spawnflags = e.Action.groupSpawn.spawnflags;
|
||||
ac.timeEvent.id = e.Action.timeEvent.id;
|
||||
|
||||
SmartScriptHolder ev = new SmartScriptHolder();
|
||||
ev.Event = ne;
|
||||
ev.event_id = e.event_id;
|
||||
ev.Target = e.Target;
|
||||
ev.Action = ac;
|
||||
InitTimer(ev);
|
||||
mStoredEvents.Add(ev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SmartActions.DisableEvade:
|
||||
{
|
||||
if (!IsSmart())
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace Game.Chat
|
||||
if (gameObjectInfo == null)
|
||||
continue;
|
||||
|
||||
handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gameObjectInfo.name, x, y, z, mapId);
|
||||
handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gameObjectInfo.name, x, y, z, mapId, "", "");
|
||||
|
||||
++count;
|
||||
} while (result.NextRow());
|
||||
@@ -412,10 +412,10 @@ namespace Game.Chat
|
||||
if (!ulong.TryParse(cValue, out ulong guidLow))
|
||||
return false;
|
||||
|
||||
GameObjectData data = Global.ObjectMgr.GetGOData(guidLow);
|
||||
GameObjectData data = Global.ObjectMgr.GetGameObjectData(guidLow);
|
||||
if (data == null)
|
||||
return false;
|
||||
entry = data.id;
|
||||
entry = data.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -427,15 +427,45 @@ namespace Game.Chat
|
||||
if (gameObjectInfo == null)
|
||||
return false;
|
||||
|
||||
GameObject thisGO = null;
|
||||
if (handler.GetSession().GetPlayer())
|
||||
thisGO = handler.GetSession().GetPlayer().FindNearestGameObject(entry, 30);
|
||||
else if (handler.GetSelectedObject() != null && handler.GetSelectedObject().IsTypeId(TypeId.GameObject))
|
||||
thisGO = handler.GetSelectedObject().ToGameObject();
|
||||
|
||||
GameObjectTypes type = gameObjectInfo.type;
|
||||
uint displayId = gameObjectInfo.displayId;
|
||||
string name = gameObjectInfo.name;
|
||||
uint lootId = gameObjectInfo.GetLootId();
|
||||
|
||||
// If we have a real object, send some info about it
|
||||
if (thisGO != null)
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.SpawninfoGuidinfo, thisGO.GetGUID().ToString());
|
||||
handler.SendSysMessage(CypherStrings.SpawninfoSpawnidLocation, thisGO.GetSpawnId(), thisGO.GetPositionX(), thisGO.GetPositionY(), thisGO.GetPositionZ());
|
||||
Player player = handler.GetSession().GetPlayer();
|
||||
if (player != null)
|
||||
{
|
||||
Position playerPos = player.GetPosition();
|
||||
float dist = thisGO.GetExactDist(playerPos);
|
||||
handler.SendSysMessage(CypherStrings.SpawninfoDistancefromplayer, dist);
|
||||
}
|
||||
}
|
||||
handler.SendSysMessage(CypherStrings.GoinfoEntry, entry);
|
||||
handler.SendSysMessage(CypherStrings.GoinfoType, type);
|
||||
handler.SendSysMessage(CypherStrings.GoinfoLootid, lootId);
|
||||
handler.SendSysMessage(CypherStrings.GoinfoDisplayid, displayId);
|
||||
WorldObject obj = handler.GetSelectedObject();
|
||||
if (obj != null)
|
||||
{
|
||||
if (obj.IsGameObject() && obj.ToGameObject().GetGameObjectData() != null && obj.ToGameObject().GetGameObjectData().spawnGroupData.groupId != 0)
|
||||
{
|
||||
SpawnGroupTemplateData groupData = obj.ToGameObject().GetGameObjectData().spawnGroupData;
|
||||
handler.SendSysMessage(CypherStrings.SpawninfoGroupId, groupData.name, groupData.groupId, groupData.flags, groupData.isActive);
|
||||
}
|
||||
if (obj.IsGameObject())
|
||||
handler.SendSysMessage(CypherStrings.SpawninfoCompatibilityMode, obj.ToGameObject().GetRespawnCompatibilityMode());
|
||||
}
|
||||
handler.SendSysMessage(CypherStrings.GoinfoName, name);
|
||||
handler.SendSysMessage(CypherStrings.GoinfoSize, gameObjectInfo.size);
|
||||
|
||||
@@ -508,7 +538,7 @@ namespace Game.Chat
|
||||
return false;
|
||||
|
||||
// TODO: is it really necessary to add both the real and DB table guid here ?
|
||||
Global.ObjectMgr.AddGameObjectToGrid(spawnId, Global.ObjectMgr.GetGOData(spawnId));
|
||||
Global.ObjectMgr.AddGameObjectToGrid(spawnId, Global.ObjectMgr.GetGameObjectData(spawnId));
|
||||
handler.SendSysMessage(CypherStrings.GameobjectAdd, objectId, objectInfo.name, spawnId, player.GetPositionX(), player.GetPositionY(), player.GetPositionZ());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -202,28 +202,17 @@ namespace Game.Chat.Commands
|
||||
if (!ulong.TryParse(id, out ulong guidLow) || guidLow == 0)
|
||||
return false;
|
||||
|
||||
float x, y, z, o;
|
||||
uint mapId;
|
||||
|
||||
// by DB guid
|
||||
GameObjectData goData = Global.ObjectMgr.GetGOData(guidLow);
|
||||
if (goData != null)
|
||||
{
|
||||
x = goData.posX;
|
||||
y = goData.posY;
|
||||
z = goData.posZ;
|
||||
o = goData.orientation;
|
||||
mapId = goData.mapid;
|
||||
}
|
||||
else
|
||||
GameObjectData goData = Global.ObjectMgr.GetGameObjectData(guidLow);
|
||||
if (goData == null)
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.CommandGoobjnotfound);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GridDefines.IsValidMapCoord(mapId, x, y, z, o) || Global.ObjectMgr.IsTransportMap(mapId))
|
||||
if (!GridDefines.IsValidMapCoord(goData.spawnPoint) || Global.ObjectMgr.IsTransportMap(goData.spawnPoint.GetMapId()))
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.InvalidTargetCoord, x, y, mapId);
|
||||
handler.SendSysMessage(CypherStrings.InvalidTargetCoord, goData.spawnPoint.GetPositionX(), goData.spawnPoint.GetPositionY(), goData.spawnPoint.GetMapId());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -237,7 +226,7 @@ namespace Game.Chat.Commands
|
||||
else
|
||||
player.SaveRecallPosition();
|
||||
|
||||
player.TeleportTo(mapId, x, y, z, o);
|
||||
player.TeleportTo(goData.spawnPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
using Framework.Constants;
|
||||
using Framework.Database;
|
||||
using Framework.IO;
|
||||
using Game.DataStorage;
|
||||
using Game.Entities;
|
||||
using Game.Maps;
|
||||
using Game.Spells;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -128,11 +130,39 @@ namespace Game.Chat.Commands
|
||||
float y = result.Read<float>(2);
|
||||
float z = result.Read<float>(3);
|
||||
ushort mapId = result.Read<ushort>(4);
|
||||
bool liveFound = false;
|
||||
|
||||
// Get map (only support base map from console)
|
||||
Map thisMap;
|
||||
if (handler.GetSession() != null)
|
||||
handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, cInfo.Name, x, y, z, mapId);
|
||||
thisMap = handler.GetSession().GetPlayer().GetMap();
|
||||
else
|
||||
handler.SendSysMessage(CypherStrings.CreatureListConsole, guid, cInfo.Name, x, y, z, mapId);
|
||||
thisMap = Global.MapMgr.FindBaseNonInstanceMap(mapId);
|
||||
|
||||
// If map found, try to find active version of this creature
|
||||
if (thisMap)
|
||||
{
|
||||
var creBounds = thisMap.GetCreatureBySpawnIdStore().LookupByKey(guid);
|
||||
if (!creBounds.Empty())
|
||||
{
|
||||
foreach (var creature in creBounds)
|
||||
{
|
||||
if (handler.GetSession())
|
||||
handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, cInfo.Name, x, y, z, mapId, creature.GetGUID().ToString(), creature.IsAlive() ? "*" : " ");
|
||||
else
|
||||
handler.SendSysMessage(CypherStrings.CreatureListConsole, guid, cInfo.Name, x, y, z, mapId, creature.GetGUID().ToString(), creature.IsAlive() ? "*" : " ");
|
||||
}
|
||||
liveFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!liveFound)
|
||||
{
|
||||
if (handler.GetSession())
|
||||
handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, cInfo.Name, x, y, z, mapId, "", "");
|
||||
else
|
||||
handler.SendSysMessage(CypherStrings.CreatureListConsole, guid, cInfo.Name, x, y, z, mapId, "", "");
|
||||
}
|
||||
}
|
||||
while (result.NextRow());
|
||||
}
|
||||
@@ -495,11 +525,39 @@ namespace Game.Chat.Commands
|
||||
float z = result.Read<float>(3);
|
||||
ushort mapId = result.Read<ushort>(4);
|
||||
uint entry = result.Read<uint>(5);
|
||||
bool liveFound = false;
|
||||
|
||||
// Get map (only support base map from console)
|
||||
Map thisMap;
|
||||
if (handler.GetSession() != null)
|
||||
handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gInfo.name, x, y, z, mapId);
|
||||
thisMap = handler.GetSession().GetPlayer().GetMap();
|
||||
else
|
||||
handler.SendSysMessage(CypherStrings.GoListConsole, guid, gInfo.name, x, y, z, mapId);
|
||||
thisMap = Global.MapMgr.FindBaseNonInstanceMap(mapId);
|
||||
|
||||
// If map found, try to find active version of this object
|
||||
if (thisMap)
|
||||
{
|
||||
var goBounds = thisMap.GetGameObjectBySpawnIdStore().LookupByKey(guid);
|
||||
if (!goBounds.Empty())
|
||||
{
|
||||
foreach (var go in goBounds)
|
||||
{
|
||||
if (handler.GetSession())
|
||||
handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gInfo.name, x, y, z, mapId, go.GetGUID(), go.IsSpawned() ? "*" : " ");
|
||||
else
|
||||
handler.SendSysMessage(CypherStrings.GoListConsole, guid, gInfo.name, x, y, z, mapId, go.GetGUID(), go.IsSpawned() ? "*" : " ");
|
||||
}
|
||||
liveFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!liveFound)
|
||||
{
|
||||
if (handler.GetSession())
|
||||
handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gInfo.name, x, y, z, mapId, "", "");
|
||||
else
|
||||
handler.SendSysMessage(CypherStrings.GoListConsole, guid, gInfo.name, x, y, z, mapId, "", "");
|
||||
}
|
||||
}
|
||||
while (result.NextRow());
|
||||
}
|
||||
@@ -509,6 +567,77 @@ namespace Game.Chat.Commands
|
||||
return true;
|
||||
}
|
||||
|
||||
[Command("respawns", RBACPermissions.CommandListRespawns)]
|
||||
static bool HandleListRespawnsCommand(StringArguments args, CommandHandler handler)
|
||||
{
|
||||
// We need a player
|
||||
Player player = handler.GetSession().GetPlayer();
|
||||
if (player == null)
|
||||
return false;
|
||||
|
||||
// And we need a map
|
||||
Map map = player.GetMap();
|
||||
if (map == null)
|
||||
return false;
|
||||
|
||||
uint range = 0;
|
||||
if (!args.Empty())
|
||||
range = args.NextUInt32();
|
||||
|
||||
List<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)]
|
||||
static bool HandleListScenesCommand(StringArguments args, CommandHandler handler)
|
||||
{
|
||||
@@ -531,5 +660,11 @@ namespace Game.Chat.Commands
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static string GetZoneName(uint zoneId, Locale locale)
|
||||
{
|
||||
AreaTableRecord zoneEntry = CliDB.AreaTableStorage.LookupByKey(zoneId);
|
||||
return zoneEntry != null ? zoneEntry.AreaName[locale] : "<unknown zone>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1260
-1248
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,41 @@ namespace Game.Chat
|
||||
[CommandGroup("npc", RBACPermissions.CommandNpc)]
|
||||
class NPCCommands
|
||||
{
|
||||
[Command("despawngroup", RBACPermissions.CommandNpcDespawngroup)]
|
||||
static bool HandleNpcDespawnGroup(StringArguments args, CommandHandler handler)
|
||||
{
|
||||
if (args.Empty())
|
||||
return false;
|
||||
|
||||
bool deleteRespawnTimes = false;
|
||||
uint groupId = 0;
|
||||
|
||||
// Decode arguments
|
||||
string arg = args.NextString();
|
||||
while (!arg.IsEmpty())
|
||||
{
|
||||
string thisArg = arg.ToLower();
|
||||
if (thisArg == "removerespawntime")
|
||||
deleteRespawnTimes = true;
|
||||
else if (thisArg.IsEmpty() || !thisArg.IsNumber())
|
||||
return false;
|
||||
else
|
||||
groupId = uint.Parse(thisArg);
|
||||
|
||||
arg = args.NextString();
|
||||
}
|
||||
|
||||
Player player = handler.GetSession().GetPlayer();
|
||||
|
||||
if (!Global.ObjectMgr.SpawnGroupDespawn(groupId, player.GetMap(), deleteRespawnTimes))
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.SpawngroupBadgroup, groupId);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Command("evade", RBACPermissions.CommandNpcEvade)]
|
||||
static bool HandleNpcEvadeCommand(StringArguments args, CommandHandler handler)
|
||||
{
|
||||
@@ -96,13 +131,21 @@ namespace Game.Chat
|
||||
uint nativeid = target.GetNativeDisplayId();
|
||||
uint Entry = target.GetEntry();
|
||||
|
||||
long curRespawnDelay = target.GetRespawnTimeEx() - Time.UnixTime;
|
||||
long curRespawnDelay = target.GetRespawnCompatibilityMode() ? target.GetRespawnTimeEx() - Time.UnixTime : target.GetMap().GetCreatureRespawnTime(target.GetSpawnId()) - Time.UnixTime;
|
||||
if (curRespawnDelay < 0)
|
||||
curRespawnDelay = 0;
|
||||
|
||||
string curRespawnDelayStr = Time.secsToTimeString((ulong)curRespawnDelay, true);
|
||||
string defRespawnDelayStr = Time.secsToTimeString(target.GetRespawnDelay(), true);
|
||||
|
||||
handler.SendSysMessage(CypherStrings.NpcinfoChar, target.GetSpawnId(), target.GetGUID().ToString(), faction, npcflags, Entry, displayid, nativeid);
|
||||
if (target.GetCreatureData() != null && target.GetCreatureData().spawnGroupData.groupId != 0)
|
||||
{
|
||||
SpawnGroupTemplateData groupData = target.GetCreatureData().spawnGroupData;
|
||||
if (groupData != null)
|
||||
handler.SendSysMessage(CypherStrings.SpawninfoGroupId, groupData.name, groupData.groupId, groupData.flags, groupData.isActive);
|
||||
}
|
||||
handler.SendSysMessage(CypherStrings.SpawninfoCompatibilityMode, target.GetRespawnCompatibilityMode());
|
||||
handler.SendSysMessage(CypherStrings.NpcinfoLevel, target.GetLevel());
|
||||
handler.SendSysMessage(CypherStrings.NpcinfoEquipment, target.GetCurrentEquipmentId(), target.GetOriginalEquipmentId());
|
||||
handler.SendSysMessage(CypherStrings.NpcinfoHealth, target.GetCreateHealth(), target.GetMaxHealth(), target.GetHealth());
|
||||
@@ -161,71 +204,60 @@ namespace Game.Chat
|
||||
ulong lowguid;
|
||||
|
||||
Creature creature = handler.GetSelectedCreature();
|
||||
if (!creature)
|
||||
Player player = handler.GetSession().GetPlayer();
|
||||
if (player == null)
|
||||
return false;
|
||||
|
||||
if (creature != null)
|
||||
lowguid = creature.GetSpawnId();
|
||||
else
|
||||
{
|
||||
// number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
|
||||
string cId = handler.ExtractKeyFromLink(args, "Hcreature");
|
||||
if (string.IsNullOrEmpty(cId))
|
||||
if (cId.IsEmpty())
|
||||
return false;
|
||||
|
||||
if (!ulong.TryParse(cId, out lowguid))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Attempting creature load from DB data
|
||||
CreatureData data = Global.ObjectMgr.GetCreatureData(lowguid);
|
||||
if (data == null)
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.CommandCreatguidnotfound, lowguid);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint map_id = data.mapid;
|
||||
if (handler.GetSession().GetPlayer().GetMapId() != map_id)
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.CommandCreatureatsamemap, lowguid);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
if (data == null)
|
||||
{
|
||||
lowguid = creature.GetSpawnId();
|
||||
handler.SendSysMessage(CypherStrings.CommandCreatguidnotfound, lowguid);
|
||||
return false;
|
||||
}
|
||||
|
||||
float x = handler.GetPlayer().GetPositionX();
|
||||
float y = handler.GetPlayer().GetPositionY();
|
||||
float z = handler.GetPlayer().GetPositionZ();
|
||||
float o = handler.GetPlayer().GetOrientation();
|
||||
|
||||
if (creature)
|
||||
if (player.GetMapId() != data.spawnPoint.GetMapId())
|
||||
{
|
||||
CreatureData data = Global.ObjectMgr.GetCreatureData(creature.GetSpawnId());
|
||||
if (data != null)
|
||||
{
|
||||
data.posX = x;
|
||||
data.posY = y;
|
||||
data.posZ = z;
|
||||
data.orientation = o;
|
||||
}
|
||||
creature.UpdatePosition(x, y, z, o);
|
||||
creature.GetMotionMaster().Initialize();
|
||||
if (creature.IsAlive()) // dead creature will reset movement generator at respawn
|
||||
{
|
||||
creature.SetDeathState(DeathState.JustDied);
|
||||
creature.Respawn();
|
||||
}
|
||||
handler.SendSysMessage(CypherStrings.CommandCreatureatsamemap, lowguid);
|
||||
return false;
|
||||
}
|
||||
|
||||
data.spawnPoint.Relocate(player);
|
||||
|
||||
// update position in DB
|
||||
PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_CREATURE_POSITION);
|
||||
|
||||
stmt.AddValue(0, x);
|
||||
stmt.AddValue(1, y);
|
||||
stmt.AddValue(2, z);
|
||||
stmt.AddValue(3, o);
|
||||
stmt.AddValue(0, player.GetPositionX());
|
||||
stmt.AddValue(1, player.GetPositionY());
|
||||
stmt.AddValue(2, player.GetPositionZ());
|
||||
stmt.AddValue(3, player.GetOrientation());
|
||||
stmt.AddValue(4, lowguid);
|
||||
|
||||
DB.World.Execute(stmt);
|
||||
|
||||
// respawn selected creature at the new location
|
||||
if (creature)
|
||||
{
|
||||
if (creature.IsAlive())
|
||||
creature.SetDeathState(DeathState.JustDied);
|
||||
creature.Respawn(true);
|
||||
if (!creature.GetRespawnCompatibilityMode())
|
||||
creature.AddObjectToRemoveList();
|
||||
}
|
||||
|
||||
handler.SendSysMessage(CypherStrings.CommandCreaturemoved);
|
||||
return true;
|
||||
}
|
||||
@@ -264,7 +296,7 @@ namespace Game.Chat
|
||||
if (creatureTemplate == null)
|
||||
continue;
|
||||
|
||||
handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, creatureTemplate.Name, x, y, z, mapId);
|
||||
handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, creatureTemplate.Name, x, y, z, mapId, "", "");
|
||||
|
||||
++count;
|
||||
}
|
||||
@@ -386,6 +418,49 @@ namespace Game.Chat
|
||||
return true;
|
||||
}
|
||||
|
||||
[Command("spawngroup", RBACPermissions.CommandNpcSpawngroup)]
|
||||
static bool HandleNpcSpawnGroup(StringArguments args, CommandHandler handler)
|
||||
{
|
||||
if (args.Empty())
|
||||
return false;
|
||||
|
||||
bool ignoreRespawn = false;
|
||||
bool force = false;
|
||||
uint groupId = 0;
|
||||
|
||||
// Decode arguments
|
||||
string arg = args.NextString();
|
||||
while (!arg.IsEmpty())
|
||||
{
|
||||
string thisArg = arg.ToLower();
|
||||
if (thisArg == "ignorerespawn")
|
||||
ignoreRespawn = true;
|
||||
else if (thisArg == "force")
|
||||
force = true;
|
||||
else if (thisArg.IsEmpty() || !thisArg.IsNumber())
|
||||
return false;
|
||||
else
|
||||
groupId = uint.Parse(thisArg);
|
||||
|
||||
arg = args.NextString();
|
||||
}
|
||||
|
||||
Player player = handler.GetSession().GetPlayer();
|
||||
|
||||
List<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)]
|
||||
static bool HandleNpcTameCommand(StringArguments args, CommandHandler handler)
|
||||
{
|
||||
@@ -581,13 +656,11 @@ namespace Game.Chat
|
||||
{
|
||||
ulong guid = map.GenerateLowGuid(HighGuid.Creature);
|
||||
CreatureData data = Global.ObjectMgr.NewOrExistCreatureData(guid);
|
||||
data.id = id;
|
||||
data.posX = chr.GetTransOffsetX();
|
||||
data.posY = chr.GetTransOffsetY();
|
||||
data.posZ = chr.GetTransOffsetZ();
|
||||
data.orientation = chr.GetTransOffsetO();
|
||||
data.spawnId = guid;
|
||||
data.Id = id;
|
||||
data.spawnPoint.Relocate(chr.GetTransOffsetX(), chr.GetTransOffsetY(), chr.GetTransOffsetZ(), chr.GetTransOffsetO());
|
||||
// @todo: add phases
|
||||
|
||||
|
||||
Creature _creature = trans.CreateNPCPassenger(guid, data);
|
||||
_creature.SaveToDB((uint)trans.GetGoInfo().MoTransport.SpawnMap, new List<Difficulty>() { map.GetDifficultyID() });
|
||||
|
||||
@@ -607,7 +680,7 @@ namespace Game.Chat
|
||||
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells()
|
||||
// current "creature" variable is deleted and created fresh new, otherwise old values might trigger asserts or cause undefined behavior
|
||||
creature.CleanupsBeforeDelete();
|
||||
creature = Creature.CreateCreatureFromDB(db_guid, map);
|
||||
creature = Creature.CreateCreatureFromDB(db_guid, map, true, true);
|
||||
if (!creature)
|
||||
return false;
|
||||
|
||||
@@ -794,7 +867,7 @@ namespace Game.Chat
|
||||
[Command("delete", RBACPermissions.CommandNpcDelete)]
|
||||
static bool HandleNpcDeleteCommand(StringArguments args, CommandHandler handler)
|
||||
{
|
||||
Creature unit = null;
|
||||
Creature creature;
|
||||
|
||||
if (!args.Empty())
|
||||
{
|
||||
@@ -806,21 +879,27 @@ namespace Game.Chat
|
||||
if (!ulong.TryParse(cId, out ulong guidLow) || guidLow == 0)
|
||||
return false;
|
||||
|
||||
unit = handler.GetCreatureFromPlayerMapByDbGuid(guidLow);
|
||||
creature = handler.GetCreatureFromPlayerMapByDbGuid(guidLow);
|
||||
}
|
||||
else
|
||||
unit = handler.GetSelectedCreature();
|
||||
creature = handler.GetSelectedCreature();
|
||||
|
||||
if (!unit || unit.IsPet() || unit.IsTotem())
|
||||
if (!creature || creature.IsPet() || creature.IsTotem())
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.SelectCreature);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete the creature
|
||||
unit.CombatStop();
|
||||
unit.DeleteFromDB();
|
||||
unit.AddObjectToRemoveList();
|
||||
TempSummon summon = creature.ToTempSummon();
|
||||
if (summon != null)
|
||||
summon.UnSummon();
|
||||
else
|
||||
{
|
||||
// Delete the creature
|
||||
creature.CombatStop();
|
||||
creature.DeleteFromDB();
|
||||
creature.AddObjectToRemoveList();
|
||||
}
|
||||
|
||||
handler.SendSysMessage(CypherStrings.CommandDelcreatmessage);
|
||||
|
||||
|
||||
@@ -559,7 +559,7 @@ namespace Game.Chat.Commands
|
||||
creature.Dispose();
|
||||
|
||||
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
|
||||
creature = Creature.CreateCreatureFromDB(dbGuid, map);
|
||||
creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
|
||||
if (!creature)
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1);
|
||||
@@ -779,7 +779,7 @@ namespace Game.Chat.Commands
|
||||
creature.Dispose();
|
||||
|
||||
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
|
||||
creature = Creature.CreateCreatureFromDB(dbGuid, map);
|
||||
creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
|
||||
if (!creature)
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, id);
|
||||
@@ -844,7 +844,7 @@ namespace Game.Chat.Commands
|
||||
creature.CleanupsBeforeDelete();
|
||||
creature.Dispose();
|
||||
|
||||
creature = Creature.CreateCreatureFromDB(dbGuid, map);
|
||||
creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
|
||||
if (!creature)
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.WaypointVpNotcreated, 1);
|
||||
@@ -899,7 +899,7 @@ namespace Game.Chat.Commands
|
||||
creature.CleanupsBeforeDelete();
|
||||
creature.Dispose();
|
||||
|
||||
creature = Creature.CreateCreatureFromDB(dbGuid, map);
|
||||
creature = Creature.CreateCreatureFromDB(dbGuid, map, true, true);
|
||||
if (!creature)
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.WaypointNotcreated, 1);
|
||||
|
||||
@@ -1328,7 +1328,7 @@ namespace Game
|
||||
CreatureData creatureData = Global.ObjectMgr.GetCreatureData(cond.ConditionValue3);
|
||||
if (creatureData != null)
|
||||
{
|
||||
if (cond.ConditionValue2 != 0 && creatureData.id != cond.ConditionValue2)
|
||||
if (cond.ConditionValue2 != 0 && creatureData.Id != cond.ConditionValue2)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "{0} has guid {1} set but does not match creature entry ({1}), skipped.", cond.ToString(true), cond.ConditionValue3, cond.ConditionValue2);
|
||||
return false;
|
||||
@@ -1349,10 +1349,10 @@ namespace Game
|
||||
}
|
||||
if (cond.ConditionValue3 != 0)
|
||||
{
|
||||
GameObjectData goData = Global.ObjectMgr.GetGOData(cond.ConditionValue3);
|
||||
GameObjectData goData = Global.ObjectMgr.GetGameObjectData(cond.ConditionValue3);
|
||||
if (goData != null)
|
||||
{
|
||||
if (cond.ConditionValue2 != 0 && goData.id != cond.ConditionValue2)
|
||||
if (cond.ConditionValue2 != 0 && goData.Id != cond.ConditionValue2)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "{0} has guid {1} set but does not match gameobject entry ({1}), skipped.", cond.ToString(true), cond.ConditionValue3, cond.ConditionValue2);
|
||||
return false;
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace Game.Entities
|
||||
//Formation var
|
||||
CreatureGroup m_formation;
|
||||
bool TriggerJustRespawned;
|
||||
bool m_respawnCompatibilityMode;
|
||||
|
||||
public uint[] m_spells = new uint[SharedConst.MaxCreatureSpells];
|
||||
|
||||
|
||||
@@ -123,45 +123,62 @@ namespace Game.Entities
|
||||
if (GetDeathState() != DeathState.Corpse)
|
||||
return;
|
||||
|
||||
m_corpseRemoveTime = Time.UnixTime;
|
||||
SetDeathState(DeathState.Dead);
|
||||
RemoveAllAuras();
|
||||
DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
|
||||
loot.Clear();
|
||||
uint respawnDelay = m_respawnDelay;
|
||||
if (IsAIEnabled)
|
||||
GetAI().CorpseRemoved(respawnDelay);
|
||||
|
||||
if (destroyForNearbyPlayers)
|
||||
DestroyForNearbyPlayers();
|
||||
|
||||
// Should get removed later, just keep "compatibility" with scripts
|
||||
if (setSpawnTime)
|
||||
m_respawnTime = Math.Max(Time.UnixTime + respawnDelay, m_respawnTime);
|
||||
|
||||
// if corpse was removed during falling, the falling will continue and override relocation to respawn position
|
||||
if (IsFalling())
|
||||
StopMoving();
|
||||
|
||||
float x, y, z, o;
|
||||
GetRespawnPosition(out x, out y, out z, out o);
|
||||
|
||||
// We were spawned on transport, calculate real position
|
||||
if (IsSpawnedOnTransport())
|
||||
if (m_respawnCompatibilityMode)
|
||||
{
|
||||
Position pos = m_movementInfo.transport.pos;
|
||||
pos.posX = x;
|
||||
pos.posY = y;
|
||||
pos.posZ = z;
|
||||
pos.SetOrientation(o);
|
||||
m_corpseRemoveTime = Time.UnixTime;
|
||||
SetDeathState(DeathState.Dead);
|
||||
RemoveAllAuras();
|
||||
//DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
|
||||
loot.Clear();
|
||||
uint respawnDelay = m_respawnDelay;
|
||||
if (IsAIEnabled)
|
||||
GetAI().CorpseRemoved(respawnDelay);
|
||||
|
||||
ITransport transport = GetDirectTransport();
|
||||
if (transport != null)
|
||||
transport.CalculatePassengerPosition(ref x, ref y, ref z, ref o);
|
||||
if (destroyForNearbyPlayers)
|
||||
DestroyForNearbyPlayers();
|
||||
|
||||
// Should get removed later, just keep "compatibility" with scripts
|
||||
if (setSpawnTime)
|
||||
m_respawnTime = Math.Max(Time.UnixTime + respawnDelay, m_respawnTime);
|
||||
|
||||
// if corpse was removed during falling, the falling will continue and override relocation to respawn position
|
||||
if (IsFalling())
|
||||
StopMoving();
|
||||
|
||||
float x, y, z, o;
|
||||
GetRespawnPosition(out x, out y, out z, out o);
|
||||
|
||||
// We were spawned on transport, calculate real position
|
||||
if (IsSpawnedOnTransport())
|
||||
{
|
||||
Position pos = m_movementInfo.transport.pos;
|
||||
pos.posX = x;
|
||||
pos.posY = y;
|
||||
pos.posZ = z;
|
||||
pos.SetOrientation(o);
|
||||
|
||||
ITransport transport = GetDirectTransport();
|
||||
if (transport != null)
|
||||
transport.CalculatePassengerPosition(ref x, ref y, ref z, ref o);
|
||||
}
|
||||
|
||||
SetHomePosition(x, y, z, o);
|
||||
GetMap().CreatureRelocation(this, x, y, z, o);
|
||||
}
|
||||
else
|
||||
{
|
||||
// In case this is called directly and normal respawn timer not set
|
||||
// Since this timer will be longer than the already present time it
|
||||
// will be ignored if the correct place added a respawn timer
|
||||
if (setSpawnTime)
|
||||
{
|
||||
uint respawnDelay = m_respawnDelay;
|
||||
m_respawnTime = Math.Max(Time.UnixTime + respawnDelay, m_respawnTime);
|
||||
|
||||
SetHomePosition(x, y, z, o);
|
||||
GetMap().CreatureRelocation(this, x, y, z, o);
|
||||
SaveRespawnTime(0, false);
|
||||
}
|
||||
AddObjectToRemoveList();
|
||||
}
|
||||
}
|
||||
|
||||
public bool InitEntry(uint entry, CreatureData data = null)
|
||||
@@ -375,7 +392,7 @@ namespace Game.Entities
|
||||
{
|
||||
case DeathState.JustRespawned:
|
||||
case DeathState.JustDied:
|
||||
Log.outError(LogFilter.Unit, "Creature ({0}) in wrong state: {2}", GetGUID().ToString(), m_deathState);
|
||||
Log.outError(LogFilter.Unit, "Creature ({0}) in wrong state: {1}", GetGUID().ToString(), m_deathState);
|
||||
break;
|
||||
case DeathState.Dead:
|
||||
{
|
||||
@@ -383,22 +400,25 @@ namespace Game.Entities
|
||||
if (m_respawnTime <= now)
|
||||
{
|
||||
// First check if there are any scripts that object to us respawning
|
||||
if (!Global.ScriptMgr.CanSpawn(GetSpawnId(), GetEntry(), GetCreatureTemplate(), GetCreatureData(), GetMap()))
|
||||
break; // Will be rechecked on next Update call
|
||||
if (!Global.ScriptMgr.CanSpawn(GetSpawnId(), GetEntry(), GetCreatureData(), GetMap()))
|
||||
{
|
||||
m_respawnTime = now + RandomHelper.URand(4, 7);
|
||||
break; // Will be rechecked on next Update call after delay expires
|
||||
}
|
||||
|
||||
ObjectGuid dbtableHighGuid = ObjectGuid.Create(HighGuid.Creature, GetMapId(), GetEntry(), m_spawnId);
|
||||
long linkedRespawntime = GetMap().GetLinkedRespawnTime(dbtableHighGuid);
|
||||
if (linkedRespawntime == 0) // Can respawn
|
||||
long linkedRespawnTime = GetMap().GetLinkedRespawnTime(dbtableHighGuid);
|
||||
if (linkedRespawnTime == 0) // Can respawn
|
||||
Respawn();
|
||||
else // the master is dead
|
||||
{
|
||||
ObjectGuid targetGuid = Global.ObjectMgr.GetLinkedRespawnGuid(dbtableHighGuid);
|
||||
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
|
||||
SetRespawnTime(Time.Day);
|
||||
SetRespawnTime(Time.Week);
|
||||
else
|
||||
{
|
||||
// else copy time from master and add a little
|
||||
long baseRespawnTime = Math.Max(linkedRespawntime, now);
|
||||
long baseRespawnTime = Math.Max(linkedRespawnTime, now);
|
||||
long offset = RandomHelper.URand(5, Time.Minute);
|
||||
|
||||
// linked guid can be a boss, uses std::numeric_limits<time_t>::max to never respawn in that instance
|
||||
@@ -731,7 +751,7 @@ namespace Game.Entities
|
||||
lowGuid = map.GenerateLowGuid(HighGuid.Creature);
|
||||
|
||||
Creature creature = new Creature();
|
||||
if (!creature.Create(lowGuid, map, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), null, vehId))
|
||||
if (!creature.Create(lowGuid, map, entry, pos, null, vehId))
|
||||
return null;
|
||||
|
||||
return creature;
|
||||
@@ -740,13 +760,13 @@ namespace Game.Entities
|
||||
public static Creature CreateCreatureFromDB(ulong spawnId, Map map, bool addToMap = true, bool allowDuplicate = false)
|
||||
{
|
||||
Creature creature = new Creature();
|
||||
if (!creature.LoadCreatureFromDB(spawnId, map, addToMap, allowDuplicate))
|
||||
if (!creature.LoadFromDB(spawnId, map, addToMap, allowDuplicate))
|
||||
return null;
|
||||
|
||||
return creature;
|
||||
}
|
||||
|
||||
public bool Create(ulong guidlow, Map map, uint entry, float x, float y, float z, float ang, CreatureData data, uint vehId)
|
||||
public bool Create(ulong guidlow, Map map, uint entry, Position pos, CreatureData data = null, uint vehId = 0, bool dynamic = false)
|
||||
{
|
||||
SetMap(map);
|
||||
|
||||
@@ -756,6 +776,10 @@ namespace Game.Entities
|
||||
PhasingHandler.InitDbVisibleMapId(GetPhaseShift(), data.terrainSwapMap);
|
||||
}
|
||||
|
||||
// Set if this creature can handle dynamic spawns
|
||||
if (!dynamic)
|
||||
SetRespawnCompatibilityMode();
|
||||
|
||||
CreatureTemplate cinfo = Global.ObjectMgr.GetCreatureTemplate(entry);
|
||||
if (cinfo == null)
|
||||
{
|
||||
@@ -765,13 +789,13 @@ namespace Game.Entities
|
||||
|
||||
//! Relocate before CreateFromProto, to initialize coords and allow
|
||||
//! returning correct zone id for selecting OutdoorPvP/Battlefield script
|
||||
Relocate(x, y, z, ang);
|
||||
Relocate(pos);
|
||||
|
||||
// Check if the position is valid before calling CreateFromProto(), otherwise we might add Auras to Creatures at
|
||||
// invalid position, triggering a crash about Auras not removed in the destructor
|
||||
if (!IsPositionValid())
|
||||
{
|
||||
Log.outError(LogFilter.Unit, "Creature.Create: given coordinates for creature (guidlow {0}, entry {1}) are not valid (X: {2}, Y: {3}, Z: {4}, O: {5})", guidlow, entry, x, y, z, ang);
|
||||
Log.outError(LogFilter.Unit, $"Creature.Create: given coordinates for creature (guidlow {guidlow}, entry {entry}) are not valid ({pos})");
|
||||
return false;
|
||||
}
|
||||
UpdatePositionData();
|
||||
@@ -810,10 +834,8 @@ namespace Game.Entities
|
||||
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
|
||||
if (HasUnitMovementFlag(MovementFlag.Hover))
|
||||
{
|
||||
z += m_unitData.HoverHeight;
|
||||
|
||||
//! Relocate again with updated Z coord
|
||||
Relocate(x, y, z, ang);
|
||||
posZ += m_unitData.HoverHeight;
|
||||
}
|
||||
|
||||
LastUsedScriptID = GetScriptId();
|
||||
@@ -924,6 +946,14 @@ namespace Game.Entities
|
||||
&& !GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoXpAtKill);
|
||||
}
|
||||
|
||||
public bool IsEscortNPC(bool onlyIfActive = true)
|
||||
{
|
||||
if (!IsAIEnabled)
|
||||
return false;
|
||||
|
||||
return GetAI().IsEscortNPC(onlyIfActive);
|
||||
}
|
||||
|
||||
public override bool IsMovementPreventedByCasting()
|
||||
{
|
||||
// first check if currently a movement allowed channel is active and we're not casting
|
||||
@@ -1068,16 +1098,20 @@ namespace Game.Entities
|
||||
dynamicflags = 0;
|
||||
}
|
||||
|
||||
// data.guid = guid must not be updated at save
|
||||
data.id = GetEntry();
|
||||
data.mapid = (ushort)mapid;
|
||||
if (data.spawnId == 0)
|
||||
data.spawnId = m_spawnId;
|
||||
Cypher.Assert(data.spawnId == m_spawnId);
|
||||
|
||||
data.Id = GetEntry();
|
||||
data.displayid = displayId;
|
||||
data.equipmentId = GetCurrentEquipmentId();
|
||||
data.posX = GetPositionX();
|
||||
data.posY = GetPositionY();
|
||||
data.posZ = GetPositionZMinusOffset();
|
||||
data.orientation = GetOrientation();
|
||||
data.spawntimesecs = m_respawnDelay;
|
||||
data.equipmentId = (sbyte)GetCurrentEquipmentId();
|
||||
|
||||
if (GetTransport() == null)
|
||||
data.spawnPoint.WorldRelocate(this);
|
||||
else
|
||||
data.spawnPoint.WorldRelocate(mapid, GetTransOffsetX(), GetTransOffsetY(), GetTransOffsetZ(), GetTransOffsetO());
|
||||
|
||||
data.spawntimesecs = (int)m_respawnDelay;
|
||||
// prevent add data integrity problems
|
||||
data.spawndist = GetDefaultMovementType() == MovementGeneratorType.Idle ? 0.0f : m_respawnradius;
|
||||
data.currentwaypoint = 0;
|
||||
@@ -1092,6 +1126,8 @@ namespace Game.Entities
|
||||
data.unit_flags2 = unitFlags2;
|
||||
data.unit_flags3 = unitFlags3;
|
||||
data.dynamicflags = (uint)dynamicflags;
|
||||
if (data.spawnGroupData == null)
|
||||
data.spawnGroupData = Global.ObjectMgr.GetDefaultSpawnGroup();
|
||||
|
||||
data.phaseId = GetDBPhase() > 0 ? (uint)GetDBPhase() : data.phaseId;
|
||||
data.phaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.phaseGroup;
|
||||
@@ -1414,7 +1450,7 @@ namespace Game.Entities
|
||||
return;
|
||||
}
|
||||
|
||||
GetMap().RemoveCreatureRespawnTime(m_spawnId);
|
||||
GetMap().RemoveRespawnTime(SpawnObjectType.Creature, m_spawnId);
|
||||
Global.ObjectMgr.DeleteCreatureData(m_spawnId);
|
||||
|
||||
SQLTransaction trans = new SQLTransaction();
|
||||
@@ -1423,6 +1459,11 @@ namespace Game.Entities
|
||||
stmt.AddValue(0, m_spawnId);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_SPAWNGROUP_MEMBER);
|
||||
stmt.AddValue(0, (byte)SpawnObjectType.Creature);
|
||||
stmt.AddValue(1, m_spawnId);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_CREATURE_ADDON);
|
||||
stmt.AddValue(0, m_spawnId);
|
||||
trans.Append(stmt);
|
||||
@@ -1549,14 +1590,31 @@ namespace Game.Entities
|
||||
if (s == DeathState.JustDied)
|
||||
{
|
||||
m_corpseRemoveTime = Time.UnixTime + m_corpseDelay;
|
||||
if (IsDungeonBoss() && m_respawnDelay == 0)
|
||||
m_respawnTime = long.MaxValue; // never respawn in this instance
|
||||
uint respawnDelay = m_respawnDelay;
|
||||
uint scalingMode = WorldConfig.GetUIntValue(WorldCfg.RespawnDynamicMode);
|
||||
if (scalingMode != 0)
|
||||
GetMap().ApplyDynamicModeRespawnScaling(this, m_spawnId, ref respawnDelay, scalingMode);
|
||||
// @todo remove the boss respawn time hack in a dynspawn follow-up once we have creature groups in instances
|
||||
if (m_respawnCompatibilityMode)
|
||||
{
|
||||
if (IsDungeonBoss() && m_respawnDelay == 0)
|
||||
m_respawnTime = long.MaxValue; // never respawn in this instance
|
||||
else
|
||||
m_respawnTime = Time.UnixTime + respawnDelay + m_corpseDelay;
|
||||
}
|
||||
else
|
||||
m_respawnTime = Time.UnixTime + m_respawnDelay + m_corpseDelay;
|
||||
{
|
||||
if (IsDungeonBoss() && m_respawnDelay == 0)
|
||||
m_respawnTime = long.MaxValue; // never respawn in this instance
|
||||
else
|
||||
m_respawnTime = Time.UnixTime + respawnDelay;
|
||||
}
|
||||
|
||||
// always save boss respawn time at death to prevent crash cheating
|
||||
if (WorldConfig.GetBoolValue(WorldCfg.SaveRespawnTimeImmediately) || IsWorldBoss())
|
||||
SaveRespawnTime();
|
||||
else if (!m_respawnCompatibilityMode)
|
||||
SaveRespawnTime(0, false);
|
||||
|
||||
ReleaseFocus(null, false); // remove spellcast focus
|
||||
DoNotReacquireTarget(); // cancel delayed re-target
|
||||
@@ -1632,8 +1690,6 @@ namespace Game.Entities
|
||||
|
||||
public void Respawn(bool force = false)
|
||||
{
|
||||
DestroyForNearbyPlayers();
|
||||
|
||||
if (force)
|
||||
{
|
||||
if (IsAlive())
|
||||
@@ -1642,49 +1698,60 @@ namespace Game.Entities
|
||||
SetDeathState(DeathState.Corpse);
|
||||
}
|
||||
|
||||
RemoveCorpse(false, false);
|
||||
|
||||
if (GetDeathState() == DeathState.Dead)
|
||||
if (m_respawnCompatibilityMode)
|
||||
{
|
||||
if (m_spawnId != 0)
|
||||
GetMap().RemoveCreatureRespawnTime(m_spawnId);
|
||||
DestroyForNearbyPlayers();
|
||||
RemoveCorpse(false, false);
|
||||
|
||||
Log.outDebug(LogFilter.Unit, "Respawning creature {0} ({1})", GetName(), GetGUID().ToString());
|
||||
m_respawnTime = 0;
|
||||
ResetPickPocketRefillTimer();
|
||||
loot.Clear();
|
||||
if (GetDeathState() == DeathState.Dead)
|
||||
{
|
||||
if (m_spawnId != 0)
|
||||
GetMap().RemoveRespawnTime(SpawnObjectType.Creature, m_spawnId);
|
||||
|
||||
Log.outDebug(LogFilter.Unit, "Respawning creature {0} ({1})", GetName(), GetGUID().ToString());
|
||||
m_respawnTime = 0;
|
||||
ResetPickPocketRefillTimer();
|
||||
loot.Clear();
|
||||
|
||||
if (m_originalEntry != GetEntry())
|
||||
UpdateEntry(m_originalEntry);
|
||||
|
||||
if (m_originalEntry != GetEntry())
|
||||
UpdateEntry(m_originalEntry);
|
||||
else
|
||||
SelectLevel();
|
||||
|
||||
SetDeathState(DeathState.JustRespawned);
|
||||
SetDeathState(DeathState.JustRespawned);
|
||||
|
||||
CreatureModel display = new CreatureModel(GetNativeDisplayId(), GetNativeDisplayScale(), 1.0f);
|
||||
if (Global.ObjectMgr.GetCreatureModelRandomGender(ref display, GetCreatureTemplate()) != null)
|
||||
{
|
||||
SetDisplayId(display.CreatureDisplayID, display.DisplayScale);
|
||||
SetNativeDisplayId(display.CreatureDisplayID, display.DisplayScale);
|
||||
CreatureModel display = new CreatureModel(GetNativeDisplayId(), GetNativeDisplayScale(), 1.0f);
|
||||
if (Global.ObjectMgr.GetCreatureModelRandomGender(ref display, GetCreatureTemplate()) != null)
|
||||
{
|
||||
SetDisplayId(display.CreatureDisplayID, display.DisplayScale);
|
||||
SetNativeDisplayId(display.CreatureDisplayID, display.DisplayScale);
|
||||
}
|
||||
|
||||
GetMotionMaster().InitDefault();
|
||||
//Re-initialize reactstate that could be altered by movementgenerators
|
||||
InitializeReactState();
|
||||
|
||||
//Call AI respawn virtual function//Call AI respawn virtual function
|
||||
if (IsAIEnabled)
|
||||
{
|
||||
//reset the AI to be sure no dirty or uninitialized values will be used till next tick
|
||||
GetAI().Reset();
|
||||
TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
|
||||
}
|
||||
|
||||
uint poolid = GetSpawnId() != 0 ? Global.PoolMgr.IsPartOfAPool<Creature>(GetSpawnId()) : 0;
|
||||
if (poolid != 0)
|
||||
Global.PoolMgr.UpdatePool<Creature>(poolid, GetSpawnId());
|
||||
}
|
||||
|
||||
GetMotionMaster().InitDefault();
|
||||
//Re-initialize reactstate that could be altered by movementgenerators
|
||||
InitializeReactState();
|
||||
|
||||
//Call AI respawn virtual function
|
||||
if (IsAIEnabled)
|
||||
{
|
||||
GetAI().Reset();
|
||||
TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
|
||||
}
|
||||
|
||||
uint poolid = GetSpawnId() != 0 ? Global.PoolMgr.IsPartOfAPool<Creature>(GetSpawnId()) : 0;
|
||||
if (poolid != 0)
|
||||
Global.PoolMgr.UpdatePool<Creature>(poolid, GetSpawnId());
|
||||
UpdateObjectVisibility();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_spawnId != 0)
|
||||
GetMap().RemoveRespawnTime(SpawnObjectType.Creature, m_spawnId, true);
|
||||
}
|
||||
|
||||
UpdateObjectVisibility();
|
||||
Log.outDebug(LogFilter.Unit, $"Respawning creature {GetName()} ({GetGUID()})");
|
||||
}
|
||||
|
||||
public void ForcedDespawn(uint timeMSToDespawn = 0, TimeSpan forceRespawnTimer = default)
|
||||
@@ -1695,30 +1762,49 @@ namespace Game.Entities
|
||||
return;
|
||||
}
|
||||
|
||||
uint corpseDelay = GetCorpseDelay();
|
||||
uint respawnDelay = GetRespawnDelay();
|
||||
|
||||
// do it before killing creature
|
||||
DestroyForNearbyPlayers();
|
||||
|
||||
bool overrideRespawnTime = false;
|
||||
if (IsAlive())
|
||||
if (m_respawnCompatibilityMode)
|
||||
{
|
||||
if (forceRespawnTimer > TimeSpan.Zero)
|
||||
uint corpseDelay = GetCorpseDelay();
|
||||
uint respawnDelay = GetRespawnDelay();
|
||||
|
||||
// do it before killing creature
|
||||
DestroyForNearbyPlayers();
|
||||
|
||||
bool overrideRespawnTime = false;
|
||||
if (IsAlive())
|
||||
{
|
||||
SetCorpseDelay(0);
|
||||
SetRespawnDelay((uint)forceRespawnTimer.TotalSeconds);
|
||||
overrideRespawnTime = false;
|
||||
if (forceRespawnTimer > TimeSpan.Zero)
|
||||
{
|
||||
SetCorpseDelay(0);
|
||||
SetRespawnDelay((uint)forceRespawnTimer.TotalSeconds);
|
||||
overrideRespawnTime = false;
|
||||
}
|
||||
|
||||
SetDeathState(DeathState.JustDied);
|
||||
}
|
||||
|
||||
SetDeathState(DeathState.JustDied);
|
||||
// Skip corpse decay time
|
||||
RemoveCorpse(overrideRespawnTime, false);
|
||||
|
||||
SetCorpseDelay(corpseDelay);
|
||||
SetRespawnDelay(respawnDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (forceRespawnTimer > TimeSpan.Zero)
|
||||
SaveRespawnTime((uint)forceRespawnTimer.TotalSeconds);
|
||||
else
|
||||
{
|
||||
uint respawnDelay = m_respawnDelay;
|
||||
uint scalingMode = WorldConfig.GetUIntValue(WorldCfg.RespawnDynamicMode);
|
||||
if (scalingMode != 0)
|
||||
GetMap().ApplyDynamicModeRespawnScaling(this, m_spawnId, ref respawnDelay, scalingMode);
|
||||
m_respawnTime = Time.UnixTime + respawnDelay;
|
||||
SaveRespawnTime();
|
||||
}
|
||||
|
||||
// Skip corpse decay time
|
||||
RemoveCorpse(overrideRespawnTime, false);
|
||||
|
||||
SetCorpseDelay(corpseDelay);
|
||||
SetRespawnDelay(respawnDelay);
|
||||
AddObjectToRemoveList();
|
||||
}
|
||||
}
|
||||
|
||||
public void DespawnOrUnsummon(TimeSpan time, TimeSpan forceRespawnTimer = default) { DespawnOrUnsummon((uint)time.TotalMilliseconds, forceRespawnTimer); }
|
||||
@@ -2074,12 +2160,19 @@ namespace Game.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void SaveRespawnTime()
|
||||
public override void SaveRespawnTime(uint forceDelay = 0, bool saveToDb = true)
|
||||
{
|
||||
if (IsSummon() || m_spawnId == 0 || (m_creatureData != null && !m_creatureData.dbData))
|
||||
return;
|
||||
|
||||
GetMap().SaveCreatureRespawnTime(m_spawnId, m_respawnTime);
|
||||
if (m_respawnCompatibilityMode)
|
||||
{
|
||||
GetMap().SaveRespawnTimeDB(SpawnObjectType.Creature, m_spawnId, m_respawnTime);
|
||||
return;
|
||||
}
|
||||
|
||||
uint thisRespawnTime = (uint)(forceDelay != 0 ? Time.UnixTime + forceDelay : m_respawnTime);
|
||||
GetMap().SaveRespawnTime(SpawnObjectType.Creature, m_spawnId, GetEntry(), thisRespawnTime, GetMap().GetZoneId(GetPhaseShift(), GetHomePosition()), GridDefines.ComputeGridCoord(GetHomePosition().GetPositionX(), GetHomePosition().GetPositionY()).GetId(), m_creatureData.dbData && saveToDb);
|
||||
}
|
||||
|
||||
public bool CanCreatureAttack(Unit victim, bool force = true)
|
||||
@@ -2293,32 +2386,20 @@ namespace Game.Entities
|
||||
}
|
||||
public void GetRespawnPosition(out float x, out float y, out float z, out float ori, out float dist)
|
||||
{
|
||||
// for npcs on transport, this will return transport offset
|
||||
if (m_spawnId != 0)
|
||||
if (m_creatureData != null)
|
||||
{
|
||||
CreatureData data = Global.ObjectMgr.GetCreatureData(GetSpawnId());
|
||||
if (data != null)
|
||||
{
|
||||
x = data.posX;
|
||||
y = data.posY;
|
||||
z = data.posZ;
|
||||
ori = data.orientation;
|
||||
dist = data.spawndist;
|
||||
|
||||
return;
|
||||
}
|
||||
m_creatureData.spawnPoint.GetPosition(out x, out y, out z, out ori);
|
||||
dist = m_creatureData.spawndist;
|
||||
}
|
||||
else
|
||||
{
|
||||
Position homePos = GetHomePosition();
|
||||
homePos.GetPosition(out x, out y, out z, out ori);
|
||||
dist = 0;
|
||||
}
|
||||
|
||||
// changed this from current position to home position, fixes world summons with infinite duration (wg npcs for example)
|
||||
Position homePos = GetHomePosition();
|
||||
x = homePos.GetPositionX();
|
||||
y = homePos.GetPositionY();
|
||||
z = homePos.GetPositionZ();
|
||||
ori = homePos.GetOrientation();
|
||||
dist = 0;
|
||||
}
|
||||
|
||||
bool IsSpawnedOnTransport() { return m_creatureData != null && m_creatureData.mapid != GetMapId(); }
|
||||
bool IsSpawnedOnTransport() { return m_creatureData != null && m_creatureData.spawnPoint.GetMapId() != GetMapId(); }
|
||||
|
||||
public void AllLootRemovedFromCorpse()
|
||||
{
|
||||
@@ -2934,12 +3015,7 @@ namespace Game.Entities
|
||||
public CreatureTemplate GetCreatureTemplate() { return m_creatureInfo; }
|
||||
public CreatureData GetCreatureData() { return m_creatureData; }
|
||||
|
||||
public override bool LoadFromDB(ulong spawnId, Map map)
|
||||
{
|
||||
return LoadCreatureFromDB(spawnId, map, false, false);
|
||||
}
|
||||
|
||||
public bool LoadCreatureFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate)
|
||||
public override bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate)
|
||||
{
|
||||
if (!allowDuplicate)
|
||||
{
|
||||
@@ -2976,39 +3052,48 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
m_spawnId = spawnId;
|
||||
m_respawnCompatibilityMode = data.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.CompatibilityMode);
|
||||
m_creatureData = data;
|
||||
m_respawnradius = data.spawndist;
|
||||
m_respawnDelay = data.spawntimesecs;
|
||||
if (!Create(map.GenerateLowGuid(HighGuid.Creature), map, data.id, data.posX, data.posY, data.posZ, data.orientation, data, 0))
|
||||
m_respawnDelay = (uint)data.spawntimesecs;
|
||||
// Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update)
|
||||
if (!m_respawnCompatibilityMode && m_respawnTime == 0 && !Global.ScriptMgr.CanSpawn(spawnId, data.Id, data, map))
|
||||
{
|
||||
SaveRespawnTime(RandomHelper.URand(4, 7));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Create(map.GenerateLowGuid(HighGuid.Creature), map, data.Id, data.spawnPoint, data, 0, !m_respawnCompatibilityMode))
|
||||
return false;
|
||||
|
||||
//We should set first home position, because then AI calls home movement
|
||||
SetHomePosition(data.posX, data.posY, data.posZ, data.orientation);
|
||||
SetHomePosition(data.spawnPoint);
|
||||
|
||||
m_deathState = DeathState.Alive;
|
||||
|
||||
m_respawnTime = GetMap().GetCreatureRespawnTime(m_spawnId);
|
||||
|
||||
// Is the creature script objecting to us spawning? If yes, delay by one second (then re-check in ::Update)
|
||||
if (m_respawnTime == 0 && !Global.ScriptMgr.CanSpawn(spawnId, GetEntry(), GetCreatureTemplate(), GetCreatureData(), map))
|
||||
m_respawnTime = Time.UnixTime + 1;
|
||||
// Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update)
|
||||
if (m_respawnCompatibilityMode && m_respawnTime == 0 && !Global.ScriptMgr.CanSpawn(spawnId, GetEntry(), GetCreatureData(), map))
|
||||
m_respawnTime = Time.UnixTime + RandomHelper.URand(4, 7);
|
||||
|
||||
if (m_respawnTime != 0) // respawn on Update
|
||||
if (m_respawnTime != 0) // respawn on UpdateLoadCreatureFromDB
|
||||
{
|
||||
m_deathState = DeathState.Dead;
|
||||
if (CanFly())
|
||||
{
|
||||
float tz = map.GetHeight(GetPhaseShift(), data.posX, data.posY, data.posZ, true, MapConst.MaxFallDistance);
|
||||
if (data.posZ - tz > 0.1f && GridDefines.IsValidMapCoord(tz))
|
||||
Relocate(data.posX, data.posY, tz);
|
||||
float tz = map.GetHeight(GetPhaseShift(), data.spawnPoint, true, MapConst.MaxFallDistance);
|
||||
if (data.spawnPoint.GetPositionZ() - tz > 0.1f && GridDefines.IsValidMapCoord(tz))
|
||||
Relocate(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY(), tz);
|
||||
}
|
||||
}
|
||||
|
||||
SetSpawnHealth();
|
||||
|
||||
// checked at creature_template loading
|
||||
DefaultMovementType = (MovementGeneratorType)data.movementType;
|
||||
|
||||
loot.SetGUID(ObjectGuid.Create(HighGuid.LootObject, data.mapid, data.id, GetMap().GenerateLowGuid(HighGuid.LootObject)));
|
||||
loot.SetGUID(ObjectGuid.Create(HighGuid.LootObject, GetMapId(), data.Id, GetMap().GenerateLowGuid(HighGuid.LootObject)));
|
||||
|
||||
if (addToMap && !GetMap().AddToMap(this))
|
||||
return false;
|
||||
@@ -3102,6 +3187,10 @@ namespace Game.Entities
|
||||
m_originalEntry = entry;
|
||||
}
|
||||
|
||||
// There's many places not ready for dynamic spawns. This allows them to live on for now.
|
||||
void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
|
||||
public bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; }
|
||||
|
||||
public Unit SelectVictim()
|
||||
{
|
||||
// function provides main threat functionality
|
||||
|
||||
@@ -21,6 +21,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Framework.Dynamic;
|
||||
using Game.Networking.Packets;
|
||||
using Game.Maps;
|
||||
|
||||
namespace Game.Entities
|
||||
{
|
||||
@@ -327,34 +328,22 @@ namespace Game.Entities
|
||||
public EquipmentItem[] Items = new EquipmentItem[SharedConst.MaxEquipmentItems];
|
||||
}
|
||||
|
||||
public class CreatureData
|
||||
public class CreatureData : SpawnData
|
||||
{
|
||||
public uint id; // entry in creature_template
|
||||
public ushort mapid;
|
||||
public uint displayid;
|
||||
public int equipmentId;
|
||||
public float posX;
|
||||
public float posY;
|
||||
public float posZ;
|
||||
public float orientation;
|
||||
public uint spawntimesecs;
|
||||
public sbyte equipmentId;
|
||||
public float spawndist;
|
||||
public uint currentwaypoint;
|
||||
public uint curhealth;
|
||||
public uint curmana;
|
||||
public byte movementType;
|
||||
public List<Difficulty> spawnDifficulties = new List<Difficulty>();
|
||||
public ulong npcflag;
|
||||
public uint unit_flags; // enum UnitFlags mask values
|
||||
public uint unit_flags2; // enum UnitFlags2 mask values
|
||||
public uint unit_flags3; // enum UnitFlags3 mask values
|
||||
public uint unit_flags; // enum UnitFlags mask values
|
||||
public uint unit_flags2; // enum UnitFlags2 mask values
|
||||
public uint unit_flags3; // enum UnitFlags3 mask values
|
||||
public uint dynamicflags;
|
||||
public PhaseUseFlagsValues phaseUseFlags;
|
||||
public uint phaseId;
|
||||
public uint phaseGroup;
|
||||
public int terrainSwapMap;
|
||||
public uint ScriptId;
|
||||
public bool dbData;
|
||||
|
||||
public CreatureData() : base(SpawnObjectType.Creature) { }
|
||||
}
|
||||
|
||||
public class CreatureModelInfo
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace Game.Entities
|
||||
return null;
|
||||
|
||||
GameObject go = new GameObject();
|
||||
if (!go.Create(entry, map, pos, rotation, animProgress, goState, artKit))
|
||||
if (!go.Create(entry, map, pos, rotation, animProgress, goState, artKit, false, 0))
|
||||
return null;
|
||||
|
||||
return go;
|
||||
@@ -175,13 +175,13 @@ namespace Game.Entities
|
||||
public static GameObject CreateGameObjectFromDB(ulong spawnId, Map map, bool addToMap = true)
|
||||
{
|
||||
GameObject go = new GameObject();
|
||||
if (!go.LoadGameObjectFromDB(spawnId, map, addToMap))
|
||||
if (!go.LoadFromDB(spawnId, map, addToMap))
|
||||
return null;
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
bool Create(uint entry, Map map, Position pos, Quaternion rotation, uint animProgress, GameObjectState goState, uint artKit)
|
||||
bool Create(uint entry, Map map, Position pos, Quaternion rotation, uint animProgress, GameObjectState goState, uint artKit, bool dynamic, ulong spawnid)
|
||||
{
|
||||
Cypher.Assert(map);
|
||||
SetMap(map);
|
||||
@@ -194,6 +194,10 @@ namespace Game.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set if this object can handle dynamic spawns
|
||||
if (!dynamic)
|
||||
SetRespawnCompatibilityMode();
|
||||
|
||||
UpdatePositionData();
|
||||
|
||||
SetZoneScript();
|
||||
@@ -373,6 +377,9 @@ namespace Game.Entities
|
||||
if (map.Is25ManRaid())
|
||||
loot.maxDuplicates = 3;
|
||||
|
||||
if (spawnid != 0)
|
||||
m_spawnId = spawnid;
|
||||
|
||||
uint linkedEntry = GetGoInfo().GetLinkedGameObjectEntry();
|
||||
if (linkedEntry != 0)
|
||||
{
|
||||
@@ -507,81 +514,88 @@ namespace Game.Entities
|
||||
goto case LootState.Ready;
|
||||
case LootState.Ready:
|
||||
{
|
||||
if (m_respawnTime > 0) // timer on
|
||||
if (m_respawnCompatibilityMode)
|
||||
{
|
||||
long now = Time.UnixTime;
|
||||
if (m_respawnTime <= now) // timer expired
|
||||
if (m_respawnTime > 0) // timer on
|
||||
{
|
||||
ObjectGuid dbtableHighGuid = ObjectGuid.Create(HighGuid.GameObject, GetMapId(), GetEntry(), m_spawnId);
|
||||
long linkedRespawntime = GetMap().GetLinkedRespawnTime(dbtableHighGuid);
|
||||
if (linkedRespawntime != 0) // Can't respawn, the master is dead
|
||||
long now = Time.UnixTime;
|
||||
if (m_respawnTime <= now) // timer expired
|
||||
{
|
||||
ObjectGuid targetGuid = Global.ObjectMgr.GetLinkedRespawnGuid(dbtableHighGuid);
|
||||
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
|
||||
SetRespawnTime(Time.Day);
|
||||
else
|
||||
m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + RandomHelper.IRand(5, Time.Minute); // else copy time from master and add a little
|
||||
SaveRespawnTime(); // also save to DB immediately
|
||||
return;
|
||||
}
|
||||
ObjectGuid dbtableHighGuid = ObjectGuid.Create(HighGuid.GameObject, GetMapId(), GetEntry(), m_spawnId);
|
||||
long linkedRespawntime = GetMap().GetLinkedRespawnTime(dbtableHighGuid);
|
||||
if (linkedRespawntime != 0) // Can't respawn, the master is dead
|
||||
{
|
||||
ObjectGuid targetGuid = Global.ObjectMgr.GetLinkedRespawnGuid(dbtableHighGuid);
|
||||
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
|
||||
SetRespawnTime(Time.Week);
|
||||
else
|
||||
m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + RandomHelper.IRand(5, Time.Minute); // else copy time from master and add a little
|
||||
SaveRespawnTime(); // also save to DB immediately
|
||||
return;
|
||||
}
|
||||
|
||||
m_respawnTime = 0;
|
||||
m_SkillupList.Clear();
|
||||
m_usetimes = 0;
|
||||
m_respawnTime = 0;
|
||||
m_SkillupList.Clear();
|
||||
m_usetimes = 0;
|
||||
|
||||
// If nearby linked trap exists, respawn it
|
||||
GameObject linkedTrap = GetLinkedTrap();
|
||||
if (linkedTrap)
|
||||
linkedTrap.SetLootState(LootState.Ready);
|
||||
// If nearby linked trap exists, respawn it
|
||||
GameObject linkedTrap = GetLinkedTrap();
|
||||
if (linkedTrap)
|
||||
linkedTrap.SetLootState(LootState.Ready);
|
||||
|
||||
switch (GetGoType())
|
||||
{
|
||||
case GameObjectTypes.FishingNode: // can't fish now
|
||||
{
|
||||
Unit caster = GetOwner();
|
||||
if (caster != null && caster.IsTypeId(TypeId.Player))
|
||||
switch (GetGoType())
|
||||
{
|
||||
case GameObjectTypes.FishingNode: // can't fish now
|
||||
{
|
||||
caster.ToPlayer().RemoveGameObject(this, false);
|
||||
caster.ToPlayer().SendPacket(new FishEscaped());
|
||||
Unit caster = GetOwner();
|
||||
if (caster != null && caster.IsTypeId(TypeId.Player))
|
||||
{
|
||||
caster.ToPlayer().RemoveGameObject(this, false);
|
||||
caster.ToPlayer().SendPacket(new FishEscaped());
|
||||
}
|
||||
// can be delete
|
||||
m_lootState = LootState.JustDeactivated;
|
||||
return;
|
||||
}
|
||||
// can be delete
|
||||
m_lootState = LootState.JustDeactivated;
|
||||
return;
|
||||
}
|
||||
case GameObjectTypes.Door:
|
||||
case GameObjectTypes.Button:
|
||||
//we need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for Battlegrounds)
|
||||
if (GetGoState() != GameObjectState.Ready)
|
||||
ResetDoorOrButton();
|
||||
break;
|
||||
case GameObjectTypes.FishingHole:
|
||||
// Initialize a new max fish count on respawn
|
||||
m_goValue.FishingHole.MaxOpens = RandomHelper.URand(GetGoInfo().FishingHole.minRestock, GetGoInfo().FishingHole.maxRestock);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case GameObjectTypes.Door:
|
||||
case GameObjectTypes.Button:
|
||||
//we need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for Battlegrounds)
|
||||
if (GetGoState() != GameObjectState.Ready)
|
||||
ResetDoorOrButton();
|
||||
break;
|
||||
case GameObjectTypes.FishingHole:
|
||||
// Initialize a new max fish count on respawn
|
||||
m_goValue.FishingHole.MaxOpens = RandomHelper.URand(GetGoInfo().FishingHole.minRestock, GetGoInfo().FishingHole.maxRestock);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_spawnedByDefault) // despawn timer
|
||||
{
|
||||
// can be despawned or destroyed
|
||||
SetLootState(LootState.JustDeactivated);
|
||||
return;
|
||||
}
|
||||
|
||||
// Call AI Reset (required for example in SmartAI to clear one time events)
|
||||
if (GetAI() != null)
|
||||
GetAI().Reset();
|
||||
|
||||
// respawn timer
|
||||
uint poolid = GetSpawnId() != 0 ? Global.PoolMgr.IsPartOfAPool<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())
|
||||
{
|
||||
GameObjectTemplate goInfo = GetGoInfo();
|
||||
@@ -785,6 +799,7 @@ namespace Game.Entities
|
||||
if (m_respawnDelayTime == 0)
|
||||
return;
|
||||
|
||||
// ToDo: Decide if we should properly despawn these. Maybe they expect to be able to manually respawn from script?
|
||||
if (!m_spawnedByDefault)
|
||||
{
|
||||
m_respawnTime = 0;
|
||||
@@ -792,12 +807,29 @@ namespace Game.Entities
|
||||
return;
|
||||
}
|
||||
|
||||
m_respawnTime = Time.UnixTime + m_respawnDelayTime;
|
||||
uint respawnDelay = m_respawnDelayTime;
|
||||
uint scalingMode = WorldConfig.GetUIntValue(WorldCfg.RespawnDynamicMode);
|
||||
if (scalingMode != 0)
|
||||
GetMap().ApplyDynamicModeRespawnScaling(this, m_spawnId, ref respawnDelay, scalingMode);
|
||||
m_respawnTime = Time.UnixTime + respawnDelay;
|
||||
|
||||
// if option not set then object will be saved at grid unload
|
||||
// Otherwise just save respawn time to map object memory
|
||||
if (WorldConfig.GetBoolValue(WorldCfg.SaveRespawnTimeImmediately))
|
||||
SaveRespawnTime();
|
||||
|
||||
if (!m_respawnCompatibilityMode)
|
||||
{
|
||||
// Respawn time was just saved if set to save to DB
|
||||
// If not, we save only to map memory
|
||||
if (!WorldConfig.GetBoolValue(WorldCfg.SaveRespawnTimeImmediately))
|
||||
SaveRespawnTime(0, false);
|
||||
|
||||
// Then despawn
|
||||
AddObjectToRemoveList();
|
||||
return;
|
||||
}
|
||||
|
||||
DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
|
||||
break;
|
||||
}
|
||||
@@ -890,7 +922,7 @@ namespace Game.Entities
|
||||
{
|
||||
// this should only be used when the gameobject has already been loaded
|
||||
// preferably after adding to map, because mapid may not be valid otherwise
|
||||
GameObjectData data = Global.ObjectMgr.GetGOData(m_spawnId);
|
||||
GameObjectData data = Global.ObjectMgr.GetGameObjectData(m_spawnId);
|
||||
if (data == null)
|
||||
{
|
||||
Log.outError(LogFilter.Maps, "GameObject.SaveToDB failed, cannot get gameobject data!");
|
||||
@@ -911,22 +943,22 @@ namespace Game.Entities
|
||||
m_spawnId = Global.ObjectMgr.GenerateGameObjectSpawnId();
|
||||
|
||||
// update in loaded data (changing data only in this place)
|
||||
GameObjectData data = new GameObjectData();
|
||||
GameObjectData data = Global.ObjectMgr.NewOrExistGameObjectData(m_spawnId);
|
||||
|
||||
// guid = guid must not be updated at save
|
||||
data.id = GetEntry();
|
||||
data.mapid = (ushort)mapid;
|
||||
data.posX = GetPositionX();
|
||||
data.posY = GetPositionY();
|
||||
data.posZ = GetPositionZ();
|
||||
data.orientation = GetOrientation();
|
||||
if (data.spawnId == 0)
|
||||
data.spawnId = m_spawnId;
|
||||
Cypher.Assert(data.spawnId == m_spawnId);
|
||||
|
||||
data.Id = GetEntry();
|
||||
data.spawnPoint.WorldRelocate(this);
|
||||
data.rotation = m_worldRotation;
|
||||
data.spawntimesecs = (int)(m_spawnedByDefault ? m_respawnDelayTime : -m_respawnDelayTime);
|
||||
data.animprogress = GetGoAnimProgress();
|
||||
data.go_state = GetGoState();
|
||||
data.goState = GetGoState();
|
||||
data.spawnDifficulties = spawnDifficulties;
|
||||
data.artKit = (byte)GetGoArtKit();
|
||||
Global.ObjectMgr.NewGOData(m_spawnId, data);
|
||||
if (data.spawnGroupData == null)
|
||||
data.spawnGroupData = Global.ObjectMgr.GetDefaultSpawnGroup();
|
||||
|
||||
data.phaseId = GetDBPhase() > 0 ? (uint)GetDBPhase() : data.phaseId;
|
||||
data.phaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.phaseGroup;
|
||||
@@ -958,24 +990,24 @@ namespace Game.Entities
|
||||
DB.World.Execute(stmt);
|
||||
}
|
||||
|
||||
bool LoadGameObjectFromDB(ulong spawnId, Map map, bool addToMap)
|
||||
public override bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool unused = true)
|
||||
{
|
||||
GameObjectData data = Global.ObjectMgr.GetGOData(spawnId);
|
||||
GameObjectData data = Global.ObjectMgr.GetGameObjectData(spawnId);
|
||||
if (data == null)
|
||||
{
|
||||
Log.outError(LogFilter.Maps, "Gameobject (SpawnId: {0}) not found in table `gameobject`, can't load. ", spawnId);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint entry = data.id;
|
||||
Position pos = new Position(data.posX, data.posY, data.posZ, data.orientation);
|
||||
uint entry = data.Id;
|
||||
|
||||
uint animprogress = data.animprogress;
|
||||
GameObjectState go_state = data.go_state;
|
||||
GameObjectState go_state = data.goState;
|
||||
uint artKit = data.artKit;
|
||||
|
||||
m_spawnId = spawnId;
|
||||
if (!Create(entry, map, pos, data.rotation, animprogress, go_state, artKit))
|
||||
m_respawnCompatibilityMode = ((data.spawnGroupData.flags & SpawnGroupFlags.CompatibilityMode) != 0);
|
||||
if (!Create(entry, map, data.spawnPoint, data.rotation, animprogress, go_state, artKit, !m_respawnCompatibilityMode, spawnId))
|
||||
return false;
|
||||
|
||||
PhasingHandler.InitDbPhaseShift(GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup);
|
||||
@@ -1000,7 +1032,7 @@ namespace Game.Entities
|
||||
if (m_respawnTime != 0 && m_respawnTime <= Time.UnixTime)
|
||||
{
|
||||
m_respawnTime = 0;
|
||||
GetMap().RemoveGORespawnTime(m_spawnId);
|
||||
GetMap().RemoveRespawnTime(SpawnObjectType.GameObject, m_spawnId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1021,20 +1053,20 @@ namespace Game.Entities
|
||||
|
||||
public void DeleteFromDB()
|
||||
{
|
||||
GetMap().RemoveGORespawnTime(m_spawnId);
|
||||
Global.ObjectMgr.DeleteGOData(m_spawnId);
|
||||
GetMap().RemoveRespawnTime(SpawnObjectType.GameObject, m_spawnId);
|
||||
Global.ObjectMgr.DeleteGameObjectData(m_spawnId);
|
||||
|
||||
SQLTransaction trans = new SQLTransaction();
|
||||
|
||||
PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_GAMEOBJECT);
|
||||
|
||||
stmt.AddValue(0, m_spawnId);
|
||||
|
||||
DB.World.Execute(stmt);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.World.GetPreparedStatement(WorldStatements.DEL_EVENT_GAMEOBJECT);
|
||||
|
||||
stmt.AddValue(0, m_spawnId);
|
||||
trans.Append(stmt);
|
||||
|
||||
DB.World.Execute(stmt);
|
||||
DB.World.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
public override bool HasQuest(uint quest_id)
|
||||
@@ -1097,10 +1129,19 @@ namespace Game.Entities
|
||||
return Global.ObjAccessor.GetUnit(this, GetOwnerGUID());
|
||||
}
|
||||
|
||||
public override void SaveRespawnTime()
|
||||
public override void SaveRespawnTime(uint forceDelay = 0, bool savetodb = true)
|
||||
{
|
||||
if (m_goData != null && m_goData.dbData && m_respawnTime > Time.UnixTime && m_spawnedByDefault)
|
||||
GetMap().SaveGORespawnTime(m_spawnId, m_respawnTime);
|
||||
if (m_goData != null && m_respawnTime > Time.UnixTime && m_spawnedByDefault)
|
||||
{
|
||||
if (m_respawnCompatibilityMode)
|
||||
{
|
||||
GetMap().SaveRespawnTimeDB(SpawnObjectType.GameObject, m_spawnId, m_respawnTime);
|
||||
return;
|
||||
}
|
||||
|
||||
uint thisRespawnTime = (uint)(forceDelay != 0 ? Time.UnixTime + forceDelay : m_respawnTime);
|
||||
GetMap().SaveRespawnTime(SpawnObjectType.GameObject, m_spawnId, GetEntry(), thisRespawnTime, GetZoneId(), GridDefines.ComputeGridCoord(GetPositionX(), GetPositionY()).GetId(), m_goData.dbData ? savetodb : false);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsNeverVisibleFor(WorldObject seer)
|
||||
@@ -1160,7 +1201,7 @@ namespace Game.Entities
|
||||
if (m_spawnedByDefault && m_respawnTime > 0)
|
||||
{
|
||||
m_respawnTime = Time.UnixTime;
|
||||
GetMap().RemoveGORespawnTime(m_spawnId);
|
||||
GetMap().RemoveRespawnTime(SpawnObjectType.GameObject, m_spawnId, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1262,7 +1303,7 @@ namespace Game.Entities
|
||||
public void SetGoArtKit(byte kit)
|
||||
{
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.ArtKit), kit);
|
||||
GameObjectData data = Global.ObjectMgr.GetGOData(m_spawnId);
|
||||
GameObjectData data = Global.ObjectMgr.GetGameObjectData(m_spawnId);
|
||||
if (data != null)
|
||||
data.artKit = kit;
|
||||
}
|
||||
@@ -1273,10 +1314,10 @@ namespace Game.Entities
|
||||
if (go != null)
|
||||
{
|
||||
go.SetGoArtKit(artkit);
|
||||
data = go.GetGoData();
|
||||
data = go.GetGameObjectData();
|
||||
}
|
||||
else if (lowguid != 0)
|
||||
data = Global.ObjectMgr.GetGOData(lowguid);
|
||||
data = Global.ObjectMgr.GetGameObjectData(lowguid);
|
||||
|
||||
if (data != null)
|
||||
data.artKit = artkit;
|
||||
@@ -2075,7 +2116,7 @@ namespace Game.Entities
|
||||
|
||||
public uint GetScriptId()
|
||||
{
|
||||
GameObjectData gameObjectData = GetGoData();
|
||||
GameObjectData gameObjectData = GetGameObjectData();
|
||||
if (gameObjectData != null)
|
||||
{
|
||||
uint scriptId = gameObjectData.ScriptId;
|
||||
@@ -2548,23 +2589,10 @@ namespace Game.Entities
|
||||
|
||||
public void GetRespawnPosition(out float x, out float y, out float z, out float ori)
|
||||
{
|
||||
if (m_spawnId != 0)
|
||||
{
|
||||
GameObjectData data = Global.ObjectMgr.GetGOData(GetSpawnId());
|
||||
if (data != null)
|
||||
{
|
||||
x = posX;
|
||||
y = posY;
|
||||
z = posZ;
|
||||
ori = data.orientation;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
x = GetPositionX();
|
||||
y = GetPositionY();
|
||||
z = GetPositionZ();
|
||||
ori = GetOrientation();
|
||||
if (m_goData != null)
|
||||
m_goData.spawnPoint.GetPosition(out x, out y, out z, out ori);
|
||||
else
|
||||
GetPosition(out x, out y, out z, out ori);
|
||||
}
|
||||
|
||||
public float GetInteractionDistance()
|
||||
@@ -2624,18 +2652,13 @@ namespace Game.Entities
|
||||
|
||||
public GameObjectTemplate GetGoInfo() { return m_goInfo; }
|
||||
public GameObjectTemplateAddon GetTemplateAddon() { return m_goTemplateAddon; }
|
||||
public GameObjectData GetGoData() { return m_goData; }
|
||||
public GameObjectData GetGameObjectData() { return m_goData; }
|
||||
public GameObjectValue GetGoValue() { return m_goValue; }
|
||||
|
||||
public ulong GetSpawnId() { return m_spawnId; }
|
||||
|
||||
public long GetPackedWorldRotation() { return m_packedRotation; }
|
||||
|
||||
public override bool LoadFromDB(ulong spawnId, Map map)
|
||||
{
|
||||
return LoadGameObjectFromDB(spawnId, map, false);
|
||||
}
|
||||
|
||||
public void SetOwnerGUID(ObjectGuid owner)
|
||||
{
|
||||
// Owner already found and different than expected owner - remove object from old owner
|
||||
@@ -2768,6 +2791,10 @@ namespace Game.Entities
|
||||
AddFlag(GameObjectFlags.MapObject);
|
||||
}
|
||||
|
||||
// There's many places not ready for dynamic spawns. This allows them to live on for now.
|
||||
void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
|
||||
public bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; }
|
||||
|
||||
#region Fields
|
||||
protected GameObjectFieldData m_gameObjectData;
|
||||
protected GameObjectValue m_goValue;
|
||||
@@ -2799,6 +2826,7 @@ namespace Game.Entities
|
||||
public Position StationaryPosition { get; set; }
|
||||
|
||||
GameObjectAI m_AI;
|
||||
bool m_respawnCompatibilityMode;
|
||||
ushort _animKitId;
|
||||
uint _worldEffectID;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ using Framework.GameMath;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Game.Networking.Packets;
|
||||
using Game.Maps;
|
||||
|
||||
namespace Game.Entities
|
||||
{
|
||||
@@ -1274,25 +1275,13 @@ namespace Game.Entities
|
||||
public uint WorldEffectID;
|
||||
}
|
||||
|
||||
public class GameObjectData
|
||||
public class GameObjectData : SpawnData
|
||||
{
|
||||
public uint id; // entry in gamobject_template
|
||||
public ushort mapid;
|
||||
public float posX;
|
||||
public float posY;
|
||||
public float posZ;
|
||||
public float orientation;
|
||||
public Quaternion rotation;
|
||||
public int spawntimesecs;
|
||||
public uint animprogress;
|
||||
public GameObjectState go_state;
|
||||
public List<Difficulty> spawnDifficulties = new List<Difficulty>();
|
||||
public GameObjectState goState;
|
||||
public byte artKit;
|
||||
public PhaseUseFlagsValues phaseUseFlags;
|
||||
public uint phaseId;
|
||||
public uint phaseGroup;
|
||||
public int terrainSwapMap;
|
||||
public uint ScriptId;
|
||||
public bool dbData = true;
|
||||
|
||||
public GameObjectData() : base(SpawnObjectType.GameObject) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,6 +345,10 @@ namespace Game.Entities
|
||||
{
|
||||
if (_nextGuid >= ObjectGuid.GetMaxCounter(_highGuid) - 1)
|
||||
HandleCounterOverflow();
|
||||
|
||||
if (_highGuid == HighGuid.Creature || _highGuid == HighGuid.Vehicle || _highGuid == HighGuid.GameObject || _highGuid == HighGuid.Transport)
|
||||
CheckGuidTrigger(_nextGuid);
|
||||
|
||||
return _nextGuid++;
|
||||
}
|
||||
|
||||
@@ -355,5 +359,13 @@ namespace Game.Entities
|
||||
Log.outFatal(LogFilter.Server, "{0} guid overflow!! Can't continue, shutting down server. ", _highGuid);
|
||||
Global.WorldMgr.StopNow();
|
||||
}
|
||||
|
||||
void CheckGuidTrigger(ulong guidlow)
|
||||
{
|
||||
if (!Global.WorldMgr.IsGuidAlert() && guidlow > WorldConfig.GetUInt64Value(WorldCfg.RespawnGuidAlertLevel))
|
||||
Global.WorldMgr.TriggerGuidAlert();
|
||||
else if (!Global.WorldMgr.IsGuidWarning() && guidlow > WorldConfig.GetUInt64Value(WorldCfg.RespawnGuidWarnLevel))
|
||||
Global.WorldMgr.TriggerGuidWarning();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,6 +364,12 @@ namespace Game.Entities
|
||||
Relocate(pos);
|
||||
}
|
||||
|
||||
public void WorldRelocate(uint mapId, Position pos)
|
||||
{
|
||||
_mapId = mapId;
|
||||
Relocate(pos);
|
||||
}
|
||||
|
||||
public void WorldRelocate(WorldLocation loc)
|
||||
{
|
||||
_mapId = loc._mapId;
|
||||
|
||||
@@ -1731,7 +1731,7 @@ namespace Game.Entities
|
||||
|
||||
public virtual uint GetLevelForTarget(WorldObject target) { return 1; }
|
||||
|
||||
public virtual void SaveRespawnTime() { }
|
||||
public virtual void SaveRespawnTime(uint forceDelay = 0, bool saveToDB = true) { }
|
||||
|
||||
public ZoneScript GetZoneScript() { return m_zoneScript; }
|
||||
|
||||
@@ -1770,7 +1770,7 @@ namespace Game.Entities
|
||||
public virtual bool IsInvisibleDueToDespawn() { return false; }
|
||||
public virtual bool IsAlwaysDetectableFor(WorldObject seer) { return false; }
|
||||
|
||||
public virtual bool LoadFromDB(ulong guid, Map map) { return true; }
|
||||
public virtual bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate) { return true; }
|
||||
|
||||
//Position
|
||||
|
||||
|
||||
@@ -160,6 +160,8 @@ namespace Game.Entities
|
||||
guild.UpdateMemberData(this, GuildMemberData.ZoneId, newZone);
|
||||
}
|
||||
|
||||
GetMap().UpdatePlayerZoneStats(m_zoneUpdateId, newZone);
|
||||
|
||||
// group update
|
||||
if (GetGroup())
|
||||
{
|
||||
|
||||
@@ -318,14 +318,12 @@ namespace Game.Entities
|
||||
{
|
||||
Map map = GetMap();
|
||||
|
||||
Creature creature = Creature.CreateCreatureFromDB(guid, map, false);
|
||||
Creature creature = Creature.CreateCreatureFromDB(guid, map, false, true);
|
||||
if (!creature)
|
||||
return null;
|
||||
|
||||
float x = data.posX;
|
||||
float y = data.posY;
|
||||
float z = data.posZ;
|
||||
float o = data.orientation;
|
||||
float x, y, z, o;
|
||||
data.spawnPoint.GetPosition(out x, out y, out z, out o);
|
||||
|
||||
creature.SetTransport(this);
|
||||
creature.m_movementInfo.transport.guid = GetGUID();
|
||||
@@ -365,10 +363,8 @@ namespace Game.Entities
|
||||
if (!go)
|
||||
return null;
|
||||
|
||||
float x = data.posX;
|
||||
float y = data.posY;
|
||||
float z = data.posZ;
|
||||
float o = data.orientation;
|
||||
float x, y, z, o;
|
||||
data.spawnPoint.GetPosition(out x, out y, out z, out o);
|
||||
|
||||
go.SetTransport(this);
|
||||
go.m_movementInfo.transport.guid = GetGUID();
|
||||
@@ -472,7 +468,7 @@ namespace Game.Entities
|
||||
pos.GetPosition(out x, out y, out z, out o);
|
||||
CalculatePassengerPosition(ref x, ref y, ref z, ref o);
|
||||
|
||||
if (!summon.Create(map.GenerateLowGuid(HighGuid.Creature), map, entry, x, y, z, o, null, vehId))
|
||||
if (!summon.Create(map.GenerateLowGuid(HighGuid.Creature), map, entry, new Position(x, y, z, o), null, vehId))
|
||||
return null;
|
||||
|
||||
PhasingHandler.InheritPhaseShift(summon, summoner ? (WorldObject)summoner : this);
|
||||
@@ -553,7 +549,7 @@ namespace Game.Entities
|
||||
|
||||
// GameObjects on transport
|
||||
foreach (var go in cell.Value.gameobjects)
|
||||
CreateGOPassenger(go, Global.ObjectMgr.GetGOData(go));
|
||||
CreateGOPassenger(go, Global.ObjectMgr.GetGameObjectData(go));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -412,7 +412,7 @@ namespace Game
|
||||
int internal_event_id = mGameEvent.Length + event_id - 1;
|
||||
|
||||
|
||||
GameObjectData data = Global.ObjectMgr.GetGOData(guid);
|
||||
GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
|
||||
if (data == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "`game_event_gameobject` contains gameobject (GUID: {0}) not found in `gameobject` table.", guid);
|
||||
@@ -773,7 +773,7 @@ namespace Game
|
||||
uint entry = 0;
|
||||
CreatureData data = Global.ObjectMgr.GetCreatureData(guid);
|
||||
if (data != null)
|
||||
entry = data.id;
|
||||
entry = data.Id;
|
||||
|
||||
VendorItem vItem = new VendorItem();
|
||||
vItem.item = result.Read<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
|
||||
CreatureData data = Global.ObjectMgr.GetCreatureData(guid);
|
||||
if (data != null)
|
||||
creaturesByMap.Add(data.mapid, guid);
|
||||
creaturesByMap.Add(data.spawnPoint.GetMapId(), guid);
|
||||
}
|
||||
|
||||
foreach (var key in creaturesByMap.Keys)
|
||||
@@ -1170,9 +1170,9 @@ namespace Game
|
||||
Global.ObjectMgr.AddCreatureToGrid(guid, data);
|
||||
|
||||
// Spawn if necessary (loaded grids only)
|
||||
Map map = Global.MapMgr.FindMap(data.mapid, 0);
|
||||
Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0);
|
||||
// We use spawn coords to spawn
|
||||
if (map != null && !map.Instanceable() && map.IsGridLoaded(data.posX, data.posY))
|
||||
if (map != null && !map.Instanceable() && map.IsGridLoaded(data.spawnPoint))
|
||||
Creature.CreateCreatureFromDB(guid, map);
|
||||
}
|
||||
}
|
||||
@@ -1187,15 +1187,15 @@ namespace Game
|
||||
foreach (var guid in mGameEventGameobjectGuids[internal_event_id])
|
||||
{
|
||||
// Add to correct cell
|
||||
GameObjectData data = Global.ObjectMgr.GetGOData(guid);
|
||||
GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
|
||||
if (data != null)
|
||||
{
|
||||
Global.ObjectMgr.AddGameObjectToGrid(guid, data);
|
||||
// Spawn if necessary (loaded grids only)
|
||||
// this base map checked as non-instanced and then only existed
|
||||
Map map = Global.MapMgr.FindMap(data.mapid, 0);
|
||||
Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0);
|
||||
// We use current coords to unspawn, not spawn coords since creature can have changed grid
|
||||
if (map != null && !map.Instanceable() && map.IsGridLoaded(data.posX, data.posY))
|
||||
if (map != null && !map.Instanceable() && map.IsGridLoaded(data.spawnPoint))
|
||||
{
|
||||
GameObject pGameobject = GameObject.CreateGameObjectFromDB(guid, map, false);
|
||||
// @todo find out when it is add to map
|
||||
@@ -1243,7 +1243,7 @@ namespace Game
|
||||
{
|
||||
Global.ObjectMgr.RemoveCreatureFromGrid(guid, data);
|
||||
|
||||
Global.MapMgr.DoForAllMapsWithMapId(data.mapid, map =>
|
||||
Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map =>
|
||||
{
|
||||
var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(guid);
|
||||
foreach (var creature in creatureBounds)
|
||||
@@ -1265,12 +1265,12 @@ namespace Game
|
||||
if (event_id > 0 && HasGameObjectActiveEventExcept(guid, (ushort)event_id))
|
||||
continue;
|
||||
// Remove the gameobject from grid
|
||||
GameObjectData data = Global.ObjectMgr.GetGOData(guid);
|
||||
GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
|
||||
if (data != null)
|
||||
{
|
||||
Global.ObjectMgr.RemoveGameObjectFromGrid(guid, data);
|
||||
|
||||
Global.MapMgr.DoForAllMapsWithMapId(data.mapid, map =>
|
||||
Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map =>
|
||||
{
|
||||
var gameobjectBounds = map.GetGameObjectBySpawnIdStore().LookupByKey(guid);
|
||||
foreach (var go in gameobjectBounds)
|
||||
@@ -1300,7 +1300,7 @@ namespace Game
|
||||
continue;
|
||||
|
||||
// Update if spawned
|
||||
Global.MapMgr.DoForAllMapsWithMapId(data.mapid, map =>
|
||||
Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map =>
|
||||
{
|
||||
var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(tuple.Item1);
|
||||
foreach (var creature in creatureBounds)
|
||||
@@ -1337,12 +1337,12 @@ namespace Game
|
||||
tuple.Item2.modelid_prev = data2.displayid;
|
||||
tuple.Item2.equipement_id_prev = (byte)data2.equipmentId;
|
||||
data2.displayid = tuple.Item2.modelid;
|
||||
data2.equipmentId = tuple.Item2.equipment_id;
|
||||
data2.equipmentId = (sbyte)tuple.Item2.equipment_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
data2.displayid = tuple.Item2.modelid_prev;
|
||||
data2.equipmentId = tuple.Item2.equipement_id_prev;
|
||||
data2.equipmentId = (sbyte)tuple.Item2.equipement_id_prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,7 +831,7 @@ namespace Game.Garrisons
|
||||
T BuildingSpawnHelper<T>(GameObject building, ulong spawnId, Map map) where T : WorldObject, new()
|
||||
{
|
||||
T spawn = new T();
|
||||
if (!spawn.LoadFromDB(spawnId, map))
|
||||
if (!spawn.LoadFromDB(spawnId, map, false, false))
|
||||
return null;
|
||||
|
||||
float x = spawn.GetPositionX();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2010,6 +2010,31 @@ namespace Game.Maps
|
||||
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>
|
||||
{
|
||||
public NearestPlayerInObjectRangeCheck(WorldObject obj, float range)
|
||||
|
||||
+535
-80
@@ -74,6 +74,8 @@ namespace Game.Maps
|
||||
}
|
||||
}
|
||||
|
||||
_zonePlayerCountMap.Clear();
|
||||
|
||||
//lets initialize visibility distance for map
|
||||
InitVisibilityDistance();
|
||||
_weatherUpdateTimer = new IntervalTimer();
|
||||
@@ -90,6 +92,10 @@ namespace Game.Maps
|
||||
{
|
||||
Global.ScriptMgr.OnDestroyMap(this);
|
||||
|
||||
// Delete all waiting spawns
|
||||
// This doesn't delete from database.
|
||||
DeleteRespawnInfo();
|
||||
|
||||
for (var i = 0; i < i_worldObjects.Count; ++i)
|
||||
{
|
||||
WorldObject obj = i_worldObjects[i];
|
||||
@@ -279,7 +285,7 @@ namespace Game.Maps
|
||||
m_VisibilityNotifyPeriod = Global.WorldMgr.GetVisibilityNotifyPeriodOnContinents();
|
||||
}
|
||||
|
||||
public void AddToGrid<T>(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());
|
||||
switch (obj.GetTypeId())
|
||||
@@ -429,7 +435,7 @@ namespace Game.Maps
|
||||
GridMaps[gx][gy] = m_parentMap.GridMaps[gx][gy];
|
||||
++rootParentTerrainMap.GridMapReference[gx][gy];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,6 +481,29 @@ namespace Game.Maps
|
||||
loader.LoadN();
|
||||
}
|
||||
|
||||
void GridMarkNoUnload(uint x, uint y)
|
||||
{
|
||||
// First make sure this grid is loaded
|
||||
float gX = (((float)x - 0.5f - MapConst.CenterGridId) * MapConst.SizeofGrids) + (MapConst.CenterGridOffset * 2);
|
||||
float gY = (((float)y - 0.5f - MapConst.CenterGridId) * MapConst.SizeofGrids) + (MapConst.CenterGridOffset * 2);
|
||||
Cell cell = new Cell(gX, gY);
|
||||
EnsureGridLoaded(cell);
|
||||
|
||||
// Mark as don't unload
|
||||
var grid = GetGrid(x, y);
|
||||
grid.SetUnloadExplicitLock(true);
|
||||
}
|
||||
|
||||
void GridUnmarkNoUnload(uint x, uint y)
|
||||
{
|
||||
// If grid is loaded, clear unload lock
|
||||
if (IsGridLoaded(new GridCoord(x, y)))
|
||||
{
|
||||
var grid = GetGrid(x, y);
|
||||
grid.SetUnloadExplicitLock(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadGrid(float x, float y)
|
||||
{
|
||||
EnsureGridLoaded(new Cell(x, y));
|
||||
@@ -598,10 +627,11 @@ namespace Game.Maps
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsGridLoaded(float x, float y)
|
||||
{
|
||||
return IsGridLoaded(GridDefines.ComputeGridCoord(x, y));
|
||||
}
|
||||
public bool IsGridLoaded(uint gridId) { return IsGridLoaded(new GridCoord(gridId % MapConst.MaxGrids, gridId / MapConst.MaxGrids)); }
|
||||
|
||||
public bool IsGridLoaded(float x, float y) { return IsGridLoaded(GridDefines.ComputeGridCoord(x, y)); }
|
||||
|
||||
public bool IsGridLoaded(Position pos) { return IsGridLoaded(pos.GetPositionX(), pos.GetPositionY()); }
|
||||
|
||||
public bool IsGridLoaded(GridCoord p)
|
||||
{
|
||||
@@ -637,6 +667,20 @@ namespace Game.Maps
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePlayerZoneStats(uint oldZone, uint newZone)
|
||||
{
|
||||
// Nothing to do if no change
|
||||
if (oldZone == newZone)
|
||||
return;
|
||||
|
||||
if (oldZone != MapConst.InvalidZone)
|
||||
{
|
||||
Cypher.Assert(_zonePlayerCountMap[oldZone] != 0, $"A player left zone {oldZone} (went to {newZone}) - but there were no players in the zone!");
|
||||
--_zonePlayerCountMap[oldZone];
|
||||
}
|
||||
++_zonePlayerCountMap[newZone];
|
||||
}
|
||||
|
||||
public virtual void Update(uint diff)
|
||||
{
|
||||
_dynamicTree.Update(diff);
|
||||
@@ -653,9 +697,18 @@ namespace Game.Maps
|
||||
}
|
||||
}
|
||||
|
||||
/// process any due respawns
|
||||
if (_respawnCheckTimer <= diff)
|
||||
{
|
||||
ProcessRespawns();
|
||||
_respawnCheckTimer = WorldConfig.GetUIntValue(WorldCfg.RespawnMinCheckIntervalMs);
|
||||
}
|
||||
else
|
||||
_respawnCheckTimer -= diff;
|
||||
|
||||
// update active cells around players and active objects
|
||||
ResetMarkedCells();
|
||||
|
||||
|
||||
var update = new UpdaterNotifier(diff);
|
||||
|
||||
var grid_object_update = new Visitor(update, GridMapTypeMask.AllGrid);
|
||||
@@ -666,10 +719,10 @@ namespace Game.Maps
|
||||
Player player = m_activePlayers[i];
|
||||
if (!player.IsInWorld)
|
||||
continue;
|
||||
|
||||
|
||||
// update players at tick
|
||||
player.Update(diff);
|
||||
|
||||
|
||||
VisitNearbyCellsOf(player, grid_object_update, world_object_update);
|
||||
|
||||
// If player is using far sight or mind vision, visit that object too
|
||||
@@ -698,7 +751,7 @@ namespace Game.Maps
|
||||
VisitNearbyCellsOf(c, grid_object_update, world_object_update);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (var i = 0; i < m_activeNonPlayers.Count; ++i)
|
||||
{
|
||||
WorldObject obj = m_activeNonPlayers[i];
|
||||
@@ -707,7 +760,7 @@ namespace Game.Maps
|
||||
|
||||
VisitNearbyCellsOf(obj, grid_object_update, world_object_update);
|
||||
}
|
||||
|
||||
|
||||
for (var i = 0; i < _transports.Count; ++i)
|
||||
{
|
||||
Transport transport = _transports[i];
|
||||
@@ -716,7 +769,7 @@ namespace Game.Maps
|
||||
|
||||
transport.Update(diff);
|
||||
}
|
||||
|
||||
|
||||
SendObjectUpdates();
|
||||
|
||||
// Process necessary scripts
|
||||
@@ -841,6 +894,8 @@ namespace Game.Maps
|
||||
|
||||
public virtual void RemovePlayerFromMap(Player player, bool remove)
|
||||
{
|
||||
// Before leaving map, update zone/area for stats
|
||||
player.UpdateZone(MapConst.InvalidZone, 0);
|
||||
Global.ScriptMgr.OnPlayerLeaveMap(this, player);
|
||||
|
||||
player.GetHostileRefManager().DeleteReferences(); // multithreading crashfix
|
||||
@@ -1167,7 +1222,7 @@ namespace Game.Maps
|
||||
{
|
||||
_creatureToMoveLock = true;
|
||||
|
||||
for (var i = 0; i< creaturesToMove.Count; ++i)
|
||||
for (var i = 0; i < creaturesToMove.Count; ++i)
|
||||
{
|
||||
Creature creature = creaturesToMove[i];
|
||||
if (creature.GetMap() != this) //pet is teleported to another map
|
||||
@@ -1493,7 +1548,7 @@ namespace Game.Maps
|
||||
// if in same cell then none do
|
||||
if (old_cell.DiffCell(new_cell))
|
||||
{
|
||||
Log.outDebug(LogFilter.Maps, "AreaTrigger ({0}) moved in grid[{0}, {1}] from cell[{2}, {3}] to cell[{4}, {5}].", at.GetGUID().ToString(), old_cell.GetGridX(), old_cell.GetGridY(),
|
||||
Log.outDebug(LogFilter.Maps, "AreaTrigger ({0}) moved in grid[{0}, {1}] from cell[{2}, {3}] to cell[{4}, {5}].", at.GetGUID().ToString(), old_cell.GetGridX(), old_cell.GetGridY(),
|
||||
old_cell.GetCellX(), old_cell.GetCellY(), new_cell.GetCellX(), new_cell.GetCellY());
|
||||
|
||||
RemoveFromGrid(at, old_cell);
|
||||
@@ -1810,6 +1865,16 @@ namespace Game.Maps
|
||||
return mapHeight; // explicitly use map data
|
||||
}
|
||||
|
||||
public float GetHeight(PhaseShift phaseShift, float x, float y, float z, bool vmap = true, float maxSearchDist = MapConst.DefaultHeightSearch)
|
||||
{
|
||||
return Math.Max(GetStaticHeight(phaseShift, x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phaseShift, x, y, z, maxSearchDist));
|
||||
}
|
||||
|
||||
public float GetHeight(PhaseShift phaseShift, Position pos, bool vmap = true, float maxSearchDist = MapConst.DefaultHeightSearch)
|
||||
{
|
||||
return GetHeight(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist);
|
||||
}
|
||||
|
||||
public float GetMinHeight(PhaseShift phaseShift, float x, float y)
|
||||
{
|
||||
GridMap grid = GetGridMap(PhasingHandler.GetTerrainMapId(phaseShift, this, x, y), x, y);
|
||||
@@ -1974,6 +2039,8 @@ namespace Game.Maps
|
||||
return areaId;
|
||||
}
|
||||
|
||||
public uint GetAreaId(PhaseShift phaseShift, Position pos) { return GetAreaId(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
|
||||
|
||||
public uint GetZoneId(PhaseShift phaseShift, float x, float y, float z)
|
||||
{
|
||||
uint areaId = GetAreaId(phaseShift, x, y, z);
|
||||
@@ -1985,6 +2052,8 @@ namespace Game.Maps
|
||||
return areaId;
|
||||
}
|
||||
|
||||
public uint GetZoneId(PhaseShift phaseShift, Position pos) { return GetZoneId(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
|
||||
|
||||
public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, float x, float y, float z)
|
||||
{
|
||||
areaid = zoneid = GetAreaId(phaseShift, x, y, z);
|
||||
@@ -1994,6 +2063,8 @@ namespace Game.Maps
|
||||
zoneid = area.ParentAreaID;
|
||||
}
|
||||
|
||||
public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, Position pos) { GetZoneAndAreaId(phaseShift, out zoneid, out areaid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
|
||||
|
||||
private byte GetTerrainType(PhaseShift phaseShift, float x, float y)
|
||||
{
|
||||
GridMap gmap = GetGridMap(PhasingHandler.GetTerrainMapId(phaseShift, this, x, y), x, y);
|
||||
@@ -2229,11 +2300,6 @@ namespace Game.Maps
|
||||
return result;
|
||||
}
|
||||
|
||||
public float GetHeight(PhaseShift phaseShift, float x, float y, float z, bool vmap = true, float maxSearchDist = MapConst.DefaultHeightSearch)
|
||||
{
|
||||
return Math.Max(GetStaticHeight(phaseShift, x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phaseShift, x, y, z, maxSearchDist));
|
||||
}
|
||||
|
||||
public bool IsInWater(PhaseShift phaseShift, float x, float y, float pZ)
|
||||
{
|
||||
return Convert.ToBoolean(GetLiquidStatus(phaseShift, x, y, pZ, MapConst.MapAllLiquidTypes) & (ZLiquidStatus.InWater | ZLiquidStatus.UnderWater));
|
||||
@@ -2379,6 +2445,349 @@ namespace Game.Maps
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckRespawn(RespawnInfo info)
|
||||
{
|
||||
uint poolId = info.spawnId != 0 ? Global.PoolMgr.IsPartOfAPool(info.type, info.spawnId) : 0;
|
||||
// First, check if there's already an instance of this object that would block the respawn
|
||||
// Only do this for unpooled spawns
|
||||
if (poolId == 0)
|
||||
{
|
||||
bool doDelete = false;
|
||||
switch (info.type)
|
||||
{
|
||||
case SpawnObjectType.Creature:
|
||||
{
|
||||
// escort check for creatures only (if the world config boolean is set)
|
||||
bool isEscort = false;
|
||||
if (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc) && info.type == SpawnObjectType.Creature)
|
||||
{
|
||||
CreatureData cdata = Global.ObjectMgr.GetCreatureData(info.spawnId);
|
||||
if (cdata != null)
|
||||
if (cdata.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.EscortQuestNpc))
|
||||
isEscort = true;
|
||||
}
|
||||
|
||||
var range = _creatureBySpawnIdStore.LookupByKey(info.spawnId);
|
||||
foreach (var creature in range)
|
||||
{
|
||||
if (!creature.IsAlive())
|
||||
continue;
|
||||
|
||||
// escort NPCs are allowed to respawn as long as all other instances are already escorting
|
||||
if (isEscort && creature.IsEscortNPC(true))
|
||||
continue;
|
||||
|
||||
doDelete = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SpawnObjectType.GameObject:
|
||||
// gameobject check is simpler - they cannot be dead or escorting
|
||||
if (_gameobjectBySpawnIdStore.ContainsKey(info.spawnId))
|
||||
doDelete = true;
|
||||
break;
|
||||
default:
|
||||
Cypher.Assert(false, $"Invalid spawn type {info.type} with spawnId {info.spawnId} on map {GetId()}");
|
||||
return true;
|
||||
}
|
||||
if (doDelete)
|
||||
{
|
||||
info.respawnTime = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// next, check linked respawn time
|
||||
ObjectGuid thisGUID = info.type == SpawnObjectType.GameObject ? ObjectGuid.Create(HighGuid.GameObject, GetId(), info.entry, info.spawnId) : ObjectGuid.Create(HighGuid.Creature, GetId(), info.entry, info.spawnId);
|
||||
long linkedTime = GetLinkedRespawnTime(thisGUID);
|
||||
if (linkedTime != 0)
|
||||
{
|
||||
long now = Time.UnixTime;
|
||||
long respawnTime;
|
||||
if (Global.ObjectMgr.GetLinkedRespawnGuid(thisGUID) == thisGUID) // never respawn, save "something" in DB
|
||||
respawnTime = now + Time.Week;
|
||||
else // set us to check again shortly after linked unit
|
||||
respawnTime = Math.Max(now, linkedTime) + RandomHelper.URand(5, 15);
|
||||
info.respawnTime = respawnTime;
|
||||
return false;
|
||||
}
|
||||
|
||||
// now, check if we're part of a pool
|
||||
if (poolId != 0)
|
||||
{
|
||||
// ok, part of a pool - hand off to pool logic to handle this, we're just going to remove the respawn and call it a day
|
||||
if (info.type == SpawnObjectType.GameObject)
|
||||
Global.PoolMgr.UpdatePool<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)
|
||||
{
|
||||
for (var i = 0; i < _transports.Count; ++i)
|
||||
@@ -2617,64 +3026,37 @@ namespace Game.Maps
|
||||
m_activeNonPlayers.Remove(obj);
|
||||
}
|
||||
|
||||
public void SaveCreatureRespawnTime(ulong dbGuid, long respawnTime)
|
||||
public void SaveRespawnTime(SpawnObjectType type, ulong spawnId, uint entry, long respawnTime, uint zoneId, uint gridId = 0, bool writeDB = true, bool replace = false, SQLTransaction dbTrans = null)
|
||||
{
|
||||
if (respawnTime == 0)
|
||||
{
|
||||
// Delete only
|
||||
RemoveCreatureRespawnTime(dbGuid);
|
||||
RemoveRespawnTime(type, spawnId, false, dbTrans);
|
||||
return;
|
||||
}
|
||||
|
||||
_creatureRespawnTimes[dbGuid] = respawnTime;
|
||||
RespawnInfo ri = new RespawnInfo();
|
||||
ri.type = type;
|
||||
ri.spawnId = spawnId;
|
||||
ri.entry = entry;
|
||||
ri.respawnTime = respawnTime;
|
||||
ri.gridId = gridId;
|
||||
ri.zoneId = zoneId;
|
||||
AddRespawnInfo(ri, replace);
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.REP_CREATURE_RESPAWN);
|
||||
stmt.AddValue(0, dbGuid);
|
||||
if (writeDB)
|
||||
SaveRespawnTimeDB(type, spawnId, ri.respawnTime, dbTrans); // might be different from original respawn time if we didn't replace
|
||||
}
|
||||
|
||||
public void SaveRespawnTimeDB(SpawnObjectType type, ulong spawnId, long respawnTime, SQLTransaction dbTrans = null)
|
||||
{
|
||||
// Just here for support of compatibility mode
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement((type == SpawnObjectType.GameObject) ? CharStatements.REP_GO_RESPAWN : CharStatements.REP_CREATURE_RESPAWN);
|
||||
stmt.AddValue(0, spawnId);
|
||||
stmt.AddValue(1, respawnTime);
|
||||
stmt.AddValue(2, GetId());
|
||||
stmt.AddValue(3, GetInstanceId());
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
|
||||
public void RemoveCreatureRespawnTime(ulong dbGuid)
|
||||
{
|
||||
_creatureRespawnTimes.Remove(dbGuid);
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CREATURE_RESPAWN);
|
||||
stmt.AddValue(0, dbGuid);
|
||||
stmt.AddValue(1, GetId());
|
||||
stmt.AddValue(2, GetInstanceId());
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
|
||||
public void SaveGORespawnTime(ulong dbGuid, long respawnTime)
|
||||
{
|
||||
if (respawnTime == 0)
|
||||
{
|
||||
// Delete only
|
||||
RemoveGORespawnTime(dbGuid);
|
||||
return;
|
||||
}
|
||||
|
||||
_goRespawnTimes[dbGuid] = respawnTime;
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.REP_GO_RESPAWN);
|
||||
stmt.AddValue(0, dbGuid);
|
||||
stmt.AddValue(1, respawnTime);
|
||||
stmt.AddValue(2, GetId());
|
||||
stmt.AddValue(3, GetInstanceId());
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
|
||||
public void RemoveGORespawnTime(ulong dbGuid)
|
||||
{
|
||||
_goRespawnTimes.Remove(dbGuid);
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_GO_RESPAWN);
|
||||
stmt.AddValue(0, dbGuid);
|
||||
stmt.AddValue(1, GetId());
|
||||
stmt.AddValue(2, GetInstanceId());
|
||||
DB.Characters.Execute(stmt);
|
||||
DB.Characters.ExecuteOrAppend(dbTrans, stmt);
|
||||
}
|
||||
|
||||
public void LoadRespawnTimes()
|
||||
@@ -2690,7 +3072,10 @@ namespace Game.Maps
|
||||
var loguid = result.Read<uint>(0);
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -2705,16 +3090,19 @@ namespace Game.Maps
|
||||
var loguid = result.Read<uint>(0);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
public long GetRespawnTime(SpawnObjectType type, ulong spawnId) { return (type == SpawnObjectType.GameObject) ? GetGORespawnTime(spawnId) : GetCreatureRespawnTime(spawnId); }
|
||||
|
||||
public void DeleteRespawnTimes()
|
||||
{
|
||||
_creatureRespawnTimes.Clear();
|
||||
_goRespawnTimes.Clear();
|
||||
|
||||
DeleteRespawnInfo();
|
||||
DeleteRespawnTimesInDB(GetId(), GetInstanceId());
|
||||
}
|
||||
|
||||
@@ -3106,7 +3494,8 @@ namespace Game.Maps
|
||||
return GetGrid(p.X_coord, p.Y_coord) == null ||
|
||||
GetGrid(p.X_coord, p.Y_coord).GetGridState() == GridState.Removal;
|
||||
}
|
||||
|
||||
public bool IsRemovalGrid(Position pos) { return IsRemovalGrid(pos.GetPositionX(), pos.GetPositionY()); }
|
||||
|
||||
private bool GetUnloadLock(GridCoord p)
|
||||
{
|
||||
return GetGrid(p.X_coord, p.Y_coord).GetUnloadLock();
|
||||
@@ -3320,7 +3709,7 @@ namespace Game.Maps
|
||||
{
|
||||
return _dynamicTree.GetHeight(x, y, z, maxSearchDist, phaseShift);
|
||||
}
|
||||
|
||||
|
||||
public virtual uint GetOwnerGuildId(Team team = Team.Other)
|
||||
{
|
||||
return 0;
|
||||
@@ -3328,12 +3717,12 @@ namespace Game.Maps
|
||||
|
||||
public long GetCreatureRespawnTime(ulong dbGuid)
|
||||
{
|
||||
return _creatureRespawnTimes.LookupByKey(dbGuid);
|
||||
return _creatureRespawnTimesBySpawnId.ContainsKey(dbGuid) ? _creatureRespawnTimesBySpawnId[dbGuid].respawnTime : 0;
|
||||
}
|
||||
|
||||
public long GetGORespawnTime(ulong dbGuid)
|
||||
{
|
||||
return _goRespawnTimes.LookupByKey(dbGuid);
|
||||
return _gameObjectRespawnTimesBySpawnId.ContainsKey(dbGuid) ? _gameObjectRespawnTimesBySpawnId[dbGuid].respawnTime : 0;
|
||||
}
|
||||
|
||||
void SetTimer(uint t)
|
||||
@@ -3421,6 +3810,33 @@ namespace Game.Maps
|
||||
return go ? go.ToTransport() : null;
|
||||
}
|
||||
|
||||
Creature GetCreatureBySpawnId(ulong spawnId)
|
||||
{
|
||||
var bounds = GetCreatureBySpawnIdStore().LookupByKey(spawnId);
|
||||
if (bounds.Empty())
|
||||
return null;
|
||||
|
||||
var foundCreature = bounds.Find(creature => creature.IsAlive());
|
||||
|
||||
return foundCreature != null ? foundCreature : bounds[0];
|
||||
}
|
||||
|
||||
GameObject GetGameObjectBySpawnId(ulong spawnId)
|
||||
{
|
||||
var bounds = GetGameObjectBySpawnIdStore().LookupByKey(spawnId);
|
||||
if (bounds.Empty())
|
||||
return null;
|
||||
|
||||
var foundGameObject = bounds.Find(gameobject => gameobject.IsSpawned());
|
||||
|
||||
return foundGameObject != null ? foundGameObject : bounds[0];
|
||||
}
|
||||
|
||||
public WorldObject GetWorldObjectBySpawnId(SpawnObjectType type, ulong spawnId)
|
||||
{
|
||||
return type == SpawnObjectType.GameObject ? (WorldObject)GetGameObjectBySpawnId(spawnId) : (WorldObject)GetCreatureBySpawnId(spawnId);
|
||||
}
|
||||
|
||||
public void Visit(Cell cell, Visitor visitor)
|
||||
{
|
||||
uint x = cell.GetGridX();
|
||||
@@ -3507,7 +3923,7 @@ namespace Game.Maps
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!summon.Create(GenerateLowGuid(HighGuid.Creature), this, entry, pos.posX, pos.posY, pos.posZ, pos.Orientation, null, vehId))
|
||||
if (!summon.Create(GenerateLowGuid(HighGuid.Creature), this, entry, pos, null, vehId))
|
||||
return null;
|
||||
|
||||
// Set the summon to the summoner's phase
|
||||
@@ -3554,7 +3970,7 @@ namespace Game.Maps
|
||||
_updateObjects.Remove(obj);
|
||||
}
|
||||
|
||||
public static implicit operator bool (Map map)
|
||||
public static implicit operator bool(Map map)
|
||||
{
|
||||
return map != null;
|
||||
}
|
||||
@@ -4178,7 +4594,7 @@ namespace Game.Maps
|
||||
uTarget = target?.ToUnit();
|
||||
break;
|
||||
case eScriptFlags.CastspellSourceToSource: // source . source
|
||||
uSource =source?.ToUnit();
|
||||
uSource = source?.ToUnit();
|
||||
uTarget = uSource;
|
||||
break;
|
||||
case eScriptFlags.CastspellTargetToTarget: // target . target
|
||||
@@ -4442,10 +4858,14 @@ namespace Game.Maps
|
||||
|
||||
GridMap[][] GridMaps = new GridMap[MapConst.MaxGrids][];
|
||||
ushort[][] GridMapReference = new ushort[MapConst.MaxGrids][];
|
||||
Dictionary<ulong, long> _creatureRespawnTimes = new Dictionary<ulong, long>();
|
||||
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>();
|
||||
Grid[][] i_grids = new Grid[MapConst.MaxGrids][];
|
||||
MapRecord i_mapRecord;
|
||||
@@ -5050,4 +5470,39 @@ namespace Game.Maps
|
||||
public Optional<AreaInfo> areaInfo;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -430,6 +430,16 @@ namespace Game.Entities
|
||||
m.GetZoneAndAreaId(phaseShift, out zoneid, out areaid, x, y, z);
|
||||
}
|
||||
|
||||
public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, uint mapid, Position pos)
|
||||
{
|
||||
GetZoneAndAreaId(phaseShift, out zoneid, out areaid, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
|
||||
}
|
||||
|
||||
public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, WorldLocation loc)
|
||||
{
|
||||
GetZoneAndAreaId(phaseShift, out zoneid, out areaid, loc.GetMapId(), loc);
|
||||
}
|
||||
|
||||
public void DoForAllMaps(Action<Map> worker)
|
||||
{
|
||||
lock (_mapsLock)
|
||||
|
||||
@@ -80,10 +80,46 @@ namespace Game.Maps
|
||||
foreach (var guid in guid_set)
|
||||
{
|
||||
T obj = new T();
|
||||
if (!obj.LoadFromDB(guid, map))
|
||||
continue;
|
||||
// Don't spawn at all if there's a respawn time
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace Game.Maps
|
||||
{
|
||||
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 void OnCreatureCreate(Creature creature) { }
|
||||
|
||||
@@ -358,7 +358,7 @@ namespace Game.PvP
|
||||
|
||||
void AddGO(uint type, ulong guid)
|
||||
{
|
||||
GameObjectData data = Global.ObjectMgr.GetGOData(guid);
|
||||
GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
@@ -378,7 +378,7 @@ namespace Game.PvP
|
||||
|
||||
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)
|
||||
{
|
||||
AddGO(type, guid);
|
||||
@@ -412,7 +412,7 @@ namespace Game.PvP
|
||||
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)
|
||||
return false;
|
||||
|
||||
@@ -472,7 +472,7 @@ namespace Game.PvP
|
||||
gameobject.Delete();
|
||||
}
|
||||
|
||||
Global.ObjectMgr.DeleteGOData(spawnId);
|
||||
Global.ObjectMgr.DeleteGameObjectData(spawnId);
|
||||
m_ObjectTypes.Remove(spawnId);
|
||||
m_Objects.Remove(type);
|
||||
return true;
|
||||
@@ -480,7 +480,7 @@ namespace Game.PvP
|
||||
|
||||
bool DelCapturePoint()
|
||||
{
|
||||
Global.ObjectMgr.DeleteGOData(m_capturePointSpawnId);
|
||||
Global.ObjectMgr.DeleteGameObjectData(m_capturePointSpawnId);
|
||||
m_capturePointSpawnId = 0;
|
||||
|
||||
if (m_capturePoint)
|
||||
|
||||
@@ -144,14 +144,14 @@ namespace Game
|
||||
uint pool_id = result.Read<uint>(1);
|
||||
float chance = result.Read<float>(2);
|
||||
|
||||
GameObjectData data = Global.ObjectMgr.GetGOData(guid);
|
||||
GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid);
|
||||
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);
|
||||
continue;
|
||||
}
|
||||
|
||||
GameObjectTemplate goinfo = Global.ObjectMgr.GetGameObjectTemplate(data.id);
|
||||
GameObjectTemplate goinfo = Global.ObjectMgr.GetGameObjectTemplate(data.Id);
|
||||
if (goinfo.type != GameObjectTypes.Chest &&
|
||||
goinfo.type != GameObjectTypes.FishingHole &&
|
||||
goinfo.type != GameObjectTypes.GatheringNode &&
|
||||
@@ -534,6 +534,21 @@ namespace Game
|
||||
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
|
||||
{
|
||||
None = 0,
|
||||
@@ -655,28 +670,40 @@ namespace Game
|
||||
if (data != null)
|
||||
{
|
||||
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())
|
||||
{
|
||||
var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(guid);
|
||||
foreach (var creature in creatureBounds)
|
||||
{
|
||||
// For dynamic spawns, save respawn time here
|
||||
if (!creature.GetRespawnCompatibilityMode())
|
||||
creature.SaveRespawnTime(0, false);
|
||||
|
||||
creature.AddObjectToRemoveList();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "GameObject":
|
||||
{
|
||||
var data = Global.ObjectMgr.GetGOData(guid);
|
||||
var data = Global.ObjectMgr.GetGameObjectData(guid);
|
||||
if (data != null)
|
||||
{
|
||||
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())
|
||||
{
|
||||
var gameobjectBounds = map.GetGameObjectBySpawnIdStore().LookupByKey(guid);
|
||||
foreach (var go in gameobjectBounds)
|
||||
{
|
||||
// For dynamic spawns, save respawn time here
|
||||
if (!go.GetRespawnCompatibilityMode())
|
||||
go.SaveRespawnTime(0, false);
|
||||
|
||||
go.AddObjectToRemoveList();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -863,24 +890,24 @@ namespace Game
|
||||
Global.ObjectMgr.AddCreatureToGrid(obj.guid, data);
|
||||
|
||||
// Spawn if necessary (loaded grids only)
|
||||
Map map = Global.MapMgr.FindMap(data.mapid, 0);
|
||||
Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0);
|
||||
// We use spawn coords to spawn
|
||||
if (map != null && !map.Instanceable() && map.IsGridLoaded(data.posX, data.posY))
|
||||
if (map != null && !map.Instanceable() && map.IsGridLoaded(data.spawnPoint))
|
||||
Creature.CreateCreatureFromDB(obj.guid, map);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "GameObject":
|
||||
{
|
||||
GameObjectData data = Global.ObjectMgr.GetGOData(obj.guid);
|
||||
GameObjectData data = Global.ObjectMgr.GetGameObjectData(obj.guid);
|
||||
if (data != null)
|
||||
{
|
||||
Global.ObjectMgr.AddGameObjectToGrid(obj.guid, data);
|
||||
// Spawn if necessary (loaded grids only)
|
||||
// this base map checked as non-instanced and then only existed
|
||||
Map map = Global.MapMgr.FindMap(data.mapid, 0);
|
||||
Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0);
|
||||
// We use current coords to unspawn, not spawn coords since creature can have changed grid
|
||||
if (map != null && !map.Instanceable() && map.IsGridLoaded(data.posX, data.posY))
|
||||
if (map != null && !map.Instanceable() && map.IsGridLoaded(data.spawnPoint))
|
||||
{
|
||||
GameObject go = GameObject.CreateGameObjectFromDB(obj.guid, map, false);
|
||||
if (go)
|
||||
|
||||
@@ -682,13 +682,36 @@ namespace Game.Scripting
|
||||
}
|
||||
|
||||
//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);
|
||||
if (baseTemplate == null)
|
||||
baseTemplate = actTemplate;
|
||||
Cypher.Assert(baseTemplate != null);
|
||||
|
||||
// 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;
|
||||
if (cData != null && cData.ScriptId != 0)
|
||||
|
||||
@@ -776,6 +776,49 @@ namespace Game
|
||||
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.VmapIndoorCheck] = GetDefaultValue("vmap.EnableIndoorCheck", false);
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace Game
|
||||
_realm = new Realm();
|
||||
|
||||
_worldUpdateTime = new WorldUpdateTime();
|
||||
_warnShutdownTime = Time.UnixTime;
|
||||
}
|
||||
|
||||
public Player FindPlayerInZone(uint zone)
|
||||
@@ -92,6 +93,61 @@ namespace Game
|
||||
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)
|
||||
{
|
||||
return m_sessions.LookupByKey(id);
|
||||
@@ -527,6 +583,9 @@ namespace Game
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Base Stats...");
|
||||
Global.ObjectMgr.LoadCreatureClassLevelStats();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Spawn Group Templates...");
|
||||
Global.ObjectMgr.LoadSpawnGroupTemplates();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Data...");
|
||||
Global.ObjectMgr.LoadCreatures();
|
||||
|
||||
@@ -543,7 +602,10 @@ namespace Game
|
||||
Global.ObjectMgr.LoadCreatureAddons();
|
||||
|
||||
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...");
|
||||
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_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", "./");
|
||||
if (reload)
|
||||
{
|
||||
@@ -1332,6 +1397,16 @@ namespace Game
|
||||
// update the instance reset times
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -2288,6 +2363,9 @@ namespace Game
|
||||
public CleaningFlags GetCleaningFlags() { return m_CleaningFlags; }
|
||||
public void SetCleaningFlags(CleaningFlags flags) { m_CleaningFlags = flags; }
|
||||
|
||||
public bool IsGuidWarning() { return _guidWarn; }
|
||||
public bool IsGuidAlert() { return _guidAlert; }
|
||||
|
||||
public WorldUpdateTime GetWorldUpdateTime() { return _worldUpdateTime; }
|
||||
|
||||
#region Fields
|
||||
@@ -2349,6 +2427,16 @@ namespace Game
|
||||
string _dataPath;
|
||||
|
||||
WorldUpdateTime _worldUpdateTime;
|
||||
|
||||
string _guidWarningMsg;
|
||||
string _alertRestartReason;
|
||||
|
||||
object _guidAlertLock = new object();
|
||||
|
||||
bool _guidWarn;
|
||||
bool _guidAlert;
|
||||
uint _warnDiff;
|
||||
long _warnShutdownTime;
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace Scripts.Northrend.IcecrownCitadel
|
||||
|
||||
CreatureData data = creature.GetCreatureData();
|
||||
if (data != null)
|
||||
creature.UpdatePosition(data.posX, data.posY, data.posZ, data.orientation);
|
||||
creature.UpdatePosition(data.spawnPoint);
|
||||
creature.DespawnOrUnsummon();
|
||||
|
||||
creature.SetCorpseDelay(corpseDelay);
|
||||
@@ -496,7 +496,7 @@ namespace Scripts.Northrend.IcecrownCitadel
|
||||
Creature crusader = ObjectAccessor.GetCreature(me, instance.GetGuidData(DataTypes.CaptainArnath + i));
|
||||
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));
|
||||
delay += 6000;
|
||||
|
||||
@@ -346,7 +346,7 @@ namespace Scripts.Northrend.IcecrownCitadel
|
||||
// Weekly quest spawn prevention
|
||||
public override uint GetCreatureEntry(ulong guidLow, CreatureData data)
|
||||
{
|
||||
uint entry = data.id;
|
||||
uint entry = data.Id;
|
||||
switch (entry)
|
||||
{
|
||||
case CreatureIds.InfiltratorMinchar:
|
||||
@@ -384,13 +384,13 @@ namespace Scripts.Northrend.IcecrownCitadel
|
||||
case CreatureIds.ZafodBoombox:
|
||||
GameObjectTemplate go = Global.ObjectMgr.GetGameObjectTemplate(GameObjectIds.TheSkybreaker_A);
|
||||
if (go != null)
|
||||
if ((TeamInInstance == Team.Alliance && data.mapid == go.MoTransport.SpawnMap) ||
|
||||
(TeamInInstance == Team.Horde && data.mapid != go.MoTransport.SpawnMap))
|
||||
if ((TeamInInstance == Team.Alliance && data.spawnPoint.GetMapId() == go.MoTransport.SpawnMap) ||
|
||||
(TeamInInstance == Team.Horde && data.spawnPoint.GetMapId() != go.MoTransport.SpawnMap))
|
||||
return entry;
|
||||
return 0;
|
||||
case CreatureIds.IGBMuradinBrozebeard:
|
||||
if ((TeamInInstance == Team.Alliance && data.posX > 10.0f) ||
|
||||
(TeamInInstance == Team.Horde && data.posX < 10.0f))
|
||||
if ((TeamInInstance == Team.Alliance && data.spawnPoint.GetPositionX() > 10.0f) ||
|
||||
(TeamInInstance == Team.Horde && data.spawnPoint.GetPositionX() < 10.0f))
|
||||
return entry;
|
||||
return 0;
|
||||
default:
|
||||
|
||||
@@ -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
|
||||
#
|
||||
|
||||
@@ -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.');
|
||||
Reference in New Issue
Block a user