feat(Core/Maps): Implement dynamic respawn rates (#21417)
Co-authored-by: r00ty-tc <r00ty-tc@users.noreply.github.com>
Co-authored-by: Jelle Meeus <sogladev@gmail.com>
Co-authored-by Treeston
This is a partial cherry-pick of: 59db2eeea0
This commit is contained in:
parent
5421f13c13
commit
16fa25b7a9
@ -67,6 +67,7 @@
|
||||
# PLAYER DUMP
|
||||
# CUSTOM
|
||||
# DEBUG
|
||||
# DYNAMIC RESPAWN SETTINGS
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
@ -4467,6 +4468,46 @@ Debug.Arena = 0
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
###################################################################################################
|
||||
# DYNAMIC RESPAWN SETTINGS
|
||||
#
|
||||
#
|
||||
# Respawn.DynamicRateCreature
|
||||
# Description: The rate at which the respawn time is adjusted for high player counts in a zone (for creatures).
|
||||
# Up to this number of players, the respawn rate is unchanged.
|
||||
# Does not affect instanced creatures, bosses or quest givers.
|
||||
# At double this number in players, you get twice as many respawns, at three times this number, three times the respawns, and so forth.
|
||||
# Default: 1 (Disabled)
|
||||
|
||||
Respawn.DynamicRateCreature = 1
|
||||
|
||||
#
|
||||
# Respawn.DynamicMinimumCreature
|
||||
# Description: The minimum respawn time for a creature under dynamic scaling.
|
||||
# Default: 10 - (10 seconds)
|
||||
|
||||
Respawn.DynamicMinimumCreature = 10
|
||||
|
||||
#
|
||||
# Respawn.DynamicRateGameObject
|
||||
# Description: The rate at which the respawn time is adjusted for high player counts in a zone (for gameobjects).
|
||||
# Up to this number of players, the respawn rate is unchanged.
|
||||
# Does not affect instanced objects or quest givers.
|
||||
# At double this number in players, you get twice as many respawns, at three times this number, three times the respawns, and so forth.
|
||||
# Default: 1 (Disabled)
|
||||
|
||||
Respawn.DynamicRateGameObject = 1
|
||||
|
||||
#
|
||||
# Respawn.DynamicMinimumGameObject
|
||||
# Description: The minimum respawn time for a gameobject under dynamic scaling.
|
||||
# Default: 10 - (10 seconds)
|
||||
|
||||
Respawn.DynamicMinimumGameObject = 10
|
||||
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
###################################################################################################
|
||||
# #
|
||||
# GAME SETTINGS END #
|
||||
|
||||
@ -1974,6 +1974,7 @@ void Creature::setDeathState(DeathState state, bool despawn)
|
||||
if (state == DeathState::JustDied)
|
||||
{
|
||||
m_corpseRemoveTime = GameTime::GetGameTime().count() + m_corpseDelay;
|
||||
GetMap()->ApplyDynamicModeRespawnScaling(this, m_respawnDelay);
|
||||
m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay + m_corpseDelay;
|
||||
|
||||
// always save boss respawn time at death to prevent crash cheating
|
||||
|
||||
@ -897,6 +897,7 @@ void GameObject::Update(uint32 diff)
|
||||
return;
|
||||
}
|
||||
|
||||
GetMap()->ApplyDynamicModeRespawnScaling(this, m_respawnDelayTime);
|
||||
m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelayTime;
|
||||
|
||||
// if option not set then object will be saved at grid unload
|
||||
|
||||
@ -1245,9 +1245,7 @@ void Player::UpdateArea(uint32 newArea)
|
||||
void Player::UpdateZone(uint32 newZone, uint32 newArea)
|
||||
{
|
||||
if (!newZone)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_zoneUpdateId != newZone)
|
||||
{
|
||||
@ -1264,6 +1262,8 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
|
||||
guild->UpdateMemberData(this, GUILD_MEMBER_DATA_ZONEID, newZone);
|
||||
}
|
||||
|
||||
GetMap()->UpdatePlayerZoneStats(m_zoneUpdateId, newZone);
|
||||
|
||||
// group update
|
||||
if (GetGroup())
|
||||
SetGroupUpdateFlag(GROUP_UPDATE_FULL);
|
||||
|
||||
@ -59,6 +59,8 @@ u_map_magic MapLiquidMagic = { {'M', 'L', 'I', 'Q'} };
|
||||
static uint16 const holetab_h[4] = { 0x1111, 0x2222, 0x4444, 0x8888 };
|
||||
static uint16 const holetab_v[4] = { 0x000F, 0x00F0, 0x0F00, 0xF000 };
|
||||
|
||||
#define MAP_INVALID_ZONE 0xFFFFFFFF
|
||||
|
||||
ZoneDynamicInfo::ZoneDynamicInfo() : MusicId(0), WeatherId(WEATHER_STATE_FINE),
|
||||
WeatherGrade(0.0f), OverrideLightId(0), LightFadeInTime(0) { }
|
||||
|
||||
@ -252,6 +254,8 @@ Map::Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) :
|
||||
}
|
||||
}
|
||||
|
||||
_zonePlayerCountMap.clear();
|
||||
|
||||
//lets initialize visibility distance for map
|
||||
Map::InitVisibilityDistance();
|
||||
|
||||
@ -736,6 +740,23 @@ void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Acore::Objec
|
||||
}
|
||||
}
|
||||
|
||||
void Map::UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone)
|
||||
{
|
||||
// Nothing to do if no change
|
||||
if (oldZone == newZone)
|
||||
return;
|
||||
|
||||
if (oldZone != MAP_INVALID_ZONE)
|
||||
{
|
||||
uint32& oldZoneCount = _zonePlayerCountMap[oldZone];
|
||||
if (!oldZoneCount)
|
||||
LOG_ERROR("maps", "A player left zone {} (went to {}) - but there were no players in the zone!", oldZone, newZone);
|
||||
else
|
||||
--oldZoneCount;
|
||||
}
|
||||
++_zonePlayerCountMap[newZone];
|
||||
}
|
||||
|
||||
void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/)
|
||||
{
|
||||
if (t_diff)
|
||||
@ -913,6 +934,8 @@ struct ResetNotifier
|
||||
|
||||
void Map::RemovePlayerFromMap(Player* player, bool remove)
|
||||
{
|
||||
// Before leaving map, update zone/area for stats
|
||||
player->UpdateZone(MAP_INVALID_ZONE, 0);
|
||||
player->getHostileRefMgr().deleteReferences(true); // pussywizard: multithreading crashfix
|
||||
|
||||
bool inWorld = player->IsInWorld();
|
||||
@ -2635,6 +2658,40 @@ void Map::SendObjectUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
void Map::ApplyDynamicModeRespawnScaling(WorldObject const* obj, uint32& respawnDelay) const
|
||||
{
|
||||
ASSERT(obj->GetMap() == this);
|
||||
|
||||
float rate = sWorld->getFloatConfig(obj->IsGameObject() ? CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT : CONFIG_RESPAWN_DYNAMICRATE_CREATURE);
|
||||
|
||||
if (rate == 1.0f)
|
||||
return;
|
||||
|
||||
// No instanced maps (dungeons, battlegrounds, arenas etc.)
|
||||
if (obj->GetMap()->Instanceable())
|
||||
return;
|
||||
|
||||
// No quest givers or world bosses
|
||||
if (Creature const* creature = obj->ToCreature())
|
||||
if (creature->IsQuestGiver() || creature->isWorldBoss())
|
||||
return;
|
||||
|
||||
auto it = _zonePlayerCountMap.find(obj->GetZoneId());
|
||||
if (it == _zonePlayerCountMap.end())
|
||||
return;
|
||||
uint32 const playerCount = it->second;
|
||||
if (!playerCount)
|
||||
return;
|
||||
double const adjustFactor = rate / playerCount;
|
||||
if (adjustFactor >= 1.0) // nothing to do here
|
||||
return;
|
||||
uint32 const timeMinimum = sWorld->getIntConfig(obj->IsGameObject() ? CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT : CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE);
|
||||
if (respawnDelay <= timeMinimum)
|
||||
return;
|
||||
|
||||
respawnDelay = std::max<uint32>(std::ceil(respawnDelay * adjustFactor), timeMinimum);
|
||||
}
|
||||
|
||||
void Map::DelayedUpdate(const uint32 t_diff)
|
||||
{
|
||||
for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();)
|
||||
|
||||
@ -594,6 +594,9 @@ public:
|
||||
void DeleteRespawnTimes();
|
||||
[[nodiscard]] time_t GetInstanceResetPeriod() const { return _instanceResetPeriod; }
|
||||
|
||||
void UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone);
|
||||
void ApplyDynamicModeRespawnScaling(WorldObject const* obj, uint32& respawnDelay) const;
|
||||
|
||||
TaskScheduler _creatureRespawnScheduler;
|
||||
|
||||
void ScheduleCreatureRespawn(ObjectGuid /*creatureGuid*/, Milliseconds /*respawnTimer*/, Position pos = Position());
|
||||
@ -780,6 +783,8 @@ private:
|
||||
std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _creatureRespawnTimes;
|
||||
std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _goRespawnTimes;
|
||||
|
||||
std::unordered_map<uint32, uint32> _zonePlayerCountMap;
|
||||
|
||||
ZoneDynamicInfoMap _zoneDynamicInfo;
|
||||
uint32 _defaultLight;
|
||||
|
||||
|
||||
@ -201,6 +201,8 @@ enum WorldFloatConfigs
|
||||
CONFIG_ARENA_WIN_RATING_MODIFIER_2,
|
||||
CONFIG_ARENA_LOSE_RATING_MODIFIER,
|
||||
CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER,
|
||||
CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT,
|
||||
CONFIG_RESPAWN_DYNAMICRATE_CREATURE,
|
||||
FLOAT_CONFIG_VALUE_COUNT
|
||||
};
|
||||
|
||||
@ -420,6 +422,8 @@ enum WorldIntConfigs
|
||||
CONFIG_AUCTIONHOUSE_WORKERTHREADS,
|
||||
CONFIG_SPELL_QUEUE_WINDOW,
|
||||
CONFIG_SUNSREACH_COUNTER_MAX,
|
||||
CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT,
|
||||
CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE,
|
||||
INT_CONFIG_VALUE_COUNT
|
||||
};
|
||||
|
||||
|
||||
@ -1295,6 +1295,13 @@ void World::LoadConfigSettings(bool reload)
|
||||
|
||||
_int_configs[CONFIG_DAILY_RBG_MIN_LEVEL_AP_REWARD] = sConfigMgr->GetOption<uint32>("DailyRBGArenaPoints.MinLevel", 71);
|
||||
|
||||
// Respawn
|
||||
_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE] = sConfigMgr->GetOption<float>("Respawn.DynamicRateCreature", 1.0f);
|
||||
_int_configs[CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE] = sConfigMgr->GetOption<int32>("Respawn.DynamicMinimumCreature", 10);
|
||||
|
||||
_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT] = sConfigMgr->GetOption<float>("Respawn.DynamicRateGameObject", 1.0f);
|
||||
_int_configs[CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT] = sConfigMgr->GetOption<int32>("Respawn.DynamicMinimumGameObject", 10);
|
||||
|
||||
///- Read the "Data" directory from the config file
|
||||
std::string dataPath = sConfigMgr->GetOption<std::string>("DataDir", "./");
|
||||
if (dataPath.empty() || (dataPath.at(dataPath.length() - 1) != '/' && dataPath.at(dataPath.length() - 1) != '\\'))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user