refactor(fight): 重构战斗模块

- 移除未使用的结构体和接口
- 优化战斗准备和邀请逻辑
- 调整玩家和怪物信息的处理方式
- 更新战斗相关的数据结构
- 重构战斗模式和邀请相关代码
This commit is contained in:
2025-09-02 00:45:29 +08:00
parent 0dbb7d9a8d
commit 39893e4df9
16 changed files with 408 additions and 325 deletions

View File

@@ -2,9 +2,9 @@ package socket
import (
"blazing/common/utils"
"blazing/modules/blazing/model"
"blazing/modules/blazing/service"
"context"
"fmt"
"sync"
@@ -41,6 +41,16 @@ func (c *Conn) SendPack(bytes []byte) error {
return nil
}
type OgreInfo struct {
Data [9]OgrePetInfo
}
type OgrePetInfo struct {
Id uint32
Shiny uint32
Lv uint32 `struc:"skip"` //等级
}
type Player struct {
MainConn *Conn
@@ -54,6 +64,9 @@ type Player struct {
context.Context
Playerinvite uint32 //当前邀请的玩家ID
Onlinetime uint32 //当前登录时间
OgreInfo *OgreInfo
FightID string //绑定战斗标识
//FightInfo info.NoteReadyToFightInfo
}
// PlayerOption 定义配置 Player 的函数类型

View File

@@ -4,6 +4,9 @@ import (
"blazing/common/data/socket"
"blazing/common/socket/errorcode"
"blazing/common/socket/handler"
"math/rand"
"time"
"blazing/logic/service/fight"
"blazing/logic/service/fight/info"
"blazing/modules/blazing/model"
@@ -11,15 +14,15 @@ import (
"github.com/jinzhu/copier"
)
func (h Controller) OnPlayerFightNpcMonster(data *fight.FightNpcMonsterInboundInfo, c *socket.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
func (h Controller) OnPlayerFightNpcMonster(data *info.FightNpcMonsterInboundInfo, c *socket.Player) (result *info.NullOutboundInfo, err errorcode.ErrorCode) {
c.IsFighting = true
t1 := handler.NewTomeeHeader(2503, c.Info.UserID)
ttt := info.NoteReadyToFightInfo{
OwnerID: data.Head.UserID,
FightId: 3,
}
ttt.OurInfo = info.FightUserInfo{UserID: c.Info.UserID, Nick: c.Info.Nick}
copier.Copy(&ttt.OurInfo, &c.Info)
len := len(c.Info.PetList)
ttt.OurPetList = make([]info.ReadyFightPetInfo, len)
for i := 0; i < len; i++ {
@@ -31,61 +34,49 @@ func (h Controller) OnPlayerFightNpcMonster(data *fight.FightNpcMonsterInboundIn
}
ttt.OpponentInfo = info.FightUserInfo{UserID: 0}
ttt.OpponentPetList = []info.ReadyFightPetInfo{{ID: 1,
Level: 100,
MaxHp: 100,
SkillListLen: 4,
Hp: 100}}
for i := 0; i < 4; i++ {
ttt.OpponentPetList[0].SkillList[i] = model.SkillInfo{ID: 10001, Pp: 1}
refpet := c.OgreInfo.Data[data.Number]
dv := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(32)
mo := model.GenPetInfo(refpet.Id, uint32(dv), 0, 1006, refpet.Shiny, refpet.Lv)
ttt.OpponentPetList = make([]info.ReadyFightPetInfo, 1)
err1 := copier.CopyWithOption(&ttt.OpponentPetList[0], &mo, copier.Option{IgnoreEmpty: true, DeepCopy: true})
if err1 != nil {
panic(err)
}
c.SendPack(t1.Pack(&ttt))
fight.NewFight(&ttt, c) //把两个玩家都传进去
return nil, -1
}
// 准备战斗
func (h Controller) OnReadyToFight(data *fight.ReadyToFightInboundInfo, c *socket.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
func (h Controller) OnReadyToFight(data *info.ReadyToFightInboundInfo, c *socket.Player) (result *info.NullOutboundInfo, err errorcode.ErrorCode) {
t1 := handler.NewTomeeHeader(2504, c.Info.UserID)
rett := fight.FightStartOutboundInfo{
IsCanAuto: 0,
Info2: fight.FightPetInfo{
UserID: 0,
ID: 1,
Hp: 1000,
MaxHp: 1000,
Level: 1,
CatchTime: 0,
Catchable: 1,
BattleLV: [6]byte{1, 1, 1, 1, 1, 1}},
}
copier.Copy(&rett.Info1, &c.Info.PetList[0])
rett.Info1.UserID = c.Info.UserID
c.SendPack(t1.Pack(&rett))
fight.ReadyFight(c)
return nil, -1
}
// 接收战斗或者取消战斗的包
func (h Controller) OnPlayerHandleFightInvite(data *fight.HandleFightInviteInboundInfo, c *socket.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
func (h Controller) OnPlayerHandleFightInvite(data *info.HandleFightInviteInboundInfo, c *socket.Player) (result *info.NullOutboundInfo, err errorcode.ErrorCode) {
return nil, -1
}
// 使用技能包
func (h Controller) UseSkill(data *fight.UseSkillInboundInfo, c *socket.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
func (h Controller) UseSkill(data *info.UseSkillInboundInfo, c *socket.Player) (result *info.NullOutboundInfo, err errorcode.ErrorCode) {
return nil, 0
}
// 战斗逃跑
func (h Controller) Escape(data *fight.EscapeFightInboundInfo, c *socket.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
func (h Controller) Escape(data *info.EscapeFightInboundInfo, c *socket.Player) (result *info.NullOutboundInfo, err errorcode.ErrorCode) {
defer func() {
//战斗结束Escape
ttt := handler.NewTomeeHeader(2506, c.Info.UserID)
c.SendPack(ttt.Pack(&fight.FightOverInfo{
c.SendPack(ttt.Pack(&info.FightOverInfo{
Reason: 0,
}))
c.IsFighting = false

View File

@@ -26,7 +26,7 @@ func (h Controller) OnGetRoomPetShowInfo(data *room.PetRoomListInboundInfo, c *s
// 获取自己房间的家具
func (h Controller) OnGetFitmentAll(data *room.FitmentAllInboundEmpty, c *socket.Player) (result *room.FitmentAllOutboundInfo, err errorcode.ErrorCode) {
result = &room.FitmentAllOutboundInfo{}
result.Fitments = make([]room.FitmentShowInfo, 0)
result.Fitments = append(result.Fitments, room.FitmentShowInfo{Id: 500001, Status: 1, X: 1, Y: 1, Dir: 1})
result.Fitments = make([]room.FitmentItemInfo, 0)
return
}

View File

@@ -1,61 +0,0 @@
package fight
import "blazing/logic/service/fight/info"
func NewBattle(i info.NoteReadyToFightInfo) {
//调用技能产生effect参数后创建effect实例而非技能创建
c := info.NewBattleContainer1v1(i) //创建战斗容器
t := info.BattleSkillEntity{} //
t.SideEffectArgs = []int{1, 2, 3}
//先提交effecet后然后计算伤害
//物理和特殊,百分比和固定伤害也算,真实伤害不分类别,故直接扣血就行,不需要计算
if t.Category() == info.Category.PHYSICAL || t.Category() == info.Category.SPECIAL {
c.Effects.Exec(func(e info.Effect) bool {
return e.PreDamage() //执行预处理效果
})
// Apply pre-damage effects for pet sources
// battle.applyEffects(context, EffectTrigger.PreDamage)
// if (context.crit) {
// battle.applyEffects(context, EffectTrigger.OnCritPreDamage) // Trigger crit pre-damage effects
// }
//假如说这里需要能力提升
// c.Exec(func(e Effect) func() {
// return e.OnBeforeAddMark()
// })
//添加印记前的效果如果有任何一个false,说明组织了添加效果
//这里能力提升
// c.Exec(func(e Effect) bool {
// return e.OnAnyMarkAdded()
// })
// var candidates []*Effect
// for _, eff := range c.Effects {
// if eff.Trigger == trigger {
// candidates = append(candidates, eff)
// }
// }
// // 按优先级排序
// sort.SliceStable(candidates, func(i, j int) bool {
// return candidates[i].Priority > candidates[j].Priority
// })
// // 执行
// for _, eff := range candidates {
// ctx.Effect = eff
// keep := eff.Apply(ctx)
// if !keep {
// // 持续回合结束 / 返回 false 的 effect 删除
// c.removeEffect(eff)
// }
// if ctx.Done {
// break // 被拦截
// }
// }
}
}

View File

@@ -1,102 +1,127 @@
package fight
import (
"blazing/common/data/socket"
"blazing/common/socket/handler"
"blazing/modules/blazing/model"
"blazing/logic/service/fight/info"
"strings"
"github.com/google/uuid"
"github.com/jinzhu/copier"
)
// 野怪对战包
type FightNpcMonsterInboundInfo struct {
Head handler.TomeeHeader `cmd:"2408" struc:"[0]pad"`
// Number 地图刷新怪物结构体对应的序号1-9的位置序号
// @UInt long类型使用uint32保持无符号特性
Number uint32 `fieldDesc:"地图刷新怪物结构体对应的序号 1 - 9 的位置序号" `
var FightCache = make(map[string]*info.NoteReadyToFightInfo) //默认our是房主
func ReadyFight(c *socket.Player) {
tt, _ := FightCache[c.FightID]
t1 := handler.NewTomeeHeader(2504, c.Info.UserID)
rett := info.FightStartOutboundInfo{
IsCanAuto: 0,
}
copier.Copy(&rett.Info1, &c.Info.PetList[0]) //复制自己的信息
switch tt.FightId { //判断战斗类型
case 1:
//1v1
if c.Info.UserID == tt.OwnerID { //这个时候是房主发来的消息
tt.AFinished = true
if tt.BFinished {
copier.Copy(&rett.Info2, &tt.OpponentPetList[0]) //复制对方信息
c.SendPack(t1.Pack(&rett)) //给自己发
tt.Opp.SendPack(t1.Pack(&rett)) //给对方发
}
} else {
tt.BFinished = true
if tt.AFinished { //如果房主完成
copier.Copy(&rett.Info2, &tt.OurPetList[0]) //复制房主信息,因为这时候不是房主发来的消息
c.SendPack(t1.Pack(&rett)) //给自己发
tt.Our.SendPack(t1.Pack(&rett)) //给房主发
}
}
case 3: //野怪战斗
copier.Copy(&rett.Info2, &tt.OpponentPetList[0])
rett.Info1.UserID = c.Info.UserID //用户ID复制进去
c.SendPack(t1.Pack(&rett))
}
}
func NewFight(i *info.NoteReadyToFightInfo, plays *socket.Player) {
t12, _ := uuid.NewV7()
uuid := strings.Replace(t12.String(), "-", "", -1) //绑定战斗ID
FightCache[uuid] = i
//先发送战斗准备包
switch i.FightId {
case 1:
//1v1
case 3: //野怪战斗
t1 := handler.NewTomeeHeader(2503, plays.Info.UserID)
plays.FightID = uuid //绑定战斗ID
plays.SendPack(t1.Pack(i)) //准备包由各自发,因为协议不一样
}
//调用技能产生effect参数后创建effect实例而非技能创建
c := info.NewBattleContainer1v1(*i) //创建战斗容器
t := info.BattleSkillEntity{} //
t.SideEffectArgs = []int{1, 2, 3}
//先提交effecet后然后计算伤害
//物理和特殊,百分比和固定伤害也算,真实伤害不分类别,故直接扣血就行,不需要计算
if t.Category() == info.Category.PHYSICAL || t.Category() == info.Category.SPECIAL {
c.Effects.Exec(func(e info.Effect) bool {
return e.PreDamage() //执行预处理效果
})
// Apply pre-damage effects for pet sources
// battle.applyEffects(context, EffectTrigger.PreDamage)
// if (context.crit) {
// battle.applyEffects(context, EffectTrigger.OnCritPreDamage) // Trigger crit pre-damage effects
// }
//假如说这里需要能力提升
// c.Exec(func(e Effect) func() {
// return e.OnBeforeAddMark()
// })
//添加印记前的效果如果有任何一个false,说明组织了添加效果
//这里能力提升
// c.Exec(func(e Effect) bool {
// return e.OnAnyMarkAdded()
// })
// var candidates []*Effect
// for _, eff := range c.Effects {
// if eff.Trigger == trigger {
// candidates = append(candidates, eff)
// }
// }
// // 按优先级排序
// sort.SliceStable(candidates, func(i, j int) bool {
// return candidates[i].Priority > candidates[j].Priority
// })
// // 执行
// for _, eff := range candidates {
// ctx.Effect = eff
// keep := eff.Apply(ctx)
// if !keep {
// // 持续回合结束 / 返回 false 的 effect 删除
// c.removeEffect(eff)
// }
// if ctx.Done {
// break // 被拦截
// }
// }
}
type NullOutboundInfo struct {
}
// 准备战斗包
type ReadyToFightInboundInfo struct {
Head handler.TomeeHeader `cmd:"2404" struc:"[0]pad"`
}
type FightStartOutboundInfo struct {
// @UInt long类型
IsCanAuto uint32 `fieldDesc:"是否自动 默认给0 怀疑是自动战斗器使用的" `
// 当前战斗精灵信息1前端通过userid判断是否为我方
Info1 FightPetInfo `fieldDesc:"当前战斗精灵的信息 可能不准.看前端代码是以userid来判断哪个结构体是我方的" serialize:"struct"`
// 当前战斗精灵信息2前端通过userid判断是否为我方
Info2 FightPetInfo `fieldDesc:"当前战斗精灵的信息 可能不准.看前端代码是以userid来判断哪个结构体是我方的" serialize:"struct"`
}
// FightPetInfo 战斗精灵信息结构体FightPetInfo类
type FightPetInfo struct {
// 用户ID野怪为0@UInt long
UserID uint32 `fieldDesc:"用户ID 野怪为0" `
// 当前对战精灵ID@UInt long
ID uint32 `fieldDesc:"当前对战精灵ID" `
Name string `struc:"[16]byte"`
// 精灵的捕获时间,@UInt long
CatchTime uint32 `fieldDesc:"精灵的捕获时间" `
// 当前HP@UInt long
Hp uint32 `fieldDesc:"当前HP" `
// 最大HP@UInt long
MaxHp uint32 `fieldDesc:"最大HP" `
// 当前等级,@UInt long
Level uint32 `fieldDesc:"当前等级" `
// 精灵是否能捕捉1为能捕捉0为不能捕捉@UInt long
Catchable uint32 `fieldDesc:"精灵是否能捕捉. 1为能捕捉 0为不能捕捉" `
// 战斗属性等级数组6个单字节
// byte[]固定长度6存储buff等级、攻击、速度等属性
BattleLV [6]byte `fieldDesc:"这里实际上应该是6个单字节byte, 内容为buff等级 攻击 速度 特攻 防御 特防 命中等.但具体顺序未知可能需要测试. 具体数值为1-6等级" serialize:"fixedLength=6,type=byteArray"`
}
// AttackValue 战斗中的攻击数值信息
type AttackValue struct {
UserID uint32 `json:"userId" fieldDescription:"玩家的米米号 与野怪对战userid = 0"`
SkillID uint32 `json:"skillId" fieldDescription:"使用技能的id"`
AttackTime uint32 `json:"attackTime" fieldDescription:"是否击中 如果为0 则miss 如果为1 则击中"`
LostHp uint32 `json:"lostHp" fieldDescription:"我方造成的伤害"`
GainHp uint32 `json:"gainHp" fieldDescription:"我方获得血量"`
RemainHp uint32 `json:"remainHp" fieldDescription:"我方剩余血量"`
MaxHp uint32 `json:"maxHp" fieldDescription:"我方最大血量"`
State uint32 `json:"state" fieldDescription:"固定值0 需要后续测试"`
SkillList []model.SkillInfo `json:"skillList" fieldDescription:"根据精灵的数据插入技能 最多4条 不定长"`
IsCritical uint32 `json:"isCritical" fieldDescription:"是否暴击"`
Status [20]byte `json:"status" fieldDescription:"20个字节 各种状态: 0:\"麻痹\",1:\"中毒\",2:\"烧伤\",4:\"寄生\",5:\"冻伤\",6:\"害怕\",7:\"疲惫\",8:\"睡眠\",9:\"石化\",10:\"混乱\",15:\"冰封\",16:\"流血\""`
BattleLv [6]byte `json:"battleLv" fieldDescription:"6个单字节byte, 内容为buff等级 攻击 速度 特攻 防御 特防命中等. 但具体顺序未知可能需要测试. 具体数值为1-6等级"`
// OwnerMaxShield uint32 `json:"ownerMaxShield" fieldDescription:"我方最大护盾"`
// OwnerCurrentShield uint32 `json:"ownerCurrentShield" fieldDescription:"我方当前护盾"`
}
// NoteUseSkillOutboundInfo 战斗技能使用通知的出站信息结构体
type NoteUseSkillOutboundInfo struct {
FirstAttackInfo AttackValue // 本轮先手的精灵在释放技能结束后的状态
SecondAttackInfo AttackValue // 本轮后手的精灵在释放技能结束后的状态
}
// 战斗逃跑
type EscapeFightInboundInfo struct {
Head handler.TomeeHeader `cmd:"2410" struc:"[0]pad"`
}
// FightOverInfo 战斗结束信息结构体 2506
type FightOverInfo struct {
Reason uint32 // 固定值0
WinnerId uint32 // 胜者的米米号 野怪为0
TwoTimes uint32 // 双倍经验剩余次数
ThreeTimes uint32 // 三倍经验剩余次数
AutoFightTimes uint32 // 自动战斗剩余次数
EnergyTimes uint32 // 能量吸收器剩余次数
LearnTimes uint32 // 双倍学习器剩余次数
}

View File

@@ -7,9 +7,9 @@ import (
const Input_ctx = "player"
type BattleInputSourceEntity struct {
FightUserInfo //用户信息
FightUserInfo //用户信息
PetEntities []*BattlePetEntity //宠物信息
ctx context.Context //输入源的上下文
ctx context.Context //输入源的上下文
}

View File

@@ -1,70 +0,0 @@
package info
import (
"blazing/modules/blazing/model"
)
type FightUserInfo struct {
// 用户ID野怪为0@UInt long
UserID uint32 `fieldDesc:"userID 如果为野怪则为0" `
// 玩家名称野怪为UTF-8的'-'固定16字节
// 使用[16]byte存储固定长度的字节数组
Nick string `struc:"[16]byte"`
}
// NoteReadyToFightInfo 战斗准备就绪消息结构体NoteReadyToFightInfo
type NoteReadyToFightInfo struct {
//战斗发起者ID
OwnerID uint32 `struc:"skip"`
// 战斗类型ID与野怪战斗为3与人战斗为1前端似乎未使用
// @UInt long
FightId uint32 `fieldDesc:"战斗类型ID 但前端好像没有用到 与野怪战斗为3与人战斗似乎是1" `
// 我方信息
OurInfo FightUserInfo `fieldDesc:"我方信息" serialize:"struct"`
OurPetListLen uint32 `struc:"sizeof=OurPetList"`
// 我方携带精灵的信息
// ArrayList<ReadyFightPetInfo>,使用切片模拟动态列表
OurPetList []ReadyFightPetInfo `fieldDesc:"我方携带精灵的信息" serialize:"lengthFirst,lengthType=uint16,type=structArray"`
// 对方信息
OpponentInfo FightUserInfo `fieldDesc:"对方信息" serialize:"struct"`
OpponentPetListLen uint32 `struc:"sizeof=OpponentPetList"`
// 敌方的精灵信息
// 野怪战斗时客户端接收此包前已生成精灵PetInfo将部分信息写入该列表
OpponentPetList []ReadyFightPetInfo `fieldDesc:"敌方的精灵信息 如果是野怪 那么再给客户端发送这个包体时就提前生成好了这只精灵的PetInfo,然后把从PetInfo中把部分信息写入到这个敌方的精灵信息中再发送这个包结构体" serialize:"lengthFirst,lengthType=uint16,type=structArray"`
}
// ReadyFightPetInfo 准备战斗的精灵信息结构体ReadyFightPetInfo类
type ReadyFightPetInfo struct {
// 精灵ID@UInt long
ID uint32 `fieldDesc:"精灵ID" `
// 精灵等级,@UInt long
Level uint32 `fieldDesc:"精灵等级" `
// 精灵当前HP@UInt long
Hp uint32 `fieldDesc:"精灵HP" `
// 精灵最大HP@UInt long
MaxHp uint32 `fieldDesc:"最大HP" `
SkillListLen uint32
// 技能信息列表固定4个元素技能ID和剩余PP无技能则为0
// List<SkillInfo>初始化容量为4
SkillList [4]model.SkillInfo `fieldDesc:"技能信息 技能ID跟剩余PP 固定32字节 没有给0" serialize:"fixedLength=4,type=structArray"`
// 精灵捕获时间,@UInt long
CatchTime uint32 `fieldDesc:"精灵捕获时间" `
// 捕捉地图固定给0@UInt long
CatchMap uint32 `fieldDesc:"捕捉地图 给0" `
// 固定给0@UInt long
CatchRect uint32 `fieldDesc:"给0" `
// 固定给0@UInt long
CatchLevel uint32 `fieldDesc:"给0" `
SkinID uint32 `fieldDesc:"精灵皮肤ID" `
Shiny uint32 `fieldDesc:"精灵是否闪" `
}

View File

@@ -65,3 +65,16 @@ var BattleOverReason = enum.New[struct {
PlayerCaptureSuccess EnumBattleOverReason `enum:"3"` //捕捉成功
DefaultEnd EnumBattleOverReason `enum:"4"` //默认结束
}]()
// 战斗模式
var BattleMode_PVP = enum.New[struct {
PVP_1V1 EnumBattleMode `enum:"1"`
PVP_6V6 EnumBattleMode `enum:"2"`
}]()
var Playerinvitemap map[uint32][]Playerinvite = make(map[uint32][]Playerinvite) //玩家邀请信息 ,比如一个玩家被多人邀请对战
type Playerinvite struct { //挂载到[]Playerinvite上? 被邀请者->邀请者
InviteID uint32 // 邀请者
InviteTime EnumBattleMode //游戏模式
}

View File

@@ -0,0 +1,203 @@
package info
import (
"blazing/common/data/socket"
"blazing/common/socket/handler"
"blazing/modules/blazing/model"
"fmt"
)
// 野怪对战包
type FightNpcMonsterInboundInfo struct {
Head handler.TomeeHeader `cmd:"2408" struc:"[0]pad"`
// Number 地图刷新怪物结构体对应的序号1-9的位置序号
// @UInt long类型使用uint32保持无符号特性
Number uint32 `fieldDesc:"地图刷新怪物结构体对应的序号 1 - 9 的位置序号" `
}
type NullOutboundInfo struct {
}
// 准备战斗包
type ReadyToFightInboundInfo struct {
Head handler.TomeeHeader `cmd:"2404" struc:"[0]pad"`
}
type FightStartOutboundInfo struct {
// @UInt long类型
IsCanAuto uint32 `fieldDesc:"是否自动 默认给0 怀疑是自动战斗器使用的" `
// 当前战斗精灵信息1前端通过userid判断是否为我方
Info1 FightPetInfo `fieldDesc:"当前战斗精灵的信息 可能不准.看前端代码是以userid来判断哪个结构体是我方的" serialize:"struct"`
// 当前战斗精灵信息2前端通过userid判断是否为我方
Info2 FightPetInfo `fieldDesc:"当前战斗精灵的信息 可能不准.看前端代码是以userid来判断哪个结构体是我方的" serialize:"struct"`
}
// FightPetInfo 战斗精灵信息结构体FightPetInfo类
type FightPetInfo struct {
// 用户ID野怪为0@UInt long
UserID uint32 `fieldDesc:"用户ID 野怪为0" `
// 当前对战精灵ID@UInt long
ID uint32 `fieldDesc:"当前对战精灵ID" `
Name string `struc:"[16]byte"`
// 精灵的捕获时间,@UInt long
CatchTime uint32 `fieldDesc:"精灵的捕获时间" `
// 当前HP@UInt long
Hp uint32 `fieldDesc:"当前HP" `
// 最大HP@UInt long
MaxHp uint32 `fieldDesc:"最大HP" `
// 当前等级,@UInt long
Level uint32 `fieldDesc:"当前等级" `
// 精灵是否能捕捉1为能捕捉0为不能捕捉@UInt long
Catchable uint32 `fieldDesc:"精灵是否能捕捉. 1为能捕捉 0为不能捕捉" `
// 战斗属性等级数组6个单字节
// byte[]固定长度6存储buff等级、攻击、速度等属性
BattleLV [6]byte `fieldDesc:"这里实际上应该是6个单字节byte, 内容为buff等级 攻击 速度 特攻 防御 特防 命中等.但具体顺序未知可能需要测试. 具体数值为1-6等级" serialize:"fixedLength=6,type=byteArray"`
}
// AttackValue 战斗中的攻击数值信息
type AttackValue struct {
UserID uint32 `json:"userId" fieldDescription:"玩家的米米号 与野怪对战userid = 0"`
SkillID uint32 `json:"skillId" fieldDescription:"使用技能的id"`
AttackTime uint32 `json:"attackTime" fieldDescription:"是否击中 如果为0 则miss 如果为1 则击中"`
LostHp uint32 `json:"lostHp" fieldDescription:"我方造成的伤害"`
GainHp uint32 `json:"gainHp" fieldDescription:"我方获得血量"`
RemainHp uint32 `json:"remainHp" fieldDescription:"我方剩余血量"`
MaxHp uint32 `json:"maxHp" fieldDescription:"我方最大血量"`
State uint32 `json:"state" fieldDescription:"固定值0 需要后续测试"`
SkillList []model.SkillInfo `json:"skillList" fieldDescription:"根据精灵的数据插入技能 最多4条 不定长"`
IsCritical uint32 `json:"isCritical" fieldDescription:"是否暴击"`
Status [20]byte `json:"status" fieldDescription:"20个字节 各种状态: 0:\"麻痹\",1:\"中毒\",2:\"烧伤\",4:\"寄生\",5:\"冻伤\",6:\"害怕\",7:\"疲惫\",8:\"睡眠\",9:\"石化\",10:\"混乱\",15:\"冰封\",16:\"流血\""`
BattleLv [6]byte `json:"battleLv" fieldDescription:"6个单字节byte, 内容为buff等级 攻击 速度 特攻 防御 特防命中等. 但具体顺序未知可能需要测试. 具体数值为1-6等级"`
// OwnerMaxShield uint32 `json:"ownerMaxShield" fieldDescription:"我方最大护盾"`
// OwnerCurrentShield uint32 `json:"ownerCurrentShield" fieldDescription:"我方当前护盾"`
}
// NoteUseSkillOutboundInfo 战斗技能使用通知的出站信息结构体
type NoteUseSkillOutboundInfo struct {
FirstAttackInfo AttackValue // 本轮先手的精灵在释放技能结束后的状态
SecondAttackInfo AttackValue // 本轮后手的精灵在释放技能结束后的状态
}
// 战斗逃跑
type EscapeFightInboundInfo struct {
Head handler.TomeeHeader `cmd:"2410" struc:"[0]pad"`
}
// FightOverInfo 战斗结束信息结构体 2506
type FightOverInfo struct {
Reason uint32 // 固定值0
WinnerId uint32 // 胜者的米米号 野怪为0
TwoTimes uint32 // 双倍经验剩余次数
ThreeTimes uint32 // 三倍经验剩余次数
AutoFightTimes uint32 // 自动战斗剩余次数
EnergyTimes uint32 // 能量吸收器剩余次数
LearnTimes uint32 // 双倍学习器剩余次数
}
// HandleFightInviteInboundInfo 处理战斗邀请的入站消息
// 回空包就行
type HandleFightInviteInboundInfo struct {
Head handler.TomeeHeader `cmd:"2403" struc:"[0]pad"`
UserID uint32 `json:"userId" codec:"userId,uint"` // 邀请我对战人的userid
Flag uint32 `json:"flag" codec:"flag,uint"` // 1为同意对战 0为取消对战
Mode uint32 `json:"mode" codec:"mode,uint"` // 战斗类型 1 = 1v1 2 = 6v6
}
// 2502的回复包 PVP邀请消息
type NoteHandleFightInviteOutboundInfo struct {
UserID uint32
Nickname string `struc:"[16]byte"` // 固定长度16字节
Result uint32 // 0=拒绝 1=同意 2=在线超6小时 3=无出战精灵 4=不在线
}
// 实现入站消息接口Go中通过方法集隐式实现
type UseSkillInboundInfo struct {
Head handler.TomeeHeader `cmd:"2405" struc:"[0]pad"`
// 技能id
SkillId uint32
}
type FightUserInfo struct {
// 用户ID野怪为0@UInt long
UserID uint32 `fieldDesc:"userID 如果为野怪则为0" `
// 玩家名称野怪为UTF-8的'-'固定16字节
// 使用[16]byte存储固定长度的字节数组
Nick string `struc:"[16]byte"`
}
// NoteReadyToFightInfo 战斗准备就绪消息结构体NoteReadyToFightInfo
type NoteReadyToFightInfo struct {
MAXPET uint32 `struc:"skip"` //,最大精灵数
//战斗发起者ID
OwnerID uint32 `struc:"skip"`
AFinished bool `struc:"skip"`
BFinished bool `struc:"skip"`
// 战斗类型ID与野怪战斗为3与人战斗为1前端似乎未使用
// @UInt long
FightId EnumBattleMode `fieldDesc:"战斗类型ID 但前端好像没有用到 与野怪战斗为3与人战斗似乎是1" `
// 我方信息
OurInfo FightUserInfo `fieldDesc:"我方信息" serialize:"struct"`
Our *socket.Player `struc:"skip"`
OurPetListLen uint32 `struc:"sizeof=OurPetList"`
// 我方携带精灵的信息
// ArrayList<ReadyFightPetInfo>,使用切片模拟动态列表
OurPetList []ReadyFightPetInfo `fieldDesc:"我方携带精灵的信息" serialize:"lengthFirst,lengthType=uint16,type=structArray"`
// 对方信息
OpponentInfo FightUserInfo `fieldDesc:"对方信息" serialize:"struct"`
Opp *socket.Player `struc:"skip"`
OpponentPetListLen uint32 `struc:"sizeof=OpponentPetList"`
// 敌方的精灵信息
// 野怪战斗时客户端接收此包前已生成精灵PetInfo将部分信息写入该列表
OpponentPetList []ReadyFightPetInfo `fieldDesc:"敌方的精灵信息 如果是野怪 那么再给客户端发送这个包体时就提前生成好了这只精灵的PetInfo,然后把从PetInfo中把部分信息写入到这个敌方的精灵信息中再发送这个包结构体" serialize:"lengthFirst,lengthType=uint16,type=structArray"`
}
// 当A和B都 这时候给双方回复开始战斗包
func (t *NoteReadyToFightInfo) onBothFinished() {
fmt.Println("A和B都已完成触发onBothFinished")
}
// ReadyFightPetInfo 准备战斗的精灵信息结构体ReadyFightPetInfo类
type ReadyFightPetInfo struct {
// 精灵ID@UInt long
ID uint32 `fieldDesc:"精灵ID" `
// 精灵等级,@UInt long
Level uint32 `fieldDesc:"精灵等级" `
// 精灵当前HP@UInt long
Hp uint32 `fieldDesc:"精灵HP" `
// 精灵最大HP@UInt long
MaxHp uint32 `fieldDesc:"最大HP" `
SkillListLen uint32
// 技能信息列表固定4个元素技能ID和剩余PP无技能则为0
// List<SkillInfo>初始化容量为4
SkillList [4]model.SkillInfo `fieldDesc:"技能信息 技能ID跟剩余PP 固定32字节 没有给0" serialize:"fixedLength=4,type=structArray"`
// 精灵捕获时间,@UInt long
CatchTime uint32 `fieldDesc:"精灵捕获时间" `
// 捕捉地图固定给0@UInt long
CatchMap uint32 `fieldDesc:"捕捉地图 给0" `
// 固定给0@UInt long
CatchRect uint32 `fieldDesc:"给0" `
// 固定给0@UInt long
CatchLevel uint32 `fieldDesc:"给0" `
SkinID uint32 `fieldDesc:"精灵皮肤ID" `
Shiny uint32 `fieldDesc:"精灵是否闪" `
}

View File

@@ -1,18 +0,0 @@
package info
import "github.com/tnnmigga/enum"
// 战斗模式
var BattleMode_PVP = enum.New[struct {
PVP_1V1 EnumBattleMode `enum:"1"`
PVP_6V6 EnumBattleMode `enum:"2"`
}]()
var Playerinvitemap map[uint32][]Playerinvite = make(map[uint32][]Playerinvite) //玩家邀请信息 ,比如一个玩家被多人邀请对战
type Playerinvite struct { //挂载到[]Playerinvite上? 被邀请者->邀请者
InviteID uint32 // 邀请者
InviteTime EnumBattleMode //游戏模式
}

View File

@@ -1,26 +0,0 @@
package fight
import "blazing/common/socket/handler"
// HandleFightInviteInboundInfo 处理战斗邀请的入站消息
// 回空包就行
type HandleFightInviteInboundInfo struct {
Head handler.TomeeHeader `cmd:"2403" struc:"[0]pad"`
UserID uint32 `json:"userId" codec:"userId,uint"` // 邀请我对战人的userid
Flag uint32 `json:"flag" codec:"flag,uint"` // 1为同意对战 0为取消对战
Mode uint32 `json:"mode" codec:"mode,uint"` // 战斗类型 1 = 1v1 2 = 6v6
}
// 2502的回复包 PVP邀请消息
type NoteHandleFightInviteOutboundInfo struct {
UserID uint32
Nickname string `struc:"[16]byte"` // 固定长度16字节
Result uint32 // 0=拒绝 1=同意 2=在线超6小时 3=无出战精灵 4=不在线
}
// 实现入站消息接口Go中通过方法集隐式实现
type UseSkillInboundInfo struct {
Head handler.TomeeHeader `cmd:"2405" struc:"[0]pad"`
// 技能id
SkillId uint32
}

View File

@@ -1,11 +0,0 @@
package maps
type OgreInfo struct {
Data [9]OgrePetInfo
}
type OgrePetInfo struct {
Id uint32
Shiny uint32
Lv uint32 `struc:"skip"` //等级
}

View File

@@ -65,6 +65,7 @@ func (t *InInfo) SpawnMonsters(c *socket.Player, isfrist bool) {
}
t1 := t.genMonster(c.Info.MapID)
if t1 != nil {
c.OgreInfo = t1
t1 := tt.Pack(t1)
c.SendPack(t1)
}
@@ -80,16 +81,16 @@ func RandomStringFromSlice(s []string) string {
}
// 应该根据怪物信息决定后端生成
func (t *InInfo) genMonster(mapid uint32) *OgreInfo {
func (t *InInfo) genMonster(mapid uint32) *socket.OgreInfo {
// 设置怪物信息
t1 := OgreInfo{}
t1 := socket.OgreInfo{}
mapss, ok := xmlres.MonsterMap[gconv.Int(mapid)]
if ok && mapss.Monsters != nil {
for i, m := range mapss.Monsters.Monsters { //这里是9个
id := strings.Split(m.ID, " ")
lv := strings.Split(m.Lv, " ")
ttt := OgrePetInfo{
ttt := socket.OgrePetInfo{
Id: gconv.Uint32(RandomStringFromSlice(id)),
}
if ttt.Id != 0 {
@@ -102,7 +103,7 @@ func (t *InInfo) genMonster(mapid uint32) *OgreInfo {
}
t2 := OgreInfo{}
t2 := socket.OgreInfo{}
for i := 0; i < 3; i++ {
t2.Data[t.monsters[i]] = t1.Data[t.monsters[i]]

View File

@@ -55,5 +55,17 @@ type FitmentAllInboundEmpty struct {
type FitmentAllOutboundInfo struct {
FitmentsLen uint32 `json:"fitmentsLen" struc:"sizeof=Fitments"`
// 基地摆放物品的数组, 就算没有摆放物品, 也必带一个小屋的参数
Fitments []FitmentShowInfo `codec:"auto" json:"fitments"`
Fitments []FitmentItemInfo `codec:"auto" json:"fitments"`
}
// FitmentItemInfo 对应Java的FitmentItemInfo类
type FitmentItemInfo struct {
// Id 家具id 或 默认房型id: 500001对应Java的@UInt long id
Id uint32 `json:"id"`
// UsedCount 使用数量默认房型id项目的使用数据固定为1对应Java的@UInt long usedCount
UsedCount uint32 `json:"usedCount"`
// AllCount 拥有数量默认房型id项目的拥有数量固定为1对应Java的@UInt long allCount
AllCount uint32 `json:"allCount"`
}

View File

@@ -22,6 +22,16 @@ type Pet struct {
Data string `gorm:"type:text;not null;comment:'精灵全部数据'" json:"data"`
}
func LastFourElements[T any](s []T) []T {
n := len(s)
if n <= 4 {
// 切片长度小于等于4时返回整个切片
return s
}
// 切片长度大于4时返回最后4个元素从n-4索引到末尾
return s[n-4:]
}
// * @param petTypeId 精灵类型ID
// * @param individualValue 个体值
// * @param natureId 性格ID
@@ -29,19 +39,19 @@ type Pet struct {
// * @param isShiny 是否为闪光
// * @param level 等级
// * @return 生成的精灵实体
func GenPetInfo(id, individual, natureId, abilityTypeEnum, shinyid, level uint32) *PetInfo {
func GenPetInfo(id, dv, natureId, abilityTypeEnum, shinyid, level uint32) *PetInfo {
p := &PetInfo{ID: id,
Shiny: shinyid, //闪光
Nature: natureId, //性格
Dv: individual,
Dv: dv,
EffectInfo: make([]PetEffectInfo, 0),
CatchTime: uint32(time.Now().Unix()),
Level: level} //等级
p.EffectInfo = append(p.EffectInfo, PetEffectInfo{ItemID: abilityTypeEnum})
petxml := xmlres.PetMAP[int(id)]
naxml := xmlres.NatureRootMap[int(natureId)]
petxml := xmlres.PetMAP[int(id)]
tttt := make([]uint32, 0)
for _, v := range petxml.LearnableMoves.Moves {
if p.Level >= uint32(v.LearningLv) {
@@ -50,6 +60,7 @@ func GenPetInfo(id, individual, natureId, abilityTypeEnum, shinyid, level uint32
}
}
tttt = LastFourElements(tttt) //获取最后四个技能,如果不足,那就取全部技能
for i := 0; i < len(tttt); i++ {
p.SkillList[i].ID = tttt[i]
p.SkillList[i].Pp = uint32(xmlres.SkillMap[int(tttt[i])].MaxPP)

Binary file not shown.