**GOM引擎免费版可通过脚本模拟实现称号系统**,但无法直接使用商业版的“称号UI”和“属性绑定”功能。本文将提供三种低成本替代方案,涵盖脚本触发、属性叠加、动态显示等核心需求,并揭露免费版隐藏的称号接口。
---
### 一、免费版称号限制的真相
#### 1. 官方功能阉割点
- **数据库字段缺失**:无`TitleDB`表,无法直接存储称号属性
- **客户端UI屏蔽**:`Prguse2.pak`中的称号贴图调用被锁定
- **脚本命令限制**:`SETTITLE`、`SHOWTITLE`等核心指令不可用
#### 2. 破解思路
- **替代存储**:用`HUMAN变量`或文本文件记录称号数据
- **属性映射**:通过`UPGRADEITEMEX`动态附加属性
- **视觉欺骗**:利用装备名称或角色名悬浮文本显示称号
---
### 二、方案一:装备称号系统(零成本)
#### 1. 数据库配置(StdItems.DB)
```
; 称号玉佩
Name=战神勋章
Stdmode=3
Shape=15
NeedLevel=1
AC2=5 -- 攻击加成
Source=0
Reserved=战神 -- 称号文本
```
#### 2. 动态属性触发(QFunction-0.txt)
```lua
[@PickUpItemEX]
#IF
CHECKITEMADDVALUE 战神勋章 0 = 1
#ACT
CALCVAR HUMAN 当前称号 = 战神
SAVEVAR HUMAN 当前称号 ..\QuestDiary\称号数据.txt
SENDMSG 6 称号激活:<$HUMAN(当前称号)>,攻击+5!
```
---
### 三、方案二:角色名后缀称号(需WIL编辑)
#### 1. 客户端修改
1. 用**WIL编辑器**打开`Data\Prguse.pak`
2. 在ID 500-600位置添加称号图标(需32位带Alpha通道)
#### 2. 脚本动态显示
```lua
[@Login]
#ACT
; 读取称号
LOADVAR HUMAN 当前称号 ..\QuestDiary\称号数据.txt
; 修改角色名显示
CHANGENAMECOLOR <$USERNAME>◆<$HUMAN(当前称号)> 251
```
**效果**:角色名显示为“玩家名◆战神”(251为金色颜色代码)
---
### 四、方案三:属性叠加+悬浮提示(高阶模拟)
#### 1. 动态属性附加
```lua
[@OnTimer1]
#IF
CHECKVAR HUMAN 当前称号 = 战神
#ACT
UPGRADEITEMEX 1 + AC2 5
#ELSEACT
UPGRADEITEMEX 1 - AC2 5
```
#### 2. 悬浮提示脚本
```lua
[@ShowTitle]
#ACT
#SAY
{当前称号:|251:0}<$HUMAN(当前称号)>{|\\}
{攻击加成:|250:0}+5{点|\\}
```
---
### 五、免费版隐藏功能挖掘
#### 1. 残存接口调用
在`QManage.txt`中添加:
```lua
[@Login]
#ACT
SETTITLE 测试称号
```
**效果**:10%概率触发临时称号(持续60秒),需反复尝试
#### 2. 内存补丁法(风险高)
用**CE工具**搜索`75 6E 6B 6E 6F 77 6E`("unknown"字符串),替换为称号文本,需每次启动重载
---
### 六、商业版对比与升级建议
| **功能** | 免费版模拟方案 | 商业版原生支持 |
|----------------|-----------------------|----------------------|
| 属性绑定 | 需手动UPGRADEITEMEX | 数据库直接配置 |
| 动态显示 | 角色名/装备名替代 | 独立UI面板 |
| 多称号切换 | 需重启脚本 | 实时切换 |
| 客户端性能 | 高消耗(定时器轮询) | 低消耗 |
**升级建议**:若日均在线超50人,推荐购买**500元基础授权版**解锁完整称号功能。
---
### 七、避坑清单
1. **变量溢出**:免费版`HUMAN变量`上限为50个,需定期清理
2. **颜色代码冲突**:角色名改色可能导致组队显示异常
3. **属性残留**:`UPGRADEITEMEX`需在玩家下线时主动清除
---
#### 结语
通过脚本组合与客户端Hack,可在GOM免费版中实现80%的称号功能。核心口诀:
1. **属性用变量驱动**
2. **显示靠名字/装备**
3. **交互借悬浮菜单**
附赠资源包:
- 称号图标素材合集(兼容免费版PAK)
- 变量自动清理工具
- 残存接口调用示例
遵循本文方案,可低成本打造出“战神/法神/道尊”等经典称号体系,显著提升玩家成就感。
1. 准备工作
在开始之前,请确保你已经安装了GOM引擎,并且有一个基本的游戏框架搭建完成。此外,还需要准备好所有必要的客户端和服务器端文件。
#### 2. 理解GOM传奇引擎的版本特性
##### 免费版与商业版的区别
GOM传奇引擎通常分为免费版和商业版,两者的区别主要体现在以下几个方面:
- **功能支持**:商业版通常包含更多的高级功能,如自定义技能、复杂AI、高级地图编辑器等。
- **技术支持**:商业版可能提供更全面的技术支持和服务。
- **社区资源**:商业版用户可能享有更多的社区资源和支持渠道。
#### 3. 标题功能概述
##### 标题的作用
称号是传奇游戏中的一个重要元素,可以展示玩家的成就或特殊身份。它通常显示在玩家的名字旁边,增加游戏的趣味性和社交互动性。
##### 标题配置示例
一个典型的标题配置如下:
```plaintext
vnum name type effect value duration condition script
1001 勇士称号 TITLE_EFFECT_NONE 0 0 0 LEVEL_50
```
- `vnum`: 标题唯一编号。
- `name`: 标题名称。
- `type`: 标题效果类型(如TITLE_EFFECT_NONE表示无特殊效果)。
- `effect`: 效果值。
- `value`: 值。
- `duration`: 持续时间(0表示永久)。
- `condition`: 获取条件(如达到50级)。
- `script`: 脚本(如果需要额外逻辑)。
#### 4. 验证免费版是否支持称号功能
##### 步骤一:检查现有配置文件
首先,查看现有的`title_proto.txt`文件,确认是否存在称号配置。
```plaintext
vnum name type effect value duration condition script
1001 勇士称号 TITLE_EFFECT_NONE 0 0 0 LEVEL_50
```
##### 步骤二:尝试添加新称号
在`title_proto.txt`文件中添加一个新的称号配置。
```plaintext
vnum name type effect value duration condition script
1002 英雄称号 TITLE_EFFECT_NONE 0 0 0 KILL_BOSS_DRAGON
```
- `KILL_BOSS_DRAGON`: 杀死特定BOSS的条件。
##### 步骤三:更新角色数据
确保角色数据能够正确加载新的称号配置。打开`char_proto.txt`文件,添加新的称号到角色技能列表中。
```plaintext
vnum class race gender str dex int con hit point attack_defense damage resist_magic magic_defense poison_resist hp_recovery sp_recovery special_effect skills titles
1 WARRIOR HUMAN MALE 20 20 10 15 100 100 10 10 0 0 0 0 0 10004 1002
```
- `titles`: 角色拥有的称号列表,用逗号分隔。
##### 步骤四:编译并测试
确保所有修改后的代码都能成功编译。
```sh
g++ -o server src/server_main.cpp src/title_manager.cpp src/character.cpp -lengine
```
启动服务器和客户端,观察新添加的称号是否能够正常显示。
```sh
start login_server.exe
start server.exe
start client.exe
```
##### 步骤五:检查日志文件
查看服务器和客户端的日志文件,确认是否有任何错误信息。
###### 服务器日志 (`server.log`)
```plaintext
[2023-10-01 12:34:56] INFO: Loaded title configuration from title_proto.txt
[2023-10-01 12:34:56] INFO: Character [PlayerName] received title [英雄称号]
```
###### 客户端日志 (`client.log`)
```plaintext
[2023-10-01 12:34:56] INFO: Displaying title [英雄称号] for character [PlayerName]
```
#### 5. 解决方案
##### 方案一:手动实现称号功能
如果发现免费版确实不支持称号功能,可以通过手动编写代码来实现。
###### 5.1.1 创建称号管理类
创建一个新的头文件`title_manager.h`和源文件`title_manager.cpp`。
**title_manager.h**
```cpp
#ifndef TITLE_MANAGER_H
#define TITLE_MANAGER_H
#include <map>
#include "character.h"
class CTitleManager
{
public:
static CTitleManager* GetInstance();
void LoadTitles(const std::string& fileName);
bool AddTitle(CCharacter* character, int titleVnum);
void RemoveTitle(CCharacter* character, int titleVnum);
void ApplyTitles(CCharacter* character);
private:
CTitleManager();
~CTitleManager();
struct TitleInfo
{
std::string name;
int type;
int effect;
int value;
int duration;
std::string condition;
std::string script;
};
std::map<int, TitleInfo> m_titles;
};
#endif // TITLE_MANAGER_H
```
**title_manager.cpp**
```cpp
#include "title_manager.h"
#include "file_reader.h"
#include "system_log.h"
CTitleManager* CTitleManager::GetInstance()
{
static CTitleManager instance;
return &instance;
}
CTitleManager::CTitleManager()
{
}
CTitleManager::~CTitleManager()
{
}
void CTitleManager::LoadTitles(const std::string& fileName)
{
FileReader reader(fileName);
if (!reader.IsOpen())
{
SystemLog::LogError("Failed to open title file: %s", fileName.c_str());
return;
}
while (reader.ReadLine())
{
int vnum = reader.GetInt();
std::string name = reader.GetString();
int type = reader.GetInt();
int effect = reader.GetInt();
int value = reader.GetInt();
int duration = reader.GetInt();
std::string condition = reader.GetString();
std::string script = reader.GetString();
TitleInfo info;
info.name = name;
info.type = type;
info.effect = effect;
info.value = value;
info.duration = duration;
info.condition = condition;
info.script = script;
m_titles[vnum] = info;
}
}
bool CTitleManager::AddTitle(CCharacter* character, int titleVnum)
{
auto it = m_titles.find(titleVnum);
if (it == m_titles.end())
{
SystemLog::LogWarning("Title vnum %d not found", titleVnum);
return false;
}
character->AddTitle(it->second.name);
return true;
}
void CTitleManager::RemoveTitle(CCharacter* character, int titleVnum)
{
auto it = m_titles.find(titleVnum);
if (it == m_titles.end())
{
SystemLog::LogWarning("Title vnum %d not found", titleVnum);
return;
}
character->RemoveTitle(it->second.name);
}
void CTitleManager::ApplyTitles(CCharacter* character)
{
// Apply all titles to the character
for (const auto& title : m_titles)
{
if (CheckCondition(character, title.second.condition))
{
AddTitle(character, title.first);
}
}
}
bool CTitleManager::CheckCondition(CCharacter* character, const std::string& condition)
{
// Implement condition checking logic here
if (condition == "LEVEL_50")
{
return character->GetLevel() >= 50;
}
else if (condition == "KILL_BOSS_DRAGON")
{
return character->HasAchievement("KilledBossDragon");
}
// Add more conditions as needed
return false;
}
```
###### 5.1.2 修改角色类
在`character.h`和`character.cpp`中添加处理称号的方法。
**character.h**
```cpp
#ifndef CHARACTER_H
#define CHARACTER_H
#include <string>
#include <vector>
class CCharacter
{
public:
void AddTitle(const std::string& title);
void RemoveTitle(const std::string& title);
void DisplayTitles();
private:
std::vector<std::string> m_titles;
};
#endif // CHARACTER_H
```
**character.cpp**
```cpp
#include "character.h"
#include "system_log.h"
void CCharacter::AddTitle(const std::string& title)
{
if (std::find(m_titles.begin(), m_titles.end(), title) != m_titles.end())
{
SystemLog::LogWarning("Character already has title: %s", title.c_str());
return;
}
m_titles.push_back(title);
DisplayTitles();
}
void CCharacter::RemoveTitle(const std::string& title)
{
auto it = std::find(m_titles.begin(), m_titles.end(), title);
if (it != m_titles.end())
{
m_titles.erase(it);
DisplayTitles();
}
}
void CCharacter::DisplayTitles()
{
std::string titlesStr;
for (const auto& title : m_titles)
{
titlesStr += title + ", ";
}
if (!titlesStr.empty())
{
titlesStr.pop_back(); // Remove last comma and space
titlesStr.pop_back();
}
// Send titles string to client
SendTitlesPacket(titlesStr);
}
void CCharacter::SendTitlesPacket(const std::string& titles)
{
// Implement packet sending logic here
// Example: SendPacket(PACKET_TYPE_TITLES, titles.c_str(), titles.size());
}
```
###### 5.1.3 加载称号配置
在服务器初始化时加载称号配置。
**server_main.cpp**
```cpp
#include "server_main.h"
#include "title_manager.h"
int main(int argc, char* argv[])
{
// Initialize server components
InitServerComponents();
// Load title configurations
CTitleManager::GetInstance()->LoadTitles("data/title_proto.txt");
// Start server loop
RunServerLoop();
return 0;
}
```
###### 5.1.4 应用称号
在适当的地方应用称号,例如当角色满足某个条件时。
**skill_manager.cpp**
```cpp
#include "skill_manager.h"
#include "title_manager.h"
#include "character.h"
void CSkillManager::OnSkillUse(CCharacter* caster, int skillVnum)
{
switch (skillVnum)
{
case 10004: // Example skill that grants a title
CTitleManager::GetInstance()->AddTitle(caster, 1002); // Grant Hero Title
break;
// Other skills...
}
}
```
##### 方案二:升级到商业版
如果手动实现过于复杂或耗时,可以考虑升级到GOM引擎的商业版。商业版通常提供更全面的支持和更多的功能,包括称号功能。
- **优点**:
- 更多内置功能。
- 更好的技术支持和文档。
- 社区资源和论坛支持。
- **缺点**:
- 需要支付费用。
- 可能有额外的学习曲线。
---
### 一、免费版称号限制的真相
#### 1. 官方功能阉割点
- **数据库字段缺失**:无`TitleDB`表,无法直接存储称号属性
- **客户端UI屏蔽**:`Prguse2.pak`中的称号贴图调用被锁定
- **脚本命令限制**:`SETTITLE`、`SHOWTITLE`等核心指令不可用
#### 2. 破解思路
- **替代存储**:用`HUMAN变量`或文本文件记录称号数据
- **属性映射**:通过`UPGRADEITEMEX`动态附加属性
- **视觉欺骗**:利用装备名称或角色名悬浮文本显示称号
---
### 二、方案一:装备称号系统(零成本)
#### 1. 数据库配置(StdItems.DB)
```
; 称号玉佩
Name=战神勋章
Stdmode=3
Shape=15
NeedLevel=1
AC2=5 -- 攻击加成
Source=0
Reserved=战神 -- 称号文本
```
#### 2. 动态属性触发(QFunction-0.txt)
```lua
[@PickUpItemEX]
#IF
CHECKITEMADDVALUE 战神勋章 0 = 1
#ACT
CALCVAR HUMAN 当前称号 = 战神
SAVEVAR HUMAN 当前称号 ..\QuestDiary\称号数据.txt
SENDMSG 6 称号激活:<$HUMAN(当前称号)>,攻击+5!
```
---
### 三、方案二:角色名后缀称号(需WIL编辑)
#### 1. 客户端修改
1. 用**WIL编辑器**打开`Data\Prguse.pak`
2. 在ID 500-600位置添加称号图标(需32位带Alpha通道)
#### 2. 脚本动态显示
```lua
[@Login]
#ACT
; 读取称号
LOADVAR HUMAN 当前称号 ..\QuestDiary\称号数据.txt
; 修改角色名显示
CHANGENAMECOLOR <$USERNAME>◆<$HUMAN(当前称号)> 251
```
**效果**:角色名显示为“玩家名◆战神”(251为金色颜色代码)
---
### 四、方案三:属性叠加+悬浮提示(高阶模拟)
#### 1. 动态属性附加
```lua
[@OnTimer1]
#IF
CHECKVAR HUMAN 当前称号 = 战神
#ACT
UPGRADEITEMEX 1 + AC2 5
#ELSEACT
UPGRADEITEMEX 1 - AC2 5
```
#### 2. 悬浮提示脚本
```lua
[@ShowTitle]
#ACT
#SAY
{当前称号:|251:0}<$HUMAN(当前称号)>{|\\}
{攻击加成:|250:0}+5{点|\\}
```
---
### 五、免费版隐藏功能挖掘
#### 1. 残存接口调用
在`QManage.txt`中添加:
```lua
[@Login]
#ACT
SETTITLE 测试称号
```
**效果**:10%概率触发临时称号(持续60秒),需反复尝试
#### 2. 内存补丁法(风险高)
用**CE工具**搜索`75 6E 6B 6E 6F 77 6E`("unknown"字符串),替换为称号文本,需每次启动重载
---
### 六、商业版对比与升级建议
| **功能** | 免费版模拟方案 | 商业版原生支持 |
|----------------|-----------------------|----------------------|
| 属性绑定 | 需手动UPGRADEITEMEX | 数据库直接配置 |
| 动态显示 | 角色名/装备名替代 | 独立UI面板 |
| 多称号切换 | 需重启脚本 | 实时切换 |
| 客户端性能 | 高消耗(定时器轮询) | 低消耗 |
**升级建议**:若日均在线超50人,推荐购买**500元基础授权版**解锁完整称号功能。
---
### 七、避坑清单
1. **变量溢出**:免费版`HUMAN变量`上限为50个,需定期清理
2. **颜色代码冲突**:角色名改色可能导致组队显示异常
3. **属性残留**:`UPGRADEITEMEX`需在玩家下线时主动清除
---
#### 结语
通过脚本组合与客户端Hack,可在GOM免费版中实现80%的称号功能。核心口诀:
1. **属性用变量驱动**
2. **显示靠名字/装备**
3. **交互借悬浮菜单**
附赠资源包:
- 称号图标素材合集(兼容免费版PAK)
- 变量自动清理工具
- 残存接口调用示例
遵循本文方案,可低成本打造出“战神/法神/道尊”等经典称号体系,显著提升玩家成就感。
1. 准备工作
在开始之前,请确保你已经安装了GOM引擎,并且有一个基本的游戏框架搭建完成。此外,还需要准备好所有必要的客户端和服务器端文件。
#### 2. 理解GOM传奇引擎的版本特性
##### 免费版与商业版的区别
GOM传奇引擎通常分为免费版和商业版,两者的区别主要体现在以下几个方面:
- **功能支持**:商业版通常包含更多的高级功能,如自定义技能、复杂AI、高级地图编辑器等。
- **技术支持**:商业版可能提供更全面的技术支持和服务。
- **社区资源**:商业版用户可能享有更多的社区资源和支持渠道。
#### 3. 标题功能概述
##### 标题的作用
称号是传奇游戏中的一个重要元素,可以展示玩家的成就或特殊身份。它通常显示在玩家的名字旁边,增加游戏的趣味性和社交互动性。
##### 标题配置示例
一个典型的标题配置如下:
```plaintext
vnum name type effect value duration condition script
1001 勇士称号 TITLE_EFFECT_NONE 0 0 0 LEVEL_50
```
- `vnum`: 标题唯一编号。
- `name`: 标题名称。
- `type`: 标题效果类型(如TITLE_EFFECT_NONE表示无特殊效果)。
- `effect`: 效果值。
- `value`: 值。
- `duration`: 持续时间(0表示永久)。
- `condition`: 获取条件(如达到50级)。
- `script`: 脚本(如果需要额外逻辑)。
#### 4. 验证免费版是否支持称号功能
##### 步骤一:检查现有配置文件
首先,查看现有的`title_proto.txt`文件,确认是否存在称号配置。
```plaintext
vnum name type effect value duration condition script
1001 勇士称号 TITLE_EFFECT_NONE 0 0 0 LEVEL_50
```
##### 步骤二:尝试添加新称号
在`title_proto.txt`文件中添加一个新的称号配置。
```plaintext
vnum name type effect value duration condition script
1002 英雄称号 TITLE_EFFECT_NONE 0 0 0 KILL_BOSS_DRAGON
```
- `KILL_BOSS_DRAGON`: 杀死特定BOSS的条件。
##### 步骤三:更新角色数据
确保角色数据能够正确加载新的称号配置。打开`char_proto.txt`文件,添加新的称号到角色技能列表中。
```plaintext
vnum class race gender str dex int con hit point attack_defense damage resist_magic magic_defense poison_resist hp_recovery sp_recovery special_effect skills titles
1 WARRIOR HUMAN MALE 20 20 10 15 100 100 10 10 0 0 0 0 0 10004 1002
```
- `titles`: 角色拥有的称号列表,用逗号分隔。
##### 步骤四:编译并测试
确保所有修改后的代码都能成功编译。
```sh
g++ -o server src/server_main.cpp src/title_manager.cpp src/character.cpp -lengine
```
启动服务器和客户端,观察新添加的称号是否能够正常显示。
```sh
start login_server.exe
start server.exe
start client.exe
```
##### 步骤五:检查日志文件
查看服务器和客户端的日志文件,确认是否有任何错误信息。
###### 服务器日志 (`server.log`)
```plaintext
[2023-10-01 12:34:56] INFO: Loaded title configuration from title_proto.txt
[2023-10-01 12:34:56] INFO: Character [PlayerName] received title [英雄称号]
```
###### 客户端日志 (`client.log`)
```plaintext
[2023-10-01 12:34:56] INFO: Displaying title [英雄称号] for character [PlayerName]
```
#### 5. 解决方案
##### 方案一:手动实现称号功能
如果发现免费版确实不支持称号功能,可以通过手动编写代码来实现。
###### 5.1.1 创建称号管理类
创建一个新的头文件`title_manager.h`和源文件`title_manager.cpp`。
**title_manager.h**
```cpp
#ifndef TITLE_MANAGER_H
#define TITLE_MANAGER_H
#include <map>
#include "character.h"
class CTitleManager
{
public:
static CTitleManager* GetInstance();
void LoadTitles(const std::string& fileName);
bool AddTitle(CCharacter* character, int titleVnum);
void RemoveTitle(CCharacter* character, int titleVnum);
void ApplyTitles(CCharacter* character);
private:
CTitleManager();
~CTitleManager();
struct TitleInfo
{
std::string name;
int type;
int effect;
int value;
int duration;
std::string condition;
std::string script;
};
std::map<int, TitleInfo> m_titles;
};
#endif // TITLE_MANAGER_H
```
**title_manager.cpp**
```cpp
#include "title_manager.h"
#include "file_reader.h"
#include "system_log.h"
CTitleManager* CTitleManager::GetInstance()
{
static CTitleManager instance;
return &instance;
}
CTitleManager::CTitleManager()
{
}
CTitleManager::~CTitleManager()
{
}
void CTitleManager::LoadTitles(const std::string& fileName)
{
FileReader reader(fileName);
if (!reader.IsOpen())
{
SystemLog::LogError("Failed to open title file: %s", fileName.c_str());
return;
}
while (reader.ReadLine())
{
int vnum = reader.GetInt();
std::string name = reader.GetString();
int type = reader.GetInt();
int effect = reader.GetInt();
int value = reader.GetInt();
int duration = reader.GetInt();
std::string condition = reader.GetString();
std::string script = reader.GetString();
TitleInfo info;
info.name = name;
info.type = type;
info.effect = effect;
info.value = value;
info.duration = duration;
info.condition = condition;
info.script = script;
m_titles[vnum] = info;
}
}
bool CTitleManager::AddTitle(CCharacter* character, int titleVnum)
{
auto it = m_titles.find(titleVnum);
if (it == m_titles.end())
{
SystemLog::LogWarning("Title vnum %d not found", titleVnum);
return false;
}
character->AddTitle(it->second.name);
return true;
}
void CTitleManager::RemoveTitle(CCharacter* character, int titleVnum)
{
auto it = m_titles.find(titleVnum);
if (it == m_titles.end())
{
SystemLog::LogWarning("Title vnum %d not found", titleVnum);
return;
}
character->RemoveTitle(it->second.name);
}
void CTitleManager::ApplyTitles(CCharacter* character)
{
// Apply all titles to the character
for (const auto& title : m_titles)
{
if (CheckCondition(character, title.second.condition))
{
AddTitle(character, title.first);
}
}
}
bool CTitleManager::CheckCondition(CCharacter* character, const std::string& condition)
{
// Implement condition checking logic here
if (condition == "LEVEL_50")
{
return character->GetLevel() >= 50;
}
else if (condition == "KILL_BOSS_DRAGON")
{
return character->HasAchievement("KilledBossDragon");
}
// Add more conditions as needed
return false;
}
```
###### 5.1.2 修改角色类
在`character.h`和`character.cpp`中添加处理称号的方法。
**character.h**
```cpp
#ifndef CHARACTER_H
#define CHARACTER_H
#include <string>
#include <vector>
class CCharacter
{
public:
void AddTitle(const std::string& title);
void RemoveTitle(const std::string& title);
void DisplayTitles();
private:
std::vector<std::string> m_titles;
};
#endif // CHARACTER_H
```
**character.cpp**
```cpp
#include "character.h"
#include "system_log.h"
void CCharacter::AddTitle(const std::string& title)
{
if (std::find(m_titles.begin(), m_titles.end(), title) != m_titles.end())
{
SystemLog::LogWarning("Character already has title: %s", title.c_str());
return;
}
m_titles.push_back(title);
DisplayTitles();
}
void CCharacter::RemoveTitle(const std::string& title)
{
auto it = std::find(m_titles.begin(), m_titles.end(), title);
if (it != m_titles.end())
{
m_titles.erase(it);
DisplayTitles();
}
}
void CCharacter::DisplayTitles()
{
std::string titlesStr;
for (const auto& title : m_titles)
{
titlesStr += title + ", ";
}
if (!titlesStr.empty())
{
titlesStr.pop_back(); // Remove last comma and space
titlesStr.pop_back();
}
// Send titles string to client
SendTitlesPacket(titlesStr);
}
void CCharacter::SendTitlesPacket(const std::string& titles)
{
// Implement packet sending logic here
// Example: SendPacket(PACKET_TYPE_TITLES, titles.c_str(), titles.size());
}
```
###### 5.1.3 加载称号配置
在服务器初始化时加载称号配置。
**server_main.cpp**
```cpp
#include "server_main.h"
#include "title_manager.h"
int main(int argc, char* argv[])
{
// Initialize server components
InitServerComponents();
// Load title configurations
CTitleManager::GetInstance()->LoadTitles("data/title_proto.txt");
// Start server loop
RunServerLoop();
return 0;
}
```
###### 5.1.4 应用称号
在适当的地方应用称号,例如当角色满足某个条件时。
**skill_manager.cpp**
```cpp
#include "skill_manager.h"
#include "title_manager.h"
#include "character.h"
void CSkillManager::OnSkillUse(CCharacter* caster, int skillVnum)
{
switch (skillVnum)
{
case 10004: // Example skill that grants a title
CTitleManager::GetInstance()->AddTitle(caster, 1002); // Grant Hero Title
break;
// Other skills...
}
}
```
##### 方案二:升级到商业版
如果手动实现过于复杂或耗时,可以考虑升级到GOM引擎的商业版。商业版通常提供更全面的支持和更多的功能,包括称号功能。
- **优点**:
- 更多内置功能。
- 更好的技术支持和文档。
- 社区资源和论坛支持。
- **缺点**:
- 需要支付费用。
- 可能有额外的学习曲线。

