在GOM传奇引擎中实现带有时间限制的称号和Buff

来源: 作者: 点击:
#### 核心问题分析
当前脚本仅实现了增益效果和称号的添加,但缺乏时间结束后的自动检测机制。需要增加以下两个关键模块:
1. **时间到期自动检测系统**
2. **状态恢复执行机制**

---

### 一、完整解决方案脚本(物品触发+自动回收)

#### 1. 物品触发脚本(QFunction-0.txt)
```lua
[@UseItemIDX123] ; 123为怒气神符的IDX
#IF
#ACT
; 设置时间标记(当前时间戳 + 600秒)
SetVariable HUMAN 怒气神符时间标记 <$DATETIMEADD(<$DATETIME>,600)>

; 添加BUFF效果(170%攻击力持续600秒)
POWERRATE 170 600

; 设置称号和颜色
SETRANKLEVELNAME 神力1.7倍
CHANGENAMECOLOR 250

; 设置到期提醒(提前30秒提示)
DelayCall 570000 @即将到期提示

; 系统公告
SENDMSG 0 [<$USERNAME>]使用了“怒气神符”,10分钟内攻击力为1.7倍!
BREAK

[@即将到期提示]
#ACT
SENDMSG 6 【系统】您的怒气神符效果将在30秒后消失!
BREAK
```


#### 2. 自动回收脚本(Robot.txt)
```ini
[AutoCheckBuffStatus]
#AutoRun NPC SEC 1 @CheckBuffStatus

[@CheckBuffStatus]
#if
#ACT
; 遍历所有在线玩家
GmExecute 查询在线玩家 Self
Loopgoto @CheckEachPlayer 0

[@CheckEachPlayer]
#if
CheckVariable HUMAN 怒气神符时间标记 > 0
#ACT
; 时间标记转换
Mov D1 <$HUMAN(怒气神符时间标记)>
Mov D2 <$DATETIME>
; 时间到期判断
#if
Large <$STR(D2)> <$STR(D1)>
#ACT
; 清除状态
SETRANKLEVELNAME
CHANGENAMECOLOR 255
POWERRATE 100 1
; 删除时间标记
DelVariable HUMAN 怒气神符时间标记
; 到期提示
SENDMSG 6 【系统】您的怒气神符效果已消失!
BREAK
```


---

### 二、脚本解析与关键点

#### 1. 时间标记原理
- **&lt;$DATETIME&gt;**:获取当前时间戳(单位:秒)
- **&lt;$DATETIMEADD&gt;**:时间加法运算
- **SetVariable**:持久化存储到期时间

#### 2. 自动检测机制
- **SEC 1**:每秒执行一次全局检测
- **GmExecute查询**:获取在线玩家列表
- **Loopgoto**:遍历每个玩家状态

#### 3. 状态恢复逻辑
- **SETRANKLEVELNAME**:空参数清除称号
- **CHANGENAMECOLOR 255**:恢复默认白色
- **POWERRATE 100**:重置攻击倍率

---

### 三、进阶优化方案

#### 1. 掉线重连检测
```lua
[@Login]
#IF
CheckVariable HUMAN 怒气神符时间标记 > 0
CheckPowerRate = 170
#ACT
; 计算剩余时间
Mov N1 <$HUMAN(怒气神符时间标记)>
Mov N2 <$DATETIME>
Mov N3 <$STR(N1)> - <$STR(N2)>
; 重新设置BUFF
POWERRATE 170 <$STR(N3)>
; 刷新颜色和称号
CHANGENAMECOLOR 250
SETRANKLEVELNAME 神力1.7倍
BREAK
```


#### 2. 多BUFF冲突处理
```lua
; 在物品触发脚本开头添加
#IF
CheckPowerRate > 100
#ACT
SENDMSG 6 已有更强大的BUFF存在!
BREAK
```


---

### 四、常见问题解决方案

| **问题现象** | **原因分析** | **解决方案** |
|----------------------|---------------------|--------------------------------|
| 时间到期后未清除称号 | 变量未正确保存 | 检查M2Server选项→基本参数→变量保存时间 |
| 下线后BUFF时间不计算 | 标准机制限制 | 使用Login触发脚本重算剩余时间 |
| 称号颜色未恢复 | 客户端缓存问题 | 在清除命令后增加@ReloadGameWindow |
| 多角色同时检测卡顿 | 遍历效率问题 | 将检测间隔改为SEC 5 |


