[硬核模式]优化:

1、player类BuildEnumData函数优化发送方案,不在需要在characters表中加入“硬核模式”字段,而是JOIN方式挂载到自定义表“角色硬核模式”的“角色编号”字段
2、设置死亡后记录详细日志信息
3、增加新的HardcoreStatus模式,辨别各种模式 0:从未选择过硬核模式  1:正在进行硬核模式  2:硬核模式中死亡  3:已完成硬核模式
This commit is contained in:
尚美 2025-10-04 18:11:58 +08:00
parent 22f07eab1d
commit 78d665b15a
7 changed files with 1208 additions and 936 deletions

View File

@ -14,8 +14,8 @@ HardcoreDatabase* HardcoreDatabase::instance()
bool HardcoreDatabase::LoadHardcoreData(uint32 playerGuid, HardcoreData& data)
{
std::string query = Acore::StringFormat("SELECT 角色编号, 状态, 难度类型编号, 启用时间, 死亡时间, 最终等级, 死亡原因, 击杀者编号,奖励已领取 FROM 角色硬核模式 WHERE 角色编号 = {}", playerGuid);
std::string query = Acore::StringFormat("SELECT 角色编号, 状态, 难度类型编号, 启用时间, 死亡时间, 最终等级, 状态信息, 击杀者编号,是否已领取奖励 FROM 角色硬核模式 WHERE 角色编号 = {}", playerGuid);
QueryResult result = CharacterDatabase.Query(query);
if (!result)
@ -30,7 +30,7 @@ bool HardcoreDatabase::LoadHardcoreData(uint32 playerGuid, HardcoreData& data)
data.finalLevel = fields[5].Get<uint8>();
data.deathCause = fields[6].Get<std::string>();
data.killerGuid = fields[7].Get<uint32>();
data.rewardReceived = fields[8].Get<bool>(); // 奖励领取状态
data.rewardReceived = fields[8].Get<bool>(); // 加载奖励领取状态
return true;
}
@ -40,8 +40,7 @@ void HardcoreDatabase::SaveHardcoreData(const HardcoreData& data)
std::string escapedCause = data.deathCause;
CharacterDatabase.EscapeString(escapedCause);
std::string query = Acore::StringFormat(
"REPLACE INTO 角色硬核模式 (角色编号, 状态, 难度类型编号, 启用时间, 死亡时间, 最终等级, 死亡原因, 击杀者编号) VALUES ({}, {}, {}, {}, {}, {}, '{}', {})",
std::string query = Acore::StringFormat("REPLACE INTO 角色硬核模式 (角色编号, 状态, 难度类型编号, 启用时间, 死亡时间, 最终等级, 状态信息, 击杀者编号,是否已领取奖励) VALUES ({}, {}, {}, {}, {}, {}, '{}', {}, {})",
data.guid,
static_cast<uint8>(data.status),
data.difficultyTypeId,
@ -50,7 +49,7 @@ void HardcoreDatabase::SaveHardcoreData(const HardcoreData& data)
data.finalLevel,
escapedCause,
data.killerGuid,
data.rewardReceived ? 1 : 0 // 新增:保存奖励领取状态
data.rewardReceived ? 1 : 0 // 保存奖励领取状态
);
CharacterDatabase.Execute(query);

View File

@ -6,12 +6,13 @@
#include "Define.h"
#include "QueryResult.h"
// 前向声明,避免循环包含
// 前向声明,避免循环包含
enum HardcoreStatus : uint8
{
HC_STATUS_NORMAL = 0, // 未启用硬核模式 - 玩家处于正常游戏模式
HC_STATUS_HARDCORE= 1, // 已启用硬核模式 - 玩家处于硬核模式,死亡将是永久的
HC_STATUS_DEAD = 2 // 硬核模式中已死亡 - 玩家在硬核模式中死亡,无法复活
HC_STATUS_NORMAL = 0, // 从未选择过硬核模式
HC_STATUS_HARDCORE = 1, // 正在进行硬核模式
HC_STATUS_DEAD = 2, // 硬核模式中死亡
HC_STATUS_COMPLETED = 3 // 已完成硬核模式
};
struct HardcoreData

File diff suppressed because it is too large Load Diff

View File

@ -17,14 +17,14 @@ struct HardcoreModeType
std::string subtitle; // 子标题
std::string description; // 描述文本
std::string backtexture; // 背景纹理
std::string HardcoreLogo; // 难度标志图
std::string RewBoxTexture; // 奖励宝箱纹理
bool permanentDeath;
float expModifier;
uint32 groupRestrictions; // 组队限制
bool questRestrictions;
bool auctionRestrictions;
uint32 RewID; // 奖励ID 关联【_模板_奖励】表
std::string HardcoreLogo; // 难度标志图
std::string RewBoxTexture; // 奖励宝箱纹理
bool permanentDeath; // 永久死亡
float expModifier; // 经验倍率
uint32 groupRestrictions; // 组队限制
bool questRestrictions; // 任务限制
bool auctionRestrictions; // 拍卖行限制
uint32 RewID; // 奖励ID 关联【_模板_奖励】表
};
class HardcoreMode
@ -32,15 +32,14 @@ class HardcoreMode
public:
static HardcoreMode* instance();
bool IsHardcorePlayer(Player* player);
bool IsHardcorePlayer(Player* player);// 玩家是否开启了硬核模式
HardcoreStatus GetHardcoreStatus(Player* player);
HardcoreStatus GetHardcoreStatusByGuid(uint32 guid);
void EnableHardcoreMode(Player* player, uint32 difficultyTypeId);
void EnableHardcoreModeForOfflineCharacter(uint32 guid);
void EnableHardcoreModeForOfflineCharacter(uint32 guid); // 命令启用硬核模式
void DisableHardcoreModeForOfflineCharacter(uint32 guid);
void SetPlayerDead(Player* player, const std::string& cause, Unit* killer = nullptr);
bool HasPermanentDeath(Player* player); // 检查玩家的难度类型是否有永久死亡
bool CanResurrect(Player* player);
bool CanGroupWith(Player* player, Player* target);
void LoadHardcoreData(Player* player);
void SaveHardcoreData(const HardcoreData& data);
void SetPlayerDifficultyType(Player* player, uint32 difficultyTypeId); // 当玩家选择特定难度时需要将难度类型ID存储到玩家的硬核数据中
@ -72,6 +71,7 @@ public:
void GiveHardcoreReward(Player* player);
bool HasReceivedHardcoreReward(Player* player);
void MarkHardcoreRewardAsGiven(Player* player);
void SetPlayerHardcoreCompleted(uint32 guid); // 完成状态设置
private:
std::unordered_map<uint32, HardcoreData> _hardcoreData;
@ -86,56 +86,59 @@ private:
class HardcorePlayerScript : public PlayerScript
{
public:
HardcorePlayerScript() : PlayerScript("HardcorePlayerScript") { }
HardcorePlayerScript() : PlayerScript("HardcorePlayerScript") {}
void OnPlayerFirstLogin(Player* player) override; // 当角色创建时调用
void OnPlayerLogin(Player* player) override;
void OnPlayerLogout(Player* player) override;
bool OnPlayerCanResurrect(Player* player) override;
void OnPlayerJustDied(Player* player) override;
void OnPlayerPVPKill(Player* killer, Player* killed) override;
void OnPlayerKilledByCreature(Creature* killer, Player* killed) override;
void OnPlayerReleasedGhost(Player* player) override;
void OnPlayerUpdate(Player* player, uint32 p_time) override;
bool OnPlayerCanGroupInvite(Player* player, std::string& membername) override; // 限制组队行为
bool OnPlayerCanGroupAccept(Player* player, Group* group) override; // 限制接受组队行为
bool OnPlayerCanPlaceAuctionBid(Player* /*player*/, AuctionEntry* /*auction*/)override; // 拍卖行交易
bool OnPlayerCanGroupInvite(Player* player, std::string& membername) override; // 限制组队行为
bool OnPlayerCanGroupAccept(Player* player, Group* group) override; // 限制接受组队行为
bool OnPlayerCanPlaceAuctionBid(Player* /*player*/, AuctionEntry* /*auction*/)override; // 拍卖行交易
bool OnPlayerBeforeQuestComplete(Player* player, uint32 quest_id) override;
void OnPlayerGiveXP(Player* /*player*/, uint32& /*amount*/, Unit* /*victim*/, uint8 /*xpSource*/) override; // 玩家获得经验时调用
void OnPlayerLevelChanged(Player* /*player*/, uint8 /*oldlevel*/) override; // 玩家等级发生变化后调用
void CheckGroupLevelRestrictions(Player* player); // 检查组队限制
void OnPlayerLevelChanged(Player* /*player*/, uint8 /*oldlevel*/) override; // 玩家等级发生变化后调用
// 限制玩家状态
void ApplyHardcoreRestrictions(Player* player);
// 启动5秒定时器
void StartHardcoreReminder(Player* player);
void CheckGroupLevelRestrictions(Player* player); // 检查组队限制
void ApplyHardcoreRestrictions(Player* player); // 限制玩家状态
void StartHardcoreReminder(Player* player); // 启动5秒定时器
private:
TaskScheduler _scheduler;
std::unordered_set<uint32> _reminderPlayers; // 需要提醒的玩家GUID
std::unordered_set<uint32> _reminderPlayers; // 需要提醒的玩家GUID
std::unordered_map<uint32, uint64> _reminderStartTime; // 记录每个玩家的提醒开始时间
std::unordered_map<uint32, uint32> _reminderCount; // 玩家GUID -> 提醒计数
std::unordered_set<uint32> _processedDeaths; // 记录已处理的死亡
std::string GetMapName(Player* player);
std::string GetAreaName(Player* player);
std::string GetCreatureRankName(uint32 rank);
std::string FormatPosition(float x, float y, float z);
std::string FormatPvPDeathMessage(Player* killer, Player* killed);
std::string FormatCreatureDeathMessage(Creature* killer, Player* killed);
std::string GetEnvironmentalDeathMessage(Player* player);
void SendDeathMessage(Player* player, const std::string& message);
};
class HardcoreWorldScript : public WorldScript
{
public:
HardcoreWorldScript() : WorldScript("HardcoreWorldScript") { }
HardcoreWorldScript() : WorldScript("HardcoreWorldScript") {}
void OnAfterConfigLoad(bool reload) override;
void OnStartup() override;
};
// 新增ServerScript 用于包处理
// 包处理
class HardcoreServerScript : public ServerScript
{
public:
HardcoreServerScript() : ServerScript("HardcoreServerScript") { }
HardcoreServerScript() : ServerScript("HardcoreServerScript") {}
bool CanPacketReceive(WorldSession* session, WorldPacket& packet) override;
};
@ -147,19 +150,13 @@ public:
Acore::ChatCommands::ChatCommandTable GetCommands() const override;
private:
static bool HandleHardcoreEnableCommand(ChatHandler* handler);
static bool HandleHardcoreEnableCommand(ChatHandler* handler); // 启用硬核模式
static bool HandleHardcoreStatusCommand(ChatHandler* handler, Optional<Acore::ChatCommands::PlayerIdentifier> target);
static bool HandleHardcoreDisableCommand(ChatHandler* handler, Optional<Acore::ChatCommands::PlayerIdentifier> target);
static bool HandleHardcoreReviveCommand(ChatHandler* handler, Optional<Acore::ChatCommands::PlayerIdentifier> target);
// 新增:替代操作码的命令
static bool HandleHardcoreToggleCommand(ChatHandler* handler, Optional<std::string> characterName);
// 移除硬核模式
static bool HandleHardcoreDelCommand(ChatHandler* handler, Optional<std::string> characterName);
static bool HandleHardcoreListCommand(ChatHandler* handler);
static bool HandleHardcoreDisableCommand(ChatHandler* handler, Optional<Acore::ChatCommands::PlayerIdentifier> target); // 移除自己的硬核模式
static bool HandleHardcoreReviveCommand(ChatHandler* handler, Optional<Acore::ChatCommands::PlayerIdentifier> target); // 复活硬核模式玩家
static bool HandleHardcoreToggleCommand(ChatHandler* handler, Optional<std::string> characterName); // 切换命令处理器
static bool HandleHardcoreDelCommand(ChatHandler* handler, Optional<std::string> characterName); // 移除硬核模式
static bool HandleHardcoreListCommand(ChatHandler* handler);// 查看硬核模式列表
};
#endif

