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:
hondacrx
2021-08-09 09:37:36 -04:00
parent bfd44a494d
commit 85ffed189f
6 changed files with 583 additions and 631 deletions
+72 -70
View File
@@ -115,7 +115,7 @@ namespace Game.Entities
return false;
}
public void SearchFormation()
{
if (IsSummon())
@@ -156,7 +156,7 @@ namespace Game.Entities
return m_formation.CanLeaderStartMoving();
}
public void RemoveCorpse(bool setSpawnTime = true, bool destroyForNearbyPlayers = true)
{
if (GetDeathState() != DeathState.Corpse)
@@ -443,47 +443,52 @@ namespace Game.Entities
{
case DeathState.JustRespawned:
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;
case DeathState.Dead:
{
if (!m_respawnCompatibilityMode)
{
long now = GameTime.GetGameTime();
if (m_respawnTime <= now)
{
// First check if there are any scripts that object to us respawning
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
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.Week);
else
{
// else copy time from master and add a little
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
// we shall inherit it instead of adding and causing an overflow
if (baseRespawnTime <= long.MaxValue - offset)
m_respawnTime = baseRespawnTime + offset;
else
m_respawnTime = long.MaxValue;
}
SaveRespawnTime(); // also save to DB immediately
}
}
Log.outError(LogFilter.Unit, $"Creature (GUID: {GetGUID().GetCounter()} Entry: {GetEntry()}) in wrong state: DEAD (3)");
break;
}
long now = GameTime.GetGameTime();
if (m_respawnTime <= now)
{
// Delay respawn if spawn group is not active
if (m_creatureData != null && !GetMap().IsSpawnGroupActive(m_creatureData.spawnGroupData.groupId))
{
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
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.Week);
else
{
// else copy time from master and add a little
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
// we shall inherit it instead of adding and causing an overflow
if (baseRespawnTime <= long.MaxValue - offset)
m_respawnTime = baseRespawnTime + offset;
else
m_respawnTime = long.MaxValue;
}
SaveRespawnTime(); // also save to DB immediately
}
}
break;
}
case DeathState.Corpse:
base.Update(diff);
if (m_deathState != DeathState.Corpse)
@@ -651,30 +656,30 @@ namespace Game.Entities
switch (power)
{
case PowerType.Focus:
{
// For hunter pets.
addvalue = 24 * WorldConfig.GetFloatValue(WorldCfg.RatePowerFocus);
break;
}
{
// For hunter pets.
addvalue = 24 * WorldConfig.GetFloatValue(WorldCfg.RatePowerFocus);
break;
}
case PowerType.Energy:
{
// For deathknight's ghoul.
addvalue = 20;
break;
}
{
// For deathknight's ghoul.
addvalue = 20;
break;
}
case PowerType.Mana:
{
// Combat and any controlled creature
if (IsInCombat() || GetCharmerOrOwnerGUID().IsEmpty())
{
// Combat and any controlled creature
if (IsInCombat() || GetCharmerOrOwnerGUID().IsEmpty())
{
float ManaIncreaseRate = WorldConfig.GetFloatValue(WorldCfg.RatePowerMana);
addvalue = (27.0f / 5.0f + 17.0f) * ManaIncreaseRate;
}
else
addvalue = maxValue / 3;
break;
float ManaIncreaseRate = WorldConfig.GetFloatValue(WorldCfg.RatePowerMana);
addvalue = (27.0f / 5.0f + 17.0f) * ManaIncreaseRate;
}
else
addvalue = maxValue / 3;
break;
}
default:
return;
}
@@ -1133,7 +1138,7 @@ namespace Game.Entities
return false;
}
public void StartPickPocketRefillTimer()
{
_pickpocketLootRestore = GameTime.GetGameTime() + WorldConfig.GetIntValue(WorldCfg.CreaturePickpocketRefill);
@@ -2495,7 +2500,7 @@ namespace Game.Entities
CreatureTemplate cInfo = GetCreatureTemplate();
CreatureLevelScaling scaling = cInfo.GetLevelScaling(GetMap().GetDifficultyID());
float baseHealth = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, level, cInfo.GetHealthScalingExpansion(), scaling.ContentTuningID, (Class)cInfo.UnitClass);
return (ulong)(baseHealth * cInfo.ModHealth * cInfo.ModHealthExtra);
}
@@ -3046,7 +3051,7 @@ namespace Game.Entities
}
public bool IsDungeonBoss() { return (GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.DungeonBoss)); }
public override bool IsAffectedByDiminishingReturns() { return base.IsAffectedByDiminishingReturns() || GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.AllDiminish); }
public void SetReactState(ReactStates st)
{
reactState = st;
@@ -3119,7 +3124,7 @@ namespace Game.Entities
CreatureData data = Global.ObjectMgr.GetCreatureData(spawnId);
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;
}
@@ -3128,12 +3133,6 @@ namespace Game.Entities
m_creatureData = data;
m_respawnradius = data.spawndist;
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;
@@ -3146,8 +3145,11 @@ namespace Game.Entities
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)
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);
}
if (m_respawnTime != 0) // respawn on UpdateLoadCreatureFromDB
{
@@ -3236,7 +3238,7 @@ namespace Game.Entities
public (uint nodeId, uint pathId) GetCurrentWaypointInfo() { return _currentWaypointNodeInfo; }
public void UpdateCurrentWaypointInfo(uint nodeId, uint pathId) { _currentWaypointNodeInfo = (nodeId, pathId); }
public CreatureGroup GetFormation() { return m_formation; }
public void SetFormation(CreatureGroup formation) { m_formation = formation; }
+1 -1
View File
@@ -249,7 +249,7 @@ namespace Game.Maps
if (doSpawn)
instance.SpawnGroupSpawn(groupId, instance);
else // otherwise, set it as inactive so it no longer respawns (but don't despawn it)
instance.SetSpawnGroupActive(groupId, false);
instance.SetSpawnGroupInactive(groupId);
}
}
+510 -511
View File
File diff suppressed because it is too large Load Diff
-8
View File
@@ -110,14 +110,6 @@ namespace Game.Maps
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))
{
-3
View File
@@ -333,9 +333,6 @@ namespace Game.Scripting
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.
public virtual CreatureAI GetAI(Creature creature) { return null; }
}
-38
View File
@@ -681,44 +681,6 @@ namespace Game.Scripting
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)
{
Cypher.Assert(creature != null);