---

#### 最佳实践建议
1. **压力测试**:使用@Make命令生成100个测试角色验证脚本性能
2. **日志监控**:在M2Server控制台输入`@ViewVariableLog`跟踪变量变化
3. **异常处理**:在Robot脚本开头增加`#IF CheckServerDelay < 1000`防止服务器卡顿

通过本方案,可实现称号系统从添加、持续到回收的完整生命周期管理,完美解决BUFF到期状态残留问题。

#### 1. 功能概述

##### 物品触发
当玩家使用特定物品(例如“怒气神符”)时,系统会为其添加一个称号和Buff,并设置一定的时间限制。时间结束后,称号会被移除,名字颜色恢复原样,Buff效果也会消失。

##### 称号与Buff
- **称号**:显示在玩家的名字上方。
- **Buff**:增强玩家的攻击力。
- **时间限制**:Buff和称号的效果持续一段时间后自动失效。

#### 2. GOM引擎简介

##### GOM引擎特点
- **高效稳定**:GOM引擎以其高效的处理能力和稳定的运行表现著称。
- **易用性强**:GOM引擎提供了简洁明了的API接口,方便开发者进行二次开发。
- **功能全面**:支持多种游戏元素的添加,包括但不限于技能、怪物、地图等。

##### 支持自定义功能
GOM引擎允许开发者通过修改代码和配置文件来实现各种自定义功能,包括动态管理玩家的状态和属性。

#### 3. 实现带有时间限制的称号和Buff步骤

##### 步骤一:准备工作
确保你已经安装了GOM引擎,并且有一个基本的游戏框架搭建完成。此外,还需要准备好所有必要的客户端和服务器端文件。

##### 步骤二:创建“怒气神符”物品

###### 修改`item_table`
在数据库中创建一个新的表来存储物品的信息。

**创建`item_table`表**
```sql
CREATE TABLE item_table (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
type INT NOT NULL, -- 物品类型(如武器、防具、药品、装饰物等)
rarity INT NOT NULL, -- 稀有度(如普通、罕见、史诗等)
attributes TEXT -- 属性信息(JSON格式)
);
```

###### 插入“怒气神符”数据
插入“怒气神符”的示例数据以便进行测试。

**插入“怒气神符”数据**
```sql
INSERT INTO item_table (name, type, rarity, attributes) VALUES
('怒气神符', 4, 4, '{"duration": 600, "buff": {"power_rate": 170, "max_power": 600}, "rank_level_name": "神力1.7倍", "name_color": 250}');
```

##### 步骤三:配置物品效果

###### 修改`item_config.txt`
在`config\item_config.txt`文件中添加“怒气神符”的详细效果配置。

**item_config.txt**
```ini
[Item1]
Name=怒气神符
Type=4 -- 药品
Rarity=4 -- 史诗
Duration=600 -- 持续时间(秒)
Buff={"power_rate": 170, "max_power": 600}
RankLevelName=神力1.7倍
NameColor=250
```

##### 步骤四:编写相关逻辑代码

###### 修改`item_handler.cpp`
在`src\item_handler.cpp`文件中添加处理“怒气神符”使用逻辑,包括添加称号、Buff和计时器。

