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:
Andrew
2025-02-12 15:06:42 -03:00
committed by GitHub
parent 5421f13c13
commit 16fa25b7a9
8 changed files with 118 additions and 2 deletions

View File

@@ -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();)