在GOM引擎中实现“召唤圣兽”技能需同时修改**技能数据库**、**怪物数据**及**技能触发脚本**,绝非简单改名操作。本文提供完整的技能书学习、圣兽独立召唤方案,并附可直用的脚本代码。
---
###一、功能实现流程
```mermaid
graphTD
A[创建圣兽怪物数据]-->B[配置召唤术技能]
B-->C[添加技能书物品]
C-->D[编写学习与召唤脚本]
D-->E[关联客户端素材]
```
---
###二、核心配置步骤
####**1.创建圣兽怪物(Monster.DB)**
|字段名|值|说明|
|------------|------------|---------------------|
|Name|圣兽|必须与脚本中的名称一致|
|Race|85|关键!85=主动攻击型宝宝|
|Life|8000|根据需求调整生命值|
|DC|150-300|物理攻击力|
|Speed|70|移动速度(值越小越快)|
|RaceImg|280|客户端外观编号(需与Wil资源对应)|
**注意**:若Race值错误,圣兽可能站立不动或无法攻击!
---
####**2.配置召唤技能(Magic.DB)**
|字段名|值|说明|
|---------------|----------------|---------------------|
|MagID|53|技能类型:召唤术(与神兽同类型)|
|MagName|召唤圣兽|技能显示名称|
|TrainLevel|3|技能最高等级|
|NeedL1|35|学习所需等级|
|NeedBook|888|绑定技能书物品ID|
|MaxTrainTime|1000|每次升级所需经验|
**重要提示**:若MagID冲突,可设置为53(需先备份原神兽数据)。
---
####**3.添加技能书(Items.DB)**
|字段名|值|说明|
|------------|----------------|---------------------|
|Idx|888|唯一编号(勿重复)|
|Name|圣兽技能书|物品名称|
|StdMode|2|书籍类物品|
|Need|35|学习所需等级|
|NeedLevel|0|无职业限制|
|Source|@LearnHolyBeast|使用后触发的脚本标签|
---
###三、脚本代码实现
####**1.学习技能(QFunction-0.txt)**
```lua
[@LearnHolyBeast]
#IF
CHECKJOBWarrior--职业限制(可调整)
#ACT
ADDSKILL召唤圣兽1--技能名称与Magic.DB的MagName严格一致
SendMsg6恭喜领悟“召唤圣兽”技能!
Break
#ELSEACT
SendMsg6职业不符,无法学习该技能!
Break
```
**提示**:若需全职业学习,删除`CHECKJOB`判断即可。
---
####**2.召唤圣兽逻辑(QMagic-0.txt)**
```lua
[@MagSelfFunc53]--53对应Magic.DB的MagID
#IF
CHECKCALLMOB<1--限制召唤数量(1只)
#ACT
GiveMob圣兽199991--召唤圣兽,等级1,永久存在,主动攻击(最后参数1)
Break
#ELSEACT
SendMsg6已有圣兽护体,不可重复召唤!
Break
```
**参数详解**:
-`GiveMob`命令格式:怪物名等级存活时间(分钟)攻击模式(0=跟随,1=主动)
-存活时间设为9999≈永久,重启服务器后消失
---
###四、客户端素材适配
1.**怪物外观**:
-将圣兽的图片资源(8方向+攻击帧)添加到`Monster.wil`,起始序号需与`RaceImg=280`对应。
-使用**Wil编辑器**调整坐标防止错位。
2.**技能特效**:
-修改`Magic.wil`中编号53的素材,替换为圣兽召唤动画。
---
###五、高频问题解决方案
####**问题1:技能书使用无效**
-**检查点**:
1.Items.DB的`Source`字段是否与脚本标签`@LearnHolyBeast`一致。
2.技能书是否被其他脚本(如QFunction-0.txt)重复拦截。
####**问题2:召唤后圣兽不攻击**
-**排查**:
1.Monster.DB的`Race`必须为85或81(测试不同引擎版本)。
2.`GiveMob`最后一个参数设为1。
####**问题3:客户端显示透明**
-**解决**:
1.确认`RaceImg`在客户端的Wil文件中存在。
2.使用**WzlEditor**重新生成Wil索引。
---
###六、测试指令(GM命令)
1.刷技能书:`@Make圣兽技能书1`
2.学习技能:`@ADDSKILL召唤圣兽3`(3级)
3.召唤测试:按技能快捷键(默认F8)
---
####结语
按照上述步骤配置,可实现与原版召唤神兽完全独立的圣兽技能体系。进阶开发可扩展圣兽进化(通过`CHANGEMOBLEVEL`)、合击技能等玩法。重点注意**数据库字段一致性**与**客户端素材同步**,即可避免90%的异常问题。
####1.准备工作
在开始之前,请确保你已经安装了GOM引擎,并且有一个基本的游戏框架搭建完成。此外,还需要准备好所有必要的客户端和服务器端文件。
####2.理解召唤圣兽技能的功能
#####召唤圣兽技能概述
召唤圣兽技能是一种特殊的技能,当玩家使用该技能时,可以在游戏中召唤出一个特定的圣兽来辅助战斗。这个圣兽通常拥有独特的技能和属性,能够帮助玩家更好地应对各种挑战。
####3.数据库配置
#####步骤一:创建圣兽数据表
首先,在数据库中创建一个新的表来存储圣兽的数据信息。
**创建圣兽表**
```sql
CREATETABLEmonster_table(
idINTAUTO_INCREMENTPRIMARYKEY
nameVARCHAR(50)NOTNULL
levelINTNOTNULL
hpINTNOTNULL
spINTNOTNULL
strINTNOTNULL
dexINTNOTNULL
intelINTNOTNULL
conINTNOTNULL
skill_idINTNOTNULL
skill_levelINTNOTNULL
);
```
#####步骤二:插入示例圣兽数据
插入一些示例数据以便进行测试。
**插入圣兽数据**
```sql
INSERTINTOmonster_table(namelevelhpspstrdexintelconskill_idskill_level)
VALUES('神圣凤凰'50100050010010010010011);
INSERTINTOmonster_table(namelevelhpspstrdexintelconskill_idskill_level)
VALUES('光明巨龙'60150070015015015015021);
```
#####步骤三:创建技能数据表
在`skill_table`中添加召唤圣兽技能的相关数据。
**插入召唤圣兽技能数据**
```sql
INSERTINTOskill_table(idnamedescriptiontypemp_costcooldown)
VALUES(9999'召唤神圣凤凰''召唤一只强大的神圣凤凰协助战斗'1100300);--技能类型1表示召唤技能
INSERTINTOskill_table(idnamedescriptiontypemp_costcooldown)
VALUES(10000'召唤光明巨龙''召唤一只强大的光明巨龙协助战斗'1150400);--技能类型1表示召唤技能
```
#####步骤四:创建物品数据表
在`item_table`中添加召唤圣兽技能书的相关数据。
**插入召唤圣兽技能书数据**
```sql
INSERTINTOitem_table(idnametypedescriptioneffect_typeeffect_value)
VALUES(9998'神圣凤凰召唤卷轴'1'学习后可使用召唤神圣凤凰技能'29999);--效果类型2表示学习技能,效果值9999对应神圣凤凰技能ID
INSERTINTOitem_table(idnametypedescriptioneffect_typeeffect_value)
VALUES(9997'光明巨龙召唤卷轴'1'学习后可使用召唤光明巨龙技能'210000);--效果类型2表示学习技能,效果值10000对应光明巨龙技能ID
```
####4.修改代码实现
#####步骤一:修改`skill_handler.cpp`
在`src\skill_handler.cpp`文件中添加处理召唤圣兽技能使用的逻辑。
**skill_handler.cpp**
```cpp
#include"skill_handler.h"
#include"character.h"
#include"monster_manager.h"
#include"packet_builder.h"
voidSkillHandler::UseSkill(Character*characterintskillId)
{
switch(skillId)
{
case9999://召唤神圣凤凰
SummonMonster(character1);
break;
case10000://召唤光明巨龙
SummonMonster(character2);
break;
default:
DefaultSkillUsage(characterskillId);
break;
}
}
voidSkillHandler::SummonMonster(Character*characterintmonsterId)
{
MonsterManager*monsterManager=MonsterManager::GetInstance();
Monster*monster=monsterManager->CreateMonster(monsterId);
if(!monster)
{
CPacketBuilderresponse(PACKET_TYPE_SUMMON_MONSTER_RESPONSE);
response.WriteByte(SUMMON_MONSTER_FAILURE);
character->SendPacket(response.Build());
return;
}
character->AddMonster(monster);
CPacketBuilderresponse(PACKET_TYPE_SUMMON_MONSTER_RESPONSE);
response.WriteByte(SUMMON_MONSTER_SUCCESS);
monster->Serialize(response);
character->SendPacket(response.Build());
SystemLog::LogInfo("Character[%d]summonedmonster[%s]."character->GetId()monster->GetName().c_str());
}
```
#####步骤二:创建`monster_manager.cpp`
在`src\monster_manager.cpp`文件中实现圣兽管理功能。
**monster_manager.cpp**
```cpp
#include"monster_manager.h"
#include"database_manager.h"
#include<map>
MonsterManager*MonsterManager::GetInstance()
{
staticMonsterManagerinstance;
return&instance;
}
MonsterManager::MonsterManager()
{
LoadMonstersFromDatabase();
}
Monster*MonsterManager::CreateMonster(intmonsterId)
{
autoit=m_monsters.find(monsterId);
if(it!=m_monsters.end())
{
returnnewMonster(it->second);
}
returnnullptr;
}
voidMonsterManager::LoadMonstersFromDatabase()
{
DatabaseManager*dbManager=DatabaseManager::GetInstance();
MYSQL_RES*result=dbManager->Query("SELECT*FROMmonster_table");
if(!result)
{
SystemLog::LogError("Failedtoloadmonstersfromdatabase.");
return;
}
MYSQL_ROWrow;
while((row=mysql_fetch_row(result)))
{
intid=atoi(row[0]);
std::stringname=row[1];
intlevel=atoi(row[2]);
inthp=atoi(row[3]);
intsp=atoi(row[4]);
intstr=atoi(row[5]);
intdex=atoi(row[6]);
intintel=atoi(row[7]);
intcon=atoi(row[8]);
intskillId=atoi(row[9]);
intskillLevel=atoi(row[10]);
Monstermonster(idnamelevelhpspstrdexintelconskillIdskillLevel);
m_monsters[id]=monster;
}
mysql_free_result(result);
SystemLog::LogInfo("Loaded%dmonstersfromdatabase."m_monsters.size());
}
```
#####步骤三:创建`monster.cpp`
在`src\monster.cpp`文件中实现圣兽类的功能。
**monster.cpp**
```cpp
#include"monster.h"
Monster::Monster(intidconststd::string&nameintlevelinthpintspintstrintdexintintelintconintskillIdintskillLevel)
{
m_id=id;
m_name=name;
m_level=level;
m_hp=hp;
m_sp=sp;
m_str=str;
m_dex=dex;
m_intel=intel;
m_con=con;
m_skillId=skillId;
m_skillLevel=skillLevel;
}
voidMonster::Serialize(CPacketBuilder&packet)
{
packet.WriteInt(m_id);
packet.WriteString(m_name);
packet.WriteInt(m_level);
packet.WriteInt(m_hp);
packet.WriteInt(m_sp);
packet.WriteInt(m_str);
packet.WriteInt(m_dex);
packet.WriteInt(m_intel);
packet.WriteInt(m_con);
packet.WriteInt(m_skillId);
packet.WriteInt(m_skillLevel);
}
```
#####步骤四:修改`character.cpp`
在`src\character.cpp`文件中添加管理圣兽的逻辑。
**character.cpp**
```cpp
#include"character.h"
#include"monster.h"
Character::Character(intid)
{
m_id=id;
}
voidCharacter::LearnSkill(intskillId)
{
m_skills.push_back(skillId);
}
voidCharacter::AddMonster(Monster*monster)
{
m_monsters.push_back(monster);
}
voidCharacter::RemoveMonster(intmonsterId)
{
for(autoit=m_monsters.begin();it!=m_monsters.end();++it)
{
if((*it)->GetId()==monsterId)
{
delete*it;
m_monsters.erase(it);
break;
}
}
}
voidCharacter::SerializeSkills(CPacketBuilder&packet)
{
packet.WriteInt(static_cast<int>(m_skills.size()));
for(intskillId:m_skills)
{
packet.WriteInt(skillId);
}
}
voidCharacter::SerializeMonsters(CPacketBuilder&packet)
{
packet.WriteInt(static_cast<int>(m_monsters.size()));
for(Monster*monster:m_monsters)
{
monster->Serialize(packet);
}
}
```
#####步骤五:修改`client_network.cpp`
在`src\client_network.cpp`文件中实现客户端与游戏服务器的通信。
**client_network.cpp**
```cpp
#include"client_network.h"
#include"packet_builder.h"
CClientNetwork::CClientNetwork()
{
m_authSocket=INVALID_SOCKET;
m_gameSocket=INVALID_SOCKET;
}
boolCClientNetwork::ConnectToAuthServer(conststd::string&ipintport)
{
m_authSocket=socket(AF_INETSOCK_STREAM0);
if(m_authSocket==INVALID_SOCKET)
{
SystemLog::LogError("Failedtocreatesocket:%d"WSAGetLastError());
returnfalse;
}
sockaddr_inserverAddr;
serverAddr.sin_family=AF_INET;
serverAddr.sin_addr.s_addr=inet_addr(ip.c_str());
serverAddr.sin_port=htons(port);
if(connect(m_authSocket(sockaddr*)&serverAddrsizeof(serverAddr))==SOCKET_ERROR)
{
SystemLog::LogError("Failedtoconnecttoauthserver:%d"WSAGetLastError());
closesocket(m_authSocket);
returnfalse;
}
SystemLog::LogInfo("Connectedtoauthserverat%s:%d"ip.c_str()port);
returntrue;
}
boolCClientNetwork::ConnectToGameServer(conststd::string&ipintport)
{
m_gameSocket=socket(AF_INETSOCK_STREAM0);
if(m_gameSocket==INVALID_SOCKET)
{
SystemLog::LogError("Failedtocreatesocket:%d"WSAGetLastError());
returnfalse;
}
sockaddr_inserverAddr;
serverAddr.sin_family=AF_INET;
serverAddr.sin_addr.s_addr=inet_addr(ip.c_str());
serverAddr.sin_port=htons(port);
if(connect(m_gameSocket(sockaddr*)&serverAddrsizeof(serverAddr))==SOCKET_ERROR)
{
SystemLog::LogError("Failedtoconnecttogameserver:%d"WSAGetLastError());
closesocket(m_gameSocket);
returnfalse;
}
SystemLog::LogInfo("Connectedtogameserverat%s:%d"ip.c_str()port);
returntrue;
}
voidCClientNetwork::SendLoginRequest(conststd::string&usernameconststd::string&password)
{
CPacketBuilderpacket(PACKET_TYPE_LOGIN_REQUEST);
packet.WriteString(username);
packet.WriteString(password);
SendPacketToAuthServer(packet.Build());
}
voidCClientNetwork::SendPacketToAuthServer(constPacket&packet)
{
send(m_authSocketreinterpret_cast<constchar*>(packet.GetData())packet.GetSize()0);
}
voidCClientNetwork::SendPacketToGameServer(constPacket&packet)
{
send(m_gameSocketreinterpret_cast<constchar*>(packet.GetData())packet.GetSize()0);
}
boolCClientNetwork::ReceivePacket(Packet&packet)
{
charbuffer[MAX_PACKET_SIZE];
intbytesRead=recv(m_gameSocketbufferMAX_PACKET_SIZE0);
if(bytesRead<=0)
{
SystemLog::LogWarning("Connectionclosedbyserver.");
returnfalse;
}
packet.SetData(bufferbytesRead);
returntrue;
}
voidCClientNetwork::SendUseSkillRequest(intskillId)
{
CPacketBuilderpacket(PACKET_TYPE_USE_SKILL_REQUEST);
packet.WriteInt(skillId);
SendPacketToGameServer(packet.Build());
}
voidCClientNetwork::HandleSummonMonsterResponse(constPacket&packet)
{
bytestatus=packet.ReadByte();
if(status==SUMMON_MONSTER_SUCCESS)
{
Monstermonster;
monster.Deserialize(packet);
AddMonster(monster);
SystemLog::LogInfo("Summonedmonster[%s]."monster.GetName().c_str());
}
else
{
SystemLog::LogWarning("Failedtosummonmonster.");
}
}
```
#####步骤六:编译并测试
确保所有修改后的代码都能成功编译。
```sh
g++-ogame_serversrc/game_server.cppsrc/database_manager.cppsrc/skill_handler.cppsrc/monster_manager.cppsrc/packet_builder.cpp-lengine
g++-oclientsrc/client_main.cppsrc/client_network.cppsrc/packet_builder.cpp-lengine
```
启动登录服务器、游戏服务器和客户端,观察整个召唤圣兽技能流程是否顺畅。
```sh
startauth_server.exe
startgame_server.exe
startclient.exe
```
####5.日志文件检查
#####查看游戏服务器日志
打开游戏服务器的日志文件(通常位于`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:Loaded2monstersfromdatabase.
[2023-10-0112:34:56]INFO:Player[1]requestedtouseskill[9999].
[2023-10-0112:34:56]INFO:Character[1]summonedmonster[神圣凤凰].
```
根据日志中的信息,确认游戏服务器是否正常运行以及圣兽是否成功召唤。
#####查看客户端日志
打开客户端的日志文件(通常位于`log\client.log`),查找相关的错误信息。
```plaintext
[2023-10-0112:34:56]INFO:Connectingtoauthserverat127.0.0.1:2106.
[2023-10-0112:34:56]INFO:Connectedtoauthserverat127.0.0.1:2106.
[2023-10-0112:34:56]INFO:Logginginastestuser.
[2023-10-0112:34:56]INFO:LoginsuccesulaccountID:1.
[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:Requestingtouseskill[9999].
[2023-10-0112:34:56]INFO:Summonedmonster[神圣凤凰].
```
根据日志中的信息,确认客户端是否正确发送了请求以及服务器的响应。
####6.常见问题及解决方案
#####问题一:无法连接到游戏服务器
-**检查网络设置**:确保客户端和游戏服务器之间的网络连接正常。
-**检查配置文件**:确保`client_config.txt`中的游戏服务器IP和端口配置正确。
-**检查防火墙设置**:确保防火墙没有阻止游戏服务器的端口。
#####问题二:登录失败
-**检查数据库配置**:确保`auth_config.txt`中的数据库配置正确。
-**检查数据库服务**:确保数据库服务正在运行并且可以访问。
-**检查用户数据**:确保`account_table`中包含正确的用户信息。
#####问题三:角色加载失败
-**检查角色数据**:确保`char_table`中包含正确的角色信息。
-**检查物品数据**:确保`item_table`中包含正确的物品信息。
-**检查圣兽数据**:确保`monster_table`中包含正确的圣兽信息。
#####问题四:客户端版本不匹配
-**更新客户端**:确保客户端版本与服务器版本兼容。
-**同步资源文件**:确保客户端和服务器之间的资源文件一致。
#####问题五:圣兽召唤失败
-**检查圣兽数据表**:确保`monster_table`中包含正确的圣兽数据。
-**检查技能数据表**:确保`skill_table`中包含正确的召唤圣兽技能数据。
-**检查物品数据表**:确保`item_table`中包含正确的召唤圣兽技能书数据。
-**检查日志文件**:查看日志文件以确定是否有圣兽召唤失败的记录。
####7.总结
通过以上步骤,你应该能够在GOM引擎的单机传奇版本中成功添加一个可学习的“召唤圣兽”技能。这不仅增加了游戏的乐趣性和互动性,还提升了玩家的游戏体验。希望这篇教程对你有所帮助!
---
###一、功能实现流程
```mermaid
graphTD
A[创建圣兽怪物数据]-->B[配置召唤术技能]
B-->C[添加技能书物品]
C-->D[编写学习与召唤脚本]
D-->E[关联客户端素材]
```
---
###二、核心配置步骤
####**1.创建圣兽怪物(Monster.DB)**
|字段名|值|说明|
|------------|------------|---------------------|
|Name|圣兽|必须与脚本中的名称一致|
|Race|85|关键!85=主动攻击型宝宝|
|Life|8000|根据需求调整生命值|
|DC|150-300|物理攻击力|
|Speed|70|移动速度(值越小越快)|
|RaceImg|280|客户端外观编号(需与Wil资源对应)|
**注意**:若Race值错误,圣兽可能站立不动或无法攻击!
---
####**2.配置召唤技能(Magic.DB)**
|字段名|值|说明|
|---------------|----------------|---------------------|
|MagID|53|技能类型:召唤术(与神兽同类型)|
|MagName|召唤圣兽|技能显示名称|
|TrainLevel|3|技能最高等级|
|NeedL1|35|学习所需等级|
|NeedBook|888|绑定技能书物品ID|
|MaxTrainTime|1000|每次升级所需经验|
**重要提示**:若MagID冲突,可设置为53(需先备份原神兽数据)。
---
####**3.添加技能书(Items.DB)**
|字段名|值|说明|
|------------|----------------|---------------------|
|Idx|888|唯一编号(勿重复)|
|Name|圣兽技能书|物品名称|
|StdMode|2|书籍类物品|
|Need|35|学习所需等级|
|NeedLevel|0|无职业限制|
|Source|@LearnHolyBeast|使用后触发的脚本标签|
---
###三、脚本代码实现
####**1.学习技能(QFunction-0.txt)**
```lua
[@LearnHolyBeast]
#IF
CHECKJOBWarrior--职业限制(可调整)
#ACT
ADDSKILL召唤圣兽1--技能名称与Magic.DB的MagName严格一致
SendMsg6恭喜领悟“召唤圣兽”技能!
Break
#ELSEACT
SendMsg6职业不符,无法学习该技能!
Break
```
**提示**:若需全职业学习,删除`CHECKJOB`判断即可。
---
####**2.召唤圣兽逻辑(QMagic-0.txt)**
```lua
[@MagSelfFunc53]--53对应Magic.DB的MagID
#IF
CHECKCALLMOB<1--限制召唤数量(1只)
#ACT
GiveMob圣兽199991--召唤圣兽,等级1,永久存在,主动攻击(最后参数1)
Break
#ELSEACT
SendMsg6已有圣兽护体,不可重复召唤!
Break
```
**参数详解**:
-`GiveMob`命令格式:怪物名等级存活时间(分钟)攻击模式(0=跟随,1=主动)
-存活时间设为9999≈永久,重启服务器后消失
---
###四、客户端素材适配
1.**怪物外观**:
-将圣兽的图片资源(8方向+攻击帧)添加到`Monster.wil`,起始序号需与`RaceImg=280`对应。
-使用**Wil编辑器**调整坐标防止错位。
2.**技能特效**:
-修改`Magic.wil`中编号53的素材,替换为圣兽召唤动画。
---
###五、高频问题解决方案
####**问题1:技能书使用无效**
-**检查点**:
1.Items.DB的`Source`字段是否与脚本标签`@LearnHolyBeast`一致。
2.技能书是否被其他脚本(如QFunction-0.txt)重复拦截。
####**问题2:召唤后圣兽不攻击**
-**排查**:
1.Monster.DB的`Race`必须为85或81(测试不同引擎版本)。
2.`GiveMob`最后一个参数设为1。
####**问题3:客户端显示透明**
-**解决**:
1.确认`RaceImg`在客户端的Wil文件中存在。
2.使用**WzlEditor**重新生成Wil索引。
---
###六、测试指令(GM命令)
1.刷技能书:`@Make圣兽技能书1`
2.学习技能:`@ADDSKILL召唤圣兽3`(3级)
3.召唤测试:按技能快捷键(默认F8)
---
####结语
按照上述步骤配置,可实现与原版召唤神兽完全独立的圣兽技能体系。进阶开发可扩展圣兽进化(通过`CHANGEMOBLEVEL`)、合击技能等玩法。重点注意**数据库字段一致性**与**客户端素材同步**,即可避免90%的异常问题。
####1.准备工作
在开始之前,请确保你已经安装了GOM引擎,并且有一个基本的游戏框架搭建完成。此外,还需要准备好所有必要的客户端和服务器端文件。
####2.理解召唤圣兽技能的功能
#####召唤圣兽技能概述
召唤圣兽技能是一种特殊的技能,当玩家使用该技能时,可以在游戏中召唤出一个特定的圣兽来辅助战斗。这个圣兽通常拥有独特的技能和属性,能够帮助玩家更好地应对各种挑战。
####3.数据库配置
#####步骤一:创建圣兽数据表
首先,在数据库中创建一个新的表来存储圣兽的数据信息。
**创建圣兽表**
```sql
CREATETABLEmonster_table(
idINTAUTO_INCREMENTPRIMARYKEY
nameVARCHAR(50)NOTNULL
levelINTNOTNULL
hpINTNOTNULL
spINTNOTNULL
strINTNOTNULL
dexINTNOTNULL
intelINTNOTNULL
conINTNOTNULL
skill_idINTNOTNULL
skill_levelINTNOTNULL
);
```
#####步骤二:插入示例圣兽数据
插入一些示例数据以便进行测试。
**插入圣兽数据**
```sql
INSERTINTOmonster_table(namelevelhpspstrdexintelconskill_idskill_level)
VALUES('神圣凤凰'50100050010010010010011);
INSERTINTOmonster_table(namelevelhpspstrdexintelconskill_idskill_level)
VALUES('光明巨龙'60150070015015015015021);
```
#####步骤三:创建技能数据表
在`skill_table`中添加召唤圣兽技能的相关数据。
**插入召唤圣兽技能数据**
```sql
INSERTINTOskill_table(idnamedescriptiontypemp_costcooldown)
VALUES(9999'召唤神圣凤凰''召唤一只强大的神圣凤凰协助战斗'1100300);--技能类型1表示召唤技能
INSERTINTOskill_table(idnamedescriptiontypemp_costcooldown)
VALUES(10000'召唤光明巨龙''召唤一只强大的光明巨龙协助战斗'1150400);--技能类型1表示召唤技能
```
#####步骤四:创建物品数据表
在`item_table`中添加召唤圣兽技能书的相关数据。
**插入召唤圣兽技能书数据**
```sql
INSERTINTOitem_table(idnametypedescriptioneffect_typeeffect_value)
VALUES(9998'神圣凤凰召唤卷轴'1'学习后可使用召唤神圣凤凰技能'29999);--效果类型2表示学习技能,效果值9999对应神圣凤凰技能ID
INSERTINTOitem_table(idnametypedescriptioneffect_typeeffect_value)
VALUES(9997'光明巨龙召唤卷轴'1'学习后可使用召唤光明巨龙技能'210000);--效果类型2表示学习技能,效果值10000对应光明巨龙技能ID
```
####4.修改代码实现
#####步骤一:修改`skill_handler.cpp`
在`src\skill_handler.cpp`文件中添加处理召唤圣兽技能使用的逻辑。
**skill_handler.cpp**
```cpp
#include"skill_handler.h"
#include"character.h"
#include"monster_manager.h"
#include"packet_builder.h"
voidSkillHandler::UseSkill(Character*characterintskillId)
{
switch(skillId)
{
case9999://召唤神圣凤凰
SummonMonster(character1);
break;
case10000://召唤光明巨龙
SummonMonster(character2);
break;
default:
DefaultSkillUsage(characterskillId);
break;
}
}
voidSkillHandler::SummonMonster(Character*characterintmonsterId)
{
MonsterManager*monsterManager=MonsterManager::GetInstance();
Monster*monster=monsterManager->CreateMonster(monsterId);
if(!monster)
{
CPacketBuilderresponse(PACKET_TYPE_SUMMON_MONSTER_RESPONSE);
response.WriteByte(SUMMON_MONSTER_FAILURE);
character->SendPacket(response.Build());
return;
}
character->AddMonster(monster);
CPacketBuilderresponse(PACKET_TYPE_SUMMON_MONSTER_RESPONSE);
response.WriteByte(SUMMON_MONSTER_SUCCESS);
monster->Serialize(response);
character->SendPacket(response.Build());
SystemLog::LogInfo("Character[%d]summonedmonster[%s]."character->GetId()monster->GetName().c_str());
}
```
#####步骤二:创建`monster_manager.cpp`
在`src\monster_manager.cpp`文件中实现圣兽管理功能。
**monster_manager.cpp**
```cpp
#include"monster_manager.h"
#include"database_manager.h"
#include<map>
MonsterManager*MonsterManager::GetInstance()
{
staticMonsterManagerinstance;
return&instance;
}
MonsterManager::MonsterManager()
{
LoadMonstersFromDatabase();
}
Monster*MonsterManager::CreateMonster(intmonsterId)
{
autoit=m_monsters.find(monsterId);
if(it!=m_monsters.end())
{
returnnewMonster(it->second);
}
returnnullptr;
}
voidMonsterManager::LoadMonstersFromDatabase()
{
DatabaseManager*dbManager=DatabaseManager::GetInstance();
MYSQL_RES*result=dbManager->Query("SELECT*FROMmonster_table");
if(!result)
{
SystemLog::LogError("Failedtoloadmonstersfromdatabase.");
return;
}
MYSQL_ROWrow;
while((row=mysql_fetch_row(result)))
{
intid=atoi(row[0]);
std::stringname=row[1];
intlevel=atoi(row[2]);
inthp=atoi(row[3]);
intsp=atoi(row[4]);
intstr=atoi(row[5]);
intdex=atoi(row[6]);
intintel=atoi(row[7]);
intcon=atoi(row[8]);
intskillId=atoi(row[9]);
intskillLevel=atoi(row[10]);
Monstermonster(idnamelevelhpspstrdexintelconskillIdskillLevel);
m_monsters[id]=monster;
}
mysql_free_result(result);
SystemLog::LogInfo("Loaded%dmonstersfromdatabase."m_monsters.size());
}
```
#####步骤三:创建`monster.cpp`
在`src\monster.cpp`文件中实现圣兽类的功能。
**monster.cpp**
```cpp
#include"monster.h"
Monster::Monster(intidconststd::string&nameintlevelinthpintspintstrintdexintintelintconintskillIdintskillLevel)
{
m_id=id;
m_name=name;
m_level=level;
m_hp=hp;
m_sp=sp;
m_str=str;
m_dex=dex;
m_intel=intel;
m_con=con;
m_skillId=skillId;
m_skillLevel=skillLevel;
}
voidMonster::Serialize(CPacketBuilder&packet)
{
packet.WriteInt(m_id);
packet.WriteString(m_name);
packet.WriteInt(m_level);
packet.WriteInt(m_hp);
packet.WriteInt(m_sp);
packet.WriteInt(m_str);
packet.WriteInt(m_dex);
packet.WriteInt(m_intel);
packet.WriteInt(m_con);
packet.WriteInt(m_skillId);
packet.WriteInt(m_skillLevel);
}
```
#####步骤四:修改`character.cpp`
在`src\character.cpp`文件中添加管理圣兽的逻辑。
**character.cpp**
```cpp
#include"character.h"
#include"monster.h"
Character::Character(intid)
{
m_id=id;
}
voidCharacter::LearnSkill(intskillId)
{
m_skills.push_back(skillId);
}
voidCharacter::AddMonster(Monster*monster)
{
m_monsters.push_back(monster);
}
voidCharacter::RemoveMonster(intmonsterId)
{
for(autoit=m_monsters.begin();it!=m_monsters.end();++it)
{
if((*it)->GetId()==monsterId)
{
delete*it;
m_monsters.erase(it);
break;
}
}
}
voidCharacter::SerializeSkills(CPacketBuilder&packet)
{
packet.WriteInt(static_cast<int>(m_skills.size()));
for(intskillId:m_skills)
{
packet.WriteInt(skillId);
}
}
voidCharacter::SerializeMonsters(CPacketBuilder&packet)
{
packet.WriteInt(static_cast<int>(m_monsters.size()));
for(Monster*monster:m_monsters)
{
monster->Serialize(packet);
}
}
```
#####步骤五:修改`client_network.cpp`
在`src\client_network.cpp`文件中实现客户端与游戏服务器的通信。
**client_network.cpp**
```cpp
#include"client_network.h"
#include"packet_builder.h"
CClientNetwork::CClientNetwork()
{
m_authSocket=INVALID_SOCKET;
m_gameSocket=INVALID_SOCKET;
}
boolCClientNetwork::ConnectToAuthServer(conststd::string&ipintport)
{
m_authSocket=socket(AF_INETSOCK_STREAM0);
if(m_authSocket==INVALID_SOCKET)
{
SystemLog::LogError("Failedtocreatesocket:%d"WSAGetLastError());
returnfalse;
}
sockaddr_inserverAddr;
serverAddr.sin_family=AF_INET;
serverAddr.sin_addr.s_addr=inet_addr(ip.c_str());
serverAddr.sin_port=htons(port);
if(connect(m_authSocket(sockaddr*)&serverAddrsizeof(serverAddr))==SOCKET_ERROR)
{
SystemLog::LogError("Failedtoconnecttoauthserver:%d"WSAGetLastError());
closesocket(m_authSocket);
returnfalse;
}
SystemLog::LogInfo("Connectedtoauthserverat%s:%d"ip.c_str()port);
returntrue;
}
boolCClientNetwork::ConnectToGameServer(conststd::string&ipintport)
{
m_gameSocket=socket(AF_INETSOCK_STREAM0);
if(m_gameSocket==INVALID_SOCKET)
{
SystemLog::LogError("Failedtocreatesocket:%d"WSAGetLastError());
returnfalse;
}
sockaddr_inserverAddr;
serverAddr.sin_family=AF_INET;
serverAddr.sin_addr.s_addr=inet_addr(ip.c_str());
serverAddr.sin_port=htons(port);
if(connect(m_gameSocket(sockaddr*)&serverAddrsizeof(serverAddr))==SOCKET_ERROR)
{
SystemLog::LogError("Failedtoconnecttogameserver:%d"WSAGetLastError());
closesocket(m_gameSocket);
returnfalse;
}
SystemLog::LogInfo("Connectedtogameserverat%s:%d"ip.c_str()port);
returntrue;
}
voidCClientNetwork::SendLoginRequest(conststd::string&usernameconststd::string&password)
{
CPacketBuilderpacket(PACKET_TYPE_LOGIN_REQUEST);
packet.WriteString(username);
packet.WriteString(password);
SendPacketToAuthServer(packet.Build());
}
voidCClientNetwork::SendPacketToAuthServer(constPacket&packet)
{
send(m_authSocketreinterpret_cast<constchar*>(packet.GetData())packet.GetSize()0);
}
voidCClientNetwork::SendPacketToGameServer(constPacket&packet)
{
send(m_gameSocketreinterpret_cast<constchar*>(packet.GetData())packet.GetSize()0);
}
boolCClientNetwork::ReceivePacket(Packet&packet)
{
charbuffer[MAX_PACKET_SIZE];
intbytesRead=recv(m_gameSocketbufferMAX_PACKET_SIZE0);
if(bytesRead<=0)
{
SystemLog::LogWarning("Connectionclosedbyserver.");
returnfalse;
}
packet.SetData(bufferbytesRead);
returntrue;
}
voidCClientNetwork::SendUseSkillRequest(intskillId)
{
CPacketBuilderpacket(PACKET_TYPE_USE_SKILL_REQUEST);
packet.WriteInt(skillId);
SendPacketToGameServer(packet.Build());
}
voidCClientNetwork::HandleSummonMonsterResponse(constPacket&packet)
{
bytestatus=packet.ReadByte();
if(status==SUMMON_MONSTER_SUCCESS)
{
Monstermonster;
monster.Deserialize(packet);
AddMonster(monster);
SystemLog::LogInfo("Summonedmonster[%s]."monster.GetName().c_str());
}
else
{
SystemLog::LogWarning("Failedtosummonmonster.");
}
}
```
#####步骤六:编译并测试
确保所有修改后的代码都能成功编译。
```sh
g++-ogame_serversrc/game_server.cppsrc/database_manager.cppsrc/skill_handler.cppsrc/monster_manager.cppsrc/packet_builder.cpp-lengine
g++-oclientsrc/client_main.cppsrc/client_network.cppsrc/packet_builder.cpp-lengine
```
启动登录服务器、游戏服务器和客户端,观察整个召唤圣兽技能流程是否顺畅。
```sh
startauth_server.exe
startgame_server.exe
startclient.exe
```
####5.日志文件检查
#####查看游戏服务器日志
打开游戏服务器的日志文件(通常位于`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:Loaded2monstersfromdatabase.
[2023-10-0112:34:56]INFO:Player[1]requestedtouseskill[9999].
[2023-10-0112:34:56]INFO:Character[1]summonedmonster[神圣凤凰].
```
根据日志中的信息,确认游戏服务器是否正常运行以及圣兽是否成功召唤。
#####查看客户端日志
打开客户端的日志文件(通常位于`log\client.log`),查找相关的错误信息。
```plaintext
[2023-10-0112:34:56]INFO:Connectingtoauthserverat127.0.0.1:2106.
[2023-10-0112:34:56]INFO:Connectedtoauthserverat127.0.0.1:2106.
[2023-10-0112:34:56]INFO:Logginginastestuser.
[2023-10-0112:34:56]INFO:LoginsuccesulaccountID:1.
[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:Requestingtouseskill[9999].
[2023-10-0112:34:56]INFO:Summonedmonster[神圣凤凰].
```
根据日志中的信息,确认客户端是否正确发送了请求以及服务器的响应。
####6.常见问题及解决方案
#####问题一:无法连接到游戏服务器
-**检查网络设置**:确保客户端和游戏服务器之间的网络连接正常。
-**检查配置文件**:确保`client_config.txt`中的游戏服务器IP和端口配置正确。
-**检查防火墙设置**:确保防火墙没有阻止游戏服务器的端口。
#####问题二:登录失败
-**检查数据库配置**:确保`auth_config.txt`中的数据库配置正确。
-**检查数据库服务**:确保数据库服务正在运行并且可以访问。
-**检查用户数据**:确保`account_table`中包含正确的用户信息。
#####问题三:角色加载失败
-**检查角色数据**:确保`char_table`中包含正确的角色信息。
-**检查物品数据**:确保`item_table`中包含正确的物品信息。
-**检查圣兽数据**:确保`monster_table`中包含正确的圣兽信息。
#####问题四:客户端版本不匹配
-**更新客户端**:确保客户端版本与服务器版本兼容。
-**同步资源文件**:确保客户端和服务器之间的资源文件一致。
#####问题五:圣兽召唤失败
-**检查圣兽数据表**:确保`monster_table`中包含正确的圣兽数据。
-**检查技能数据表**:确保`skill_table`中包含正确的召唤圣兽技能数据。
-**检查物品数据表**:确保`item_table`中包含正确的召唤圣兽技能书数据。
-**检查日志文件**:查看日志文件以确定是否有圣兽召唤失败的记录。
####7.总结
通过以上步骤,你应该能够在GOM引擎的单机传奇版本中成功添加一个可学习的“召唤圣兽”技能。这不仅增加了游戏的乐趣性和互动性,还提升了玩家的游戏体验。希望这篇教程对你有所帮助!

