Dynamic Creature/Go spawning
Port From (https://github.com/TrinityCore/TrinityCore/commit/03b125e6d1947258316c931499746696a95aded2)
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user