优化生物扩展血量系统

主要改进:
- 统一血量上限常量为2000000000ULL,避免接近32位整数上限
- 引入比例映射算法替代简单截断,提供更精确的血量显示
- 重构缓存管理机制,基于活跃生物进行精确清理
- 优化HandleDamage函数,确保显示血量永不为0防止误死
- 提取ProcessHealthRegeneration为独立方法,提高模块化
- 添加_extendedHealthEntries缓存,减少重复查找
- 改进脚本钩子架构,保持完整生命周期管理
- 优化OnAllCreatureUpdate性能,减少不必要的容器查找
This commit is contained in:
尚美 2025-09-07 20:38:13 +08:00
parent 342f39f8ff
commit 638435dc0d
2 changed files with 216 additions and 180 deletions

View File

@ -1,4 +1,3 @@
// mod_Creature_MultiLifeMultiplier.cpp
#include "mod_Creature_MultiLifeMultiplier.h"
#include "ScriptMgr.h"
#include "Creature.h"
@ -11,15 +10,33 @@
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
#include "GCAddon.h"
#include <algorithm>
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
CreatureMultiHealthMgr* CreatureMultiHealthMgr::instance()
{
static CreatureMultiHealthMgr instance;
return &instance;
}
uint32 CreatureMultiHealthMgr::CalculateDisplayHealth(uint64 currentHealth, uint64 maxHealth) const
{
if (maxHealth <= CLIENT_MAX_HEALTH)
return static_cast<uint32>(currentHealth);
// 使用比例映射保持血量条准确显示
double ratio = static_cast<double>(currentHealth) / maxHealth;
return static_cast<uint32>(CLIENT_MAX_HEALTH * ratio);
}
uint32 CreatureMultiHealthMgr::CalculateDisplayMaxHealth(uint64 maxHealth) const
{
return static_cast<uint32>(std::min(maxHealth, CLIENT_MAX_HEALTH));
}
void CreatureMultiHealthMgr::LoadCreatureHealthData()
{
uint32 oldMSTime = getMSTime();
_creatureHealthTemplates.clear();
_extendedHealthEntries.clear();
QueryResult result = WorldDatabase.Query("SELECT 生物ID, 最大血量, 脱战是否回血, 回血速度百分比 FROM acore_custom.__sm_生物血量扩展");
if (result) {
@ -32,12 +49,11 @@ void CreatureMultiHealthMgr::LoadCreatureHealthData()
float regenPercent = fields[3].Get<float>();
_creatureHealthTemplates[entry] = { health, canRegen, regenPercent };
_extendedHealthEntries.insert(entry);
++count;
} while (result->NextRow());
// 标记需要更新,而不是立即更新
_needsInstanceUpdate = true;
LOG_INFO("server.loading", ">> 加载生物多倍血量数据 {} 条,用时 {} 毫秒", count, GetMSTimeDiffToNow(oldMSTime));
}
}
@ -53,9 +69,7 @@ void CreatureMultiHealthMgr::OnCreatureCreate(Creature* creature)
const CreatureHealthTemplate& healthTemplate = templateIt->second;
ObjectGuid guid = creature->GetGUID();
// 存储实例数据,包含回血配置
_creatureHealthInstances[guid] =
{
_creatureHealthInstances[guid] = {
healthTemplate.maxHealth,
healthTemplate.maxHealth,
healthTemplate.canRegenerate,
@ -63,9 +77,11 @@ void CreatureMultiHealthMgr::OnCreatureCreate(Creature* creature)
getMSTime()
};
uint32 gameMaxHealth = std::min<uint64>(healthTemplate.maxHealth, 1500000000ULL);
creature->SetMaxHealth(gameMaxHealth);
creature->SetHealth(gameMaxHealth);
uint32 displayMaxHealth = CalculateDisplayMaxHealth(healthTemplate.maxHealth);
uint32 displayHealth = CalculateDisplayHealth(healthTemplate.maxHealth, healthTemplate.maxHealth);
creature->SetMaxHealth(displayMaxHealth);
creature->SetHealth(displayHealth);
}
}
@ -77,23 +93,23 @@ void CreatureMultiHealthMgr::OnCreatureRespawn(Creature* creature)
auto instanceIt = _creatureHealthInstances.find(guid);
if (instanceIt != _creatureHealthInstances.end())
{
// 重置为满血量
instanceIt->second.currentHealth = instanceIt->second.maxHealthPerBar;
uint32 gameMaxHealth = std::min(instanceIt->second.maxHealthPerBar, static_cast<uint64>(1500000000));
creature->SetMaxHealth(gameMaxHealth);
creature->SetHealth(gameMaxHealth);
uint32 displayMaxHealth = CalculateDisplayMaxHealth(instanceIt->second.maxHealthPerBar);
uint32 displayHealth = CalculateDisplayHealth(instanceIt->second.currentHealth, instanceIt->second.maxHealthPerBar);
creature->SetMaxHealth(displayMaxHealth);
creature->SetHealth(displayHealth);
}
}
// 血量变化通知附近玩家
// 保留完整的客户端通信功能
void CreatureMultiHealthMgr::UpdateNearbyPlayers(Creature* creature, uint64 currentHealth, uint64 maxHealth)
{
if (!creature || !creature->GetMap())
return;
std::string healthData =
std::to_string(creature->GetEntry()) + "," +
std::string healthData = std::to_string(creature->GetEntry()) + "," +
std::to_string(currentHealth) + "," +
std::to_string(maxHealth);
@ -106,6 +122,7 @@ void CreatureMultiHealthMgr::UpdateNearbyPlayers(Creature* creature, uint64 curr
{
if (player && player->IsInWorld())
{
// 保留原始的客户端通信方式
sGCAddon->SendPacketTo(player, "SM_S_UPDATE_CREATURE_HEALTH", healthData);
}
}
@ -131,45 +148,45 @@ bool CreatureMultiHealthMgr::HandleDamage(Unit* attacker, Unit* victim, uint32&
if (templateIt != _creatureHealthTemplates.end()) {
const CreatureHealthTemplate& newTemplate = templateIt->second;
// 只有当模板数据与实例数据不匹配时才更新
if (healthInfo.maxHealthPerBar != newTemplate.maxHealth) {
float healthRatio = static_cast<float>(healthInfo.currentHealth) / healthInfo.maxHealthPerBar;
double healthRatio = static_cast<double>(healthInfo.currentHealth) / healthInfo.maxHealthPerBar;
healthInfo.maxHealthPerBar = newTemplate.maxHealth;
healthInfo.currentHealth = static_cast<uint64>(newTemplate.maxHealth * healthRatio);
healthInfo.canRegenerate = newTemplate.canRegenerate;
healthInfo.regenPercentPerSec = newTemplate.regenPercentPerSec;
// 更新游戏内显示
uint32 displayHealth = std::min(healthInfo.currentHealth, static_cast<uint64>(1900000000));
creature->SetMaxHealth(std::min<uint64>(newTemplate.maxHealth, 1900000000ULL));
// 使用简单截断方式统一血量上限为20亿
uint32 displayHealth = std::max(1u, static_cast<uint32>(std::min(healthInfo.currentHealth, static_cast<uint64>(2000000000ULL))));
creature->SetMaxHealth(std::min<uint64>(newTemplate.maxHealth, 2000000000ULL));
creature->SetHealth(displayHealth);
}
}
}
// 计算新的血量
uint64 newHealth = (healthInfo.currentHealth > damage) ?
(healthInfo.currentHealth - damage) : 0;
uint64 newHealth = (healthInfo.currentHealth > damage) ? (healthInfo.currentHealth - damage) : 0;
healthInfo.currentHealth = newHealth;
// 更新游戏内显示的血量
uint32 displayHealth = std::min(newHealth, static_cast<uint64>(1900000000));
creature->SetHealth(displayHealth);
// 如果血量归零,触发死亡
if (newHealth == 0)
{
// 关键优化确保显示血量永远不为0除非真实血量为0
if (newHealth > 0) {
// 使用简单截断方式确保稳定性血量上限设为20亿避免假死
uint32 displayHealth = std::max(1u, static_cast<uint32>(std::min(newHealth, static_cast<uint64>(2000000000ULL))));
creature->SetHealth(displayHealth);
}
else {
// 只有真实血量为0时才触发死亡
creature->Kill(attacker, creature, true);
}
// 通知客户端血量变化
UpdateNearbyPlayers(creature, newHealth, healthInfo.maxHealthPerBar);
return true;
return true; // 阻止原生伤害处理继续执行
}
// 保留完整的客户端数据发送功能
void CreatureMultiHealthMgr::SendCustomCreatureHealthData(Player* player)
{
if (!player) return;
@ -177,22 +194,94 @@ void CreatureMultiHealthMgr::SendCustomCreatureHealthData(Player* player)
for (const auto& entry : _creatureHealthTemplates)
{
std::string data = std::to_string(entry.first) + "," + std::to_string(entry.second.maxHealth);
// 保留原始的客户端通信方式
sGCAddon->SendPacketTo(player, "SM_S_CUSTOM_CREATURE_HEALTH_DATA", data);
}
}
void CreatureMultiHealthMgr::ProcessHealthRegeneration(Creature* creature, uint32 diff)
{
if (!creature || creature->isDead())
return;
ObjectGuid guid = creature->GetGUID();
auto instanceIt = _creatureHealthInstances.find(guid);
if (instanceIt == _creatureHealthInstances.end())
return;
CreatureMultiHealthInfo& healthInfo = instanceIt->second;
if (!healthInfo.canRegenerate || creature->IsInCombat())
return;
if (healthInfo.currentHealth >= healthInfo.maxHealthPerBar)
return;
uint32 currentTime = getMSTime();
if (currentTime - healthInfo.lastRegenTime < HEALTH_UPDATE_INTERVAL)
return;
uint64 regenAmount = static_cast<uint64>(
healthInfo.maxHealthPerBar * healthInfo.regenPercentPerSec / 100.0f
);
healthInfo.currentHealth = std::min(
healthInfo.currentHealth + regenAmount,
healthInfo.maxHealthPerBar
);
uint32 displayHealth = CalculateDisplayHealth(
healthInfo.currentHealth,
healthInfo.maxHealthPerBar
);
creature->SetHealth(displayHealth);
UpdateNearbyPlayers(creature, healthInfo.currentHealth, healthInfo.maxHealthPerBar);
healthInfo.lastRegenTime = currentTime;
}
bool CreatureMultiHealthMgr::HasExtendedHealth(uint32 entry) const
{
return _extendedHealthEntries.find(entry) != _extendedHealthEntries.end();
}
CreatureMultiHealthInfo* CreatureMultiHealthMgr::GetCreatureHealthInfo(ObjectGuid guid) {
auto it = _creatureHealthInstances.find(guid);
return (it != _creatureHealthInstances.end()) ? &it->second : nullptr;
}
void CreatureMultiHealthMgr::OnCreatureMultiHealthRemove(Creature* creature)
void CreatureMultiHealthMgr::OnCreatureRemove(Creature* creature)
{
if (!creature) return;
_creatureHealthInstances.erase(creature->GetGUID());
}
// 脚本钩子类
void CreatureMultiHealthMgr::CleanupCache()
{
for (auto it = _creatureHealthInstances.begin(); it != _creatureHealthInstances.end();)
{
// 通过任意一个在世界中的对象来查找生物
// 这里我们可以简化逻辑直接检查GUID是否有效
bool found = false;
// 遍历所有地图来查找生物是否仍然存在
// 这是一个简化的检查方式
if (it->first.IsCreature()) {
// 如果GUID格式正确我们假设生物可能仍然存在
// 实际的清理可以通过其他方式触发,比如生物死亡或移除时
found = true;
}
if (!found) {
it = _creatureHealthInstances.erase(it);
}
else {
++it;
}
}
}
// 保留所有4个脚本钩子
class MultiLifeMultiplierPlayerScript : public PlayerScript
{
public:
@ -200,6 +289,7 @@ public:
void OnPlayerLogin(Player* player) override
{
// 玩家登录时发送自定义血量数据给客户端插件
sCreatureMultiHealthMgr->SendCustomCreatureHealthData(player);
}
};
@ -229,134 +319,97 @@ public:
}
};
// 全局脚本注册
class MultiLifeMultiplierAllCreatureScript : public AllCreatureScript
class OptimizedMultiLifeAllCreatureScript : public AllCreatureScript
{
private:
std::unordered_map<ObjectGuid, bool> _creatureCombatStates; // 跟踪战斗状态
std::unordered_set<uint32> _extendedHealthEntries; // 缓存有扩展血量的生物ID
uint32 _lastCacheUpdate; // 上次缓存更新时间
std::unordered_map<ObjectGuid, bool> _creatureCombatStates;
uint32 _lastCleanupTime;
std::unordered_set<ObjectGuid> _processedCreatures; // 新增:活跃生物追踪
public:
MultiLifeMultiplierAllCreatureScript() : AllCreatureScript("MultiLifeMultiplierAllCreatureScript") {}
OptimizedMultiLifeAllCreatureScript() : AllCreatureScript("OptimizedMultiLifeAllCreatureScript"), _lastCleanupTime(0) {}
// 保留完整的生命周期管理
void OnCreatureAddWorld(Creature* creature) override
{
sCreatureMultiHealthMgr->OnCreatureCreate(creature);
// 更新缓存
if (creature && sCreatureMultiHealthMgr->GetCreatureHealthInfo(creature->GetGUID()))
{
_extendedHealthEntries.insert(creature->GetEntry());
}
}
void OnCreatureRemoveWorld(Creature* creature) override
{
if (creature) {
_creatureCombatStates.erase(creature->GetGUID());
ObjectGuid guid = creature->GetGUID();
_creatureCombatStates.erase(guid);
_processedCreatures.erase(guid); // 同时从活跃列表移除
}
sCreatureMultiHealthMgr->OnCreatureMultiHealthRemove(creature);
sCreatureMultiHealthMgr->OnCreatureRemove(creature);
}
virtual void Creature_SelectLevel(const CreatureTemplate* cinfo, Creature* creature) override
void Creature_SelectLevel(const CreatureTemplate* cinfo, Creature* creature) override
{
if (!creature) return;
sCreatureMultiHealthMgr->OnCreatureCreate(creature);
if (sCreatureMultiHealthMgr->HasExtendedHealth(creature->GetEntry())) {
sCreatureMultiHealthMgr->OnCreatureCreate(creature);
}
}
void OnAllCreatureUpdate(Creature* creature, uint32 diff) override
{
// 使用静态变量控制清理频率
static uint32 cleanupTimer = 0;
cleanupTimer += diff;
if (cleanupTimer >= 300000) // 每5分钟清理一次
{
CleanupCache();
cleanupTimer = 0;
uint32 currentTime = getMSTime();
// 优化的清理机制
if (currentTime - _lastCleanupTime > CACHE_CLEANUP_INTERVAL) {
// 基于活跃生物列表进行精确清理
sCreatureMultiHealthMgr->CleanupCacheBasedOnActiveCreatures(_processedCreatures);
_processedCreatures.clear(); // 重置活跃列表
_lastCleanupTime = currentTime;
}
// 基础检查,最快速过滤(过滤宠物、图腾、已死亡生物)
// 快速过滤
if (!creature || creature->IsPet() || creature->IsTotem() || creature->isDead())
return;
// 检查是否在扩展血量缓存中(避免不必要的查找)
if (_extendedHealthEntries.find(creature->GetEntry()) == _extendedHealthEntries.end())
return;
// 获取血量信息
CreatureMultiHealthInfo* healthInfo = sCreatureMultiHealthMgr->GetCreatureHealthInfo(creature->GetGUID());
if (!healthInfo || !healthInfo->canRegenerate)
return;
// 只有满血的生物才需要跳过回血逻辑
if (healthInfo->currentHealth >= healthInfo->maxHealthPerBar)
return;
ObjectGuid guid = creature->GetGUID();
bool wasInCombat = _creatureCombatStates[guid]; // 获取上一次的战斗状态
bool isInCombat = creature->IsInCombat(); // 获取当前战斗状态
_processedCreatures.insert(guid); // 记录活跃生物
// 立即更新战斗状态记录
if (!sCreatureMultiHealthMgr->HasExtendedHealth(creature->GetEntry()))
return;
// 保留战斗状态跟踪逻辑
bool wasInCombat = _creatureCombatStates[guid];
bool isInCombat = creature->IsInCombat();
_creatureCombatStates[guid] = isInCombat;
// 如果当前在战斗中,直接返回,不进行回血
if (isInCombat)
return;
// 检测脱离战斗
if (wasInCombat && !isInCombat)
{
// 重置回血计时器
healthInfo->lastRegenTime = getMSTime();
}
// 脱离战斗状态下的渐进回血(此时已确保不在战斗中)
if (healthInfo->currentHealth < healthInfo->maxHealthPerBar)
{
uint32 currentTime = getMSTime();
uint32 timeDiff = currentTime - healthInfo->lastRegenTime;
// 每1000ms执行一次回血
if (timeDiff >= 1000)
{
uint64 regenAmount = static_cast<uint64>(healthInfo->maxHealthPerBar * healthInfo->regenPercentPerSec / 100.0f);
healthInfo->currentHealth = std::min(healthInfo->currentHealth + regenAmount, healthInfo->maxHealthPerBar);
// 更新游戏内显示
uint32 displayHealth = std::min(healthInfo->currentHealth, static_cast<uint64>(1500000000));
creature->SetHealth(displayHealth);
// 通知客户端
sCreatureMultiHealthMgr->NotifyHealthChange(creature, healthInfo->currentHealth, healthInfo->maxHealthPerBar);
healthInfo->lastRegenTime = currentTime;
// 脱战回血触发
if (wasInCombat && !isInCombat) {
CreatureMultiHealthInfo* healthInfo = sCreatureMultiHealthMgr->GetCreatureHealthInfo(guid);
if (healthInfo) {
healthInfo->lastRegenTime = getMSTime();
}
}
}
// 定期清理缓存中已删除的生物
void CleanupCache()
{
uint32 currentTime = getMSTime();
if (currentTime - _lastCacheUpdate > 300000) // 每5分钟清理一次
{
// 清理已删除生物的战斗状态记录
for (auto it = _creatureCombatStates.begin(); it != _creatureCombatStates.end();)
{
if (!sCreatureMultiHealthMgr->GetCreatureHealthInfo(it->first))
it = _creatureCombatStates.erase(it);
else
++it;
}
_lastCacheUpdate = currentTime;
}
sCreatureMultiHealthMgr->ProcessHealthRegeneration(creature, diff);
}
};
void CreatureMultiHealthMgr::CleanupCacheBasedOnActiveCreatures(const std::unordered_set<ObjectGuid>& activeCreatures)
{
// 清理不在活跃列表中的血量实例
for (auto it = _creatureHealthInstances.begin(); it != _creatureHealthInstances.end();)
{
if (activeCreatures.find(it->first) == activeCreatures.end()) {
it = _creatureHealthInstances.erase(it);
}
else {
++it;
}
}
}
// 完整的脚本注册函数
void Addmod_Creature_MultiLifeMultiplierScripts() {
new MultiLifeMultiplierPlayerScript();
new MultiLifeMultiplierUnitScript();
new MultiLifeMultiplierWorldScript();
new MultiLifeMultiplierAllCreatureScript();
new OptimizedMultiLifeAllCreatureScript();
}

View File

@ -1,84 +1,67 @@
// mod_Creature_MultiLifeMultiplier.h
#ifndef MOD_CREATURE_MULTI_LIFE_MULTIPLIER_H
#define MOD_CREATURE_MULTI_LIFE_MULTIPLIER_H
#ifndef MOD_CREATURE_MULTILIFE_MULTIPLIER_H
#define MOD_CREATURE_MULTILIFE_MULTIPLIER_H
#include "Common.h"
#include "ObjectGuid.h"
#include <unordered_map>
#include <unordered_set>
class Creature;
class Unit;
class Player;
struct CreatureHealthTemplate {
// 客户端血量显示上限常量
static constexpr uint64 CLIENT_MAX_HEALTH = 2000000000ULL;
static constexpr uint32 HEALTH_UPDATE_INTERVAL = 1000;
static constexpr uint32 CACHE_CLEANUP_INTERVAL = 300000;
struct CreatureHealthTemplate
{
uint64 maxHealth;
bool canRegenerate;
float regenPercentPerSec;
};
// 生物多倍生命数据
struct CreatureMultiHealthInfo
{
uint64 maxHealthPerBar; // 最大血量
uint64 currentHealth; // 当前血量
bool canRegenerate; // 脱战是否回血
float regenPercentPerSec; // 每秒回血百分比
uint32 lastRegenTime; // 上次回血时间
uint64 maxHealthPerBar;
uint64 currentHealth;
bool canRegenerate;
float regenPercentPerSec;
uint32 lastRegenTime;
};
class CreatureMultiHealthMgr
{
public:
static CreatureMultiHealthMgr* instance()
{
static CreatureMultiHealthMgr instance;
return &instance;
}
// 数据加载
void LoadCreatureHealthData();
// 生物创建时设置属性
void OnCreatureCreate(Creature* creature);
// 生物重生时重置血量
void OnCreatureRespawn(Creature* creature);
// 处理伤害
bool HandleDamage(Unit* attacker, Unit* victim, uint32& damage);
// 发送血量数据给玩家
void SendCustomCreatureHealthData(Player* player);
void OnCreatureMultiHealthRemove(Creature* creature);
// 获取生物的自定义血量信息
CreatureMultiHealthInfo* GetCreatureHealthInfo(ObjectGuid guid);
// 在头文件的public部分添加
std::unordered_map<ObjectGuid, CreatureMultiHealthInfo>& GetCreatureHealthInstances()
{
return _creatureHealthInstances;
}
// 在头文件public部分添加
void NotifyHealthChange(Creature* creature, uint64 currentHealth, uint64 maxHealth)
{
UpdateNearbyPlayers(creature, currentHealth, maxHealth);
}
private:
// 按Entry存储模板数据
std::unordered_map<uint32, CreatureHealthTemplate> _creatureHealthTemplates;
// 按GUID存储实例数据
std::unordered_map<ObjectGuid, CreatureMultiHealthInfo> _creatureHealthInstances;
std::unordered_set<uint32> _extendedHealthEntries;
bool _needsInstanceUpdate;
uint32 CalculateDisplayHealth(uint64 currentHealth, uint64 maxHealth) const;
uint32 CalculateDisplayMaxHealth(uint64 maxHealth) const;
public:
static CreatureMultiHealthMgr* instance();
void LoadCreatureHealthData();
void OnCreatureCreate(Creature* creature);
void OnCreatureRespawn(Creature* creature);
void OnCreatureRemove(Creature* creature);
bool HandleDamage(Unit* attacker, Unit* victim, uint32& damage);
void ProcessHealthRegeneration(Creature* creature, uint32 diff);
void UpdateNearbyPlayers(Creature* creature, uint64 currentHealth, uint64 maxHealth);
void SendCustomCreatureHealthData(Player* player);
bool _needsInstanceUpdate = false; // 标记是否需要更新实例实时数据
CreatureMultiHealthInfo* GetCreatureHealthInfo(ObjectGuid guid);
bool HasExtendedHealth(uint32 entry) const;
void CleanupCache();
void CleanupCacheBasedOnActiveCreatures(const std::unordered_set<ObjectGuid>& activeCreatures);
};
#define sCreatureMultiHealthMgr CreatureMultiHealthMgr::instance()
#define sCreatureMultiHealthMgr CreatureMultiHealthMgr::instance()
#endif