View File

@ -40,15 +40,39 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_GUID_BY_NAME_FILTER, "SELECT guid, name FROM characters WHERE name LIKE CONCAT('%%', ?, '%%')", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_BANINFO_LIST, "SELECT bandate, unbandate, bannedby, banreason FROM character_banned WHERE guid = ? ORDER BY unbandate", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_BANNED_NAME, "SELECT characters.name FROM characters, character_banned WHERE character_banned.guid = ? AND character_banned.guid = characters.guid", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, "
"gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.extra_flags, c.硬核模式 "
"FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, "
"c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, "
"cb.guid, c.extra_flags, c.硬核模式, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? "
"LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
// [自定义模组:硬核模式] 添加角色硬核模式表的 LEFT JOIN 以获取难度类型
PrepareStatement(CHAR_SEL_ENUM,
"SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, "
"c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, "
"gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, "
"cb.guid, c.extra_flags, hc.难度类型编号 " // [自定义] 从外部表读取硬核难度类型
"FROM characters AS c "
"LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? "
"LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 "
"LEFT JOIN 角色硬核模式 AS hc ON c.guid = hc.角色编号 " // [自定义模组:硬核模式] 外部表关联
"WHERE c.account = ? AND c.deleteInfos_Name IS NULL "
"ORDER BY COALESCE(c.order, c.guid)",
CONNECTION_ASYNC);
// [自定义模组:硬核模式] 调整字段顺序,将 declined name 保持在索引 25
PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME,
"SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, "
"c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, "
"gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, "
"cb.guid, c.extra_flags, cd.genitive, hc.难度类型编号 " // [自定义] 调整顺序:先 genitive 后硬核模式
"FROM characters AS c "
"LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? "
"LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid "
"LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 "
"LEFT JOIN 角色硬核模式 AS hc ON c.guid = hc.角色编号 " // [自定义模组:硬核模式] 外部表关联
"WHERE c.account = ? AND c.deleteInfos_Name IS NULL "
"ORDER BY COALESCE(c.order, c.guid)",
CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name, at_login FROM characters WHERE guid = ? AND account = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_SYNCH);

