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

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

---

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

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

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

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

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

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

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


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

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

[@CheckEachPlayer]
#if
CheckVariableHUMAN怒气神符时间标记>0
#ACT
;时间标记转换
MovD1<$HUMAN(怒气神符时间标记)>
MovD2<$DATETIME>
;时间到期判断
#if
Large<$STR(D2)><$STR(D1)>
#ACT
;清除状态
SETRANKLEVELNAME
CHANGENAMECOLOR255
POWERRATE1001
;删除时间标记
DelVariableHUMAN怒气神符时间标记
;到期提示
SENDMSG6【系统】您的怒气神符效果已消失!
BREAK
```


---

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

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

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

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

---

###三、进阶优化方案

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


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


---

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

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


---

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

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

####1.功能概述

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

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

####2.GOM引擎简介

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

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

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

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

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

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

**创建`item_table`表**
```sql
CREATETABLEitem_table(
idINTAUTO_INCREMENTPRIMARYKEY
nameVARCHAR(50)NOTNULL
typeINTNOTNULL--物品类型(如武器、防具、药品、装饰物等)
rarityINTNOTNULL--稀有度(如普通、罕见、史诗等)
attributesTEXT--属性信息(JSON格式)
);
```

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

**插入“怒气神符”数据**
```sql
INSERTINTOitem_table(nametyperarityattributes)VALUES
('怒气神符'44'{"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()
{
staticItemHandlerinstance;
return&instance;
}

boolItemHandler::UseItem(Character*characterintitemId)
{
DatabaseManager*dbManager=DatabaseManager::GetInstance();
std::stringquery="SELECTnametyperarityattributesFROMitem_tableWHEREid="+std::to_string(itemId);
MYSQL_RES*result=dbManager->Query(query.c_str());
if(!result||mysql_num_rows(result)==0)
{
SystemLog::LogWarning("Item[%d]notfoundindatabase."itemId);
returnfalse;
}

MYSQL_ROWrow=mysql_fetch_row(result);
std::stringitemName=row[0];
intitemType=atoi(row[1]);
intitemRarity=atoi(row[2]);
std::stringitemAttributes=row[3];
mysql_free_result(result);

//Parseitemattributes
jsonattrJson=json::parse(itemAttributes);
intduration=attrJson["duration"];
jsonbuffJson=attrJson["buff"];
std::stringrankLevelName=attrJson["rank_level_name"];
intnameColor=attrJson["name_color"];

//Checkifthecharacteralreadyhasthisbuff
if(character->HasActiveBuff(BUFF_TYPE_POWER_RATE))
{
CPacketBuilderresponse(PACKET_TYPE_ITEM_USE_RESPONSE);
response.WriteByte(ITEM_USE_FAILURE_ALREADY_HAS_BUFF);
character->SendPacket(response.Build());
SystemLog::LogWarning("Character[%d]alreadyhaspowerratebuff."character->GetId());
returnfalse;
}

//ApplyBuff
intpowerRate=buffJson["power_rate"];
intmaxPower=buffJson["max_power"];
character->AddBuff(BUFF_TYPE_POWER_RATEpowerRatemaxPowerduration);

//SetRankLevelName
character->SetRankLevelName(rankLevelName);

//ChangeNameColor
character->ChangeNameColor(nameColor);

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

//Logaction
SystemLog::LogInfo("Character[%d]useditem[%d]:%sandreceivedpowerratebufffor%dseconds."character->GetId()itemIditemName.c_str()duration);

//Addtimertoremovebuffafterduration
TimerManager*timerManager=TimerManager::GetInstance();
timerManager->AddTimer(duration*1000[thischaracter](){
RemoveBuff(character);
});

returntrue;
}

voidItemHandler::RemoveBuff(Character*character)
{
//RemovePowerRateBuff
character->RemoveBuff(BUFF_TYPE_POWER_RATE);

//ResetRankLevelName
character->SetRankLevelName("");

//ResetNameColor
character->ChangeNameColor(DEFAULT_NAME_COLOR);

//Sendupdatepacketstoclient
CPacketBuilderresponse(PACKET_TYPE_STATS_UPDATE_RESPONSE);
response.WriteInt(character->GetAttack());
response.WriteInt(character->GetDefense());
character->SendPacket(response.Build());

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

//Logaction
SystemLog::LogInfo("Removedbuffsandresettitleforcharacter[%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(intidconststd::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=newInventory(this);
}

voidCharacter::AddExperience(intexp)
{
m_experience+=exp;
CheckLevelUp();
}

voidCharacter::CheckLevelUp()
{
//Leveluplogichere
}

voidCharacter::AddMoney(intmoney)
{
m_money+=money;
}

voidCharacter::SubtractMoney(intmoney)
{
if(m_money>=money)
{
m_money-=money;
}
else
{
SystemLog::LogWarning("Character[%d]doesnothaveenoughmoney."m_id);
}
}

voidCharacter::AddAttack(intattack)
{
m_attack+=attack;
UpdateStats();
}

voidCharacter::AddDefense(intdefense)
{
m_defense+=defense;
UpdateStats();
}

voidCharacter::UpdateStats()
{
CPacketBuilderresponse(PACKET_TYPE_STATS_UPDATE_RESPONSE);
response.WriteInt(m_attack);
response.WriteInt(m_defense);
SendPacket(response.Build());

SystemLog::LogInfo("Updatedstatsforcharacter[%d].Attack:%dDefense:%d"m_idm_attackm_defense);
}

voidCharacter::SetDurability(intitemIdintdurability)
{
m_inventory->SetItemDurability(itemIddurability);
}

voidCharacter::AddSpecialEffect(inteffectIddoublechance)
{
m_specialEffects[effectId]=chance;
SystemLog::LogInfo("Addedspecialeffect[%d]withchance%.2f%%tocharacter[%d]"effectIdchance*100m_id);
}

voidCharacter::AddBuff(intbuffTypeintvalue1intvalue2intduration)
{
m_buffs[buffType]={value1value2duration};
UpdateStats();

SystemLog::LogInfo("Addedbuff[%d]withvalues(%d%d)for%dsecondstocharacter[%d]"buffTypevalue1value2durationm_id);
}

voidCharacter::RemoveBuff(intbuffType)
{
if(m_buffs.find(buffType)!=m_buffs.end())
{
m_buffs.erase(buffType);
UpdateStats();
SystemLog::LogInfo("Removedbuff[%d]fromcharacter[%d]"buffTypem_id);
}
}

voidCharacter::SetRankLevelName(conststd::string&rankLevelName)
{
m_rankLevelName=rankLevelName;

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

SystemLog::LogInfo("Setranklevelnameto[%s]forcharacter[%d]"rankLevelName.c_str()m_id);
}

voidCharacter::ChangeNameColor(intcolor)
{
m_nameColor=color;

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

SystemLog::LogInfo("Changednamecolorto[%d]forcharacter[%d]"colorm_id);
}

voidCharacter::SendPacket(constPacket&packet)
{
//Sendpackettoclient
}
```

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

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

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

voidTimerManager::Start()
{
while(true)
{
autonow=std::chrono::steady_clock::now();
for(autoit=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));
}
}

voidTimerManager::AddTimer(intmillisecondsstd::function<void()>callback)
{
autotimePoint=std::chrono::steady_clock::now()+std::chrono::milliseconds(milliseconds);
m_timers.emplace(timePointcallback);
}
```

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

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

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

**启动服务器命令**
```sh
startgame_server.exe
startclient.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-0112:34:56]INFO:Gameserverstartedonport2107.
[2023-10-0112:34:56]INFO:Connectedtodatabasesuccesully.
[2023-10-0112:34:56]INFO:Character[1]loggedin.
[2023-10-0112:34:56]INFO:Addeditem[1]:怒气神符tocharacter[1].
[2023-10-0112:34:56]INFO:Character[1]useditem[1]:怒气神符andreceivedpowerratebufffor600seconds.
[2023-10-0112:34:56]INFO:Removedbuffsandresettitleforcharacter[1].
```

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

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

**客户端日志示例**
```plaintext
[2023-10-0112:34:56]INFO:Connectingtogameserverat127.0.0.1:2107.
[2023-10-0112:34:56]INFO:Connectedtogameserverat127.0.0.1:2107.
[2023-10-0112:34:56]INFO:Loggedinastestuser.
[2023-10-0112:34:56]INFO:Receivedmessage:[<testuser>]使用了“怒气神符”,10分钟内攻击力为1.7倍……
[2023-10-0112:34:56]INFO:Statsupdated:Attack:85Defense:10.
[2023-10-0112:34:56]INFO:Nameupdated:testuserRankLevelName:神力1.7倍NameColor:250.
[2023-10-0112:44:56]INFO:Statsupdated:Attack:50Defense:10.
[2023-10-0112:44:56]INFO:Nameupdated:testuserRankLevelName:NameColor: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,并在时间结束后自动移除这些效果。这不仅增加了游戏的乐趣和挑战性,还提升了玩家的游戏体验。希望这篇教程对你有所帮助!
[顶部]