feat(fight): 重构战斗效果触发机制与ID管理

- 统一将 Skill_Hit_Pre 和 Skill_Hit_Pre_ex 方法重命名为 Action_start 和 Action_start_ex
- 新增 Action_end 和 Action_end_ex 接口方法,完善行动周期控制
- 修改效果ID生成逻辑,使用 EffectIDCombiner 替代简单整数运算,提升扩展性
- 调整状态类效果判断方式,通过前缀匹配识别状态类型
- 增加随机持续时间和参数设置功能,增强部分效果的表现力
- 优化战斗流程中效果执行时机,确保行为前后逻辑完整闭环
This commit is contained in:
2025-11-22 00:44:42 +08:00
parent 684d79981a
commit 7f443736bc
21 changed files with 254 additions and 46 deletions

View File

@@ -152,6 +152,8 @@ func (e *EffectDefeatTrigger) triggerNextEnemyStatusOnDefeat(at info.AttackValue
if v > 0 {
nv := input.Geteffect(input.EffectType.Status, int(i))
if nv != nil {
nv.Duration(int(e.Input.FightC.GetRand().Int31n(2)))
nv.SetArgs(e.Ctx().Our) //输入参数是对方
e.Ctx().Opp.AddEffect(e.Ctx().Our, nv)
}
}

View File

@@ -21,7 +21,7 @@ type Effect32 struct {
node.EffectNode
}
func (e *Effect32) Skill_Hit_Pre(a, b *action.SelectSkillAction) bool {
func (e *Effect32) Action_start(a, b *action.SelectSkillAction) bool {
if !e.Hit() {
return true
}

View File

@@ -21,9 +21,13 @@ func (e *Effect38) OnSkill() bool {
}
ee := &Effect38_sub{}
ee.EffectNode.Duration(-1) //给对方挂3回合子buff
ee.ID(e.ID() + int(input.EffectType.Sub)) //子效果ID
ee.EffectNode.Duration(-1) //给对方挂3回合子buff
//ee.ID(e.ID() + int(input.EffectType.Sub)) //子效果ID
_, _, pr := input.EffectIDCombiner{}.Split(ee.ID())
ids, _ := input.NewEffectCombined(input.EffectType.Sub, 0, pr)
ee.ID(ids.GetEffectID())
ee.SetArgs(e.Ctx().Our, e.SideEffectArgs...)
e.Ctx().Opp.AddEffect(e.Ctx().Our, ee)
return true

View File

@@ -30,7 +30,7 @@ func (e *Effect58) OnSkill() bool {
return true
}
func (e *Effect58) Skill_Hit_Pre(a, b *action.SelectSkillAction) bool {
func (e *Effect58) Action_start(a, b *action.SelectSkillAction) bool {
if !e.Hit() {
return true
}

View File

@@ -99,8 +99,11 @@ func (e *Effect62) OnSkill() bool {
// bind: ctx.Input,
}
ee.duy = e.EffectNode.SideEffectArgs[0]
ee.EffectNode.Duration(math.MaxInt) //给对方挂3回合子buff
ee.ID(e.ID() + int(input.EffectType.Sub)) //子效果ID
ee.EffectNode.Duration(math.MaxInt) //给对方挂3回合子buff
_, _, pr := input.EffectIDCombiner{}.Split(ee.ID())
ids, _ := input.NewEffectCombined(input.EffectType.Sub, 0, pr)
ee.ID(ids.GetEffectID())
//e.e.EffectNode.Duration(e.EffectNode.SideEffectArgs[0])
//给对方添加我方施加的buff
ee.SetArgs(e.Ctx().Our, e.SideEffectArgs...)

View File

@@ -31,7 +31,10 @@ func (e *Effect69) OnSkill() bool {
t := &Effect69_sub{
EffectNode: node.EffectNode{},
}
t.ID(e.ID() + int(input.EffectType.Sub)) //子效果ID
_, _, pr := input.EffectIDCombiner{}.Split(t.ID())
ids, _ := input.NewEffectCombined(input.EffectType.Sub, 0, pr)
t.ID(ids.GetEffectID())
t.SetArgs(e.Input, e.SideEffectArgs...)
t.Duration(e.SideEffectArgs[0])
e.Ctx().Opp.AddEffect(e.Ctx().Our, t)

View File

@@ -25,7 +25,7 @@ type Effect7 struct {
node.EffectNode
}
func (e *Effect7) Skill_Hit_Pre(a, b *action.SelectSkillAction) bool {
func (e *Effect7) Action_start(a, b *action.SelectSkillAction) bool {
if e.Ctx().Opp.CurrentPet.Info.Hp <= e.Ctx().Our.CurrentPet.Info.Hp {
e.Ctx().SkillEntity.Accuracy = 0
}

View File

@@ -53,7 +53,10 @@ func (e *Effect71) Switch(in *input.Input, at info.AttackValue, oldpet *info.Bat
}
t := &Effect71_sub{}
t.Duration(1)
t.ID(e.ID() + int(input.EffectType.Sub))
_, _, pr := input.EffectIDCombiner{}.Split(t.ID())
ids, _ := input.NewEffectCombined(input.EffectType.Sub, 0, pr)
t.ID(ids.GetEffectID())
e.Ctx().Our.AddEffect(e.Ctx().Our, t)
e.Alive(false)
@@ -64,7 +67,7 @@ type Effect71_sub struct {
node.EffectNode
}
func (e *Effect71_sub) Skill_Hit_Pre(a, b *action.SelectSkillAction) bool {
func (e *Effect71_sub) Action_start(a, b *action.SelectSkillAction) bool {
//fmt.Println(e.Ctx().SkillEntity)
if e.Ctx().SkillEntity == nil {

View File

@@ -28,7 +28,7 @@ func (e *Effect81) OnSkill() bool {
return true
}
func (e *Effect81) Skill_Hit_Pre(a, b *action.SelectSkillAction) bool {
func (e *Effect81) Action_start(a, b *action.SelectSkillAction) bool {
if !e.Hit() {
return true
}

View File

@@ -55,7 +55,7 @@ func (e *Effect83) Compare_Pre(fattack *action.SelectSkillAction, sattack *actio
}
// /自身雄性,下两回合必定先手;自身雌性,下两回合必定致命一击
func (e *Effect83) Skill_Hit_Pre(a, b *action.SelectSkillAction) bool {
func (e *Effect83) Action_start(a, b *action.SelectSkillAction) bool {
if !e.Hit() {
return true
}

View File

@@ -20,7 +20,7 @@ type Effect95 struct {
node.EffectNode
}
func (e *Effect95) Skill_Hit_Pre(a, b *action.SelectSkillAction) bool {
func (e *Effect95) Action_start(a, b *action.SelectSkillAction) bool {
if !e.Hit() {
return true
}

View File

@@ -59,9 +59,11 @@ func (e *EffectConditionalAddDamage) OnSkill() bool {
if !e.Hit() {
return true
}
_, _, pr := input.EffectIDCombiner{}.Split(e.ID())
ids, _ := input.NewEffectCombined(input.EffectType.Sub, 0, pr)
// 2. 获取当前效果ID对应的条件函数
cond, ok := conditionMap[e.ID()-int(input.EffectType.Skill)]
cond, ok := conditionMap[int(ids.GetSuffix())]
if !ok {
return true // 无对应条件函数,不触发
}

View File

@@ -33,7 +33,7 @@ type StatusCannotAct struct {
}
// 技能命中前拦截:阻止出手
func (e *StatusCannotAct) Skill_Hit_Pre(attacker, defender *action.SelectSkillAction) bool {
func (e *StatusCannotAct) Action_start(attacker, defender *action.SelectSkillAction) bool {
return false
}
@@ -44,9 +44,9 @@ type StatusSleep struct {
}
// 尝试出手时标记状态
func (e *StatusSleep) Skill_Hit_Pre(attacker, defender *action.SelectSkillAction) bool {
func (e *StatusSleep) Action_start(attacker, defender *action.SelectSkillAction) bool {
e.hasTriedAct = true
return e.StatusCannotAct.Skill_Hit_Pre(attacker, defender)
return e.StatusCannotAct.Action_start(attacker, defender)
}
// 技能使用后处理:非状态类技能触发后解除睡眠
@@ -67,7 +67,7 @@ type ContinuousDamage struct {
}
// 技能命中前触发伤害1/8最大生命值真实伤害
func (e *ContinuousDamage) Skill_Hit_Pre(attacker, defender *action.SelectSkillAction) bool {
func (e *ContinuousDamage) Action_start(attacker, defender *action.SelectSkillAction) bool {
damage := e.calculateDamage()
e.Ctx().Our.Damage(e.Input, &info.DamageZone{
Type: info.DamageType.True,
@@ -98,7 +98,7 @@ type ParasiticSeed struct {
}
// 技能命中前触发寄生效果
func (e *ParasiticSeed) Skill_Hit_Pre_ex(attacker, defender *action.SelectSkillAction) bool {
func (e *ParasiticSeed) Action_start_ex(attacker, defender *action.SelectSkillAction) bool {
// 过滤特定类型单位假设1是植物类型使用枚举替代魔法数字
if gconv.Int(e.Ctx().Our.CurrentPet.Type) == int(element.ElementTypeGrass) {
return true
@@ -122,7 +122,7 @@ type Flammable struct {
BaseStatus
}
func (e *Flammable) Skill_Hit_Pre(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) bool {
func (e *Flammable) Action_start(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) bool {
if e.Ctx().SkillEntity == nil {
return true
}
@@ -159,7 +159,7 @@ type Confused struct {
BaseStatus
}
func (e *Confused) Skill_Hit_Pre(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) bool {
func (e *Confused) Action_start(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) bool {
if e.Ctx().SkillEntity == nil {
return true
}

View File

@@ -197,14 +197,14 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
//结算状态
//然后这里还可以处理自爆类
t.Ctx().SkillEntity = currentskill
return t.Skill_Hit_Pre_ex(fattack, sattack) //返回本身结算,如果false,说明不能使用技能了
return t.Action_start_ex(fattack, sattack) //返回本身结算,如果false,说明不能使用技能了
})
canuseskill := attacker.Exec(func(t input.Effect) bool { //这个是能否使用技能
//结算状态
//然后这里还可以处理自爆类
t.Ctx().SkillEntity = currentskill
return t.Skill_Hit_Pre(fattack, sattack) //返回本身结算,如果false,说明不能使用技能了
return t.Action_start(fattack, sattack) //返回本身结算,如果false,说明不能使用技能了
})
@@ -263,7 +263,22 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
return true
})
defender.Exec(func(t input.Effect) bool {
t.Ctx().SkillEntity = currentskill
t.Action_end_ex()
return true
})
//技能使用后
attacker.Exec(func(t input.Effect) bool { //技能使用后的我方效果
t.Ctx().SkillEntity = currentskill
t.Action_end()
return true
})
fmt.Println(i,
// "玩家技能:", oldskill.(*info.SkillEntity).ID,
"玩家技能伤害:", attacker.SumDamage,

View File

@@ -9,35 +9,50 @@ import (
"blazing/modules/blazing/model"
"github.com/brunoga/deep"
"github.com/gogf/gf/v2/util/gconv"
"github.com/tnnmigga/enum"
)
// 战斗结束原因枚举
type EnumEffectType int
type EnumEffectType uint16
var EffectType = enum.New[struct {
Skill EnumEffectType `enum:"1000000"` //技能
NewSel EnumEffectType `enum:"2000000"` //特性
Status EnumEffectType `enum:"3000000"` //状态
Sub EnumEffectType `enum:"4000000"` //子效果
Skill EnumEffectType `enum:"1"` //技能
NewSel EnumEffectType `enum:"2"` //特性
Status EnumEffectType `enum:"3"` //状态
Sub EnumEffectType `enum:"4"` //子效果
}]()
var NodeM = make(map[int]Effect, 0)
var NodeM = make(map[int64]Effect, 0)
func InitEffect(etype EnumEffectType, id int, t Effect) {
t.ID(id + int(etype)) //设置ID
ids, err := NewEffectCombined(etype, 0, gconv.Uint16(id))
if err != nil {
panic(err)
NodeM[id+int(etype)] = t
}
t.ID(ids.effectID) //设置ID
NodeM[ids.effectID] = t
}
func Geteffect[T int | byte](etype EnumEffectType, id T) Effect {
// 这里的catchtime为0,取出来之后如果是魂印,要重新赋值
func Geteffect[T int | byte](etype EnumEffectType, id T) Effect {
ids, err := NewEffectCombined(etype, 0, gconv.Uint16(id))
if err != nil {
panic(err)
}
//todo 获取前GetEffect
ret, ok := NodeM[int(id)+int(etype)]
ret, ok := NodeM[ids.effectID]
if ok {
//todo 获取前GetEffect
eff := deep.MustCopy(ret)
if eff.ID() >= int(EffectType.Status) && eff.ID() < int(EffectType.Sub) {
combiner := EffectIDCombiner{}
prefix, _, _ := combiner.Split(eff.ID())
if prefix == EffectType.Status {
eff.CanStack(true) //状态类不能被覆盖,只能无限叠加
}
@@ -76,8 +91,12 @@ func (our *Input) GetProp(id int, istue bool) int {
func (our *Input) GetEffect(etype EnumEffectType, id int) Effect {
var ret []Effect
ids, err := NewEffectCombined(etype, 0, gconv.Uint16(id))
if err != nil {
panic(err)
eid := id + int(etype)
}
eid := ids.effectID
for _, v := range our.Effects {
if v.ID() == eid && v.Alive() {
ret = append(ret, v)
@@ -99,7 +118,9 @@ func (our *Input) StatEffect_Exist(id info.EnumPetStatus) bool {
}
func (our *Input) StatEffect_Exist_all() bool {
for _, v := range our.Effects {
if v.ID() >= int(EffectType.Status) && v.ID() < int(EffectType.Sub) && v.Alive() {
combiner := EffectIDCombiner{}
prefix, _, _ := combiner.Split(v.ID())
if prefix == EffectType.Status && v.Alive() {
return true
}
@@ -107,8 +128,11 @@ func (our *Input) StatEffect_Exist_all() bool {
return false
}
// 判断是否是状态技能
func (our *Input) IS_Stat(v Effect) bool {
if v.ID() >= int(EffectType.Status) && v.ID() < int(EffectType.Sub) && v.Alive() {
em, _, _ := EffectIDCombiner{}.Split(v.ID())
if em == EffectType.Status && v.Alive() {
return true
}

View File

@@ -0,0 +1,140 @@
package input
import "fmt"
// -------------------------- 位运算常量(统一管理) --------------------------
const (
// 位移偏移量64位ID结构 = [高16位:效果类型][中32位:CatchTime][低16位:扩展标识]
prefixOffset = 48 // 高16位uint16效果类型
catchOffset = 16 // 中32位uint32CatchTime核心调整点
suffixOffset = 0 // 低16位uint16扩展标识/子ID
// 掩码(提取对应位段)
suffixMask = 0xFFFF // 低16位掩码
catchMask = 0xFFFFFFFF // 中32位掩码CatchTime专属
prefixMask = 0xFFFF // 高16位掩码
maxPrefix = prefixMask // 效果类型最大值uint16范围
maxCatch = catchMask // CatchTime最大值uint32范围
maxSuffix = suffixMask // 扩展标识最大值uint16范围
)
// -------------------------- 64位拼接工具无状态工具类 --------------------------
// EffectIDCombiner 64位效果ID拼接器[效果类型(uint16)+CatchTime(uint32)+扩展标识(uint16)] → int64
// 无状态设计,可全局复用
type EffectIDCombiner struct{}
// Combine 拼接三个字段为64位效果ID
// 参数:
// prefix: 高16位效果类型如EffectType.Skill
// catchTime: 中32位核心字段32位CatchTime
// suffix: 低16位扩展标识/子ID
// 返回拼接后的int64参数超出范围返回错误
func (c EffectIDCombiner) Combine(prefix uint16, catchTime uint32, suffix uint16) (int64, error) {
// 范围校验(避免溢出)
if prefix > maxPrefix {
return 0, fmt.Errorf("效果类型值%d超出uint16范围最大%d", prefix, maxPrefix)
}
if catchTime > maxCatch {
return 0, fmt.Errorf("CatchTime值%d超出uint32范围最大%d", catchTime, maxCatch)
}
if suffix > maxSuffix {
return 0, fmt.Errorf("扩展标识值%d超出uint16范围最大%d", suffix, maxSuffix)
}
// 位运算拼接(无重叠,按位或)
return (int64(prefix) << prefixOffset) |
(int64(catchTime) << catchOffset) |
int64(suffix), nil
}
// Split 从64位效果ID拆分出三个字段
// 返回:效果类型(prefix)、CatchTime、扩展标识(suffix)
func (c EffectIDCombiner) Split(val int64) (prefix EnumEffectType, catchTime uint32, suffix uint16) {
suffix = uint16(val & suffixMask)
catchTime = uint32((val >> catchOffset) & catchMask)
prefix = EnumEffectType((val >> prefixOffset) & prefixMask)
return
}
// -------------------------- 组合结构体封装64位ID支持自由取值 --------------------------
// EffectCombined 效果组合结构体封装64位ID提供语义化取值方法
type EffectCombined struct {
effectID int64 // 拼接后的64位效果ID
combiner EffectIDCombiner // 内置拼接器,避免重复创建
}
// NewEffectCombined 构造函数:创建效果组合实例
// 参数:
// effectType: 效果类型如EffectType.Skill
// catchTime: 32位CatchTime中32位核心值
// suffix: 低16位扩展标识
// 返回:组合实例,失败返回错误
func NewEffectCombined(effectType EnumEffectType, catchTime uint32, suffix uint16) (*EffectCombined, error) {
combiner := EffectIDCombiner{}
// 校验效果类型是否在uint16范围内
prefix := uint16(effectType)
if uint64(effectType) > uint64(maxPrefix) {
return nil, fmt.Errorf("效果类型(值%d超出uint16范围", effectType)
}
// 拼接64位ID
effectID, err := combiner.Combine(prefix, catchTime, suffix)
if err != nil {
return nil, fmt.Errorf("拼接效果ID失败%w", err)
}
return &EffectCombined{
effectID: effectID,
combiner: combiner,
}, nil
}
// -------------------------- 自由取值方法(核心需求:按需取不同类型) --------------------------
// GetEffectID 获取完整的64位效果ID
func (e *EffectCombined) GetEffectID() int64 {
return e.effectID
}
// GetEffectType 获取效果类型转为EnumEffectType类型安全
func (e *EffectCombined) GetEffectType() EnumEffectType {
prefix, _, _ := e.combiner.Split(e.effectID)
return EnumEffectType(prefix)
}
// GetCatchTime 获取32位CatchTime核心字段直接从64位ID中拆分
func (e *EffectCombined) GetCatchTime() uint32 {
_, catchTime, _ := e.combiner.Split(e.effectID)
return catchTime
}
// GetSuffix 获取低16位扩展标识
func (e *EffectCombined) GetSuffix() uint16 {
_, _, suffix := e.combiner.Split(e.effectID)
return suffix
}
func Test() {
// 2. 创建组合实例
combined, err := NewEffectCombined(EffectType.Skill, 584464, 8980)
if err != nil {
fmt.Printf("创建失败:%v\n", err)
return
}
// 3. 自由获取不同类型的值(核心需求)
fmt.Println("完整64位效果ID", combined.GetEffectID())
fmt.Println("效果类型(枚举值):", combined.GetEffectType())
// fmt.Println("效果类型(描述):", combined.GetEffectTypeDesc())
fmt.Println("32位CatchTime", combined.GetCatchTime()) // 直接取中32位的CatchTime
fmt.Println("低16位扩展标识", combined.GetSuffix())
// 4. 手动拆分验证(可选)
combiner := EffectIDCombiner{}
prefix, ct, sf := combiner.Split(combined.GetEffectID())
fmt.Printf("\n手动拆分结果\n")
fmt.Printf("效果类型uint16%d\n", prefix)
fmt.Printf("CatchTimeuint32%d\n", ct)
fmt.Printf("扩展标识uint16%d\n", sf)
}

View File

@@ -11,10 +11,10 @@ type Effect interface {
Compare_Pre(fattack, sattack *action.SelectSkillAction) bool //比较前对优先级的修改
//技能命中前的返回值代表是否可以出手 ,对命中本身的修改应该是对上下文本身的修改
//对技能修改 行动开始前,注入视为等参数在这里实现
Skill_Hit_Pre_ex(fattack, sattack *action.SelectSkillAction) bool //比较前对优先级的修改
Skill_Hit_Pre(fattack, sattack *action.SelectSkillAction) bool //比较前对优先级的修改
Skill_Hit() bool //这是是命中后的对技能的修改,比如变威力
Skill_Hit_ex() bool // 技能命中前触发//预处理受击技能 被攻击方效果,比如受击时无效技能这样
Action_start_ex(fattack, sattack *action.SelectSkillAction) bool //比较前对优先级的修改
Action_start(fattack, sattack *action.SelectSkillAction) bool //比较前对优先级的修改
Skill_Hit() bool //这是是命中后的对技能的修改,比如变威力
Skill_Hit_ex() bool // 技能命中前触发//预处理受击技能 被攻击方效果,比如受击时无效技能这样
//Calculate_Pre() bool //视为 无视效果,相当于这里对敌方的修改
OnSkill() bool // 触发on miss onhit
@@ -32,6 +32,8 @@ type Effect interface {
//Damage_Use() bool // 伤害作用
Skill_Use_ex() bool //技能PP减少节点
Skill_Useed() bool //技能PP减少节点
Action_end_ex() bool
Action_end() bool
//OnDefeat(opp *Input) bool // 精灵被击败时触发
// 首发其实就是切换的精灵为nil
@@ -63,6 +65,6 @@ type Effect interface {
//Owner(...bool) bool // 技能属主,比如寄生和镇魂歌,属主是对方)
GetInput() *Input
ID(...int) int
ID(...int64) int64
//GetSkill() *BattleSkillEntity //获得技能ctx
}

View File

@@ -4,10 +4,10 @@ import (
"blazing/logic/service/fight/action"
)
func (e *EffectNode) Skill_Hit_Pre(fattack, sattack *action.SelectSkillAction) bool {
func (e *EffectNode) Action_start(fattack, sattack *action.SelectSkillAction) bool {
return true
}
func (e *EffectNode) Skill_Hit_Pre_ex(fattack, sattack *action.SelectSkillAction) bool {
func (e *EffectNode) Action_start_ex(fattack, sattack *action.SelectSkillAction) bool {
return true
}
func (e *EffectNode) Compare_Pre(fattack, sattack *action.SelectSkillAction) bool { //比较前对优先级的修改 {

View File

@@ -18,3 +18,11 @@ func (e *EffectNode) OnBattleEnd() bool {
func (e *EffectNode) EFFect_Befer(in *input.Input, effEffect input.Effect) bool {
return true
}
func (e *EffectNode) Action_end_ex() bool {
// panic("not implemented") // TODO: Implement
return true
}
func (e *EffectNode) Action_end() bool {
// panic("not implemented") // TODO: Implement
return true
}

View File

@@ -13,7 +13,7 @@ type EffectNode struct {
Input *input.Input
stacks int // 当前层数
id int
id int64
canStack bool // 最大叠加层数 ,正常都是不允许叠加的,除了衰弱特殊效果 ,异常和能力的叠层
SideEffectArgs []int // 附加效果参数
// owner bool //是否作用自身
@@ -53,7 +53,7 @@ func (e *EffectNode) Stack(t ...int) int {
return e.stacks
}
func (e *EffectNode) ID(t ...int) int {
func (e *EffectNode) ID(t ...int64) int64 {
if len(t) > 0 {
e.id = t[0]
}

View File

@@ -22,6 +22,8 @@ import (
)
func main() {
//input.Test()
// element.TestAllScenarios()
// for i := 0; i < 1000000; i++ {