refactor(fight): 重构战斗逻辑和数据结构

- 重构了 Input 结构体和相关方法,新增 NewInput 函数
- 优化了 NodeManager 结构体和 Exec 方法的实现
- 调整了 FightC 结构体和 enterturn 方法的逻辑
- 修改了 BattleSkillEntity 结构体,移除了冗余字段
- 更新了 EffectNode 中的相关方法,使其适应新的逻辑
This commit is contained in:
2025-09-14 04:48:38 +08:00
parent 9d87ce9e46
commit 93ae004683
11 changed files with 64 additions and 560 deletions

View File

@@ -1,46 +0,0 @@
package input
import (
"blazing/logic/service/common"
"blazing/logic/service/fight/info"
"github.com/jinzhu/copier"
"github.com/shopspring/decimal"
)
type Input struct {
CanChange bool //是否可以死亡切换CanChange
CurrentPet *info.BattlePetEntity //当前精灵
AllPet []*info.BattlePetEntity
Player common.PlayerI
Finished bool //是否加载完成
*info.AttackValue
FightC common.FightI
// info.BattleActionI
Effect NodeManager //effects容器OurEffect node.NodeManager //effects容器
Damage decimal.Decimal //造成伤害
First bool
}
func (i *Input) GetPetInfo() *info.BattlePetEntity {
return i.CurrentPet
}
func (i *Input) InitAttackValue() {
i.AttackValue = info.NewAttackValue(i.Player.ID())
}
func (i *Input) GetPet(id uint32) (ii *info.BattlePetEntity, Reason info.ChangePetInfo) {
for _, v := range i.AllPet {
if v.Info.CatchTime == uint32(id) {
copier.Copy(&Reason, &v.Info)
Reason.UserId = i.Player.ID()
ii = v
}
}
return
}

View File

@@ -1,46 +0,0 @@
package input
import (
"blazing/logic/service/common"
"blazing/logic/service/fight/info"
"github.com/jinzhu/copier"
"github.com/shopspring/decimal"
)
type Input struct {
CanChange bool //是否可以死亡切换CanChange
CurrentPet *info.BattlePetEntity //当前精灵
AllPet []*info.BattlePetEntity
Player common.PlayerI
Finished bool //是否加载完成
*info.AttackValue
FightC common.FightI
// info.BattleActionI
Effect NodeManager //effects容器OurEffect node.NodeManager //effects容器
Damage decimal.Decimal //造成伤害
First bool
}
func (i *Input) GetPetInfo() *info.BattlePetEntity {
return i.CurrentPet
}
func (i *Input) InitAttackValue() {
i.AttackValue = info.NewAttackValue(i.Player.ID())
}
func (i *Input) GetPet(id uint32) (ii *info.BattlePetEntity, Reason info.ChangePetInfo) {
for _, v := range i.AllPet {
if v.Info.CatchTime == uint32(id) {
copier.Copy(&Reason, &v.Info)
Reason.UserId = i.Player.ID()
ii = v
}
}
return
}

View File

