Files
bl/logic/service/fight/input/effect.go
昔念 e1a994ba11
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
```
feat(fight): 添加效果工厂模式支持以解决闭包变量捕获问题

- 新增initskillFactory函数用于注册效果工厂
- 修改技能效果注册逻辑从直接实例化改为工厂模式
- 解决循环中闭包捕获变量导致的潜在问题

feat(fight): 实现对手输入获取逻辑优化回合处理

- 添加roundOpponentInput方法获取对手输入
- 重构enterturn方法中的先后手逻辑
- 确保攻击方和被攻击
2026-04-12 22:44:13 +08:00

377 lines
9.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package input
import (
"blazing/common/utils"
"blazing/logic/service/fight/info"
"github.com/alpacahq/alpacadecimal"
"github.com/brunoga/deep"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
"github.com/tnnmigga/enum"
)
// 战斗结束原因枚举
type EnumEffectType uint16
var EffectType = enum.New[struct {
// ========== 新增精灵特效Stat枚举项 ==========
//Invalid EnumEffectType `enum:"0"` // 无效(默认值)
NewSel EnumEffectType `enum:"0"` // 特性
//LimitedTimes EnumEffectType `enum:"2"` // 有有效次数的特效
// Burst EnumEffectType `enum:"3"` // 爆发特效
// SpecialTrait EnumEffectType `enum:"4"` // 异能精灵特质
// Training EnumEffectType `enum:"5"` // 特训
// SoulMark EnumEffectType `enum:"6"` // 魂印
// ========== 原有枚举项 ==========
Skill EnumEffectType `enum:"1"` // 技能
Status EnumEffectType `enum:"2"` // 状态 这里是为了防止ID和技能重复
Sub EnumEffectType `enum:"3"` // 子效果,防止ID重复,所以单独划分
}]()
var NodeM = make(map[int64]Effect, 0)
var NodeFactoryM = make(map[int64]func() Effect, 0)
func InitEffect(etype EnumEffectType, id int, t Effect) {
pr := EffectIDCombiner{}
pr.Combine(etype, 0, gconv.Uint16(id))
t.ID(pr) //设置ID
NodeM[pr.EffectID()] = t
}
func InitEffectFactory(etype EnumEffectType, id int, factory func() Effect) {
pr := EffectIDCombiner{}
pr.Combine(etype, 0, gconv.Uint16(id))
NodeFactoryM[pr.EffectID()] = factory
}
func GeteffectIDs(etype EnumEffectType) []uint32 {
var ret []uint32 = make([]uint32, 0)
for _, v := range NodeM {
if v.ID().GetEffectType() == etype {
ret = append(ret, uint32(v.ID().Suffix()))
}
}
return ret
}
// 这里的catchtime为0,取出来之后如果是魂印,要重新赋值
func geteffect[T int | byte | uint16](etype EnumEffectType, id T) Effect {
pr := EffectIDCombiner{}
pr.Combine(etype, 0, gconv.Uint16(id))
if factory, ok := NodeFactoryM[pr.EffectID()]; ok {
eff := factory()
if eff == nil {
return nil
}
eff.ID(pr)
if etype == EffectType.Status {
eff.CanStack(true)
eff.Duration(grand.N(1, 2))
}
return eff
}
//todo 获取前GetEffect
ret, ok := NodeM[pr.EffectID()]
if ok {
//todo 获取前GetEffect
eff := deep.MustCopy(ret)
if etype == EffectType.Status {
eff.CanStack(true) //状态类不能被覆盖,只能无限叠加
eff.Duration(grand.N(1, 2))
}
return eff
//todo 获取后GetEffect
}
return nil
}
func (our *Input) InitEffect(etype EnumEffectType, id int, a ...int) Effect {
ret := geteffect(etype, id)
if ret != nil {
ret.SetArgs(our, a...) //输入参数是对方
}
return ret
}
// * battle_lv: atk(0), def(1), sp_atk(2), sp_def(3), spd(4), accuracy(5)
// 是否需要真实提升
func (our *Input) GetProp(id int) alpacadecimal.Decimal {
currentPet := our.CurrentPet()
if currentPet == nil {
return alpacadecimal.Zero
}
// 计算实际值(这里可以插入后续优化的函数调用)
realValue := info.CalculateRealValue(alpacadecimal.NewFromInt(int64(currentPet.Info.Prop[id])), our.AttackValue.Prop[id])
// todo: 插入获取后处理函数,例如:
// realValue = postProcessValue(realValue, id, c)
return realValue
}
func (our *Input) GetEffect(etype EnumEffectType, id int) Effect {
pr := EffectIDCombiner{}
pr.Combine(etype, 0, gconv.Uint16(id))
our.ensureEffectIndex()
bucket := our.effectsByBase[pr.Base]
for i := len(bucket) - 1; i >= 0; i-- {
if bucket[i] != nil && bucket[i].Alive() {
return bucket[i]
}
}
return nil
}
func (our *Input) StatEffect_Exist(id info.EnumPetStatus) bool {
t := our.GetEffect(EffectType.Status, int(id))
if t == nil {
return false
}
return t.Alive()
}
func (our *Input) StatEffect_Exist_all() bool {
for _, v := range our.Effects {
t := v.ID()
if t.GetEffectType() == EffectType.Status && v.Alive() {
return true
}
}
return false
}
// 判断是否是状态技能
func IS_Stat(v Effect) bool {
t := v.ID()
if t.GetEffectType() == EffectType.Status {
return true
}
return false
}
//
// 比较两个[]int是否内容相等
func equalInts(a, b []alpacadecimal.Decimal) bool {
// 先判断长度是否相等
if len(a) != len(b) {
return false
}
// 逐个比较元素
for i := range a {
if a[i].Cmp(b[i]) != 0 {
return false
}
}
return true
}
// 返回被替换eddect
func (our *Input) AddEffect(in *Input, e Effect) Effect {
if e == nil {
return nil
}
our.ensureEffectIndex()
ctx := e.Ctx()
if ctx != nil {
if ctx.Source == nil {
ctx.Source = in
}
ctx.Carrier = our
ctx.Target = our
}
if in != our {
canuseskill := our.ExecWithOpponent(in, func(t Effect) bool { //这个是能否使用技能
//结算状态
return t.EFFect_Befer(in, e) //返回本身结算,如果false,说明不能使用技能了
})
if !canuseskill {
return nil
}
}
e.Alive(true) //添加后默认激活
//todo 免疫
//TODO 先激活
//fmt.Println("产生回合数", e.ID(), e.Duration())
// 如果已有同 ID 的效果,尝试叠加
for _, v := range our.effectsByBase[e.ID().Base] {
if v == e {
return nil //完全相同,跳过执行
}
//如果效果相同,id相同,参数相同,就是同一个,确认是否可以叠加,正常来说本身就可以共存
//衰弱本身参数也是相同的,区别只是传入的回合数不一样和层数不一样
if v.ID().Base == e.ID().Base && //找到相同的效果id
v.Alive() && //如果之前的效果还存活
equalInts(v.Args(), e.Args()) { //如果层数可以叠加或者是无限层数
//fmt.Println("重复效果", e.ID().Suffix(), v.ID().Suffix())
if !v.CanStack() { //说明进行了替换
v.Alive(false) //不允许叠层,取消效果
e.Duration(utils.Max(e.Duration(), v.Duration()))
our.appendEffect(e)
return v //这里把V替换掉了
} else {
//默认给叠一层
v.Stack(v.Stack() + 1) //获取到当前叠层数然后叠加
//这里直接返回,不再继续执行后续效果,因为这里是可以叠加的效果
//v.Duration(e.Duration()) //回合数覆盖
v.Duration(utils.Max(e.Duration(), v.Duration()))
return nil
// c.Effects = append(c.Effects, e)
//return
}
}
}
//无限叠加比如能力提升类buff
// 如果没有同 ID 的效果,直接添加
our.appendEffect(e)
return nil
}
// ForEachEffectBool 遍历所有 Effect执行“无参数、返回 bool”的方法
// 参数 fn接收单个 Effect返回 bool如 func(e Effect) bool { return e.OnBattleStart() }
// 返回值:所有 Effect 的方法返回值列表
func (our *Input) defaultOpponent() *Input {
if our == nil {
return nil
}
for _, in := range our.OppTeam {
if in == nil {
continue
}
if pet := in.CurrentPet(); pet != nil && pet.Info.Hp > 0 {
return in
}
}
for _, in := range our.OppTeam {
if in != nil {
return in
}
}
return our.Opp
}
func (our *Input) Exec(fn func(Effect) bool) bool {
return our.ExecWithOpponent(nil, fn)
}
func (our *Input) ExecWithOpponent(opponent *Input, fn func(Effect) bool) bool {
if opponent == nil {
opponent = our.defaultOpponent()
}
result := true
for _, value := range our.Effects {
if value.Alive() {
ctx := value.Ctx()
// 多战位语义Our=当前持有效果槽位Opp=本次结算对位/动作目标槽位。
ctx.Our = our
ctx.Opp = opponent
ctx.Carrier = our
ctx.Target = our
ctx.Source = value.GetInput()
if ctx.Source == nil {
ctx.Source = our
}
//value.Ctx().DamageZone = &info.DamageZone{}
if !fn(value) { //存在false,但是仍然要向下执行
result = false //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
}
}
}
return result
}
// 消除回合类效果 efftype 输入是消对方的还是自己的,false是自己,true是对方
func (our *Input) CancelTurn(in *Input) {
for _, value := range our.Effects {
if value.Duration() > 0 && value.Alive() { //false是自身,true是对方,反转后为真就是自己的
//slice = append(slice[:i], slice[i+1:]...)
value.Alive(false)
}
}
}
func (our *Input) ensureEffectIndex() {
if our == nil {
return
}
if our.effectsByBase == nil {
our.effectsByBase = make(map[int64][]Effect, len(our.Effects))
}
if our.indexedEffects > len(our.Effects) {
our.effectsByBase = make(map[int64][]Effect, len(our.Effects))
our.indexedEffects = 0
}
for our.indexedEffects < len(our.Effects) {
effect := our.Effects[our.indexedEffects]
if effect != nil {
our.effectsByBase[effect.ID().Base] = append(our.effectsByBase[effect.ID().Base], effect)
}
our.indexedEffects++
}
}
func (our *Input) appendEffect(effect Effect) {
if our == nil || effect == nil {
return
}
our.Effects = append(our.Effects, effect)
our.ensureEffectIndex()
}
// // 消除全部 断回合效果,但是我放下场的时候应该断掉所有的回合类效果
// func (our *Input) CancelAll() {
// our.Effects = make([]Effect, 0)
// for _, value := range our.Effects {
// value.Alive(false)
// }
// //取消到在对方的我方对对方的效果
// for _, value := range our.Opp.Effects {
// if value.GetInput() == our { //false是自身,true是对方,反转后为真就是自己的
// //slice = append(slice[:i], slice[i+1:]...)
// value.Alive(false)
// }
// }
// }