**item_handler.cpp**
```cpp
#include "item_handler.h"
#include "character.h"
#include "database_manager.h"
#include "packet_builder.h"
#include "timer_manager.h"

ItemHandler* ItemHandler::GetInstance()
{
static ItemHandler instance;
return &instance;
}

bool ItemHandler::UseItem(Character* character, int itemId)
{
DatabaseManager* dbManager = DatabaseManager::GetInstance();
std::string query = "SELECT name, type, rarity, attributes FROM item_table WHERE id = " + std::to_string(itemId);
MYSQL_RES* result = dbManager->Query(query.c_str());
if (!result || mysql_num_rows(result) == 0)
{
SystemLog::LogWarning("Item [%d] not found in database.", itemId);
return false;
}

MYSQL_ROW row = mysql_fetch_row(result);
std::string itemName = row[0];
int itemType = atoi(row[1]);
int itemRarity = atoi(row[2]);
std::string itemAttributes = row[3];
mysql_free_result(result);

// Parse item attributes
json attrJson = json::parse(itemAttributes);
int duration = attrJson["duration"];
json buffJson = attrJson["buff"];
std::string rankLevelName = attrJson["rank_level_name"];
int nameColor = attrJson["name_color"];

// Check if the character already has this buff
if (character->HasActiveBuff(BUFF_TYPE_POWER_RATE))
{
CPacketBuilder response(PACKET_TYPE_ITEM_USE_RESPONSE);
response.WriteByte(ITEM_USE_FAILURE_ALREADY_HAS_BUFF);
character->SendPacket(response.Build());
SystemLog::LogWarning("Character [%d] already has power rate buff.", character->GetId());
return false;
}

// Apply Buff
int powerRate = buffJson["power_rate"];
int maxPower = buffJson["max_power"];
character->AddBuff(BUFF_TYPE_POWER_RATE, powerRate, maxPower, duration);

// Set Rank Level Name
character->SetRankLevelName(rankLevelName);

// Change Name Color
character->ChangeNameColor(nameColor);

// Send success message to player
CPacketBuilder response(PACKET_TYPE_ITEM_USE_RESPONSE);
response.WriteByte(ITEM_USE_SUCCESS);
response.WriteString("[<" + character->GetName() + ">]使用了\"" + itemName + "\",10分钟内攻击力为1.7倍……");
character->SendPacket(response.Build());

// Log action
SystemLog::LogInfo("Character [%d] used item [%d]: %s and received power rate buff for %d seconds.", character->GetId(), itemId, itemName.c_str(), duration);

// Add timer to remove buff after duration
TimerManager* timerManager = TimerManager::GetInstance();
timerManager->AddTimer(duration * 1000, [this, character]() {
RemoveBuff(character);
});

return true;
}

void ItemHandler::RemoveBuff(Character* character)
{
// Remove Power Rate Buff
character->RemoveBuff(BUFF_TYPE_POWER_RATE);

// Reset Rank Level Name
character->SetRankLevelName("");

// Reset Name Color
character->ChangeNameColor(DEFAULT_NAME_COLOR);

// Send update packets to client
CPacketBuilder response(PACKET_TYPE_STATS_UPDATE_RESPONSE);
response.WriteInt(character->GetAttack());
response.WriteInt(character->GetDefense());
character->SendPacket(response.Build());

CPacketBuilder nameResponse(PACKET_TYPE_NAME_UPDATE_RESPONSE);
nameResponse.WriteString(character->GetName());
nameResponse.WriteString(character->GetRankLevelName());
nameResponse.WriteByte(character->GetNameColor());
character->SendPacket(nameResponse.Build());

// Log action
SystemLog::LogInfo("Removed buffs and reset title for character [%d].", character->GetId());
}
```

###### 修改`character.cpp`
在`src\character.cpp`文件中添加处理角色状态的方法。

**character.cpp**
```cpp
#include "character.h"
#include "inventory.h"
#include "skill_handler.h"
#include "packet_builder.h"

Character::Character(int id, const std::string& username)
: m_id(id), m_username(username), m_level(1), m_experience(0), m_money(0), m_attack(0), m_defense(0), m_dexterity(0), m_vitality(0), m_intelligence(0), m_luck(0), m_rankLevelName(""), m_nameColor(DEFAULT_NAME_COLOR)
{
m_inventory = new Inventory(this);
}

void Character::AddExperience(int exp)
{
m_experience += exp;
CheckLevelUp();
}

void Character::CheckLevelUp()
{
// Level up logic here
}

void Character::AddMoney(int money)
{
m_money += money;
}

void Character::SubtractMoney(int money)
{
if (m_money >= money)
{
m_money -= money;
}
else
{
SystemLog::LogWarning("Character [%d] does not have enough money.", m_id);
}
}

void Character::AddAttack(int attack)
{
m_attack += attack;
UpdateStats();
}

void Character::AddDefense(int defense)
{
m_defense += defense;
UpdateStats();
}

void Character::UpdateStats()
{
CPacketBuilder response(PACKET_TYPE_STATS_UPDATE_RESPONSE);
response.WriteInt(m_attack);
response.WriteInt(m_defense);
SendPacket(response.Build());

SystemLog::LogInfo("Updated stats for character [%d]. Attack: %d, Defense: %d", m_id, m_attack, m_defense);
}

void Character::SetDurability(int itemId, int durability)
{
m_inventory->SetItemDurability(itemId, durability);
}

void Character::AddSpecialEffect(int effectId, double chance)
{
m_specialEffects[effectId] = chance;
SystemLog::LogInfo("Added special effect [%d] with chance %.2f%% to character [%d]", effectId, chance * 100, m_id);
}

void Character::AddBuff(int buffType, int value1, int value2, int duration)
{
m_buffs[buffType] = {value1, value2, duration};
UpdateStats();

SystemLog::LogInfo("Added buff [%d] with values (%d, %d) for %d seconds to character [%d]", buffType, value1, value2, duration, m_id);
}

void Character::RemoveBuff(int buffType)
{
if (m_buffs.find(buffType) != m_buffs.end())
{
m_buffs.erase(buffType);
UpdateStats();
SystemLog::LogInfo("Removed buff [%d] from character [%d]", buffType, m_id);
}
}

void Character::SetRankLevelName(const std::string& rankLevelName)
{
m_rankLevelName = rankLevelName;

CPacketBuilder response(PACKET_TYPE_NAME_UPDATE_RESPONSE);
response.WriteString(GetName());
response.WriteString(GetRankLevelName());
response.WriteByte(GetNameColor());
SendPacket(response.Build());

SystemLog::LogInfo("Set rank level name to [%s] for character [%d]", rankLevelName.c_str(), m_id);
}

void Character::ChangeNameColor(int color)
{
m_nameColor = color;

CPacketBuilder response(PACKET_TYPE_NAME_UPDATE_RESPONSE);
response.WriteString(GetName());
response.WriteString(GetRankLevelName());
response.WriteByte(GetNameColor());
SendPacket(response.Build());

SystemLog::LogInfo("Changed name color to [%d] for character [%d]", color, m_id);
}

void Character::SendPacket(const Packet& packet)
{
// Send packet to client
}
```

