feat(fight): 调整战斗逻辑与伤害计算流程

- 移除 `Over` 方法中的冗余回调参数 `fn`
- 修复部分技能效果中错误的伤害目标对象(Our/Opp)
- 优化战斗循环逻辑,使用 `over` channel 替代 `quit` 作为战斗结束信号
- 增加回合效果执行前的存活状态判断
- 修正伤害计算过程中对血量扣减的逻辑错误
-
This commit is contained in:
2025-11-12 01:19:24 +08:00
parent a0a5163f13
commit 0b5cfac0b2
16 changed files with 73 additions and 71 deletions

View File

@@ -170,7 +170,7 @@ func (h Controller) Escape(data *fight.EscapeFightInboundInfo, c *player.Player)
return nil, 0 return nil, 0
} }
c.FightC.Over(c, info.BattleOverReason.PlayerEscape, nil) c.FightC.Over(c, info.BattleOverReason.PlayerEscape)
return nil, 0 return nil, 0
} }

View File

@@ -6,9 +6,9 @@ import (
) )
type FightI interface { type FightI interface {
Over(c PlayerI, id info.EnumBattleOverReason, fn func()) //逃跑 Over(c PlayerI, id info.EnumBattleOverReason) //逃跑
UseSkill(c PlayerI, id int32) //使用技能 UseSkill(c PlayerI, id int32) //使用技能
GetCurrPET(c PlayerI) *info.BattlePetEntity //当前精灵 GetCurrPET(c PlayerI) *info.BattlePetEntity //当前精灵
Ownerid() uint32 Ownerid() uint32
ReadyFight(c PlayerI) //是否准备战斗 ReadyFight(c PlayerI) //是否准备战斗
@@ -19,5 +19,5 @@ type FightI interface {
UseItem(c PlayerI, cacthid, itemid uint32) UseItem(c PlayerI, cacthid, itemid uint32)
CanEscape() bool CanEscape() bool
IsFirst(c PlayerI) bool IsFirst(c PlayerI) bool
//GetOverChan() chan struct{} GetOverChan() chan struct{}
} }

View File

@@ -29,7 +29,7 @@ func (f *FightC) Compare(a, b action.BattleActionI) (action.BattleActionI, actio
} }
// 玩家逃跑/无响应/掉线 // 玩家逃跑/无响应/掉线
func (f *FightC) Over(c common.PlayerI, res info.EnumBattleOverReason, fn func()) { func (f *FightC) Over(c common.PlayerI, res info.EnumBattleOverReason) {
if f.closefight { if f.closefight {
cool.Loger.Debug(context.Background(), " 战斗chan已关闭") cool.Loger.Debug(context.Background(), " 战斗chan已关闭")
return return
@@ -48,9 +48,6 @@ func (f *FightC) Over(c common.PlayerI, res info.EnumBattleOverReason, fn func()
f.Reason = res f.Reason = res
f.WinnerId = f.GetInputByPlayer(c, true).UserID f.WinnerId = f.GetInputByPlayer(c, true).UserID
close(f.quit) close(f.quit)
if fn != nil {
fn()
}
}) })

View File

@@ -28,7 +28,7 @@ func (e *Effect28) OnSkill() bool {
return true return true
} }
e.Ctx().Opp.Damage(&info.DamageZone{ e.Ctx().Our.Damage(&info.DamageZone{
Type: info.DamageType.Fixed, Type: info.DamageType.Fixed,
Damage: decimal.NewFromInt(int64(e.Ctx().Opp.CurrentPet.Info.Hp)).Div(decimal.NewFromInt(int64(e.SideEffectArgs[0]))), Damage: decimal.NewFromInt(int64(e.Ctx().Opp.CurrentPet.Info.Hp)).Div(decimal.NewFromInt(int64(e.SideEffectArgs[0]))),
}) })

View File

