fix(Core/Creature): Make Respawn() respect conditions and linked respawns (#17597)
This commit is contained in:
@@ -622,40 +622,7 @@ void Creature::Update(uint32 diff)
|
|||||||
time_t now = GameTime::GetGameTime().count();
|
time_t now = GameTime::GetGameTime().count();
|
||||||
if (m_respawnTime <= now)
|
if (m_respawnTime <= now)
|
||||||
{
|
{
|
||||||
|
Respawn();
|
||||||
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_RESPAWN, GetEntry());
|
|
||||||
|
|
||||||
if (!sConditionMgr->IsObjectMeetToConditions(this, conditions))
|
|
||||||
{
|
|
||||||
// Creature should not respawn, reset respawn timer. Conditions will be checked again the next time it tries to respawn.
|
|
||||||
m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool allowed = !IsAIEnabled || AI()->CanRespawn(); // First check if there are any scripts that prevent us respawning
|
|
||||||
if (!allowed) // Will be rechecked on next Update call
|
|
||||||
break;
|
|
||||||
|
|
||||||
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(m_creatureData ? m_creatureData->id1 : GetEntry(), m_spawnId);
|
|
||||||
time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
|
|
||||||
|
|
||||||
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(GetEntry());
|
|
||||||
|
|
||||||
if (!linkedRespawntime || (cInfo && cInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_HARD_RESET))) // Can respawn
|
|
||||||
Respawn();
|
|
||||||
else // the master is dead
|
|
||||||
{
|
|
||||||
ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
|
|
||||||
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
|
|
||||||
{
|
|
||||||
SetRespawnTime(DAY);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
|
|
||||||
}
|
|
||||||
SaveRespawnTime(); // also save to DB immediately
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2006,83 +1973,121 @@ void Creature::setDeathState(DeathState s, bool despawn)
|
|||||||
|
|
||||||
void Creature::Respawn(bool force)
|
void Creature::Respawn(bool force)
|
||||||
{
|
{
|
||||||
//DestroyForNearbyPlayers(); // pussywizard: not needed
|
|
||||||
|
|
||||||
if (force)
|
if (force)
|
||||||
{
|
{
|
||||||
if (IsAlive())
|
if (IsAlive())
|
||||||
|
{
|
||||||
setDeathState(DeathState::JustDied);
|
setDeathState(DeathState::JustDied);
|
||||||
|
}
|
||||||
else if (getDeathState() != DeathState::Corpse)
|
else if (getDeathState() != DeathState::Corpse)
|
||||||
|
{
|
||||||
setDeathState(DeathState::Corpse);
|
setDeathState(DeathState::Corpse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveCorpse(false, false);
|
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_RESPAWN, GetEntry());
|
||||||
|
|
||||||
if (getDeathState() == DeathState::Dead)
|
if (!sConditionMgr->IsObjectMeetToConditions(this, conditions) && !force)
|
||||||
{
|
{
|
||||||
if (m_spawnId)
|
// Creature should not respawn, reset respawn timer. Conditions will be checked again the next time it tries to respawn.
|
||||||
{
|
m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay;
|
||||||
GetMap()->RemoveCreatureRespawnTime(m_spawnId);
|
return;
|
||||||
CreatureData const* data = sObjectMgr->GetCreatureData(m_spawnId);
|
}
|
||||||
// Respawn check if spawn has 2 entries
|
|
||||||
if (data->id2)
|
bool allowed = !IsAIEnabled || AI()->CanRespawn(); // First check if there are any scripts that prevent us respawning
|
||||||
{
|
if (!allowed && !force) // Will be rechecked on next Update call
|
||||||
uint32 entry = GetRandomId(data->id1, data->id2, data->id3);
|
return;
|
||||||
UpdateEntry(entry, data, true); // Select Random Entry
|
|
||||||
m_defaultMovementType = MovementGeneratorType(data->movementType); // Reload Movement Type
|
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(m_creatureData ? m_creatureData->id1 : GetEntry(), m_spawnId);
|
||||||
LoadEquipment(data->equipmentId); // Reload Equipment
|
time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
|
||||||
AIM_Initialize(); // Reload AI
|
|
||||||
}
|
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(GetEntry());
|
||||||
else
|
|
||||||
{
|
if (!linkedRespawntime || (cInfo && cInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_HARD_RESET)) || force) // Should respawn
|
||||||
if (m_originalEntry != GetEntry())
|
{
|
||||||
UpdateEntry(m_originalEntry);
|
RemoveCorpse(false, false);
|
||||||
}
|
|
||||||
}
|
if (getDeathState() == DeathState::Dead)
|
||||||
|
{
|
||||||
LOG_DEBUG("entities.unit", "Respawning creature {} (SpawnId: {}, {})", GetName(), GetSpawnId(), GetGUID().ToString());
|
if (m_spawnId)
|
||||||
m_respawnTime = 0;
|
{
|
||||||
ResetPickPocketLootTime();
|
GetMap()->RemoveCreatureRespawnTime(m_spawnId);
|
||||||
loot.clear();
|
CreatureData const* data = sObjectMgr->GetCreatureData(m_spawnId);
|
||||||
SelectLevel();
|
// Respawn check if spawn has 2 entries
|
||||||
|
if (data->id2)
|
||||||
setDeathState(DeathState::JustRespawned);
|
{
|
||||||
|
uint32 entry = GetRandomId(data->id1, data->id2, data->id3);
|
||||||
// MDic - Acidmanifesto
|
UpdateEntry(entry, data, true); // Select Random Entry
|
||||||
// Do not override transform auras
|
m_defaultMovementType = MovementGeneratorType(data->movementType); // Reload Movement Type
|
||||||
if (GetAuraEffectsByType(SPELL_AURA_TRANSFORM).empty())
|
LoadEquipment(data->equipmentId); // Reload Equipment
|
||||||
{
|
AIM_Initialize(); // Reload AI
|
||||||
uint32 displayID = GetNativeDisplayId();
|
}
|
||||||
if (sObjectMgr->GetCreatureModelRandomGender(&displayID)) // Cancel load if no model defined
|
else
|
||||||
{
|
{
|
||||||
SetDisplayId(displayID);
|
if (m_originalEntry != GetEntry())
|
||||||
SetNativeDisplayId(displayID);
|
UpdateEntry(m_originalEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GetMotionMaster()->InitDefault();
|
LOG_DEBUG("entities.unit", "Respawning creature {} (SpawnId: {}, {})", GetName(), GetSpawnId(), GetGUID().ToString());
|
||||||
|
m_respawnTime = 0;
|
||||||
//Call AI respawn virtual function
|
ResetPickPocketLootTime();
|
||||||
if (IsAIEnabled)
|
loot.clear();
|
||||||
{
|
SelectLevel();
|
||||||
//reset the AI to be sure no dirty or uninitialized values will be used till next tick
|
|
||||||
AI()->Reset();
|
setDeathState(DeathState::JustRespawned);
|
||||||
TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
|
|
||||||
}
|
// MDic - Acidmanifesto
|
||||||
|
// Do not override transform auras
|
||||||
uint32 poolid = m_spawnId ? sPoolMgr->IsPartOfAPool<Creature>(m_spawnId) : 0;
|
if (GetAuraEffectsByType(SPELL_AURA_TRANSFORM).empty())
|
||||||
if (poolid)
|
{
|
||||||
sPoolMgr->UpdatePool<Creature>(poolid, m_spawnId);
|
uint32 displayID = GetNativeDisplayId();
|
||||||
|
if (sObjectMgr->GetCreatureModelRandomGender(&displayID)) // Cancel load if no model defined
|
||||||
//Re-initialize reactstate that could be altered by movementgenerators
|
{
|
||||||
InitializeReactState();
|
SetDisplayId(displayID);
|
||||||
|
SetNativeDisplayId(displayID);
|
||||||
m_respawnedTime = GameTime::GetGameTime().count();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GetMotionMaster()->InitDefault();
|
||||||
|
|
||||||
|
//Call AI respawn virtual function
|
||||||
|
if (IsAIEnabled)
|
||||||
|
{
|
||||||
|
//reset the AI to be sure no dirty or uninitialized values will be used till next tick
|
||||||
|
AI()->Reset();
|
||||||
|
TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 poolid = m_spawnId ? sPoolMgr->IsPartOfAPool<Creature>(m_spawnId) : 0;
|
||||||
|
if (poolid)
|
||||||
|
sPoolMgr->UpdatePool<Creature>(poolid, m_spawnId);
|
||||||
|
|
||||||
|
//Re-initialize reactstate that could be altered by movementgenerators
|
||||||
|
InitializeReactState();
|
||||||
|
|
||||||
|
m_respawnedTime = GameTime::GetGameTime().count();
|
||||||
|
}
|
||||||
|
m_respawnedTime = GameTime::GetGameTime().count();
|
||||||
|
// xinef: relocate notifier, fixes npc appearing in corpse position after forced respawn (instead of spawn)
|
||||||
|
m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);
|
||||||
|
UpdateObjectVisibility(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
else // the master is dead
|
||||||
|
{
|
||||||
|
ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
|
||||||
|
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
|
||||||
|
{
|
||||||
|
SetRespawnTime(DAY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
time_t now = GameTime::GetGameTime().count();
|
||||||
|
m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
|
||||||
|
}
|
||||||
|
SaveRespawnTime(); // also save to DB immediately
|
||||||
}
|
}
|
||||||
m_respawnedTime = GameTime::GetGameTime().count();
|
|
||||||
// xinef: relocate notifier, fixes npc appearing in corpse position after forced respawn (instead of spawn)
|
|
||||||
m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);
|
|
||||||
UpdateObjectVisibility(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds forceRespawnTimer)
|
void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds forceRespawnTimer)
|
||||||
|
|||||||
@@ -2391,7 +2391,7 @@ public:
|
|||||||
|
|
||||||
if (target->isDead())
|
if (target->isDead())
|
||||||
{
|
{
|
||||||
target->ToCreature()->Respawn();
|
target->ToCreature()->Respawn(true);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user