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.

Also remove CreatureScript::CanSpawn since nobody uses it, and spawn groups do the same thing.
This commit is contained in:
Treeston
2018-02-22 20:31:42 +01:00
parent 3753ec5647
commit 94b5d9bfa1
7 changed files with 45 additions and 74 deletions

View File

@@ -644,12 +644,16 @@ void Creature::Update(uint32 diff)
break;
case DEAD:
{
if (!m_respawnCompatibilityMode)
{
TC_LOG_ERROR("entities.unit", "Creature (GUID: %u Entry: %u) in wrong state: DEAD (3)", GetGUID().GetCounter(), GetEntry());
break;
}
time_t now = GameTime::GetGameTime();
if (m_respawnTime <= now)
{
// First check if there are any scripts that object to us respawning
if (!sScriptMgr->CanSpawn(GetSpawnId(), GetEntry(), GetCreatureData(), GetMap()))
// Delay respawn if spawn group is not active
if (m_creatureData && !GetMap()->IsSpawnGroupActive(m_creatureData->spawnGroupData->groupId))
{
m_respawnTime = now + urand(4,7);
break; // Will be rechecked on next Update call after delay expires
@@ -1608,7 +1612,7 @@ bool Creature::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap,
if (!data)
{
TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) not found in table `creature`, can't load. ", spawnId);
TC_LOG_ERROR("sql.sql", "Creature (SpawnID %u) not found in table `creature`, can't load. ", spawnId);
return false;
}
@@ -1619,13 +1623,6 @@ bool Creature::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap,
m_respawnradius = data->spawndist;
m_respawnDelay = 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 && !sScriptMgr->CanSpawn(spawnId, data->id, data, map))
{
SaveRespawnTime(urand(4,7));
return false;
}
if (!Create(map->GenerateLowGuid<HighGuid::Unit>(), map, data->phaseMask, data->id, data->spawnPoint, data, 0U , !m_respawnCompatibilityMode))
return false;
@@ -1637,8 +1634,11 @@ bool Creature::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap,
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 && !sScriptMgr->CanSpawn(spawnId, GetEntry(), GetCreatureData(), map))
m_respawnTime = GameTime::GetGameTime() + urand(4,7);
if (!m_respawnTime && !map->IsSpawnGroupActive(data->spawnGroupData->groupId))
{
ASSERT(m_respawnCompatibilityMode, "Creature (SpawnID %u) trying to load in inactive spawn group %s.", spawnId, data->spawnGroupData->name.c_str());
m_respawnTime = GameTime::GetGameTime() + urand(4, 7);
}
if (m_respawnTime) // respawn on Update
{

View File

@@ -140,14 +140,6 @@ void LoadHelper(CellGuidSet const& guid_set, CellCoord &cell, GridRefManager<T>
delete obj;
continue;
}
// If script is blocking spawn, don't spawn but queue for a re-check in a little bit
if (!(group->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE) && !sScriptMgr->CanSpawn(guid, cdata->id, cdata, map))
{
map->SaveRespawnTime(SPAWN_TYPE_CREATURE, guid, cdata->id, GameTime::GetGameTime() + urand(4,7), map->GetZoneId(cdata->spawnPoint), Trinity::ComputeGridCoord(cdata->spawnPoint.GetPositionX(), cdata->spawnPoint.GetPositionY()).GetId(), false);
delete obj;
continue;
}
}
else if (obj->GetTypeId() == TYPEID_GAMEOBJECT)
{

View File

@@ -265,7 +265,7 @@ void InstanceScript::UpdateSpawnGroups()
if (doSpawn)
instance->SpawnGroupSpawn(groupId);
else // otherwise, set it as inactive so it no longer respawns (but don't despawn it)
instance->SetSpawnGroupActive(groupId, false);
instance->SetSpawnGroupInactive(groupId);
}
}

View File

@@ -2924,10 +2924,24 @@ void Map::SendObjectUpdates()
}
}
// CheckRespawn MUST do one of the following:
// -) return true
// -) set info->respawnTime to zero, which indicates the respawn time should be deleted (and will never be processed again without outside intervention)
// -) set info->respawnTime to a new respawn time, which must be strictly GREATER than the current time (GameTime::GetGameTime())
bool Map::CheckRespawn(RespawnInfo* info)
{
SpawnData const* data = sObjectMgr->GetSpawnData(info->type, info->spawnId);
ASSERT(data, "Invalid respawn info with type %u, spawnID %u in respawn queue.", info->type, info->spawnId);
// First, check if this creature's spawn group is inactive
if (!IsSpawnGroupActive(data->spawnGroupData->groupId))
{
info->respawnTime = 0;
return false;
}
uint32 poolId = info->spawnId ? sPoolMgr->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
if (!poolId)
{
@@ -2937,11 +2951,7 @@ bool Map::CheckRespawn(RespawnInfo* info)
case SPAWN_TYPE_CREATURE:
{
// escort check for creatures only (if the world config boolean is set)
bool isEscort = false;
if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && info->type == SPAWN_TYPE_CREATURE)
if (CreatureData const* cdata = sObjectMgr->GetCreatureData(info->spawnId))
if (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC)
isEscort = true;
bool const isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && data->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC);
auto range = _creatureBySpawnIdStore.equal_range(info->spawnId);
for (auto it = range.first; it != range.second; ++it)
@@ -3003,15 +3013,7 @@ bool Map::CheckRespawn(RespawnInfo* info)
return false;
}
// if we're a creature, see if the script objects to us spawning
if (info->type == SPAWN_TYPE_CREATURE)
{
if (!sScriptMgr->CanSpawn(info->spawnId, info->entry, sObjectMgr->GetCreatureData(info->spawnId), this))
{ // if a script blocks our respawn, schedule next check in a little bit
info->respawnTime = GameTime::GetGameTime() + urand(4, 7);
return false;
}
}
// everything ok, let's spawn
return true;
}