@@ -1,218 +0,0 @@
package input
import (
"blazing/logic/service/fight/info"
"reflect"
)
type Effect interface {
OnBattleStart() bool //战斗开始
OnTurnStart(attacker, defender *Input) bool //回合开始
UseSkill(attacker, defender *Input) bool //使用技能 可以取消用技能节点
// OnSkillPP() bool //技能PP减少节点
// BeforeMultiHit() bool //多段攻击前
// BeforeHit() bool //命中前
// OnCritPreDamage() bool //暴击判定成功且伤害计算前触发
// PreDamage() bool // 技能伤害计算前触发(增伤 / 减伤等)
// OnBeforeCalculateDamage() bool // 最终伤害计算前触发
// OnDamage() bool // 造成伤害时触发
//使用技能 可以取消用技能节点
SetArgs(param []int)
IsCrit(attacker, defender *Input, skill *info.BattleSkillEntity) bool //是否暴击
CalculateDamage(attacker, defender *Input, skill *info.BattleSkillEntity) //击判定成功且伤害计算前触发
// Shield() bool // 护盾值变化时触发
// PostDamage() bool // 伤害结算后触发(血量扣除后)
IsHit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //闪避率计算,,实际上是修改命中的判断
TakeHit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //闪避率计算,,实际上是修改命中的判断
//() bool // 暴击伤害结算后触发
// OnHit() bool // 技能命中时触发
// OnMiss() bool // 技能未命中时触发
// AfterAttacked() bool // 被攻击后触发(受击判定)
// SetOwner(bool)
// OnDefeat() bool // 精灵被击败时触发
// TurnEnd() bool // 回合结束
// // 堆叠Stack相关触发
// OnStackBefore() bool // 堆叠效果前触发
// OnStack() bool // 堆叠效果触发
// OnBeforeConsumeStack() bool // 消耗堆叠前触发
// OnConsumeStack() bool // 消耗堆叠时触发
// // 治疗相关触发
// OnBeforeHeal() bool // 治疗前触发
// OnHeal() bool // 治疗生效时触发
// // // 怒气Rage相关触发
// // BeforeRageGain EnumEffectTrigger enum:"37" // 增怒前触发
// // OnRageGain EnumEffectTrigger enum:"38" // 增怒时触发
// // BeforeRageLoss EnumEffectTrigger enum:"39" // 减怒前触发
// // OnRageLoss EnumEffectTrigger enum:"40" // 减怒时触发
// // 精灵切换相关触发
// OnSwitchIn() bool // 精灵出战 / 上场时触发
// OnSwitchOut() bool // 精灵下场时触发
// OnOwnerSwitchIn() bool // 所属玩家精灵出战时触发
// OnOwnerSwitchOut() bool // 所属玩家精灵下场时触发
// PreBattleEnd() bool //战斗结束前
// OnBattleEnd() bool //战斗结束
Duration(int) int
ID() int
GetArgSize() int
Stack(int) int
MaxStack() int
GetOwner() bool // 技能属主,比如寄生和镇魂歌,属主是对方)
//GetSkill() *BattleSkillEntity //获得技能ctx
}
// ========================
// 容器:存放多个效果
// ========================
type NodeManager struct {
//GlobalEffects []*Effect // 全局常驻/回合/次数效果
Effects []Effect //effects 实际上全局就是effect无限回合
}
var NodeM = make(map[int]Effect, 0)
func InitEffect(id int, t Effect) {
NodeM[id] = t
}
func getTypeName(v interface{}) string {
// 获取类型信息
t := reflect.TypeOf(v)
// 如果是指针类型,需要先获取其指向的元素类型
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// 如果是结构体类型,返回其名称
if t.Kind() == reflect.Struct {
return t.Name()
}
// 非结构体类型返回空或对应的类型名
return t.Kind().String()
}
func (c *NodeManager) AddEffect(e Effect) {
// 如果已有同 ID 的效果,尝试叠加
for _, eff := range c.Effects {
if eff.ID() == e.ID() {
if eff.Stack(0) < eff.MaxStack() { //如果小于最大叠层
eff.Stack(eff.Stack(0)) //获取到当前叠层数然后叠加
} else {
//这里,说明是延续回合效果
eff.Duration(eff.Duration(0))
}
return
}
}
// 否则新加入
c.Effects = append(c.Effects, e)
}
// 删除
func (c *NodeManager) RemoveEffect(e Effect) {
var remain []Effect
for _, eff := range c.Effects {
if eff.ID() != e.ID() {
remain = append(remain, eff)
}
}
c.Effects = remain
}
// ForEachEffectBool 遍历所有 Effect执行“无参数、返回 bool”的方法
// 参数 fn接收单个 Effect返回 bool如 func(e Effect) bool { return e.OnBattleStart() }
// 返回值:所有 Effect 的方法返回值列表
func (c *NodeManager) Exec(fn func(Effect) bool) bool {
var results bool
for _, effect := range c.Effects {
result := fn(effect)
if !result {
results = result //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
} else {
break //如果是false,停止向下循环
}
}
return results
}
// 消除回合类效果 efftype 输入是消对方的还是自己的,false是自己,true是对方
func (c *NodeManager) CancelTurn(efftype bool) {
var remain []Effect
for _, eff := range c.Effects {
if eff.Duration(0) <= 0 && eff.GetOwner() == efftype { //false是自身,true是对方,反转后为真就是自己的
remain = append(remain, eff)
}
}
c.Effects = remain
}
// // 添加效果
// func (c **NodeManager) AddEffect(e Effect) {
// var fff *NodeManager
// switch t := (e.Duration(0) > 1); t { //判断是否是回合类效果
// case t: //t>0就是回合类效果了 -1和0都是非回合效果和无限效果,无法被断回合
// fff = c.Turn
// default:
// fff = c.Mark
// }
// fff.AddEffect(e)
// }
// // 删除
// func (c **NodeManager) RemoveEffect(e Effect) {
// var fff *NodeManager
// switch t := (e.Duration(0) > 1); t { //判断是否是回合类效果
// case t: //t>0就是回合类效果了 -1和0都是非回合效果和无限效果,无法被断回合
// fff = c.Turn
// default:
// fff = c.Mark
// }
// fff.RemoveEffect(e)
// }
// // ForEachEffectBool 遍历所有 Effect执行“无参数、返回 bool”的方法
// // 参数 fn接收单个 Effect返回 bool如 func(e Effect) bool { return e.OnBattleStart() }
// // 返回值:所有 Effect 的方法返回值列表
// func (c *NodeManagerE) Exec(fn func(Effect) bool) bool {
// // var results bool
// // execfun := func(nm *NodeManager) {
// // for _, effect := range nm.Effects {
// // result := fn(effect)
// // if !result {
// // results = result //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
// // }
// // }
// // }
// results := true
// result := c.Mark.Exec(fn) //执行叠层
// if !result {
// results = result
// }
// result = c.Turn.Exec(fn) //执行回合类效果
// if !result {
// results = result
// }
// return results
// }

