Core/Spawning: Actually check spawn group state before processing a respawn. It feels like that is something that should've been noticed at some point.
Port From (https://github.com/TrinityCore/TrinityCore/commit/5c80f5073493a3d31fd4b463891727beb6fe7eb1)
This commit is contained in:
@@ -443,15 +443,20 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
case DeathState.JustRespawned:
|
case DeathState.JustRespawned:
|
||||||
case DeathState.JustDied:
|
case DeathState.JustDied:
|
||||||
Log.outError(LogFilter.Unit, "Creature ({0}) in wrong state: {1}", GetGUID().ToString(), m_deathState);
|
Log.outError(LogFilter.Unit, $"Creature ({GetGUID()}) in wrong state: {m_deathState}");
|
||||||
break;
|
break;
|
||||||
case DeathState.Dead:
|
case DeathState.Dead:
|
||||||
{
|
{
|
||||||
|
if (!m_respawnCompatibilityMode)
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Unit, $"Creature (GUID: {GetGUID().GetCounter()} Entry: {GetEntry()}) in wrong state: DEAD (3)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
long now = GameTime.GetGameTime();
|
long now = GameTime.GetGameTime();
|
||||||
if (m_respawnTime <= now)
|
if (m_respawnTime <= now)
|
||||||
{
|
{
|
||||||
// First check if there are any scripts that object to us respawning
|
// Delay respawn if spawn group is not active
|
||||||
if (!Global.ScriptMgr.CanSpawn(GetSpawnId(), GetEntry(), GetCreatureData(), GetMap()))
|
if (m_creatureData != null && !GetMap().IsSpawnGroupActive(m_creatureData.spawnGroupData.groupId))
|
||||||
{
|
{
|
||||||
m_respawnTime = now + RandomHelper.URand(4, 7);
|
m_respawnTime = now + RandomHelper.URand(4, 7);
|
||||||
break; // Will be rechecked on next Update call after delay expires
|
break; // Will be rechecked on next Update call after delay expires
|
||||||
@@ -3119,7 +3124,7 @@ namespace Game.Entities
|
|||||||
CreatureData data = Global.ObjectMgr.GetCreatureData(spawnId);
|
CreatureData data = Global.ObjectMgr.GetCreatureData(spawnId);
|
||||||
if (data == null)
|
if (data == null)
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Sql, "Creature (GUID: {0}) not found in table `creature`, can't load. ", spawnId);
|
Log.outError(LogFilter.Sql, $"Creature (SpawnID: {spawnId}) not found in table `creature`, can't load.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3128,12 +3133,6 @@ namespace Game.Entities
|
|||||||
m_creatureData = data;
|
m_creatureData = data;
|
||||||
m_respawnradius = data.spawndist;
|
m_respawnradius = data.spawndist;
|
||||||
m_respawnDelay = (uint)data.spawntimesecs;
|
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))
|
if (!Create(map.GenerateLowGuid(HighGuid.Creature), map, data.Id, data.spawnPoint, data, 0, !m_respawnCompatibilityMode))
|
||||||
return false;
|
return false;
|
||||||
@@ -3146,8 +3145,11 @@ namespace Game.Entities
|
|||||||
m_respawnTime = GetMap().GetCreatureRespawnTime(m_spawnId);
|
m_respawnTime = GetMap().GetCreatureRespawnTime(m_spawnId);
|
||||||
|
|
||||||
// Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update)
|
// Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update)
|
||||||
if (m_respawnCompatibilityMode && m_respawnTime == 0 && !Global.ScriptMgr.CanSpawn(spawnId, GetEntry(), GetCreatureData(), map))
|
if (m_respawnTime == 0 && !map.IsSpawnGroupActive(data.spawnGroupData.groupId))
|
||||||
|
{
|
||||||
|
Cypher.Assert(m_respawnCompatibilityMode, $"Creature (SpawnID {spawnId}) trying to load in inactive spawn group {data.spawnGroupData.name}.");
|
||||||
m_respawnTime = GameTime.GetGameTime() + RandomHelper.URand(4, 7);
|
m_respawnTime = GameTime.GetGameTime() + RandomHelper.URand(4, 7);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_respawnTime != 0) // respawn on UpdateLoadCreatureFromDB
|
if (m_respawnTime != 0) // respawn on UpdateLoadCreatureFromDB
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ namespace Game.Maps
|
|||||||
if (doSpawn)
|
if (doSpawn)
|
||||||
instance.SpawnGroupSpawn(groupId, instance);
|
instance.SpawnGroupSpawn(groupId, instance);
|
||||||
else // otherwise, set it as inactive so it no longer respawns (but don't despawn it)
|
else // otherwise, set it as inactive so it no longer respawns (but don't despawn it)
|
||||||
instance.SetSpawnGroupActive(groupId, false);
|
instance.SetSpawnGroupInactive(groupId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-18
@@ -2329,8 +2329,18 @@ namespace Game.Maps
|
|||||||
|
|
||||||
bool CheckRespawn(RespawnInfo info)
|
bool CheckRespawn(RespawnInfo info)
|
||||||
{
|
{
|
||||||
|
SpawnData data = Global.ObjectMgr.GetSpawnData(info.type, info.spawnId);
|
||||||
|
Cypher.Assert(data != null, $"Invalid respawn info with type {info.type}, spawnID {info.spawnId} in respawn queue.");
|
||||||
|
|
||||||
|
// First, check if this creature's spawn group is inactive
|
||||||
|
if (!IsSpawnGroupActive(data.spawnGroupData.groupId))
|
||||||
|
{
|
||||||
|
info.respawnTime = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint poolId = info.spawnId != 0 ? Global.PoolMgr.IsPartOfAPool(info.type, info.spawnId) : 0;
|
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
|
// Next, check if there's already an instance of this object that would block the respawn
|
||||||
// Only do this for unpooled spawns
|
// Only do this for unpooled spawns
|
||||||
if (poolId == 0)
|
if (poolId == 0)
|
||||||
{
|
{
|
||||||
@@ -2340,14 +2350,7 @@ namespace Game.Maps
|
|||||||
case SpawnObjectType.Creature:
|
case SpawnObjectType.Creature:
|
||||||
{
|
{
|
||||||
// escort check for creatures only (if the world config boolean is set)
|
// escort check for creatures only (if the world config boolean is set)
|
||||||
bool isEscort = false;
|
bool isEscort = WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc) && data.spawnGroupData.flags.HasFlag(SpawnGroupFlags.EscortQuestNpc);
|
||||||
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);
|
var range = _creatureBySpawnIdStore.LookupByKey(info.spawnId);
|
||||||
foreach (var creature in range)
|
foreach (var creature in range)
|
||||||
@@ -2411,15 +2414,7 @@ namespace Game.Maps
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're a creature, see if the script objects to us spawning
|
// everything ok, let's spawn
|
||||||
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 = GameTime.GetGameTime() + RandomHelper.URand(4, 7);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2822,6 +2817,10 @@ namespace Game.Maps
|
|||||||
_toggledSpawnGroupIds.Remove(groupId);
|
_toggledSpawnGroupIds.Remove(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable the spawn group, which prevents any creatures in the group from respawning until re-enabled
|
||||||
|
// This will not affect any already-present creatures in the group
|
||||||
|
public void SetSpawnGroupInactive(uint groupId) { SetSpawnGroupActive(groupId, false); }
|
||||||
|
|
||||||
public bool IsSpawnGroupActive(uint groupId)
|
public bool IsSpawnGroupActive(uint groupId)
|
||||||
{
|
{
|
||||||
SpawnGroupTemplateData data = GetSpawnGroupData(groupId);
|
SpawnGroupTemplateData data = GetSpawnGroupData(groupId);
|
||||||
|
|||||||
@@ -110,14 +110,6 @@ namespace Game.Maps
|
|||||||
continue;
|
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, GameTime.GetGameTime() + RandomHelper.URand(4, 7), map.GetZoneId(PhasingHandler.EmptyPhaseShift, cdata.spawnPoint), GridDefines.ComputeGridCoord(cdata.spawnPoint.GetPositionX(), cdata.spawnPoint.GetPositionY()).GetId(), false);
|
|
||||||
obj.Dispose();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (obj.IsTypeId(TypeId.GameObject))
|
else if (obj.IsTypeId(TypeId.GameObject))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -333,9 +333,6 @@ namespace Game.Scripting
|
|||||||
|
|
||||||
public override bool IsDatabaseBound() { return true; }
|
public override bool IsDatabaseBound() { return true; }
|
||||||
|
|
||||||
// Called when the creature tries to spawn. Return false to block spawn and re-evaluate on next tick.
|
|
||||||
public virtual bool CanSpawn(ulong spawnId, uint entry, CreatureTemplate baseTemplate, CreatureTemplate actTemplate, CreatureData cData, Map map) { return true; }
|
|
||||||
|
|
||||||
// Called when a CreatureAI object is needed for the creature.
|
// Called when a CreatureAI object is needed for the creature.
|
||||||
public virtual CreatureAI GetAI(Creature creature) { return null; }
|
public virtual CreatureAI GetAI(Creature creature) { return null; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -681,44 +681,6 @@ namespace Game.Scripting
|
|||||||
return RunScriptRet<ItemScript>(tmpscript => tmpscript.OnCastItemCombatSpell(player, victim, spellInfo, item), item.GetScriptId());
|
return RunScriptRet<ItemScript>(tmpscript => tmpscript.OnCastItemCombatSpell(player, victim, spellInfo, item), item.GetScriptId());
|
||||||
}
|
}
|
||||||
|
|
||||||
//CreatureScript
|
|
||||||
public bool CanSpawn(ulong spawnId, uint entry, CreatureData cData, Map map)
|
|
||||||
{
|
|
||||||
Cypher.Assert(map != null);
|
|
||||||
|
|
||||||
CreatureTemplate baseTemplate = Global.ObjectMgr.GetCreatureTemplate(entry);
|
|
||||||
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)
|
|
||||||
scriptId = cData.ScriptId;
|
|
||||||
|
|
||||||
return RunScriptRet<CreatureScript, bool>(p => p.CanSpawn(spawnId, entry, baseTemplate, actTemplate, cData, map), scriptId, true);
|
|
||||||
}
|
|
||||||
public CreatureAI GetCreatureAI(Creature creature)
|
public CreatureAI GetCreatureAI(Creature creature)
|
||||||
{
|
{
|
||||||
Cypher.Assert(creature != null);
|
Cypher.Assert(creature != null);
|
||||||
|
|||||||
Reference in New Issue
Block a user