###### 修改`timer_manager.cpp`
在`src\timer_manager.cpp`文件中添加定时器管理逻辑。

**timer_manager.cpp**
```cpp
#include "timer_manager.h"
#include <chrono>
#include <thread>

TimerManager* TimerManager::GetInstance()
{
static TimerManager instance;
return &instance;
}

void TimerManager::Start()
{
while (true)
{
auto now = std::chrono::steady_clock::now();
for (auto it = m_timers.begin(); it != m_timers.end(); )
{
if (it->second.first <= now)
{
it->second.second();
it = m_timers.erase(it);
}
else
{
++it;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}

void TimerManager::AddTimer(int milliseconds, std::function<void()> callback)
{
auto timePoint = std::chrono::steady_clock::now() + std::chrono::milliseconds(milliseconds);
m_timers.emplace(timePoint, callback);
}
```

##### 步骤五:编译并测试
确保所有修改后的代码都能成功编译。

**编译服务器端**
```sh
g++ -o game_server src/game_server.cpp src/database_manager.cpp src/item_handler.cpp src/inventory.cpp src/character.cpp src/packet_builder.cpp src/config_manager.cpp src/random_generator.cpp src/timer_manager.cpp -lengine -ljansson -pthread
```

启动游戏服务器和客户端,观察整个“怒气神符”使用流程是否正常工作。

**启动服务器命令**
```sh
start game_server.exe
start client.exe
```

##### 步骤六:验证“怒气神符”效果

###### 测试“怒气神符”使用
1. 启动游戏服务器。
2. 使用客户端登录游戏。
3. 获取“怒气神符”。
4. 使用“怒气神符”,检查角色是否获得称号和Buff。
5. 等待10分钟后,检查称号和Buff是否被移除。

**测试“怒气神符”使用流程**
```plaintext
1. 进入游戏后,通过任务或怪物掉落获取“怒气神符”。
2. 打开背包,确认“怒气神符”已加入背包。
3. 使用“怒气神符”,查看聊天窗口是否有提示消息:“[<用户名>]使用了“怒气神符”,10分钟内攻击力为1.7倍……”
4. 观察角色名字上方是否有“神力1.7倍”称号。
5. 观察角色名字颜色是否变为指定的颜色。
6. 等待10分钟后,检查称号和名字颜色是否恢复原样。
7. 观察角色的攻击力是否恢复正常。
```

#### 4. 日志文件检查

##### 查看游戏服务器日志
打开游戏服务器的日志文件(通常位于`log\game_server.log`),查找相关的错误信息。