View File

@@ -1,218 +0,0 @@
package input
import (
"blazing/logic/service/fight/info"
"reflect"
)
type Effect interface {
OnBattleStart() bool //战斗开始
OnTurnStart(attacker, defender *Input) bool //回合开始
UseSkill(attacker, defender *Input) bool //使用技能 可以取消用技能节点
// OnSkillPP() bool //技能PP减少节点
// BeforeMultiHit() bool //多段攻击前
// BeforeHit() bool //命中前
// OnCritPreDamage() bool //暴击判定成功且伤害计算前触发
// PreDamage() bool // 技能伤害计算前触发(增伤 / 减伤等)
// OnBeforeCalculateDamage() bool // 最终伤害计算前触发
// OnDamage() bool // 造成伤害时触发
//使用技能 可以取消用技能节点
SetArgs(param []int)
IsCrit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //是否暴击
CalculateDamage(attacker, defender *Input, skill *info.BattleSkillEntity) //击判定成功且伤害计算前触发
// Shield() bool // 护盾值变化时触发
// PostDamage() bool // 伤害结算后触发(血量扣除后)
IsHit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //闪避率计算,,实际上是修改命中的判断
TakeHit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //闪避率计算,,实际上是修改命中的判断
//() bool // 暴击伤害结算后触发
// OnHit() bool // 技能命中时触发
// OnMiss() bool // 技能未命中时触发
// AfterAttacked() bool // 被攻击后触发(受击判定)
// SetOwner(bool)
// OnDefeat() bool // 精灵被击败时触发
// TurnEnd() bool // 回合结束
// // 堆叠Stack相关触发
// OnStackBefore() bool // 堆叠效果前触发
// OnStack() bool // 堆叠效果触发
// OnBeforeConsumeStack() bool // 消耗堆叠前触发
// OnConsumeStack() bool // 消耗堆叠时触发
// // 治疗相关触发
// OnBeforeHeal() bool // 治疗前触发
// OnHeal() bool // 治疗生效时触发
// // // 怒气Rage相关触发
// // BeforeRageGain EnumEffectTrigger enum:"37" // 增怒前触发
// // OnRageGain EnumEffectTrigger enum:"38" // 增怒时触发
// // BeforeRageLoss EnumEffectTrigger enum:"39" // 减怒前触发
// // OnRageLoss EnumEffectTrigger enum:"40" // 减怒时触发
// // 精灵切换相关触发
// OnSwitchIn() bool // 精灵出战 / 上场时触发
// OnSwitchOut() bool // 精灵下场时触发
// OnOwnerSwitchIn() bool // 所属玩家精灵出战时触发
// OnOwnerSwitchOut() bool // 所属玩家精灵下场时触发
// PreBattleEnd() bool //战斗结束前
// OnBattleEnd() bool //战斗结束
Duration(int) int
ID() int
GetArgSize() int
Stack(int) int
MaxStack() int
GetOwner() bool // 技能属主,比如寄生和镇魂歌,属主是对方)
//GetSkill() *BattleSkillEntity //获得技能ctx
}
// ========================
// 容器:存放多个效果
// ========================
type NodeManager struct {
//GlobalEffects []*Effect // 全局常驻/回合/次数效果
Effects []Effect //effects 实际上全局就是effect无限回合
}
var NodeM = make(map[int]Effect, 0)
func InitEffect(id int, t Effect) {
NodeM[id] = t
}
func getTypeName(v interface{}) string {
// 获取类型信息
t := reflect.TypeOf(v)
// 如果是指针类型,需要先获取其指向的元素类型
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// 如果是结构体类型,返回其名称
if t.Kind() == reflect.Struct {
return t.Name()
}
// 非结构体类型返回空或对应的类型名
return t.Kind().String()
}
func (c *NodeManager) AddEffect(e Effect) {
// 如果已有同 ID 的效果,尝试叠加
for _, eff := range c.Effects {
if eff.ID() == e.ID() {
if eff.Stack(0) < eff.MaxStack() { //如果小于最大叠层
eff.Stack(eff.Stack(0)) //获取到当前叠层数然后叠加
} else {
//这里,说明是延续回合效果
eff.Duration(eff.Duration(0))
}
return
}
}
// 否则新加入
c.Effects = append(c.Effects, e)
}
// 删除
func (c *NodeManager) RemoveEffect(e Effect) {
var remain []Effect
for _, eff := range c.Effects {
if eff.ID() != e.ID() {
remain = append(remain, eff)
}
}
c.Effects = remain
}
// ForEachEffectBool 遍历所有 Effect执行“无参数、返回 bool”的方法
// 参数 fn接收单个 Effect返回 bool如 func(e Effect) bool { return e.OnBattleStart() }
// 返回值:所有 Effect 的方法返回值列表
func (c *NodeManager) Exec(fn func(Effect) bool) bool {
var results bool
for _, effect := range c.Effects {
result := fn(effect)
if !result {
results = result //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
} else {
break //如果是false,停止向下循环
}
}
return results
}
// 消除回合类效果 efftype 输入是消对方的还是自己的,false是自己,true是对方
func (c *NodeManager) CancelTurn(efftype bool) {
var remain []Effect
for _, eff := range c.Effects {
if eff.Duration(0) <= 0 && eff.GetOwner() == efftype { //false是自身,true是对方,反转后为真就是自己的
remain = append(remain, eff)
}
}
c.Effects = remain
}
// // 添加效果
// func (c **NodeManager) AddEffect(e Effect) {
// var fff *NodeManager
// switch t := (e.Duration(0) > 1); t { //判断是否是回合类效果
// case t: //t>0就是回合类效果了 -1和0都是非回合效果和无限效果,无法被断回合
// fff = c.Turn
// default:
// fff = c.Mark
// }
// fff.AddEffect(e)
// }
// // 删除
// func (c **NodeManager) RemoveEffect(e Effect) {
// var fff *NodeManager
// switch t := (e.Duration(0) > 1); t { //判断是否是回合类效果
// case t: //t>0就是回合类效果了 -1和0都是非回合效果和无限效果,无法被断回合
// fff = c.Turn
// default:
// fff = c.Mark
// }
// fff.RemoveEffect(e)
// }
// // ForEachEffectBool 遍历所有 Effect执行“无参数、返回 bool”的方法
// // 参数 fn接收单个 Effect返回 bool如 func(e Effect) bool { return e.OnBattleStart() }
// // 返回值:所有 Effect 的方法返回值列表
// func (c *NodeManagerE) Exec(fn func(Effect) bool) bool {
// // var results bool
// // execfun := func(nm *NodeManager) {
// // for _, effect := range nm.Effects {
// // result := fn(effect)
// // if !result {
// // results = result //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
// // }
// // }
// // }
// results := true
// result := c.Mark.Exec(fn) //执行叠层
// if !result {
// results = result
// }
// result = c.Turn.Exec(fn) //执行回合类效果
// if !result {
// results = result
// }
// return results
// }