@@ -28,7 +28,7 @@ func (e *Effect29) OnSkill() bool {
return true return true
} }
e.Ctx().Opp.Damage(&info.DamageZone{ e.Ctx().Our.Damage(&info.DamageZone{
Type: info.DamageType.Fixed, Type: info.DamageType.Fixed,
Damage: decimal.NewFromInt(int64(e.SideEffectArgs[0])), Damage: decimal.NewFromInt(int64(e.SideEffectArgs[0])),

View File

@@ -32,8 +32,8 @@ func (e *Effect62_sub) OnSkill() bool {
//defer e.Alive(false) //defer e.Alive(false)
if e.Duration() == 0 { //说明对方没有切换精灵 if e.Duration() == 0 { //说明对方没有切换精灵
//直接扣除所有血量OnSkill //直接扣除所有血量OnSkill
//相当于对方给自己的伤害
e.Ctx().Our.Damage(&info.DamageZone{ e.Ctx().Opp.Damage(&info.DamageZone{
Type: info.DamageType.Fixed, Type: info.DamageType.Fixed,
Damage: decimal.NewFromInt(int64(e.Ctx().Our.CurrentPet.Info.MaxHp)), Damage: decimal.NewFromInt(int64(e.Ctx().Our.CurrentPet.Info.MaxHp)),
}) })

View File

@@ -4,6 +4,7 @@ import (
"blazing/logic/service/fight/info" "blazing/logic/service/fight/info"
"blazing/logic/service/fight/input" "blazing/logic/service/fight/input"
"blazing/logic/service/fight/node" "blazing/logic/service/fight/node"
"fmt"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
) )
@@ -34,10 +35,11 @@ func (e *Effect7) Damage_Floor() bool {
if !e.Hit() { if !e.Hit() {
return true return true
} }
fmt.Println("Effect7_old", e.Ctx().DamageZone.Damage.IntPart())
if e.Ctx().DamageZone.Type == info.DamageType.Red { if e.Ctx().DamageZone.Type == info.DamageType.Red {
e.Ctx().DamageZone.Damage = decimal.NewFromInt(int64(e.Ctx().Our.CurrentPet.Info.Hp - e.Ctx().Opp.CurrentPet.Info.Hp)) e.Ctx().DamageZone.Damage = decimal.NewFromInt(int64(e.Ctx().Our.CurrentPet.Info.Hp - e.Ctx().Opp.CurrentPet.Info.Hp))
} }
fmt.Println("Effect7_new", e.Ctx().DamageZone.Damage.IntPart())
return true return true
} }

View File

@@ -49,12 +49,12 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, a *info.Ski
if attacker.AttackTime > 0 { //如果命中 if attacker.AttackTime > 0 { //如果命中
attacker.CalculateCrit(defender, a) //暴击计算 attacker.CalculateCrit(defender, a) //暴击计算
attacker.AttackValue.IsCritical = a.Crit attacker.IsCritical = a.Crit
attacker.DamageZone.Damage = attacker.CalculatePower(defender, a) attacker.DamageZone.Damage = attacker.CalculatePower(defender, a)
//睡眠受击消除 //睡眠受击消除
if attacker.AttackValue.IsCritical == 1 { if attacker.IsCritical == 1 {
//暴击破防 //暴击破防
if a.Category() == info.Category.PHYSICAL && defender.Prop[1] > 0 { if a.Category() == info.Category.PHYSICAL && defender.Prop[1] > 0 {
@@ -74,11 +74,6 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, a *info.Ski
//这里实现应该参考本地技能是否命中,然后 //这里实现应该参考本地技能是否命中,然后
e.Hit(a.AttackTime != 0) //我方效果命中 e.Hit(a.AttackTime != 0) //我方效果命中
} }
// for _, t := range defender.EffectCache {
// if t.GetInput() == attacker { //如果取反,说明是给对方添加的回合效果
// t.Hit(a.AttackTime != 0)
// }
// }
// 扣减防御方血量 // 扣减防御方血量
attacker.Exec(func(t input.Effect) bool { attacker.Exec(func(t input.Effect) bool {
@@ -89,7 +84,7 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, a *info.Ski
return true return true
}) })
defender.Damage(&info.DamageZone{ attacker.Damage(&info.DamageZone{
Damage: attacker.DamageZone.Damage, Damage: attacker.DamageZone.Damage,
}, },
@@ -216,7 +211,9 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
currentskill = oldskill currentskill = oldskill
attacker.Initeffectcache() attacker.Initeffectcache()
// fmt.Println("开始攻击威力", oldskill.Power) // fmt.Println("开始攻击威力", oldskill.Power)
if oldskill != nil {
fmt.Println("开始攻击威力", oldskill.Power)
}
canuseskill := attacker.ExecCace(func(t input.Effect) bool { //这个是能否使用技能 canuseskill := attacker.ExecCace(func(t input.Effect) bool { //这个是能否使用技能
//结算状态 //结算状态
//然后这里还可以处理自爆类 //然后这里还可以处理自爆类
@@ -232,8 +229,11 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
f.processSkillAttack(attacker, defender, currentskill) f.processSkillAttack(attacker, defender, currentskill)
currentskill = oldskill //还原技能 currentskill = oldskill //还原技能
// fmt.Println("结束攻击1", oldskill.Power) if oldskill != nil {
// fmt.Println("结束攻击", currentskill.Power) fmt.Println("结束攻击_old", oldskill.Power)
fmt.Println("结束攻击_new", currentskill.Power)
}
_, skill, ok := utils.FindWithIndex(attacker.CurrentPet.Info.SkillList, func(item model.SkillInfo) bool { _, skill, ok := utils.FindWithIndex(attacker.CurrentPet.Info.SkillList, func(item model.SkillInfo) bool {
return item.ID == currentskill.Info.ID return item.ID == currentskill.Info.ID
}) })

View File

@@ -28,6 +28,7 @@ type FightC struct {
actionChan chan action.BattleActionI // 所有操作统一从这里进入 actionChan chan action.BattleActionI // 所有操作统一从这里进入
Round int //回合数 Round int //回合数
quit chan struct{} quit chan struct{}
over chan struct{}
First *input.Input First *input.Input
Second *input.Input Second *input.Input
closefight bool closefight bool
@@ -165,8 +166,7 @@ func (f *FightC) initplayer(c common.PlayerI, opp bool) bool {
} }
} }
f.Opp.SetOPP(f.Our)
f.Our.SetOPP(f.Opp)
temp.CurrentPet = temp.AllPet[0] temp.CurrentPet = temp.AllPet[0]
return true return true
} }
@@ -176,6 +176,7 @@ func NewFight(mode, status info.EnumBattleMode, p1 common.PlayerI, p2 common.Pla
f := &FightC{} f := &FightC{}
f.ownerID = p1.GetInfo().UserID f.ownerID = p1.GetInfo().UserID
f.quit = make(chan struct{}) f.quit = make(chan struct{})
f.over = make(chan struct{})
f.StartTime = time.Now() f.StartTime = time.Now()
seed := f.StartTime.UnixNano() ^ int64(p1.GetInfo().UserID) ^ int64(p2.GetInfo().UserID) // ^ int64(f.Round) // 用异或运算混合多维度信息 seed := f.StartTime.UnixNano() ^ int64(p1.GetInfo().UserID) ^ int64(p2.GetInfo().UserID) // ^ int64(f.Round) // 用异或运算混合多维度信息
f.rand = rand.New(rand.NewSource(seed)) f.rand = rand.New(rand.NewSource(seed))
@@ -194,6 +195,8 @@ func NewFight(mode, status info.EnumBattleMode, p1 common.PlayerI, p2 common.Pla
if !ok { if !ok {
return nil return nil
} }
f.Our.SetOPP(f.Opp)
f.Opp.SetOPP(f.Our)
i := Fightpool.Free() i := Fightpool.Free()
if i <= 0 { if i <= 0 {
Fightpool.Tune(Fightpool.Cap() + 1) Fightpool.Tune(Fightpool.Cap() + 1)
@@ -246,3 +249,8 @@ func (f *FightC) Broadcast(t func(ff *input.Input)) {
t(f.Opp) t(f.Opp)
} }
func (f *FightC) GetOverChan() chan struct{} {
return f.over
}

View File

@@ -202,13 +202,14 @@ func (our *Input) ExecCace(fn func(Effect) bool) bool {
result := true result := true
for _, value := range our.EffectCache { for _, value := range our.EffectCache {
value.Ctx().Our = our if value.Alive() {
value.Ctx().Opp = our.Opp value.Ctx().Our = our
value.Ctx().DamageZone = &info.DamageZone{} value.Ctx().Opp = our.Opp
if !fn(value) { //存在false,但是仍然要向下执行 value.Ctx().DamageZone = &info.DamageZone{}
result = false //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果 if !fn(value) { //存在false,但是仍然要向下执行
result = false //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
}
} }
} }
return result return result

View File

@@ -92,6 +92,7 @@ func (our *Input) DelPP(value int) {
} }
// /红伤只允许调用一次来保持锁伤 // /红伤只允许调用一次来保持锁伤
// 这个方法是对对方造成伤害
// 伤害落实 // 血量扣减节点比如触发回神,反弹也在这里实现 // 伤害落实 // 血量扣减节点比如触发回神,反弹也在这里实现
func (our *Input) Damage(sub *info.DamageZone) { func (our *Input) Damage(sub *info.DamageZone) {
// sub := deep.MustCopy(ctx.DamageZone) //拷贝伤害,避免直接上下文传递,便于附加伤害 // sub := deep.MustCopy(ctx.DamageZone) //拷贝伤害,避免直接上下文传递,便于附加伤害
@@ -166,15 +167,15 @@ func (our *Input) Damage(sub *info.DamageZone) {
} }
if sub.Type == info.DamageType.Red { //红才会产生造成伤害 if sub.Type == info.DamageType.Red { //红才会产生造成伤害
our.DamageZone.Damage.Add(sub.Damage) // 叠加总伤害 our.DamageZone.Damage = sub.Damage // 叠加总伤害
our.AttackValue.LostHp = uint32(sub.Damage.IntPart()) //红伤落实 our.AttackValue.LostHp = uint32(our.DamageZone.Damage.IntPart()) //红伤落实
} }
if uint32(sub.Damage.IntPart()) > our.CurrentPet.Info.Hp { if uint32(our.DamageZone.Damage.IntPart()) > our.Opp.CurrentPet.Info.Hp {
our.CurrentPet.Info.Hp = 0 our.Opp.CurrentPet.Info.Hp = 0
} else { } else {
our.CurrentPet.Info.Hp = our.CurrentPet.Info.Hp - uint32(sub.Damage.IntPart()) our.Opp.CurrentPet.Info.Hp = our.Opp.CurrentPet.Info.Hp - uint32(our.DamageZone.Damage.IntPart())
} }
//todo 待实现死亡effet //todo 待实现死亡effet

View File

@@ -143,7 +143,6 @@ func (our *Input) GetStatusBonus() float64 {
} }
func (our *Input) Initeffectcache() { func (our *Input) Initeffectcache() {
our.EffectCache = make([]Effect, 0) //先把上一回合数据清空,但是应该把本身延续类效果集成过来
for _, v := range our.Effects { for _, v := range our.Effects {
@@ -160,7 +159,8 @@ func (our *Input) Initeffectcache() {
// 解析并 施加effect // 解析并 施加effect
func (our *Input) Parseskill(defender *Input, skill *action.SelectSkillAction) { func (our *Input) Parseskill(defender *Input, skill *action.SelectSkillAction) {
our.Initeffectcache() //这里说明是延续效果,每次复制出来一个新的就好了 our.EffectCache = make([]Effect, 0) //先把上一回合数据清空,但是应该把本身延续效果集成过来
our.Initeffectcache() //这里说明是延续的效果,每次复制出来一个新的就好了
//i.NewEffects = make([]Effect, 0) //这里说明是新增的效果 //i.NewEffects = make([]Effect, 0) //这里说明是新增的效果
temparg := skill.SideEffectArgS temparg := skill.SideEffectArgS
@@ -183,7 +183,7 @@ func (our *Input) Parseskill(defender *Input, skill *action.SelectSkillAction) {
//i.AddEffect(t) //i.AddEffect(t)
// } // }
//这里是临时缓存buff,后面确认命中后修改HIT状态 //这里是临时缓存buff,后面确认命中后修改HIT状态
// t.Alive() //先让效果保持存活 t.Alive() //先让效果保持存活
our.EffectCache = append(our.EffectCache, t) our.EffectCache = append(our.EffectCache, t)
// i.NewEffects = append(i.NewEffects, t) // i.NewEffects = append(i.NewEffects, t)
} }

View File

@@ -23,7 +23,7 @@ func (f *FightC) battleLoop() {
ourID := f.Our.Player.GetInfo().UserID ourID := f.Our.Player.GetInfo().UserID
oppID := f.Opp.Player.GetInfo().UserID oppID := f.Opp.Player.GetInfo().UserID
for { for !f.closefight {
f.Round++ f.Round++
fmt.Printf("—— 第 %d 回合开始 ——\n", f.Round) fmt.Printf("—— 第 %d 回合开始 ——\n", f.Round)
@@ -37,7 +37,6 @@ func (f *FightC) battleLoop() {
} }
fmt.Println("战斗循环结束")
f.Broadcast(func(ff *input.Input) { f.Broadcast(func(ff *input.Input) {
//todo 将血量和技能pp传回enterturn //todo 将血量和技能pp传回enterturn
@@ -45,7 +44,8 @@ func (f *FightC) battleLoop() {
}) })
close(f.actionChan) close(f.actionChan)
close(f.quit) fmt.Println("战斗循环结束")
close(f.over)
} }
// 收集玩家动作(含超时判定) // 收集玩家动作(含超时判定)
@@ -252,9 +252,3 @@ func (f *FightC) getPlayerByID(id uint32) common.PlayerI {
} }
return f.Opp.Player return f.Opp.Player
} }
// 根据玩家ID返回对应对象
func (f *FightC) GetOverChan() chan struct{} {
return f.quit
}

View File

@@ -19,14 +19,14 @@ func (e *EffectNode) Turn_End() {
e.Alive(false) e.Alive(false)
} else { } else {
e.trunl.Do(func() { // e.trunl.Do(func() {
if e.Ctx().Opp.FightC.IsFirst(e.Ctx().Opp.Player) { //如果对方先手 // if e.Ctx().Opp.FightC.IsFirst(e.Ctx().Opp.Player) { //如果对方先手
e.duration++ // e.duration++
// e.Alive(true) // // e.Alive(true)
} // }
}) // })
e.duration-- e.duration--
} }

View File

@@ -16,14 +16,14 @@ type EffectNode struct {
id int id int
maxStack int // 最大叠加层数 ,正常都是不允许叠加的,除了衰弱特殊效果 ,异常和能力的叠层 maxStack int // 最大叠加层数 ,正常都是不允许叠加的,除了衰弱特殊效果 ,异常和能力的叠层
SideEffectArgs []int // 附加效果参数 SideEffectArgs []int // 附加效果参数
owner bool //是否作用自身 // owner bool //是否作用自身
Success bool // 是否执行成功 成功XXX失败XXX Success bool // 是否执行成功 成功XXX失败XXX
arget bool // 传出作用对象,默认0是自身,1是作用于对面 arget bool // 传出作用对象,默认0是自身,1是作用于对面
Flag int //过滤掉的战斗类型 pvp pve boss战斗,野怪全部生效 Flag int //过滤掉的战斗类型 pvp pve boss战斗,野怪全部生效
alive bool // 是否失效 effect返回值是否被取消是否被删除 alive bool // 是否失效 effect返回值是否被取消是否被删除
hit bool hit bool
trunl sync.Once trunl sync.Once
ctx input.Ctx ctx input.Ctx
//增加owner target如果owner target都为自身就回合效果结束后再使用回合效果 //增加owner target如果owner target都为自身就回合效果结束后再使用回合效果
} }

View File

@@ -319,7 +319,7 @@ func (p *Player) Save() {
} }
if p.FightC != nil { if p.FightC != nil {
ov := make(chan struct{}) //ov := make(chan struct{})
go func() { go func() {
defer func() { defer func() {
@@ -330,13 +330,12 @@ func (p *Player) Save() {
} }
}() }()
p.FightC.Over(p, info.BattleOverReason.PlayerOffline, func() { p.FightC.Over(p, info.BattleOverReason.PlayerOffline) //玩家逃跑,但是不能锁线程
<-ov
}) //玩家逃跑,但是不能锁线程
}() }()
//<-ov
//<-p.FightC.GetOverChan() //等待结束 <-p.FightC.GetOverChan() //等待结束
} }
p.Info.TimeToday = p.Info.TimeToday + uint32(time.Now().Unix()) - uint32(p.Onlinetime) //保存电池时间 p.Info.TimeToday = p.Info.TimeToday + uint32(time.Now().Unix()) - uint32(p.Onlinetime) //保存电池时间
p.Onlinetime = uint32(time.Now().Unix()) p.Onlinetime = uint32(time.Now().Unix())