View File

@ -1147,12 +1147,24 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data)
*data << uint8(hairColor);
*data << uint8(facialStyle);
// 获取硬核模式状态 - 使用硬核模式字段
uint8 hardcoreModeId = fields[25].Get<uint8>();
// [自定义模组:硬核模式] 根据查询类型读取硬核模式字段 - 开始
uint32 hardcoreModeId = 0;
if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED))
{
// CHAR_SEL_ENUM_DECLINED_NAME: 硬核模式在索引 26
hardcoreModeId = fields[26].IsNull() ? 0 : fields[26].Get<uint32>();
}
else
{
// CHAR_SEL_ENUM: 硬核模式在索引 25
hardcoreModeId = fields[25].IsNull() ? 0 : fields[25].Get<uint32>();
}
uint8 actualLevel = fields[10].Get<uint8>();
// 如果是硬核模式ID > 0使用最高位标记
bool isHardcore = hardcoreModeId > 0;
bool isHardcore = hardcoreModeId > 1; // 只有难度类型 > 1 才是硬核模式
uint8 encodedLevel = actualLevel | (isHardcore ? 1 << 7 : 0);
// [自定义模组:硬核模式] 根据查询类型读取硬核模式字段 - 结束
*data << uint8(encodedLevel); // level
*data << uint32(zone); // zone
*data << uint32(fields[12].Get<uint16>()); // map

View File

@ -1,4 +1,4 @@
/*
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
@ -35,6 +35,7 @@
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "../../../../modules/mod_GhostScripts/src/mod_CharNameMod/CharNameMod.h"
class Aura;
@ -66,6 +67,16 @@ void WorldSession::HandleGroupInviteOpcode(WorldPacket& recvData)
recvData >> membername;
recvData.read_skip<uint32>();
// 自定义名字清理逻辑 - 让拥有自定义前缀后缀的玩家能够正常组队
if (sCharNameMod)
{
std::string cleanName = sCharNameMod->GetPureName(membername);
if (!cleanName.empty() && cleanName != membername)
{
membername = cleanName; // 用清理后的纯净名称替换原始输入
}
}
// attempt add selected player
// cheating