View File

@@ -0,0 +1,16 @@
package effect
import (
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
)
// 施加一个基类effect
type Effect0 struct {
node.EffectNode
}
func init() {
input.InitEffect(0, &Effect0{})
}

View File

@@ -75,7 +75,7 @@ func (f *FightC) GetRand() *rand.Rand {
func (f *FightC) initplayer(c common.PlayerI, opp bool) {
temp := &input.Input{Player: c}
temp := input.NewInput(f, c)
temp.AllPet = make([]*info.BattlePetEntity, 0)
for i := 0; i < len(c.GetPetInfo()); i++ {
@@ -132,7 +132,7 @@ func (f *FightC) initplayer(c common.PlayerI, opp bool) {
}
temp.CurrentPet = temp.AllPet[0]
c.SetFightC(f)
}
// 创建新战斗,邀请方和被邀请方,或者玩家和野怪方
@@ -444,45 +444,44 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *Sele
}
//回合有先手方和后手方,同时有攻击方和被攻击方
func (f *FightC) enterturn(fattack, sattack BattleActionI) {
f.initAttackers(fattack, sattack) //初始化先后手
var attacker, defender *input.Input
//**回合开始前enterturn
attacker.Effect.Exec(func(t input.Effect) bool { //计算命中
//结算状态
t.OnTurnStart(attacker, defender)
return true
})
//开始回合操作
for i := 0; i < 2; i++ {
var attackeraction BattleActionI
skill, ok := attackeraction.(*SelectSkillAction)
f.parseskill(attacker, defender, skill) //解析effect
if !ok { //还有系统选择放弃出手的
continue
}
if attacker.CurrentPet.Info.Hp <= 0 { //攻击方死亡
continue
}
if i == 0 { //
attacker, defender = f.First, f.Second
attackeraction = fattack
skill.Skill.First = true //先手技能
attacker.First = true //先手技能
//attacker.BattleActionI, defender.BattleActionI = fattack, sattack
} else {
attacker, defender = f.Second, f.First
attackeraction = sattack
skill.Skill.First = false //手技能
attacker.First = false //手技能
}
skill, ok := attackeraction.(*SelectSkillAction)
if !ok || attacker.CurrentPet.Info.Hp <= 0 { //还有系统选择放弃出手的
continue
}
//**回合开始前enterturn
attacker.Effect.Exec(func(t input.Effect) bool { //计算命中
//结算状态
t.OnTurnStart(attacker, defender)
return true
})
canuseskill := attacker.Effect.Exec(func(t input.Effect) bool { //计算命中
//结算状态
return t.UseSkill(attacker, defender) //返回本身结算,如果false,说明不能使用技能了
@@ -490,7 +489,7 @@ func (f *FightC) enterturn(fattack, sattack BattleActionI) {
})
if canuseskill { //可以使用技能
f.parseskill(attacker, defender, skill) //解析effect
f.processSkillAttack(attacker, defender, skill)
skill.Skill.Info.PP-- //减少PP

View File

@@ -42,8 +42,8 @@ type BattleSkillEntity struct {
DamageValue decimal.Decimal // 伤害值
Rand *rand.Rand
Pet *BattlePetEntity
First bool
Hit uint32
Hit uint32
}
// CreateBattleSkillWithInfinity 创建战斗技能实例可指定是否无限PP

View File

@@ -5,6 +5,7 @@ import (
"blazing/logic/service/fight/info"
"github.com/jinzhu/copier"
"github.com/mohae/deepcopy"
"github.com/shopspring/decimal"
)
@@ -22,11 +23,21 @@ type Input struct {
First bool
}
func NewInput(c common.FightI, p common.PlayerI) *Input {
ret := &Input{FightC: c, Player: p}
t, _ := NodeM[0]
ret.Effect.AddEffect(deepcopy.Copy(t).(Effect)) //添加默认基类,实现继承
p.SetFightC(c) //给玩家设置战斗容器
return ret
}
func (i *Input) GetPetInfo() *info.BattlePetEntity {
return i.CurrentPet
}
// 这个每回合都会调用
func (i *Input) InitAttackValue() {
i.AttackValue = info.NewAttackValue(i.Player.ID())

View File

@@ -140,14 +140,20 @@ func (c *NodeManager) RemoveEffect(e Effect) {
func (c *NodeManager) Exec(fn func(Effect) bool) bool {
var results bool
// if len(c.Effects) == 0 {
// return true
// }
for _, effect := range c.Effects {
result := fn(effect)
if !result {
results = result //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
} else {
break //如果是false,停止向下循环
}
if result {
results = true
}
}
return results
}

View File

@@ -18,7 +18,7 @@ func (this *EffectNode) TakeHit(attacker, defender *input.Input, skill *info.Bat
}
func (this *EffectNode) UseSkill(attacker, defender *input.Input) bool {
panic("not implemented") // TODO: Implement
return true
}
func (this *EffectNode) OnSkillPP() bool {
panic("not implemented") // TODO: Implement
@@ -63,11 +63,11 @@ func (this *EffectNode) IsCrit(attacker, defender *input.Input, skill *info.Batt
CritRate := utils.Max(skill.CritRate, 1)
CritRateR := attacker.FightC.GetRand().Int31n(16)
//CritAtkFirst: 先出手时必定致命一击; 默认: 0
if skill.CritAtkFirst != 0 && skill.First {
if skill.CritAtkFirst != 0 && attacker.First {
CritRate = 16
}
//CritAtkSecond: 后出手时必定致命一击; 默认: 0
if skill.CritAtkSecond != 0 && !skill.First {
if skill.CritAtkSecond != 0 && !attacker.First {
CritRate = 16
}
// CritSelfHalfHp: 自身体力低于一半时必定致命一击; 默认: 0

View File

@@ -203,7 +203,7 @@ func (p *Player) SendFightEndInfo(b info.FightOverInfo) {
t1 := NewTomeeHeader(2506, p.Info.UserID)
p.SendPack(t1.Pack(&b))
p.SetFightC(nil)
p.FightC = nil
}
func (p *Player) GetPetInfo() []model.PetInfo {