**游戏服务器日志示例**
```plaintext
[2023-10-01 12:34:56] INFO: Game server started on port 2107.
[2023-10-01 12:34:56] INFO: Connected to database succesully.
[2023-10-01 12:34:56] INFO: Character [1] logged in.
[2023-10-01 12:34:56] INFO: Added item [1]: 怒气神符 to character [1].
[2023-10-01 12:34:56] INFO: Character [1] used item [1]: 怒气神符 and received power rate buff for 600 seconds.
[2023-10-01 12:34:56] INFO: Removed buffs and reset title for character [1].
```

根据日志中的信息,确认游戏服务器是否正常运行以及“怒气神符”的使用和效果移除操作是否正确执行。

##### 查看客户端日志
打开客户端的日志文件(通常位于`log\client.log`),查找相关的错误信息。

**客户端日志示例**
```plaintext
[2023-10-01 12:34:56] INFO: Connecting to game server at 127.0.0.1:2107.
[2023-10-01 12:34:56] INFO: Connected to game server at 127.0.0.1:2107.
[2023-10-01 12:34:56] INFO: Logged in as testuser.
[2023-10-01 12:34:56] INFO: Received message: [<testuser>]使用了“怒气神符”,10分钟内攻击力为1.7倍……
[2023-10-01 12:34:56] INFO: Stats updated: Attack: 85, Defense: 10.
[2023-10-01 12:34:56] INFO: Name updated: testuser, Rank Level Name: 神力1.7倍, Name Color: 250.
[2023-10-01 12:44:56] INFO: Stats updated: Attack: 50, Defense: 10.
[2023-10-01 12:44:56] INFO: Name updated: testuser, Rank Level Name: , Name Color: 255.
```

根据日志中的信息,确认客户端是否正确接收了服务器的响应并且显示了相应的结果。

#### 5. 常见问题及解决方案

##### 问题一:无法连接到游戏服务器
- **检查网络设置**:确保客户端和游戏服务器之间的网络连接正常。
- **检查配置文件**:确保`client_config.txt`中的游戏服务器IP和端口配置正确。
- **检查防火墙设置**:确保防火墙没有阻止游戏服务器的端口。

##### 问题二:登录失败
- **检查数据库配置**:确保`game_config.txt`中的数据库配置正确。
- **检查数据库服务**:确保数据库服务正在运行并且可以访问。
- **检查用户数据**:确保`account_table`中包含正确的用户信息。

##### 问题三:角色加载失败
- **检查角色数据**:确保`char_table`中包含正确的角色信息。
- **检查物品数据**:确保`item_table`中包含正确的物品信息。
- **检查技能数据**:确保`skill_table`中包含正确的技能信息。

##### 问题四:客户端版本不匹配
- **更新客户端**:确保客户端版本与服务器版本兼容。
- **同步资源文件**:确保客户端和服务器之间的资源文件一致。

##### 问题五:物品未添加到背包
- **检查物品存在性**:确保提供的物品ID存在于数据库中。
- **检查权限**:确保角色具有足够的权限获取物品。
- **检查日志文件**:查看日志文件以确定是否有物品添加失败的记录。

##### 问题六:称号和Buff无效
- **检查物品效果配置**:确保`item_config.txt`中正确配置了物品效果。
- **检查逻辑代码**:确保`item_handler.cpp`中正确解析并应用了物品效果。
- **检查日志文件**:查看日志文件以确定是否有物品效果应用失败的记录。

##### 问题七:称号和Buff时间限制无效
- **检查计时器逻辑**:确保`timer_manager.cpp`中正确实现了计时器逻辑。
- **检查回调函数**:确保`item_handler.cpp`中正确设置了回调函数。
- **检查日志文件**:查看日志文件以确定是否有计时器或回调函数调用失败的记录。

##### 问题八:数据库连接失败
- **检查数据库配置**:确保`game_config.txt`中的数据库配置正确。
- **检查数据库服务**:确保数据库服务正在运行并且可以访问。
- **检查网络设置**:确保服务器能够访问数据库所在的主机。

##### 问题九:多线程计时器问题
- **检查线程安全**:确保计时器管理器的线程安全。
- **调试计时器**:逐步调试计时器逻辑以确保其按预期工作。
- **日志记录**:在关键点添加日志记录以跟踪计时器的行为。

#### 6. 总结
通过以上步骤,你应该能够在GOM传奇引擎中成功实现一个带有时间限制的“怒气神符”技能,该技能会在使用后赋予玩家称号和Buff,并在时间结束后自动移除这些效果。这不仅增加了游戏的乐趣和挑战性,还提升了玩家的游戏体验。希望这篇教程对你有所帮助!