refactor(fight): 重构战斗逻辑

- 新增 AI_player 结构体和相关方法,用于创建和管理 AI 玩家
- 重构 FightC 结构体,增加 Input 结构体用于封装玩家输入
- 优化战斗流程,包括回合处理、技能使用、伤害计算等
- 改进广播机制,使用函数回调替代直接调用方法
- 优化玩家和 AI 的动作处理逻辑
This commit is contained in:
2025-09-08 01:23:12 +08:00
parent e111e08638
commit 7ef001f1b9
8 changed files with 408 additions and 317 deletions

View File

@@ -51,8 +51,9 @@ func (h Controller) OnPlayerFightNpcMonster(data *fight.FightNpcMonsterInboundIn
if c.FightC != nil {
return nil, errorcode.ErrorCodes.ErrOnlineOver6HoursCannotFight
}
c.FightC = &service.FightC{}
c.FightC.NewFight(&ttt, c, mo) //把两个玩家都传进去
ai := service.NewAI_player(*mo)
service.NewFight(ttt, c, ai)
c.FightC.OwnerID = c.Info.UserID
return nil, -1

View File

@@ -7,15 +7,24 @@ import (
)
type AI_player struct {
fightinfo info.NoteReadyToFightInfo
id uint32 //0
FightC *FightC //绑定战斗标识 替代本身的是否战斗标记 //IsFighting bool
//petinfo []model.PlayerInfo //精灵信息
FightC *FightC //绑定战斗标识 替代本身的是否战斗标记 //IsFighting bool
petinfo []model.PetInfo //精灵信息
}
func NewAI_player(m model.PetInfo) *AI_player {
ret := &AI_player{}
ret.petinfo = make([]model.PetInfo, 0)
ret.petinfo = append(ret.petinfo, m)
return ret
}
func (f *AI_player) SetFightC(ff *FightC) {
f.FightC = ff
}
func (f *AI_player) ID() uint32 {
return f.id
return 0
}
func (f *AI_player) MapID() uint32 {
@@ -38,16 +47,16 @@ func (f *AI_player) SendReadyToFightInfo(gg info.FightStartOutboundInfo) {
//这时候给个出招
}
func (f *AI_player) SendNoteReadyToFightInfo(_ info.NoteReadyToFightInfo) {
panic("not implemented") // TODO: Implement
func (f *AI_player) SendNoteReadyToFightInfo(fs info.NoteReadyToFightInfo) {
fmt.Println(fs)
}
func (f *AI_player) SendFightEndInfo(_ info.FightOverInfo) {
fmt.Println("战斗结束")
//fmt.Println("战斗结束")
}
func (f *AI_player) GetAction() {
f.FightC.UseSkill(f, int32(f.fightinfo.OpponentPetList[0].SkillList[0].ID)) //使用1#技能,实际上要按照四个技能权重去使用
f.FightC.UseSkill(f, int32(f.FightC.Opp.CurrentPet.Skills[0].ID)) //使用1#技能,实际上要按照四个技能权重去使用
}
func (p *AI_player) End() {
p.FightC = nil
@@ -55,9 +64,9 @@ func (p *AI_player) End() {
return
}
func (p *AI_player) GetPetInfo() []*model.PetInfo {
func (p *AI_player) GetPetInfo() []model.PetInfo {
return nil
return p.petinfo
}
func (p *AI_player) SendAttackValue(info.AttackValueS) {

View File

@@ -11,13 +11,13 @@ type EnumPlayerOperation int
// 定义读秒倒计时期间玩家可执行的操作枚举
var PlayerOperations = enum.New[struct {
SystemGiveUp EnumPlayerOperation `enum:"-1"` // 系统选择放弃出手(比如没有PP)
SelectSkill EnumPlayerOperation `enum:"0"` // 选择技能-6到6
ActiveSwitch EnumPlayerOperation `enum:"2"` // 主动切换(中切)
UsePotion EnumPlayerOperation `enum:"3"` // 使用药剂(捕捉、逃跑等)
Escape EnumPlayerOperation `enum:"4"` // 逃跑(等级最高,以及掉线)
PlayerOffline EnumPlayerOperation `enum:"5"` // 玩家掉线
BeExpelledSwitch EnumPlayerOperation `enum:"6"` // 被驱逐切换
SystemGiveUp EnumPlayerOperation `enum:"-1"` // 系统选择放弃出手(比如没有PP)
SelectSkill EnumPlayerOperation `enum:"0"` // 选择技能-6到6
ActiveSwitch EnumPlayerOperation `enum:"2"` // 主动切换(中切)
UsePotion EnumPlayerOperation `enum:"3"` // 使用药剂(捕捉、逃跑等)
Escape EnumPlayerOperation `enum:"4"` // 逃跑(等级最高,以及掉线)
PlayerOffline EnumPlayerOperation `enum:"5"` // 玩家掉线
// BeExpelledSwitch EnumPlayerOperation `enum:"6"` // 被驱逐切换
}]()
// Compare 比较两个1v1战斗动作的执行优先级核心逻辑
@@ -84,8 +84,8 @@ func (s *SelectSkillAction) Priority() int {
// ActiveSwitchAction 主动切换宠物的战斗动作
type ActiveSwitchAction struct {
PlayerID uint32 // 玩家ID
Type bool //是否主动切换
Reason ChangePetInfo
Reason ChangePetInfo
// CurrentPet BattlePetEntity // 当前在场宠物
// TargetPet BattlePetEntity // 要切换上场的宠物
// SwitchReason string // 切换原因
@@ -93,16 +93,7 @@ type ActiveSwitchAction struct {
// Priority 返回动作优先级
func (a *ActiveSwitchAction) Priority() int {
if a.Type {
return int(PlayerOperations.ActiveSwitch)
}
return int(PlayerOperations.BeExpelledSwitch)
}
// Broadcast 广播切换宠物的动作信息
func (a *ActiveSwitchAction) Broadcast() {
// fmt.Printf("玩家[%d]主动切换宠物:从%s切换到%s原因%s\n",
// // a.PlayerID, a.CurrentPet.Name, a.TargetPet.Name, a.SwitchReason)
return int(PlayerOperations.ActiveSwitch)
}
func (a *ActiveSwitchAction) GetPlayerID() uint32 {
@@ -171,10 +162,9 @@ func (s *SystemGiveUpAction) Broadcast() {
// PlayerOfflineAction 玩家掉线的战斗动作
type PlayerOfflineAction struct {
PlayerID uint32 // 掉线玩家ID
OfflineTime time.Time // 掉线时间
CurrentPet BattlePetEntity // 掉线时在场的宠物
IsReconnect bool // 是否后续重连(预留字段)
PlayerID uint32 // 掉线玩家ID
OfflineTime time.Time // 掉线时间
Reason FightOverInfo
}
// Priority 返回动作优先级
@@ -182,6 +172,10 @@ func (p *PlayerOfflineAction) Priority() int {
return int(PlayerOperations.PlayerOffline)
}
func (p *PlayerOfflineAction) GetPlayerID() uint32 {
return p.PlayerID
}
// Broadcast 广播玩家掉线信息
func (p *PlayerOfflineAction) Broadcast() {
// /status := "未重连"

View File

@@ -73,11 +73,11 @@ type BattlePetEntity struct {
}
// 创建精灵实例
func CreateBattlePetEntity(info *model.PetInfo, rand *rand.Rand) *BattlePetEntity {
func CreateBattlePetEntity(info model.PetInfo, rand *rand.Rand) *BattlePetEntity {
ret := &BattlePetEntity{}
ret.PetInfo = xmlres.PetMAP[int(info.ID)] //注入精灵信息
ret.Info = *info
ret.Info = info
for i := 0; i < 4; i++ {
//todo 技能信息应该每回合进行深拷贝,保证每次的技能效果都是不一样的
ret.Skills[i] = CreateBattleSkillWithInfinity(&info.SkillList[i], rand, ret)

View File

@@ -37,16 +37,11 @@ var Category = enum.New[struct {
// 战斗中可以修改技能实体值,比如是否暴击,是否必中等
type BattleSkillEntity struct {
xmlres.Move
// SideEffects []int
// SideEffectArgs []int
PP int
DamageZone map[EnumCategory]map[EnumsZoneType]map[EnumsZoneType][]float64 // 三维map 伤害类型-》增还是减-》加还是乘-》值
DamageValue decimal.Decimal // 伤害值
Rand *rand.Rand
Pet *BattlePetEntity
// 技能类型属性
//SkillType EnumCategory // 技能类型(物理/特殊/状态)
}
// CreateBattleSkillWithInfinity 创建战斗技能实例可指定是否无限PP
@@ -241,14 +236,14 @@ func (s *BattleSkillEntity) PutDamageZone(e EnumCategory, dtype EnumsZoneType, v
// }
// 暴击伤害 返回暴击率和是否暴击
func (s *BattleSkillEntity) CriticalRate() (decimal.Decimal, uint32) {
func (s *BattleSkillEntity) IsCritical() (decimal.Decimal, bool) {
return decimal.NewFromFloat(1), 1
return decimal.NewFromFloat(1), true
}
// 计算是否命中
func (s *BattleSkillEntity) CriticalisCritical() uint32 {
func (s *BattleSkillEntity) AttackTime() uint32 {
if int64(s.Pet.Accuracy(int64(s.Accuracy))) > s.Rand.Int63n(100) {
return 1
@@ -343,8 +338,8 @@ func (s *BattleSkillEntity) CalculatePower(deftype int) uint32 {
Mul(typeRate). // 克制系数
Mul(s.criticalrandom()) //随机波动
v, ok := s.CriticalRate()
if ok == 1 {
v, ok := s.IsCritical() //获取暴击
if ok {
damage.Mul(v)
}
return uint32(damage.IntPart())

View File

@@ -52,6 +52,24 @@ type AttackValueS struct {
SAttack AttackValue
}
func NewAttackValue(userid uint32) *AttackValue {
return &AttackValue{
userid,
0,
0,
0,
0,
0,
0,
0,
0,
[]model.SkillInfo{},
0,
StatusDict{},
PropDict{},
}
}
// AttackValue 战斗中的攻击数值信息
type AttackValue struct {
UserID uint32 `json:"userId" fieldDescription:"玩家的米米号 与野怪对战userid = 0"`
@@ -142,6 +160,7 @@ type FightUserInfo struct {
// NoteReadyToFightInfo 战斗准备就绪消息结构体NoteReadyToFightInfo
type NoteReadyToFightInfo struct {
MAXPET uint32 `struc:"skip"` // 最大精灵数 struc:"skip"`
// 战斗类型ID与野怪战斗为3与人战斗为1前端似乎未使用
// @UInt long
FightId EnumBattleMode `fieldDesc:"战斗类型ID 但前端好像没有用到 与野怪战斗为3与人战斗似乎是1" `

View File

@@ -19,32 +19,64 @@ type PlayerI interface {
ID() uint32
MapID() uint32
GetAction()
GetPetInfo() []*model.PetInfo
End() //结束战斗
GetPetInfo() []model.PetInfo
SendPack(b []byte) error
SendReadyToFightInfo(info.FightStartOutboundInfo)
SendNoteReadyToFightInfo(info.NoteReadyToFightInfo)
SendFightEndInfo(info.FightOverInfo)
SendAttackValue(info.AttackValueS)
SendChangePet(info.ChangePetInfo)
SetFightC(*FightC)
}
type Input struct {
CanChange bool //是否可以死亡切换CanChange
CurrentPet *info.BattlePetEntity //当前精灵
AllPet []*info.BattlePetEntity
Player PlayerI
Finished bool //是否加载完成
}
func (i *Input) GetPetInfo() *info.BattlePetEntity {
return i.CurrentPet
}
type FightC struct {
Info *info.NoteReadyToFightInfo
Our PlayerI
OurCurrentPet *info.BattlePetEntity //我方精灵
Opp PlayerI
OppCurrentPet *info.BattlePetEntity //对方当前精灵
Info info.NoteReadyToFightInfo
MAXPET uint32 // 最大精灵数
OwnerID uint32 // 战斗发起者ID
AFinished bool
BFinished bool
random *rand.Rand //随机数种子
Our *Input //始终等于房主ID
Opp *Input //对手ID
rand *rand.Rand
StartTime time.Time
actionChan chan info.BattleActionI // 所有操作统一从这里进入
Round int //回合数
EffectS info.NodeManager //effects容器
First *BPET
Second *BPET
}
func (f *FightC) GetInputByPlayer(c PlayerI, isOpposite bool) *Input {
// 判断当前玩家是否为我方玩家
isOurPlayer := c == f.Our.Player
// 逻辑简化:当"是否为我方玩家"与"是否需要对方"状态一致时,返回对方输入,否则返回我方输入
if isOurPlayer == isOpposite {
return f.Opp
}
return f.Our
}
func (f *FightC) GetInputByAction(c info.BattleActionI, isOpposite bool) *Input {
// 判断动作所属玩家是否为我方
isOurAction := c.GetPlayerID() == f.Our.Player.ID()
// 根据isOpposite决定是否返回相反方向的输入
if isOurAction == !isOpposite {
return f.Our
}
return f.Opp
}
// 玩家逃跑
@@ -66,26 +98,20 @@ func (f *FightC) ChangePet(c PlayerI, id int32) {
PlayerID: c.ID(),
}
rett := func(t PlayerI) *info.BattlePetEntity {
for _, v := range t.GetPetInfo() {
if v.CatchTime == uint32(id) {
fs := info.CreateBattlePetEntity(v, f.Random()) //存入自己的精灵信息
rett := func(t *Input) *info.BattlePetEntity {
for _, v := range t.AllPet {
if v.Info.CatchTime == uint32(id) {
copier.Copy(&ret.Reason, &v)
ret.Reason.UserId = c.ID()
return fs
return v
}
}
return nil
}
if c == f.Our {
f.OurCurrentPet = rett(f.Our)
f.GetInputByPlayer(c, false).CurrentPet = rett(f.GetInputByPlayer(c, false))
} else {
f.OppCurrentPet = rett(f.Opp)
}
f.actionChan <- ret
}
@@ -94,14 +120,7 @@ func (f *FightC) UseSkill(c PlayerI, id int32) {
ret := &info.SelectSkillAction{
PlayerID: c.ID(),
}
if c == f.Our {
ret.PetInfo = f.OurCurrentPet //存入自己的精灵信息
} else {
ret.PetInfo = f.OppCurrentPet
}
ret.PetInfo = f.GetInputByPlayer(c, false).CurrentPet
for _, v := range ret.PetInfo.Skills {
@@ -124,27 +143,18 @@ func (f *FightC) ReadyFight(c PlayerI) {
rrsult := func() { //传回函数
f.Our.SendReadyToFightInfo(rett)
f.Opp.SendReadyToFightInfo(rett)
f.Our.Player.SendReadyToFightInfo(rett)
f.Opp.Player.SendReadyToFightInfo(rett)
}
switch f.Info.FightId {
case 1: // 1v1
if c == f.Our {
f.AFinished = true
if f.BFinished {
rrsult()
}
} else {
f.Opp = c
f.OppCurrentPet = info.CreateBattlePetEntity(c.GetPetInfo()[0], f.Random())
f.BFinished = true
if f.AFinished {
rrsult()
}
f.GetInputByPlayer(c, false).Finished = true
if f.GetInputByPlayer(c, true).Finished {
rrsult()
}
case 3: // 野怪战斗
//判断捕捉率大于0
@@ -155,16 +165,6 @@ func (f *FightC) ReadyFight(c PlayerI) {
rrsult()
}
}
func (f *FightC) Random() *rand.Rand {
//先产生战斗的随机数
// 组合「时间戳(纳秒精度)+ 双方ID + 回合数」生成种子
seed := f.StartTime.UnixNano() ^ int64(f.OwnerID) ^ int64(f.Our.ID()) ^ int64(f.Round) // 用异或运算混合多维度信息
ret := rand.New(rand.NewSource(seed))
return ret
}
var Fightpool *ants.Pool
@@ -173,104 +173,67 @@ func init() {
//defer p.Release()
}
// 创建新战斗
func (f *FightC) NewFight(i *info.NoteReadyToFightInfo, plays PlayerI, mo *model.PetInfo) {
// 创建新战斗,邀请方和被邀请方,或者玩家和野怪方
func NewFight(i info.NoteReadyToFightInfo, p1 PlayerI, p2 PlayerI) *FightC {
f := &FightC{}
f.OwnerID = p1.ID()
f.Info = i //房主
seed := f.StartTime.UnixNano() ^ int64(p1.ID()) ^ int64(p2.ID()) // ^ int64(f.Round) // 用异或运算混合多维度信息
f.rand = rand.New(rand.NewSource(seed))
f.Our = plays
f.Info = i
f.StartTime = time.Now()
f.actionChan = make(chan info.BattleActionI, 2) // 初始化全局操作通道
fmt.Println("战斗开始精灵", plays.GetPetInfo()[0].CatchTime)
f.Our = &Input{
CurrentPet: info.CreateBattlePetEntity(p1.GetPetInfo()[0], f.rand),
Player: p1,
}
for k, v := range p1.GetPetInfo() {
if k < int(i.MAXPET) {
f.Our.AllPet = append(f.Our.AllPet, info.CreateBattlePetEntity(v, f.rand))
}
for _, v := range plays.GetPetInfo() {
if v.Hp > 0 {
f.OurCurrentPet = info.CreateBattlePetEntity(plays.GetPetInfo()[0], f.Random())
}
f.Opp = &Input{
CurrentPet: info.CreateBattlePetEntity(p2.GetPetInfo()[0], f.rand),
Player: p2,
}
for k, v := range p2.GetPetInfo() {
if k < int(i.MAXPET) {
f.Opp.AllPet = append(f.Opp.AllPet, info.CreateBattlePetEntity(v, f.rand))
}
}
// }
// f.OurCurrentPet = info.CreateBattlePetEntity(plays.GetPetInfo()[0], f.Random())
if mo != nil {
f.OppCurrentPet = info.CreateBattlePetEntity(mo, f.Random())
}
switch i.FightId {
case 1:
// 1v1等双方进入
case 3: // 野怪战斗
p1.SetFightC(f) //给我方追加战斗容器
p2.SetFightC(f) //给对方增加战斗容器
defer func() {
rr := Fightpool.Submit(f.battleLoop)
if rr != nil {
panic(rr)
}
f.Broadcast(func(ff *Input) {
f.Opp = &AI_player{fightinfo: *i, FightC: f} // 创建虚拟对手
plays.SendNoteReadyToFightInfo(*i)
}
rr := Fightpool.Submit(f.battleLoop)
if rr != nil {
panic(rr)
}
ff.Player.SendNoteReadyToFightInfo(i)
})
}()
//go f.battleLoop() // 起战斗循环
return f
}
// 广播,并是否结束回合
func (f *FightC) Broadcast(t info.BattleActionI) bool {
switch ff := t.(type) {
func (f *FightC) Broadcast(t func(ff *Input)) {
case *info.EscapeAction:
t(f.Our)
f.Our.SendFightEndInfo(ff.Reason)
f.Opp.SendFightEndInfo(ff.Reason)
return true
t(f.Opp)
case *info.ActiveSwitchAction: //切换精灵
f.Our.SendChangePet(ff.Reason)
f.Opp.SendChangePet(ff.Reason)
default:
}
return false
}
func (f *FightC) BroadcastSkill(t info.AttackValueS) {
f.Our.SendAttackValue(t)
f.Opp.SendAttackValue(t)
}
// 检查战斗是否结束
func (f *FightC) CheakEnd(b uint32) bool {
fmt.Println(f.Round, "检查战斗是否结束")
var tt []info.ReadyFightPetInfo
for _, v := range f.Info.OurPetList {
if v.CatchTime == b {
v.NotAlive = true
}
tt = append(tt, v)
}
f.Info.OurPetList = tt
var tt1 []info.ReadyFightPetInfo
for _, v := range f.Info.OpponentPetList {
if v.CatchTime == b {
v.NotAlive = true
}
tt1 = append(tt1, v)
}
f.Info.OpponentPetList = tt1
for _, v := range f.Info.OurPetList {
if !v.NotAlive {
return false
}
}
for _, v := range f.Info.OpponentPetList {
if !v.NotAlive {
return false
}
}
return true
}
// 战斗回合循环
func (f *FightC) battleLoop() {
f.StartTime = time.Now()
f.actionChan = make(chan info.BattleActionI, 2) // 初始化全局操作通道
fmt.Println("战斗开始精灵", f.Our.Player.GetPetInfo()[0].CatchTime)
//战斗开始前操作
for {
f.Round++ //回合数自增
@@ -284,23 +247,37 @@ func (f *FightC) battleLoop() {
for len(actions) < 2 {
select {
case action := <-f.actionChan:
// 只接受有效玩家 ID
if action.GetPlayerID() != f.Our.ID() && action.GetPlayerID() != f.Opp.ID() {
// 只接受有效玩家 ID\
if action == nil {
continue
}
if action.GetPlayerID() != f.Our.Player.ID() && action.GetPlayerID() != f.Opp.Player.ID() {
continue
}
if a, isExpelled := action.(*info.ActiveSwitchAction); isExpelled {
//fmt.Println("对方死亡切换")
f.Broadcast(a)
if !a.Type {
f.Broadcast(func(ff *Input) {
ff.Player.SendChangePet(a.Reason)
})
if f.GetInputByAction(action, false).CanChange {
//如果是被动切换,不计入回合结算
f.GetInputByAction(action, false).CanChange = false
continue
}
}
if action.GetPlayerID() == f.Our.ID() && f.Info.FightId == 3 {
go f.Opp.GetAction() //获取AI的动作
if action.GetPlayerID() != 0 && f.Info.FightId == 3 {
f.GetInputByAction(action, true).Player.GetAction()
}
// 如果该玩家已经提交过,就忽略重复动作
if _, exists := actions[uint32(action.GetPlayerID())]; exists {
fmt.Printf("玩家%d 已经提交过动作,忽略重复\n", action.GetPlayerID())
@@ -312,140 +289,241 @@ func (f *FightC) battleLoop() {
case <-timeout:
fmt.Println("回合操作超时")
if _, exists := actions[f.Our.ID()]; !exists {
actions[f.Our.ID()] = &info.SystemGiveUpAction{PlayerID: f.Our.ID()} //系统选择出手
if _, exists := actions[f.Our.Player.ID()]; !exists {
actions[f.Our.Player.ID()] = &info.SystemGiveUpAction{PlayerID: f.Our.Player.ID()} //系统选择出手
}
if _, exists := actions[f.Opp.ID()]; !exists {
actions[f.Opp.ID()] = &info.SystemGiveUpAction{PlayerID: f.Opp.ID()} //系统选择出手
if _, exists := actions[f.Opp.Player.ID()]; !exists {
actions[f.Opp.Player.ID()] = &info.SystemGiveUpAction{PlayerID: f.Opp.Player.ID()} //系统选择出手
}
}
}
// 双方动作齐了,取出来结算
p1Action := actions[f.Our.ID()]
p2Action := actions[f.Opp.ID()]
p1Action := actions[f.Our.Player.ID()]
p2Action := actions[f.Opp.Player.ID()]
fmt.Println("开始结算回合")
var BattleActionI [2]info.BattleActionI
BattleActionI[0], BattleActionI[1] = info.Compare(p1Action, p2Action)
if BattleActionI[0].Priority() > 0 { //如果优先级大于0,说明是在技能之上的
t := f.Broadcast(BattleActionI[0])
if t {
switch faction := BattleActionI[0].(type) {
case *info.EscapeAction: //优先逃跑
break
}
}
if BattleActionI[0].Priority() < 0 && BattleActionI[1].Priority() < 0 { //双方都空过
f.Broadcast(func(ff *Input) {
ff.Player.SendFightEndInfo(faction.Reason) //广播逃跑原因
})
break
}
case *info.PlayerOfflineAction: //单方掉线
var p_skill [2]*info.SelectSkillAction
var currpet [2]*info.BattlePetEntity
var currpetvale [2]int // 伤害值
if BattleActionI[0].GetPlayerID() == f.OwnerID { //先反转战斗属性
f.Broadcast(func(ff *Input) {
ff.Player.SendFightEndInfo(faction.Reason) //广播逃跑原因
})
break
case *info.ActiveSwitchAction: //切换上场的
f.enterturn(BattleActionI[1], nil) //切换,相当于后手直接出手
currpet[0] = f.OurCurrentPet
currpet[1] = f.OppCurrentPet
case *info.SelectSkillAction: //选择技能
//回合前操作,比如挂载buff
f.enterturn(BattleActionI[0], BattleActionI[1])
} else {
currpet[0] = f.OppCurrentPet
currpet[1] = f.OurCurrentPet
}
fmt.Println("当前", currpet[0].Info.CatchTime, "敌方", currpet[1].Info.CatchTime)
for i := 0; i < 2; i++ {
// TODO: 在这里调用技能结算逻辑
p_skill[i] = BattleActionI[i].(*info.SelectSkillAction)
temparg := p_skill[i].Skill.SideEffectArgS
for _, v := range p_skill[i].Skill.SideEffectS {
t, ok := info.NodeM[v]
if ok { //获取成功
args := t.GetArgSize()
t.SetArgs(temparg[:args]) //设置入参
//如果不是是房主方,说明施加的对象是反的,比如本来是false,实际上是给邀请方施加的
//所以这里要对target取反
if BattleActionI[i].GetPlayerID() != f.OwnerID {
t.SetTarget(!t.Target())
}
temparg = temparg[args:]
f.EffectS.AddEffect(deepcopy.Copy(t).(info.Effect))
}
}
spower := p_skill[i].Skill.CalculatePower(currpet[i].Type().ID)
//开始计算技能效果battleLoop
_, ok := p_skill[i].Skill.CriticalRate()
//CalculatePower()
//记录伤害,到后手方出手时计算伤害
currpetvale[i] = int(spower)
fmt.Println("技能伤害", spower, "剩余血量", currpet[i].Info.Hp)
p_skill[i].Attack = info.AttackValue{
p_skill[i].PlayerID,
uint32(p_skill[i].Skill.ID),
p_skill[i].Skill.CriticalisCritical(),
spower,
0,
int32(currpet[i].Info.Hp),
currpet[i].Info.MaxHp,
0,
0,
[]model.SkillInfo{},
ok,
info.StatusDict{},
info.PropDict{},
}
//回合后操作
}
p_skill[0].Attack.RemainHp = int32(int(currpet[0].Info.Hp) - currpetvale[1])
currpet[0].Info.Hp = uint32(p_skill[0].Attack.RemainHp)
p_skill[1].Attack.RemainHp = int32(int(currpet[1].Info.Hp) - currpetvale[0])
currpet[1].Info.Hp = uint32(p_skill[1].Attack.RemainHp)
if p_skill[1].Attack.RemainHp <= 0 { //如果后手方被打死
p_skill[1].Attack = info.AttackValue{
p_skill[1].PlayerID,
0,
0,
0,
0,
0,
0,
0,
0,
[]model.SkillInfo{},
0,
info.StatusDict{},
info.PropDict{},
}
fmt.Println(currpet[1].Info.CatchTime, "被打死")
if f.CheakEnd(currpet[1].Info.CatchTime) {
break
}
}
if p_skill[0].Attack.RemainHp <= 0 {
if f.CheakEnd(currpet[0].Info.CatchTime) {
break
}
}
fmt.Println("P1", p_skill[0].Attack.RemainHp, "P2", p_skill[1].Attack.RemainHp)
f.BroadcastSkill(info.AttackValueS{
p_skill[0].Attack, p_skill[1].Attack,
})
}
close(f.actionChan) //关闭战斗通道
//取消战斗信息
f.Our.End()
f.Opp.End()
}
type BPET struct {
*Input
//*info.SelectSkillAction //技能实体
*info.BattlePetEntity //精灵实体
*info.AttackValue
*FightC
Damage uint32 //造成伤害
}
// 被击败的ID
func (b *BPET) IsWin(cache uint32) bool {
fmt.Println("当前回合", b.FightC.Round, "开始检查战斗是否结束")
var tt []info.ReadyFightPetInfo
bbb := b.FightC.Info.OurPetList
if b.Input.Player.ID() == b.FightC.OwnerID { //如果是房主
bbb = b.FightC.Info.OpponentPetList
} else {
bbb = b.FightC.Info.OurPetList
}
for _, v := range bbb {
if v.CatchTime == cache {
v.NotAlive = true
}
tt = append(tt, v)
}
for _, v := range tt {
if !v.NotAlive { //如果存活
return false
}
}
return true
}
// 解析并 施加effect
func (f *FightC) parseskill(id *info.SelectSkillAction) {
temparg := id.Skill.SideEffectArgS
for _, v := range id.Skill.SideEffectS {
t, ok := info.NodeM[v]
if ok { //获取成功
args := t.GetArgSize()
t.SetArgs(temparg[:args]) //设置入参
//如果不是是房主方,说明施加的对象是反的,比如本来是false,实际上是给邀请方施加的
//所以这里要对target取反
if id.GetPlayerID() != f.OwnerID {
t.SetTarget(!t.Target())
}
temparg = temparg[args:]
f.EffectS.AddEffect(deepcopy.Copy(t).(info.Effect))
}
}
}
func (f *FightC) enterturn(fattack, sattack info.BattleActionI) {
// 伤害值
// 根据攻击方归属设置当前战斗的主/次攻击方属性
var first, second *Input // 定义临时变量存储主/次攻击方
if fattack.GetPlayerID() == f.OwnerID {
first = f.Our // 攻击方为我方时,主攻击方是我方
second = f.Opp // 次攻击方是对方
} else {
first = f.Opp // 攻击方为对方时,主攻击方是对方
second = f.Our // 次攻击方是我方
}
// 统一赋值,减少重复代码
f.First = &BPET{
FightC: f,
Input: first,
BattlePetEntity: first.CurrentPet,
AttackValue: info.NewAttackValue(first.Player.ID()),
Damage: 0}
f.Second = &BPET{
FightC: f,
Input: second,
BattlePetEntity: second.CurrentPet,
AttackValue: info.NewAttackValue(second.Player.ID()),
Damage: 0}
fmt.Println("先手", f.First.CurrentPet.Info.CatchTime, "后手", f.Second.CurrentPet.Info.CatchTime)
// TODO: 在这里调用技能结算逻辑
skill, _ := fattack.(*info.SelectSkillAction)
f.parseskill(skill)
spower := skill.Skill.CalculatePower(f.Second.Type().ID)
//开始计算技能效果battleLoop
_, ok := skill.Skill.IsCritical()
//CalculatePower()
//记录伤害,到后手方出手时计算伤害
f.First.Damage = (spower)
fmt.Println("先手技能伤害", spower, "先手剩余血量", f.First.CurrentPet.Info.Hp)
f.First.AttackValue.SkillID = uint32(skill.Skill.ID)
f.First.AttackValue.AttackTime = skill.Skill.AttackTime()
if ok { //判断暴击
f.First.AttackValue.IsCritical = 1
}
fmt.Println(f.First.CurrentPet.Info.CatchTime, "先手出招结束")
if f.First.Damage > f.Second.CurrentPet.Info.Hp {
f.Second.CurrentPet.Info.Hp = 0
} else {
f.Second.CurrentPet.Info.Hp = f.Second.CurrentPet.Info.Hp - f.First.Damage //扣减后手方血量
}
if f.Second.CurrentPet.Info.Hp > 0 { //如果后手方没被打死
skill, ok := sattack.(*info.SelectSkillAction)
if ok {
f.parseskill(skill)
spower := skill.Skill.CalculatePower(f.First.Type().ID)
//开始计算技能效果battleLoop
_, ok := skill.Skill.IsCritical()
//CalculatePower()
//记录伤害,到后手方出手时计算伤害
f.Second.Damage = (spower)
fmt.Println("后手技能伤害", spower, "后手剩余血量", f.Second.CurrentPet.Info.Hp)
f.Second.AttackValue.SkillID = uint32(skill.Skill.ID)
f.Second.AttackValue.AttackTime = skill.Skill.AttackTime()
if ok { //判断暴击
f.Second.AttackValue.IsCritical = 1
}
fmt.Println(f.Second.CurrentPet.Info.CatchTime, "后手出招结束")
if f.Second.Damage > f.First.CurrentPet.Info.Hp {
f.First.CurrentPet.Info.Hp = 0
} else {
f.First.CurrentPet.Info.Hp = f.First.CurrentPet.Info.Hp - f.Second.Damage //扣减先手方血量
}
} //还有系统选择放弃出手的
if f.First.CurrentPet.Info.Hp == 0 {
f.First.CanChange = true //被打死就可以切精灵了
if f.Second.IsWin(f.First.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束
defer f.Broadcast(func(ff *Input) {
ff.Player.SendFightEndInfo(info.FightOverInfo{
WinnerId: f.Second.UserID,
}) //广播逃跑原因
})
defer close(f.actionChan) //关闭战斗通道
}
}
} else {
f.Second.AttackValue = info.NewAttackValue(second.Player.ID()) //已经被打死了,直接将后手方输出归0
f.Second.CanChange = true //被打死就可以切精灵了
if f.First.IsWin(f.Second.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束
defer f.Broadcast(func(ff *Input) {
ff.Player.SendFightEndInfo(info.FightOverInfo{
WinnerId: f.First.UserID,
}) //广播逃跑原因
})
defer close(f.actionChan) //关闭战斗通道
}
}
f.Broadcast(func(ff *Input) {
ret := info.AttackValueS{
FAttack: *f.First.AttackValue,
SAttack: *f.Second.AttackValue,
}
ret.FAttack.RemainHp = int32(f.First.CurrentPet.Info.Hp)
ret.FAttack.LostHp = uint32(f.First.Damage) //先手方造成血量
ret.SAttack.RemainHp = int32(f.Second.CurrentPet.Info.Hp)
ret.SAttack.LostHp = uint32(f.Second.Damage) //后手方造成血量
ff.Player.SendAttackValue(ret)
})
return
}

View File

@@ -128,11 +128,7 @@ func (p *Player) GetAction() {
return
}
func (p *Player) End() {
p.FightC = nil
return
}
func (p *Player) ID() uint32 {
return p.Info.UserID
@@ -182,16 +178,15 @@ func (p *Player) SendFightEndInfo(b info.FightOverInfo) {
t1 := NewTomeeHeader(2506, p.Info.UserID)
p.SendPack(t1.Pack(&b))
p.FightC = nil
}
func (p *Player) GetPetInfo() []*model.PetInfo {
var ret = make([]*model.PetInfo, 0)
func (p *Player) GetPetInfo() []model.PetInfo {
for _, v := range p.Info.PetList {
ret = append(ret, &v)
return p.Info.PetList
}
}
return ret
func (f *Player) SetFightC(ff *FightC) {
f.FightC = ff
}
// Save 保存玩家数据