refactor(fight): 重构战斗系统

- 优化了技能解析和存储逻辑
- 重构了战斗流程和回合结算机制
- 调整了数据结构以提高性能
- 移除了未使用的代码和注释
This commit is contained in:
2025-09-04 23:57:22 +08:00
parent 51407864b7
commit 7d48e9ab64
17 changed files with 224 additions and 377 deletions

View File

@@ -49,10 +49,18 @@ func initfile() {
})
Skill := getXml[MovesTbl](path + "227.xml")
SkillMap = utils.ToMap[Move, int](Skill.Moves, func(m Move) int {
return m.ID
// SkillMap = utils.ToMap[Move, int](Skill.Moves, func(m Move) int {
// return m.ID
// })
SkillMap = make(map[int]Move, len(Skill.Moves))
for _, v := range Skill.Moves {
v.SideEffectS = parseSideEffectArgs(v.SideEffect)
v.SideEffectArgS = parseSideEffectArgs(v.SideEffectArg)
SkillMap[v.ID] = v
}
})
pet := getXml[Monsters](path + "226.xml")
PetMAP = utils.ToMap[PetInfo, int](pet.Monsters, func(m PetInfo) int {
return m.ID

View File

@@ -20,11 +20,11 @@ type PetInfo struct {
Type int `xml:"Type,attr"` //类型
GrowthType int `xml:"GrowthType,attr"` //成长类型
HP int `xml:"HP,attr"` //血量种族值
Atk int `xml:"Atk,attr"`
Def int `xml:"Def,attr"`
SpAtk int `xml:"SpAtk,attr"`
SpDef int `xml:"SpDef,attr"`
Spd int `xml:"Spd,attr"`
Atk uint32 `xml:"Atk,attr"` //攻击种族值
Def uint32 `xml:"Def,attr"`
SpAtk uint32 `xml:"SpAtk,attr"`
SpDef uint32 `xml:"SpDef,attr"`
Spd uint32 `xml:"Spd,attr"`
YieldingExp int `xml:"YieldingExp,attr"`
CatchRate string `xml:"CatchRate,attr"`
YieldingEV string `xml:"YieldingEV,attr"`

View File

@@ -5,9 +5,28 @@ import (
"fmt"
"io"
"net/http"
"strconv"
"strings"
"time"
)
func parseSideEffectArgs(argsStr string) []int {
if argsStr == "" {
return []int{}
}
parts := strings.Fields(argsStr)
args := make([]int, 0, len(parts))
for _, part := range parts {
if num, err := strconv.Atoi(part); err == nil {
args = append(args, num)
}
}
return args
}
// MovesTbl 定义 XML 根元素
type MovesTbl struct {
XMLName xml.Name `xml:"MovesTbl"`
@@ -43,15 +62,18 @@ type Move struct {
PwrBindDv int `xml:"PwrBindDv,attr,omitempty"` //威力power取决于自身的潜力个体值
PwrDouble int `xml:"PwrDouble,attr,omitempty"` //攻击时,若对方处于异常状态, 则威力翻倍;
SideEffect string `xml:"SideEffect,attr,omitempty"`
SideEffectArg string `xml:"SideEffectArg,attr,omitempty"`
AtkNum int `xml:"AtkNum,attr,omitempty"`
Url string `xml:"Url,attr,omitempty"`
SideEffect string `xml:"SideEffect,attr,omitempty"`
SideEffectArg string `xml:"SideEffectArg,attr,omitempty"`
SideEffectS []int
SideEffectArgS []int
AtkNum int `xml:"AtkNum,attr,omitempty"`
Url string `xml:"Url,attr,omitempty"`
Info string `xml:"info,attr,omitempty"`
CD int `xml:"CD,attr"`
}
type SideEffect struct {
ID int `xml:"ID,attr"`
Help string `xml:"help,attr"`

View File

@@ -50,6 +50,7 @@ var s = `
func Test_main(t *testing.T) {
initfile()
fmt.Println(SkillMap[10073])
}

View File

@@ -17,7 +17,7 @@ import (
func (h Controller) OnPlayerFightNpcMonster(data *fight.FightNpcMonsterInboundInfo, c *service.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
ttt := info.NoteReadyToFightInfo{
OwnerID: data.Head.UserID,
FightId: 3,
}
@@ -45,7 +45,7 @@ func (h Controller) OnPlayerFightNpcMonster(data *fight.FightNpcMonsterInboundIn
}
c.FightC = &service.FightC{}
c.FightC.NewFight(&ttt, c) //把两个玩家都传进去
c.FightC.OwnerID = c.Info.UserID
return nil, -1
}
@@ -64,7 +64,7 @@ func (h Controller) OnPlayerHandleFightInvite(data *fight.HandleFightInviteInbou
// 使用技能包
func (h Controller) UseSkill(data *fight.UseSkillInboundInfo, c *service.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
c.FightC.UseSkill(c, data.SkillId)
return nil, 0
}

View File

@@ -2,7 +2,6 @@ package service
import (
"blazing/logic/service/fight/info"
"blazing/modules/blazing/model"
"fmt"
)
@@ -22,9 +21,10 @@ func (f *AI_player) MapID() uint32 {
panic("not implemented") // TODO: Implement
}
func (f *AI_player) GetInfo() model.PlayerInfo {
panic("not implemented") // TODO: Implement
}
// func (f *AI_player) GetInfo() model.PlayerInfo {
// return f.FightC.Opp.
// }
func (f *AI_player) SendPack(b []byte) error {
panic("not implemented") // TODO: Implement

View File

@@ -77,20 +77,14 @@ func (this *EffectNode) GetSkill() *info.BattleSkillEntity {
}
// 获取对方精灵
func (this *EffectNode) GetBattle() *info.Battle1V1 {
pet, _ := this.Ctx.Value(info.BattleContainerCtx).(*info.Battle1V1)
return pet
// // 获取我方输入源
// func (this *EffectNode) GetInput() *info.BattleInputSourceEntity {
// pet, _ := this.Ctx.Value(info.Input_ctx).(*info.BattleInputSourceEntity)
}
// return pet
// 获取我方输入源
func (this *EffectNode) GetInput() *info.BattleInputSourceEntity {
pet, _ := this.Ctx.Value(info.Input_ctx).(*info.BattleInputSourceEntity)
return pet
}
// }
// 获取自身精灵
func (this *EffectNode) GetOwnerPet() *info.BattlePetEntity {

View File

@@ -1,36 +0,0 @@
package base
// 生命周期管理器
type LifeCycle struct {
remainTurn int // 剩余回合(-1 表示不限回合)
remainCount int // 剩余次数(-1 表示不限次数)
}
// 创建一个回合持续类效果
func NewTurnLife(turns int) LifeCycle {
return LifeCycle{remainTurn: turns, remainCount: -1}
}
// 创建一个次数持续类效果
func NewCountLife(count int) LifeCycle {
return LifeCycle{remainTurn: -1, remainCount: count}
}
// 每回合 tick
func (lc *LifeCycle) Tick() {
if lc.remainTurn > 0 {
lc.remainTurn--
}
}
// 使用一次
func (lc *LifeCycle) Use() {
if lc.remainCount > 0 {
lc.remainCount--
}
}
// 是否还活跃
func (lc *LifeCycle) Alive() bool {
return (lc.remainTurn != 0) && (lc.remainCount != 0)
}

View File

@@ -1,8 +1,6 @@
package info
import (
"context"
"github.com/tnnmigga/enum"
)
@@ -17,14 +15,18 @@ var PlayerOperations = enum.New[struct {
ActiveSwitch EnumPlayerOperation `enum:"2"` //`enum:"主动切换(中切)"`
UsePotion EnumPlayerOperation `enum:"3"` //`enum:"使用药剂"` 捕捉 逃跑
Escape EnumPlayerOperation `enum:"4"` //`enum:"使用药剂"` 逃跑 等级最高
Escape EnumPlayerOperation `enum:"4"` //`enum:"使用药剂"` 逃跑 等级最高 ,以及掉线
//DeathSwitch EnumPlayerOperation `enum:"4"` //`enum:"死亡切换"` 这两个本质上还是对action的比较
// ExpelledSwitch EnumPlayerOperation `enum:"5"` //`enum:"(被)驱逐切换"`
}]()
type BattleAction struct {
PlayerID uint32
Priority EnumPlayerOperation //优先级本质上是action的itoa
ctx context.Context
Skill BattleSkillEntity
PetInfo BattlePetEntity
//ctx context.Context
}
// Compare 比较两个1v1战斗动作的执行优先级核心逻辑
@@ -39,20 +41,15 @@ func (c *BattleAction) Compare(a *BattleAction) *BattleAction {
}
if a.Priority == 0 {
skillo, _ := a.ctx.Value(BattleSkillEntityCtx).(*BattleSkillEntity)
skillt, _ := a.ctx.Value(BattleSkillEntityCtx).(*BattleSkillEntity)
// 使用技能
p2 := skillo.Priority - skillt.Priority
p2 := a.Skill.Priority - c.Skill.Priority //对方减自己
if p2 > 0 {
return a
} else if p2 < 0 {
return c
}
peto, _ := a.ctx.Value(Pet_O_Ctx).(*BattlePetEntity)
pett, _ := a.ctx.Value(Pet_O_Ctx).(*BattlePetEntity)
p2 = int(peto.info.Speed) - int(pett.info.Speed) // 假设 Use1v1SkillAction 有 SkillPriority() 方法
p2 = int(a.PetInfo.Value(4)) - int(c.PetInfo.Value(4)) // 假设 Use1v1SkillAction 有 SkillPriority() 方法
if p2 > 0 {
return a
} else if p2 < 0 {

View File

@@ -1,35 +0,0 @@
package info
import (
"context"
)
const Input_ctx = "player"
type BattleInputSourceEntity struct {
FightUserInfo //用户信息
PetEntities []*BattlePetEntity //宠物信息
ctx context.Context //输入源的上下文
}
// 新建一个宠物
func (u *BattleInputSourceEntity) NewBattlePetEntity(ctx context.Context) {
ret := BattlePetEntity{}
//ret.UnitAttributes = make(map[EnumAttrType]*Attribute)
//todo 待实现精灵特性+加成的封装
ctx = context.WithValue(ctx, Input_ctx, &ret) //添加用户到上下文
ret.ctx = ctx
}
func (u *BattleInputSourceEntity) NewBattleAction(ctx context.Context, actiontype EnumPlayerOperation) {
ret := BattleAction{
Priority: actiontype,
}
ctx = context.WithValue(ctx, Input_ctx, &ret) //添加用户到上下文
ret.ctx = ctx
}

View File

@@ -6,6 +6,7 @@ import (
"blazing/modules/blazing/model"
"context"
"sync"
"unsafe"
)
// 战斗属性类型
@@ -15,18 +16,20 @@ type EnumAttrType int
const Pet_O_Ctx = "PET_O"
const Pet_T_Ctx = "PET_T"
// 属性封装结构Ext 存储临时血量增量key=状态IDvalue=增量值)
type Attribute struct {
//CanSet bool // 是否允许修改
originalValue int
// 这里获得攻击,防御,特工,特防,速度
func (a *BattlePetEntity) Value(tt uint32) uint32 {
Stat int //提升等级
//Ext map[string]interface{} // 例如:{"buff_hp_123": 200} 存储临时血量增量
}
offsetAtk := unsafe.Offsetof(a.info.Attack) // c字段的偏移量通常为4+16=20
// 2. 将结构体指针转换为原始内存地址uintptr
baseAddr := uintptr(unsafe.Pointer(&offsetAtk))
func (a *Attribute) Value() int64 {
addrA := unsafe.Pointer(baseAddr + 4*uintptr(tt)) //根据0是攻击
offsetAtkP := unsafe.Offsetof(a.Prop.Attack) // c字段的偏移量通常为4+16=20
// 2. 将结构体指针转换为原始内存地址uintptr
baseAddrp := uintptr(unsafe.Pointer(&offsetAtkP))
return calculateRealValue(int64(a.originalValue), a.Stat)
addrB := unsafe.Pointer(baseAddrp + 4*uintptr(tt)) //根据0是攻击
return uint32(calculateRealValue(int64(*(*uint32)(addrA)), int(*(*byte)(addrB))))
}
type BattlePetEntity struct {
@@ -36,7 +39,9 @@ type BattlePetEntity struct {
Capturable bool // 是否可捕获
statusConditions sync.Map // key: StatusCondition, value: int (剩余回合)
skills [4]*BattleSkillEntity // 技能槽最多4个技能
Status StatusDict //精灵的状态
//能力提升属性
Prop PropDict
}
// calculateRealValue 计算实际属性值根据状态变化计算实际值
@@ -64,15 +69,6 @@ func calculateRealValue(value int64, stat int) int64 {
}
}
// 新建一个宠物
func (u *BattlePetEntity) NewBattleSkillEntity(ctx context.Context, id, pp int) {
ret := CreateBattleSkillWithInfinity(id, pp) //创建PP
ctx = context.WithValue(ctx, "pet", &ret) //添加用户到上下文
ret.ctx = ctx
}
func (u *BattlePetEntity) Type() element.ElementType {
//todo 待实现获取精灵的类型

View File

@@ -3,12 +3,10 @@ package info
import (
element "blazing/common/data/Element"
"blazing/common/data/xmlres"
"blazing/common/utils/random"
"context"
"fmt"
"strconv"
"strings"
"github.com/gogf/gf/v2/os/glog"
"github.com/shopspring/decimal"
@@ -36,15 +34,11 @@ var Category = enum.New[struct {
// 战斗中可以修改技能实体值,比如是否暴击,是否必中等
type BattleSkillEntity struct {
xmlres.Move
ctx context.Context
SideEffects []int
SideEffectArgs []int
PP int
InfinityPP bool
DamageZone map[EnumCategory]map[EnumsZoneType]map[EnumsZoneType][]float64 // 三维map 伤害类型-》增还是减-》加还是乘-》值
ATTACK_COUNT_ZONE int //攻击次数
DamageValue decimal.Decimal // 伤害值
// SideEffects []int
// SideEffectArgs []int
PP int
DamageZone map[EnumCategory]map[EnumsZoneType]map[EnumsZoneType][]float64 // 三维map 伤害类型-》增还是减-》加还是乘-》值
DamageValue decimal.Decimal // 伤害值
// 技能类型属性
//SkillType EnumCategory // 技能类型(物理/特殊/状态)
@@ -52,28 +46,30 @@ type BattleSkillEntity struct {
}
// CreateBattleSkillWithInfinity 创建战斗技能实例可指定是否无限PP
func CreateBattleSkillWithInfinity(id int, pp int) *BattleSkillEntity {
func CreateBattleSkillWithInfinity(id uint32) *BattleSkillEntity {
//如果PP是-1 ,那就是无限PP
// ID小于10001的视为无效技能
if id < 10001 {
return nil
}
var ret BattleSkillEntity
// 从资源仓库获取技能数据
move, ok := xmlres.SkillMap[id]
move, ok := xmlres.SkillMap[int(id)]
if !ok {
glog.Error(context.Background(), "技能ID无效", "id", id)
}
var ret BattleSkillEntity
// 解析副作用参数
sideEffectArgs := parseSideEffectArgs(move.SideEffectArg)
tt := strings.Split(move.SideEffect, " ")
rf, err := strSliceToIntSlice(tt)
if err == nil {
ret.SideEffects = rf
}
ret.Move = move
// // 解析副作用参数
// sideEffectArgs := parseSideEffectArgs(move.SideEffectArg)
// tt := strings.Split(move.SideEffect, " ")
// rf, err := strSliceToIntSlice(tt)
// if err == nil {
// ret.SideEffects = rf
// }
ret.SideEffectArgs = sideEffectArgs
// ret.SideEffectArgs = sideEffectArgs
ret.DamageZone = make(map[EnumCategory]map[EnumsZoneType]map[EnumsZoneType][]float64) //初始化第一层类型
for _, v := range enum.Values[EnumCategory](Category) {
@@ -122,15 +118,6 @@ func (s *BattleSkillEntity) Type() element.ElementType {
return element.ElementType(s.Move.Type)
}
// 技能产生动作
func (u *BattleSkillEntity) NewBattleAction(ctx context.Context) {
ret := BattleAction{}
ctx = context.WithValue(ctx, BattleSkillEntityCtx, &ret) //添加用户到上下文
ret.ctx = ctx
}
// // 技能产生effect
// func (u *BattleSkillEntity) NewEffect(ctx context.Context)*node.EffectNode {
@@ -140,22 +127,6 @@ func (u *BattleSkillEntity) NewBattleAction(ctx context.Context) {
// }
// 解析副作用参数字符串为整数列表
func parseSideEffectArgs(argsStr string) []int {
if argsStr == "" {
return []int{}
}
parts := strings.Fields(argsStr)
args := make([]int, 0, len(parts))
for _, part := range parts {
if num, err := strconv.Atoi(part); err == nil {
args = append(args, num)
}
}
return args
}
// 获取技能名称为空时使用ID
func getSkillName(move *BattleSkillEntity) string {
@@ -166,12 +137,12 @@ func getSkillName(move *BattleSkillEntity) string {
}
// 获取副作用列表,处理空值情况
func getSideEffects(move *BattleSkillEntity) []int {
if move.SideEffect == "" {
return []int{}
}
return move.SideEffects
}
// func getSideEffects(move *BattleSkillEntity) []int {
// if move.SideEffect == "" {
// return []int{}
// }
// return move.SideEffects
// }
type EnumsZoneType int
@@ -185,12 +156,12 @@ var DamageC = enum.New[struct {
reduction EnumsZoneType // 减伤区
}]()
func (s *BattleSkillEntity) Random() *random.RandomXS128 {
battle, _ := s.ctx.Value(BattleContainerCtx).(*Battle1V1)
// func (s *BattleSkillEntity) Random() *random.RandomXS128 {
// battle, _ := s.ctx.Value(BattleContainerCtx).(*Battle1V1)
return battle.Rand
// return battle.Rand
}
// }
// 获得指定的+-区
func (s *BattleSkillEntity) GetAddValue(e EnumCategory, c EnumsZoneType) decimal.Decimal {
@@ -253,12 +224,13 @@ func (s *BattleSkillEntity) PutDamageZone(e EnumCategory, dtype EnumsZoneType, v
// }
}
func (s *BattleSkillEntity) Pet() (*BattlePetEntity, bool) {
pet, ok := s.ctx.Value(Pet_O_Ctx).(*BattlePetEntity)
return pet, ok
// func (s *BattleSkillEntity) Pet() (*BattlePetEntity, bool) {
// pet, ok := s.ctx.Value(Pet_O_Ctx).(*BattlePetEntity)
}
// return pet, ok
// }
// 暴击伤害 返回暴击率和是否暴击
func (s *BattleSkillEntity) CriticalRate() decimal.Decimal {
@@ -267,16 +239,16 @@ func (s *BattleSkillEntity) CriticalRate() decimal.Decimal {
}
func (s *BattleSkillEntity) criticalrandom() decimal.Decimal {
//这里应该算上威力区
// 初始化随机值范围217~255
// func (s *BattleSkillEntity) criticalrandom() decimal.Decimal {
// //这里应该算上威力区
// // 初始化随机值范围217~255
randomnum := s.Random().NextLongN(39) + 217
// 10. 随机倍率随机值除以255
randomFactor := decimal.NewFromInt(int64(randomnum)).Div(decimal.NewFromInt(255))
return randomFactor
// randomnum := s.Random().NextLongN(39) + 217
// // 10. 随机倍率随机值除以255
// randomFactor := decimal.NewFromInt(int64(randomnum)).Div(decimal.NewFromInt(255))
// return randomFactor
}
// }
// // 计算技能威力
// func (s *BattleSkillEntity) CalculatePower(p *BattlePetEntity) int64 {

View File

@@ -1,28 +1,26 @@
package info
import (
"blazing/common/utils/random"
"github.com/tnnmigga/enum"
)
type Battle struct {
Rand *random.RandomXS128 //本次战斗随机数
same []BattleInputSourceEntity //同阵营
Round int //回合数
BattleMode EnumBattleMode //战斗模式
opposite []BattleInputSourceEntity //不同阵营
Effects *NodeManager //挂载effect,实际上是给每个输入源,然后触发时候循环调用Effects ,
// type Battle struct {
// Rand *random.RandomXS128 //本次战斗随机数
// same []BattleInputSourceEntity //同阵营
// Round int //回合数
// BattleMode EnumBattleMode //战斗模式
// opposite []BattleInputSourceEntity //不同阵营
// Effects *NodeManager //挂载effect,实际上是给每个输入源,然后触发时候循环调用Effects ,
//A的effect->触发死亡->这时候就应该调用对手的切换,实现effect62
}
// //A的effect->触发死亡->这时候就应该调用对手的切换,实现effect62
// }
// 执行下一回合
func (b *Battle) NextRound() {
// func (b *Battle) NextRound() {
b.Round++ //回合数加1
// b.Round++ //回合数加1
}
// }
// 战斗模式
type EnumBattleMode int

View File

@@ -1,51 +0,0 @@
package info
import (
"blazing/common/utils/random"
"context"
"time"
)
type Battle1V1 struct {
Battle
Ownerid uint32 // 房主ID
FirstPerson uint32 //先手方ID
}
// 返回房主信息
func (b *Battle1V1) Owner() *BattleInputSourceEntity {
return &b.same[0]
}
// 返回邀请者信息,比如野怪
func (b *Battle1V1) Target() *BattleInputSourceEntity {
return &b.opposite[0]
}
func NewBattleContainer1v1(i NoteReadyToFightInfo) *Battle1V1 {
ret := Battle1V1{}
ret.same = make([]BattleInputSourceEntity, 0) //初始化本阵营
ret.opposite = make([]BattleInputSourceEntity, 0) //初始化敌方阵营
ret.same = append(ret.same, BattleInputSourceEntity{
FightUserInfo: i.OurInfo,
//todo 待初始化精灵实体
}) //添加战斗实体
ret.opposite = append(ret.same, BattleInputSourceEntity{
FightUserInfo: i.OpponentInfo,
})
ret.Ownerid = i.OwnerID //房主ID
ret.Rand = random.NewRandomXS128WithTwoSeeds(uint64(ret.Ownerid), uint64(time.Now().Unix()))
ctx := context.Background()
context.WithValue(ctx, BattleContainerCtx, &ret) //传入容器的上下文
return &ret
}
const BattleContainerCtx = "battle"

View File

@@ -77,18 +77,18 @@ type StatusDict struct {
// 精灵的能力提升
type PropDict struct {
// 攻击(@UInt long → uint32
Attack uint32 `struc:"[1]byte"`
Attack byte
// 防御(@UInt long → uint32
Defence uint32 `struc:"[1]byte"`
Defence byte
// 特攻(@UInt long → uint32
SpecialAttack uint32 `struc:"[1]byte"`
SpecialAttack byte
// 特防(@UInt long → uint32
SpecialDefence uint32 `struc:"[1]byte"`
SpecialDefence byte
// 速度(@UInt long → uint32
Speed uint32 `struc:"[1]byte"`
Speed byte
// 命中(@UInt long → uint32
Accuracy uint32 `struc:"[1]byte"`
Accuracy byte
}
// BattleLevels 战斗属性等级结构体对应原6字节数组

View File

@@ -2,8 +2,8 @@ package service
import (
"blazing/logic/service/fight/info"
"blazing/modules/blazing/model"
"fmt"
"math/rand"
"time"
"github.com/jinzhu/copier"
@@ -12,167 +12,147 @@ import (
type PlayerI interface {
ID() uint32
MapID() uint32
GetInfo() model.PlayerInfo
//GetInfo() info.BattlePetEntity
SendPack(b []byte) error
SendReadyToFightInfo(info.FightStartOutboundInfo)
SendNoteReadyToFightInfo(info.NoteReadyToFightInfo)
}
type FightC struct {
Info *info.NoteReadyToFightInfo
Our PlayerI
Opp PlayerI
MAXPET uint32 //,最大精灵数
//战斗发起者ID
OwnerID uint32
//玩家拥有者是否准备完成
Info *info.NoteReadyToFightInfo
Our PlayerI
Opp PlayerI
MAXPET uint32 // 最大精灵数
OwnerID uint32 // 战斗发起者ID
AFinished bool
BFinished bool
//random *rand.Rand //随机数种子
StartTime time.Time
actionChan chan info.BattleAction // 所有操作统一从这里进入
Round int //回合数
}
// 使用技能
// 玩家使用技能
func (f *FightC) UseSkill(c PlayerI, id uint32) {
ret := info.BattleAction{
PlayerID: c.ID(),
Priority: info.PlayerOperations.SelectSkill,
// Skill: info.CreateBattleSkillWithInfinity(id),
}
f.actionChan <- ret
}
// 战斗准备
func (f *FightC) ReadyFight(c PlayerI) {
rett := info.FightStartOutboundInfo{}
copier.Copy(&rett.Info1, &f.Info.OurPetList[0]) //复制自己的信息
switch f.Info.FightId { //判断战斗类型
case 1:
//1v1
if c == f.Our { //这个时候是房主发来的消
f.Info.AFinished = true
if f.Info.BFinished {
copier.Copy(&rett.Info1, &f.Info.OurPetList[0]) // 复制自己的信
copier.Copy(&rett.Info2, &f.Info.OpponentPetList[0]) //复制对方信息
switch f.Info.FightId {
case 1: // 1v1
if c == f.Our {
f.AFinished = true
if f.BFinished {
copier.Copy(&rett.Info2, &f.Info.OpponentPetList[0])
f.Our.SendReadyToFightInfo(rett)
f.Opp.SendReadyToFightInfo(rett)
}
} else {
f.Our = c
f.Info.BFinished = true
if f.Info.AFinished { //如果房主完成
copier.Copy(&rett.Info2, &f.Info.OurPetList[0]) //复制房主信息,因为这时候不是房主发来的消息
f.Opp = c
f.BFinished = true
if f.AFinished {
copier.Copy(&rett.Info2, &f.Info.OurPetList[0])
f.Our.SendReadyToFightInfo(rett)
f.Opp.SendReadyToFightInfo(rett)
}
}
case 3: //野怪战斗
case 3: // 野怪战斗
copier.Copy(&rett.Info2, &f.Info.OpponentPetList[0])
rett.Info1.UserID = f.Info.OurInfo.UserID
f.Our.SendReadyToFightInfo(rett)
f.Opp.SendReadyToFightInfo(rett)
}
}
func (f *FightC) Random() {
//先产生战斗的随机数
// 组合「时间戳(纳秒精度)+ 双方ID + 回合数」生成种子
seed := f.StartTime.UnixNano() ^ int64(f.OwnerID) ^ int64(f.Our.ID()) ^ int64(f.Round) // 用异或运算混合多维度信息
rand.Seed(seed)
}
// 创建新战斗
func (f *FightC) NewFight(i *info.NoteReadyToFightInfo, plays PlayerI) {
f.Our = plays
f.Info = i
f.StartTime = time.Now()
f.actionChan = make(chan info.BattleAction, 2) // 初始化全局操作通道
f.Info.FightId = i.FightId
switch i.FightId {
case 1:
//1v1
case 3: //野怪战斗
// 1v1,等双方进入
case 3: // 野怪战斗
plays.SendNoteReadyToFightInfo(*i)
f.Opp = &AI_player{
fightinfo: *i,
}
//这时候应该建立一个虚拟的player
f.Opp = &AI_player{fightinfo: *i, FightC: f} // 创建虚拟对手
}
f.battleLoop()
go f.battleLoop() // 起战斗循环
}
// 定义操作类型,存储玩家的操作信息(如技能选择、目标等)
type Action struct {
PlayerID int // 玩家ID1或2
SkillID int // 使用的技能ID
TargetID int // 目标ID
}
// 战斗回合循环
func (f *FightC) battleLoop() {
for { // 战斗回合循环
// 每个回合重置:创建两个通道接收双方操作
p1ActionChan := make(chan Action) // 玩家1的操作通道
p2ActionChan := make(chan Action) // 玩家2的操作通道
for {
f.Round++ //回合数自增
actions := make(map[uint32]info.BattleAction) // 每个玩家一条记录
timeout := time.After(60 * time.Second)
// 启动goroutine处理双方输入实际项目中这里会绑定用户输入逻辑
go handlePlayerInput(1, p1ActionChan)
go handlePlayerInput(2, p2ActionChan)
// 等待双方操作或超时
var p1Action, p2Action Action
var p1Done, p2Done bool
timeout := time.After(60 * time.Second) // 60秒超时
// 循环等待双方都完成操作
for !(p1Done && p2Done) {
for len(actions) < 2 {
select {
case action := <-p1ActionChan:
p1Action = action
p1Done = true
fmt.Printf("玩家%d已选择技能%d\n", action.PlayerID, action.SkillID)
case action := <-p2ActionChan:
p2Action = action
p2Done = true
fmt.Printf("玩家%d已选择技能%d\n", action.PlayerID, action.SkillID)
case action := <-f.actionChan:
// 只接受有效玩家 ID
if action.PlayerID != f.Our.ID() && action.PlayerID != f.Opp.ID() {
continue
}
// 如果该玩家已经提交过,就忽略重复动作
if _, exists := actions[uint32(action.PlayerID)]; exists {
fmt.Printf("玩家%d 已经提交过动作,忽略重复\n", action.PlayerID)
continue
}
actions[uint32(action.PlayerID)] = action
fmt.Printf("玩家%d 执行动作", action.PlayerID)
case <-timeout:
// 超时处理:未操作的视为放弃
fmt.Println("操作超时,未操作方视为放弃本回合")
// 根据超时情况补充未完成的操作(如空操作)
if !p1Done {
p1Action = Action{PlayerID: 1, SkillID: 0} // 0表示放弃
fmt.Println("回合操作超时")
if _, exists := actions[f.Our.ID()]; !exists {
actions[f.Our.ID()] = info.BattleAction{PlayerID: f.Our.ID(), Priority: info.PlayerOperations.SystemGiveUp} //系统选择出手
}
if !p2Done {
p2Action = Action{PlayerID: 2, SkillID: 0}
if _, exists := actions[f.Opp.ID()]; !exists {
actions[f.Opp.ID()] = info.BattleAction{PlayerID: f.Our.ID(), Priority: info.PlayerOperations.SystemGiveUp} //系统选择出手
}
p1Done = true
p2Done = true
}
}
// 双方都已操作,执行战斗计
fmt.Println("双方操作完成,开始计算本回合结果...")
calculateBattleResult(p1Action, p2Action)
// 双方动作齐了,取出来结
p1Action := actions[f.Our.ID()]
p2Action := actions[f.Opp.ID()]
fmt.Printf("开始结算回合")
fmt.Println(p1Action)
fmt.Println(p2Action)
// TODO: 在这里调用技能结算逻辑
// 检查战斗是否结束如一方血量为0
if isBattleEnd() {
fmt.Println("战斗结束")
break
return
}
}
}
// 模拟处理玩家输入实际中会绑定UI或网络输入
func handlePlayerInput(playerID int, actionChan chan<- Action) {
// 这里只是示例:实际中会等待用户输入(如点击技能按钮)
// 假设玩家1选择技能1目标是玩家2玩家2选择技能2目标是玩家1
time.Sleep(time.Second * 2) // 模拟思考时间
if playerID == 1 {
actionChan <- Action{PlayerID: 1, SkillID: 1, TargetID: 2}
} else {
actionChan <- Action{PlayerID: 2, SkillID: 2, TargetID: 1}
}
}
// 战斗计算逻辑(示例)
func calculateBattleResult(p1, p2 Action) {
// 实际项目中会根据技能ID计算伤害、 buff等
fmt.Printf("玩家1使用技能%d攻击玩家%d玩家2使用技能%d攻击玩家%d\n",
p1.SkillID, p1.TargetID, p2.SkillID, p2.TargetID)
}
// 检查战斗是否结束(示例)
// 判断战斗是否结束
func isBattleEnd() bool {
// 实际中会判断双方血量等条件
return false // 暂时返回false持续战斗
return false
}

View File

@@ -34,6 +34,7 @@ var tt task.GetTaskBufOutboundInfo
func main() {
//Test_kick()
cmd.Main.Run(gctx.New())
}