View File

@@ -838,11 +838,20 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
}
SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const;
bool SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn = false, bool force = false, std::vector<WorldObject*>* spawnedObjects = nullptr);
bool SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes = false, size_t* count = nullptr);
void SetSpawnGroupActive(uint32 groupId, bool state);
bool IsSpawnGroupActive(uint32 groupId) const;
// Enable the spawn group, which causes all creatures in it to respawn (unless they have a respawn timer)
// The force flag can be used to force spawning additional copies even if old copies are still around from a previous spawn
bool SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn = false, bool force = false, std::vector<WorldObject*>* spawnedObjects = nullptr);
// Despawn all creatures in the spawn group if spawned, optionally delete their respawn timer, and disable the group
bool SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes = false, size_t* count = nullptr);
// 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
void SetSpawnGroupInactive(uint32 groupId) { SetSpawnGroupActive(groupId, false); }
private:
// Type specific code for add/remove to/from grid
template<class T>
@@ -877,6 +886,8 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
RespawnInfoMap _gameObjectRespawnTimesBySpawnId;
RespawnInfoMap& GetRespawnMapForType(SpawnObjectType type) { return (type == SPAWN_TYPE_GAMEOBJECT) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; }
RespawnInfoMap const& GetRespawnMapForType(SpawnObjectType type) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; }
void SetSpawnGroupActive(uint32 groupId, bool state);
std::unordered_set<uint32> _toggledSpawnGroupIds;
uint32 _respawnCheckTimer;

View File

@@ -1565,36 +1565,6 @@ bool ScriptMgr::OnCastItemCombatSpell(Player* player, Unit* victim, SpellInfo co
return tmpscript->OnCastItemCombatSpell(player, victim, spellInfo, item);
}
bool ScriptMgr::CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureData const* cData, Map const* map)
{
ASSERT(map);
CreatureTemplate const* baseTemplate = sObjectMgr->GetCreatureTemplate(entry);
ASSERT(baseTemplate);
// find out which template we'd be using
CreatureTemplate const* actTemplate = baseTemplate;
for (uint8 diff = uint8(map->GetSpawnMode()); diff > 0;)
{
if (uint32 diffEntry = baseTemplate->DifficultyEntry[diff - 1])
if (CreatureTemplate const* diffTemplate = sObjectMgr->GetCreatureTemplate(diffEntry))
{
actTemplate = diffTemplate;
break;
}
if (diff >= RAID_DIFFICULTY_10MAN_HEROIC && map->IsRaid())
diff -= 2;
else
diff -= 1;
}
uint32 scriptId = baseTemplate->ScriptID;
if (cData && cData->scriptId)
scriptId = cData->scriptId;
GET_SCRIPT_RET(CreatureScript, scriptId, tmpscript, true);
return tmpscript->CanSpawn(spawnId, entry, baseTemplate, actTemplate, cData, map);
}
CreatureAI* ScriptMgr::GetCreatureAI(Creature* creature)
{
ASSERT(creature);

View File

@@ -417,9 +417,6 @@ class TC_GAME_API CreatureScript : public UnitScript
public:
// Called when the creature tries to spawn. Return false to block spawn and re-evaluate on next tick.
virtual bool CanSpawn(ObjectGuid::LowType /*spawnId*/, uint32 /*entry*/, CreatureTemplate const* /*baseTemplate*/, CreatureTemplate const* /*actTemplate*/, CreatureData const* /*cData*/, Map const* /*map*/) const { return true; }
// Called when a CreatureAI object is needed for the creature.
virtual CreatureAI* GetAI(Creature* /*creature*/) const = 0;
};
@@ -920,7 +917,6 @@ class TC_GAME_API ScriptMgr
public: /* CreatureScript */
bool CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureData const* cData, Map const* map);
CreatureAI* GetCreatureAI(Creature* creature);
public: /* GameObjectScript */