From 334398b3f7ea2a09f1048d09aebbecbf1778e7df Mon Sep 17 00:00:00 2001 From: NoFantasy Date: Thu, 9 Sep 2010 11:57:20 +0200 Subject: [PATCH] [10458] Changes to corpse decay/respawn times for creatures *CORPSE_DECAY values adjusted (Rare/RareElite values are guessed) with more proper. *RATE_CORPSE_DECAY_LOOTED is now 0.0 as default and a modifier of the creatures spawntimesecs are used for corpse decay. Respawn time for creature is now set at death (result: database spawntimesecs are in most cases the time it takes from kill to respawn) Overall, this will affect four things: * corpse will stay visible longer before looted * corpse will stay visible longer after looted, when creature has long respawn time * creature without loot will "skip" the default decay times and then fix a "should respawn almost instant" -problem * creature with loot and very short respawn time may respawn instantly after looted Signed-off-by: NoFantasy --- src/game/Creature.cpp | 115 ++++++++++++++++++++----------- src/game/Creature.h | 2 +- src/game/Pet.cpp | 2 +- src/game/Unit.cpp | 3 + src/game/World.cpp | 10 +-- src/mangosd/mangosd.conf.dist.in | 16 ++--- src/shared/revision_nr.h | 2 +- 7 files changed, 95 insertions(+), 55 deletions(-) diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index acb4625d6..dd58c2c14 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -119,7 +119,7 @@ Creature::Creature(CreatureSubtype subtype) : Unit(), i_AI(NULL), lootForPickPocketed(false), lootForBody(false), lootForSkin(false), m_groupLootTimer(0), m_groupLootId(0), m_lootMoney(0), m_lootGroupRecipientId(0), -m_deathTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(5.0f), +m_corpseDecayTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(5.0f), m_subtype(subtype), m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false), m_needNotify(false), @@ -172,7 +172,7 @@ void Creature::RemoveCorpse() if ((getDeathState() != CORPSE && !m_isDeadByDefault) || (getDeathState() != ALIVE && m_isDeadByDefault)) return; - m_deathTimer = 0; + m_corpseDecayTimer = 0; setDeathState(DEAD); UpdateObjectVisibility(); @@ -180,11 +180,14 @@ void Creature::RemoveCorpse() StopGroupLoot(); loot.clear(); - uint32 respawnDelay = m_respawnDelay; + uint32 respawnDelay = 0; + if (AI()) AI()->CorpseRemoved(respawnDelay); - m_respawnTime = time(NULL) + respawnDelay; + // script can set time (in seconds) explicit, override the original + if (respawnDelay) + m_respawnTime = time(NULL) + respawnDelay; float x, y, z, o; GetRespawnCoord(x, y, z, &o); @@ -468,7 +471,7 @@ void Creature::Update(uint32 diff) if (m_isDeadByDefault) break; - if( m_deathTimer <= diff ) + if (m_corpseDecayTimer <= diff) { // since pool system can fail to roll unspawned object, this one can remain spawned, so must set respawn nevertheless uint16 poolid = GetDBTableGUIDLow() ? sPoolMgr.IsPartOfAPool(GetDBTableGUIDLow()) : 0; @@ -483,7 +486,7 @@ void Creature::Update(uint32 diff) } else { - m_deathTimer -= diff; + m_corpseDecayTimer -= diff; if (m_groupLootId) { if(diff < m_groupLootTimer) @@ -499,7 +502,7 @@ void Creature::Update(uint32 diff) { if (m_isDeadByDefault) { - if( m_deathTimer <= diff ) + if (m_corpseDecayTimer <= diff) { // since pool system can fail to roll unspawned object, this one can remain spawned, so must set respawn nevertheless uint16 poolid = GetDBTableGUIDLow() ? sPoolMgr.IsPartOfAPool(GetDBTableGUIDLow()) : 0; @@ -517,7 +520,7 @@ void Creature::Update(uint32 diff) } else { - m_deathTimer -= diff; + m_corpseDecayTimer -= diff; } } @@ -856,34 +859,37 @@ void Creature::PrepareBodyLootState() { loot.clear(); - // if have normal loot then prepare it access - if (!isAlive() && !lootForBody) + // only dead + if (!isAlive()) { - // have normal loot - if (GetCreatureInfo()->maxgold > 0 || GetCreatureInfo()->lootid || - // ... or can have skinning after - GetCreatureInfo()->SkinLootId && sWorld.getConfig(CONFIG_BOOL_CORPSE_EMPTY_LOOT_SHOW)) + // if have normal loot then prepare it access + if (!lootForBody) { - SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); - return; + // have normal loot + if (GetCreatureInfo()->maxgold > 0 || GetCreatureInfo()->lootid || + // ... or can have skinning after + GetCreatureInfo()->SkinLootId && sWorld.getConfig(CONFIG_BOOL_CORPSE_EMPTY_LOOT_SHOW)) + { + SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + return; + } } - } - // if not have normal loot allow skinning if need - if (!isAlive() && !lootForSkin && GetCreatureInfo()->SkinLootId) - { lootForBody = true; // pass this loot mode - RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); - return; + // if not have normal loot allow skinning if need + if (!lootForSkin && GetCreatureInfo()->SkinLootId) + { + RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); + return; + } } RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); } - /** * Return original player who tap creature, it can be different from player/group allowed to loot so not use it for loot code */ @@ -1371,7 +1377,8 @@ void Creature::setDeathState(DeathState s) { if ((s == JUST_DIED && !m_isDeadByDefault) || (s == JUST_ALIVED && m_isDeadByDefault)) { - m_deathTimer = m_corpseDelay*IN_MILLISECONDS; + m_corpseDecayTimer = m_corpseDelay*IN_MILLISECONDS; // the max/default time for corpse decay (before creature is looted/AllLootRemovedFromCorpse() is called) + m_respawnTime = time(NULL) + m_respawnDelay; // respawn delay (spawntimesecs) // always save boss respawn time at death to prevent crash cheating if (sWorld.getConfig(CONFIG_BOOL_SAVE_RESPAWN_TIME_IMMEDIATLY) || isWorldBoss()) @@ -1640,7 +1647,7 @@ bool Creature::IsVisibleInGridForPlayer(Player* pl) const // Live player (or with not release body see live creatures or death creatures with corpse disappearing time > 0 if(pl->isAlive() || pl->GetDeathTimer() > 0) { - return (isAlive() || m_deathTimer > 0 || (m_isDeadByDefault && m_deathState == CORPSE)); + return (isAlive() || m_corpseDecayTimer > 0 || (m_isDeadByDefault && m_deathState == CORPSE)); } // Dead player see live creatures near own corpse @@ -1772,8 +1779,8 @@ void Creature::SaveRespawnTime() if(m_respawnTime > time(NULL)) // dead (no corpse) sObjectMgr.SaveCreatureRespawnTime(m_DBTableGuid, GetInstanceId(), m_respawnTime); - else if(m_deathTimer > 0) // dead (corpse) - sObjectMgr.SaveCreatureRespawnTime(m_DBTableGuid, GetInstanceId(), time(NULL) + m_respawnDelay + m_deathTimer / IN_MILLISECONDS); + else if (m_corpseDecayTimer > 0) // dead (corpse) + sObjectMgr.SaveCreatureRespawnTime(m_DBTableGuid, GetInstanceId(), time(NULL) + m_respawnDelay + m_corpseDecayTimer / IN_MILLISECONDS); } bool Creature::IsOutOfThreatArea(Unit* pVictim) const @@ -2059,8 +2066,8 @@ time_t Creature::GetRespawnTimeEx() const time_t now = time(NULL); if(m_respawnTime > now) // dead (no corpse) return m_respawnTime; - else if(m_deathTimer > 0) // dead (corpse) - return now + m_respawnDelay + m_deathTimer / IN_MILLISECONDS; + else if (m_corpseDecayTimer > 0) // dead (corpse) + return now + m_respawnDelay + m_corpseDecayTimer / IN_MILLISECONDS; else return now; } @@ -2097,18 +2104,48 @@ void Creature::AllLootRemovedFromCorpse() { if (lootForBody && !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE)) { - uint32 nDeathTimer; + uint32 corpseLootedDelay; - // corpse was not skinned -> apply corpse looted timer - if (!lootForSkin) - nDeathTimer = (uint32)((m_corpseDelay * IN_MILLISECONDS) * sWorld.getConfig(CONFIG_FLOAT_RATE_CORPSE_DECAY_LOOTED)); - // corpse was skinned, corpse will despawn next update + if (!lootForSkin) // corpse was not skinned -> apply corpseLootedDelay + { + // use a static spawntimesecs/3 modifier (guessed/made up value) unless config are more than 0.0 + // spawntimesecs=3min: corpse decay after 1min + // spawntimesecs=4hour: corpse decay after 1hour 20min + if (sWorld.getConfig(CONFIG_FLOAT_RATE_CORPSE_DECAY_LOOTED) > 0.0f) + corpseLootedDelay = (uint32)((m_corpseDelay * IN_MILLISECONDS) * sWorld.getConfig(CONFIG_FLOAT_RATE_CORPSE_DECAY_LOOTED)); + else + corpseLootedDelay = (m_respawnDelay*IN_MILLISECONDS) /3; + } + else // corpse was skinned, corpse will despawn next update + corpseLootedDelay = 0; + + // if m_respawnTime is not expired already + if (m_respawnTime >= time(NULL)) + { + // if spawntimesecs is larger than default corpse delay always use corpseLootedDelay + if (m_respawnDelay > m_corpseDelay) + { + m_corpseDecayTimer = corpseLootedDelay; + } + else + { + // if m_respawnDelay is relatively short and corpseDecayTimer is larger than corpseLootedDelay + if (m_corpseDecayTimer > corpseLootedDelay) + m_corpseDecayTimer = corpseLootedDelay; + } + } else - nDeathTimer = 0; + { + m_corpseDecayTimer = 0; - // update death timer only if looted timer is shorter - if (m_deathTimer > nDeathTimer) - m_deathTimer = nDeathTimer; + // TODO: reaching here, means mob will respawn at next tick. + // This might be a place to set some aggro delay so creature has + // ~5 seconds before it can react to hostile surroundings. + + // It's worth noting that it will not be fully correct either way. + // At this point another "instance" of the creature are presumably expected to + // be spawned already, while this corpse will not appear in respawned form. + } } } diff --git a/src/game/Creature.h b/src/game/Creature.h index b2fb73c6d..79a86576a 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -661,7 +661,7 @@ class MANGOS_DLL_SPEC Creature : public Unit uint32 m_lootGroupRecipientId; // group who will have rights for looting if set and exist /// Timers - uint32 m_deathTimer; // (msecs)timer for death or corpse disappearance + uint32 m_corpseDecayTimer; // (msecs)timer for death or corpse disappearance time_t m_respawnTime; // (secs) time of next respawn uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index 5c1271716..255b611da 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -489,7 +489,7 @@ void Pet::Update(uint32 diff) { case CORPSE: { - if( m_deathTimer <= diff ) + if (m_corpseDecayTimer <= diff) { MANGOS_ASSERT(getPetType()!=SUMMON_PET && "Must be already removed."); Remove(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER! diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 8428cf225..1fd33fe32 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -797,7 +797,10 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa cVictim->DeleteThreatList(); // only lootable if it has loot or can drop gold cVictim->PrepareBodyLootState(); + // may have no loot, so update death timer if allowed + cVictim->AllLootRemovedFromCorpse(); } + // Call creature just died function if (cVictim->AI()) cVictim->AI()->JustDied(this); diff --git a/src/game/World.cpp b/src/game/World.cpp index 21afa066e..551fcfe81 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -491,7 +491,7 @@ void World::LoadConfigSettings(bool reload) setConfigPos(CONFIG_FLOAT_RATE_MINING_NEXT, "Rate.Mining.Next", 1.0f); setConfigPos(CONFIG_FLOAT_RATE_INSTANCE_RESET_TIME, "Rate.InstanceResetTime", 1.0f); setConfigPos(CONFIG_FLOAT_RATE_TALENT, "Rate.Talent", 1.0f); - setConfigPos(CONFIG_FLOAT_RATE_CORPSE_DECAY_LOOTED, "Rate.Corpse.Decay.Looted", 0.1f); + setConfigPos(CONFIG_FLOAT_RATE_CORPSE_DECAY_LOOTED, "Rate.Corpse.Decay.Looted", 0.0f); setConfigMinMax(CONFIG_FLOAT_RATE_TARGET_POS_RECALCULATION_RANGE, "TargetPosRecalculateRange", 1.5f, CONTACT_DISTANCE, ATTACK_DISTANCE); @@ -695,10 +695,10 @@ void World::LoadConfigSettings(bool reload) setConfig(CONFIG_UINT32_CHAT_STRICT_LINK_CHECKING_KICK, "ChatStrictLinkChecking.Kick", 0); setConfig(CONFIG_BOOL_CORPSE_EMPTY_LOOT_SHOW, "Corpse.EmptyLootShow", true); - setConfigPos(CONFIG_UINT32_CORPSE_DECAY_NORMAL, "Corpse.Decay.NORMAL", 60); - setConfigPos(CONFIG_UINT32_CORPSE_DECAY_RARE, "Corpse.Decay.RARE", 300); - setConfigPos(CONFIG_UINT32_CORPSE_DECAY_ELITE, "Corpse.Decay.ELITE", 300); - setConfigPos(CONFIG_UINT32_CORPSE_DECAY_RAREELITE, "Corpse.Decay.RAREELITE", 300); + setConfigPos(CONFIG_UINT32_CORPSE_DECAY_NORMAL, "Corpse.Decay.NORMAL", 300); + setConfigPos(CONFIG_UINT32_CORPSE_DECAY_RARE, "Corpse.Decay.RARE", 900); + setConfigPos(CONFIG_UINT32_CORPSE_DECAY_ELITE, "Corpse.Decay.ELITE", 600); + setConfigPos(CONFIG_UINT32_CORPSE_DECAY_RAREELITE, "Corpse.Decay.RAREELITE", 1200); setConfigPos(CONFIG_UINT32_CORPSE_DECAY_WORLDBOSS, "Corpse.Decay.WORLDBOSS", 3600); setConfig(CONFIG_INT32_DEATH_SICKNESS_LEVEL, "Death.SicknessLevel", 11); diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index a4f3cee22..1ab5190ef 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -869,12 +869,12 @@ TalentsInspecting = 1 # Corpse.Decay.ELITE # Corpse.Decay.RAREELITE # Corpse.Decay.WORLDBOSS -# Seconds until creature corpse will decay without being looted or skinned. -# Default: 60, 300, 300, 300, 3600 +# Seconds until creature corpse will decay without being looted or skinned (not used when creature does not have loot initially) +# Default: 300, 900, 600, 1200, 3600 # # Rate.Corpse.Decay.Looted # Controls how long the creature corpse stays after it had been looted, as a multiplier of its Corpse.Decay.* config. -# Default: 0.1 +# Default: 0.0 (will use modifier /3 of the creatures spawntimesecs when 0.0) # # Rate.Creature.Normal.Damage # Rate.Creature.Elite.Elite.Damage @@ -922,12 +922,12 @@ CreatureFamilyAssistanceDelay = 1500 CreatureFamilyFleeDelay = 7000 WorldBossLevelDiff = 3 Corpse.EmptyLootShow = 1 -Corpse.Decay.NORMAL = 60 -Corpse.Decay.RARE = 300 -Corpse.Decay.ELITE = 300 -Corpse.Decay.RAREELITE = 300 +Corpse.Decay.NORMAL = 300 +Corpse.Decay.RARE = 900 +Corpse.Decay.ELITE = 600 +Corpse.Decay.RAREELITE = 1200 Corpse.Decay.WORLDBOSS = 3600 -Rate.Corpse.Decay.Looted = 0.1 +Rate.Corpse.Decay.Looted = 0.0 Rate.Creature.Normal.Damage = 1 Rate.Creature.Elite.Elite.Damage = 1 Rate.Creature.Elite.RAREELITE.Damage = 1 diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index a50f6b871..d2b3437d7 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "10457" + #define REVISION_NR "10458" #endif // __REVISION_NR_H__