feat(fight): 调整效果叠加逻辑与精灵属性处理

- 将 `MaxStack` 方法重命名为 `CanStack`,并修改其逻辑为布尔值控制是否可叠加
- 更新多个效果文件中的注释和调用方式以适配新的叠加控制方法
- 修复精灵属性类型获取逻辑,增加缓存字段 `PType`
- 修改战斗回合处理流程,优化技能解析顺序和状态比较时机
- 调整状态效果初始化逻辑,确保状态类效果默认允许无限叠加
- 更正伤害类型缺失问题,在固定伤害
This commit is contained in:
2025-11-14 06:14:49 +08:00
parent d52c6cbb79
commit 004ce51c5e
23 changed files with 298 additions and 69 deletions

View File

@@ -12,7 +12,7 @@ import (
)
const profInterval = 10 * time.Millisecond
const profMaxStackDepth = 64
const profCanStackDepth = 64
const (
profReqNone int32 = iota
@@ -34,7 +34,7 @@ type profTracker struct {
req, finished int32
start, stop time.Time
numFrames int
frames [profMaxStackDepth]StackFrame
frames [profCanStackDepth]StackFrame
}
type profiler struct {

View File

@@ -0,0 +1,52 @@
package effect
import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
)
/**
* 降低对方 n 点 btl_Max体力
*/
type Effect38 struct {
node.EffectNode
}
func (e *Effect38) OnSkill() bool {
if !e.Hit() {
return true
}
ee := &Effect38_sub{}
ee.EffectNode.Duration(-1) //给对方挂3回合子buff
ee.ID(e.ID() + int(input.EffectType.Sub)) //子效果ID
ee.SetArgs(e.Ctx().Our, e.SideEffectArgs...)
e.Ctx().Opp.AddEffect(e.Ctx().Our, ee)
return true
}
// 命中之后
func (e *Effect38_sub) Turn_Start(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) {
if uint32(e.Args()[0]) < e.Ctx().Our.CurrentPet.Info.MaxHp {
e.Ctx().Our.CurrentPet.Info.MaxHp -= uint32(e.Args()[0])
}
}
func (e *Effect38_sub) Switch(in *input.Input, at info.AttackValue, oldpet *info.BattlePetEntity) bool {
return true
}
type Effect38_sub struct {
node.EffectNode
}
func init() {
ret := &Effect38{}
input.InitEffect(input.EffectType.Skill, 38, ret)
}

View File

@@ -0,0 +1,40 @@
package effect
import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
)
/**
* n回合防御力和对手相同
*/
type Effect51 struct {
node.EffectNode
}
func (e *Effect51) OnSkill() bool {
if !e.Hit() {
return true
}
return true
}
func (e *Effect51) Turn_Start(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) {
if !e.Hit() {
return
}
e.Ctx().Our.CurrentPet.Info.Prop[0] = e.Ctx().Opp.CurrentPet.Info.Prop[0]
}
func init() {
ret := &Effect51{}
input.InitEffect(input.EffectType.Skill, 51, ret)
}
func (e *Effect51) SetArgs(t *input.Input, a ...int) {
e.EffectNode.SetArgs(t, a...)
e.EffectNode.Duration(e.EffectNode.SideEffectArgs[0])
}

View File

@@ -0,0 +1,40 @@
package effect
import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
)
/**
* n回合防御力和对手相同
*/
type Effect45 struct {
node.EffectNode
}
func (e *Effect45) OnSkill() bool {
if !e.Hit() {
return true
}
return true
}
func (e *Effect45) Turn_Start(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) {
if !e.Hit() {
return
}
e.Ctx().Our.CurrentPet.Info.Prop[1] = e.Ctx().Opp.CurrentPet.Info.Prop[1]
}
func init() {
ret := &Effect45{}
input.InitEffect(input.EffectType.Skill, 45, ret)
}
func (e *Effect45) SetArgs(t *input.Input, a ...int) {
e.EffectNode.SetArgs(t, a...)
e.EffectNode.Duration(e.EffectNode.SideEffectArgs[0])
}

View File

@@ -43,7 +43,7 @@ func (e *Effect46) Damage_DIV_ex(t *info.DamageZone) bool {
}
func (e *Effect46) SetArgs(t *input.Input, a ...int) {
//e.MaxStack(-1)//后续的不会顶掉这个效果
//e.CanStack(-1)//后续的不会顶掉这个效果
e.EffectNode.SetArgs(t, a...)
e.Duration(-1) //次数类,无限回合

View File

@@ -23,7 +23,7 @@ func (e *Effect47) Prop_Befer(in *input.Input, prop int8, level int8, ptype info
}
func (e *Effect47) SetArgs(t *input.Input, a ...int) {
//e.MaxStack(-1)//后续的不会顶掉这个效果
//e.CanStack(-1)//后续的不会顶掉这个效果
e.EffectNode.SetArgs(t, a...)
e.Duration(e.Args()[0]) //次数类,无限回合

View File

@@ -30,7 +30,7 @@ func (e *Effect48) EFFect_Befer(in *input.Input, effEffect input.Effect) bool {
}
func (e *Effect48) SetArgs(t *input.Input, a ...int) {
//e.MaxStack(-1)//后续的不会顶掉这个效果
//e.CanStack(-1)//后续的不会顶掉这个效果
e.EffectNode.SetArgs(t, a...)
e.Duration(e.Args()[0]) //次数类,无限回合

View File

@@ -37,7 +37,7 @@ func (e *Effect49) Damage_SUB_ex(t *info.DamageZone) bool {
}
func (e *Effect49) SetArgs(t *input.Input, a ...int) {
//e.MaxStack(-1)//后续的不会顶掉这个效果
//e.CanStack(-1)//后续的不会顶掉这个效果
e.EffectNode.SetArgs(t, a...)
//e.Duration(-1) //次数类,无限回合

View File

@@ -23,7 +23,7 @@ func newEffectStat(targetOpponent bool) input.Effect {
e := &EffectStat{
Etype: targetOpponent,
}
//e.MaxStack(-1) // 无限叠加
//e.CanStack(-1) // 无限叠加
return e
}

View File

@@ -0,0 +1,40 @@
package effect
import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
)
/**
*n 回合内,自身与对方反转战斗时属性
*/
type Effect55 struct {
node.EffectNode
}
func (e *Effect55) OnSkill() bool {
if !e.Hit() {
return true
}
return true
}
func (e *Effect55) Turn_Start(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) {
if !e.Hit() {
return
}
e.Ctx().Our.CurrentPet.PType, e.Ctx().Opp.CurrentPet.PType = e.Ctx().Opp.CurrentPet.PType, e.Ctx().Our.CurrentPet.PType
}
func init() {
ret := &Effect55{}
input.InitEffect(input.EffectType.Skill, 55, ret)
}
func (e *Effect55) SetArgs(t *input.Input, a ...int) {
e.EffectNode.SetArgs(t, a...)
e.EffectNode.Duration(e.EffectNode.SideEffectArgs[0])
}

View File

@@ -0,0 +1,40 @@
package effect
import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
)
/**
* n 回合内与对方战斗时属性相同
*/
type Effect56 struct {
node.EffectNode
}
func (e *Effect56) OnSkill() bool {
if !e.Hit() {
return true
}
return true
}
func (e *Effect56) Turn_Start(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) {
if !e.Hit() {
return
}
e.Ctx().Our.CurrentPet.PType = e.Ctx().Opp.CurrentPet.PType
}
func init() {
ret := &Effect56{}
input.InitEffect(input.EffectType.Skill, 56, ret)
}
func (e *Effect56) SetArgs(t *input.Input, a ...int) {
e.EffectNode.SetArgs(t, a...)
e.EffectNode.Duration(e.EffectNode.SideEffectArgs[0])
}

View File

@@ -23,7 +23,7 @@ func init() {
}
func (e *Effect59) SetArgs(t *input.Input, a ...int) {
//e.MaxStack(-1)//后续的不会顶掉这个效果
//e.CanStack(-1)//后续的不会顶掉这个效果
e.EffectNode.SetArgs(t, a...)
e.Duration(-1) //次数类,无限回合

View File

@@ -12,9 +12,7 @@ import (
//被动效果都是自己施加给自己的,所以不能免疫
func init() {
input.InitEffect(input.EffectType.Skill, 6, &Effect6{
EffectNode: node.EffectNode{},
})
input.InitEffect(input.EffectType.Skill, 6, &Effect6{})
}
@@ -26,6 +24,7 @@ type Effect6 struct {
func (e *Effect6) Skill_Useed() bool {
e.Ctx().Our.Damage(e.Ctx().Our, &info.DamageZone{
Type: info.DamageType.Fixed,
//这个对面计算前是在他的回合,所以后手也能拿到伤害
Damage: e.Ctx().Our.DamageZone.Damage.Div(decimal.NewFromInt(int64(e.SideEffectArgs[0]))),
})

View File

@@ -23,7 +23,7 @@ func init() {
}
func (e *Effect71) SetArgs(t *input.Input, a ...int) {
//e.MaxStack(-1)//后续的不会顶掉这个效果
//e.CanStack(-1)//后续的不会顶掉这个效果
e.EffectNode.SetArgs(t, a...)
e.Duration(-1) //次数类,无限回合

View File

@@ -12,8 +12,8 @@ import (
func init() {
t := &Effect9{}
t.Duration(-1) //次数类无限回合
t.MaxStack(-1) //后续的不会顶掉这个效果
t.Duration(-1) //次数类无限回合
t.CanStack(true) //后续的不会顶掉这个效果
input.InitEffect(input.EffectType.Skill, 9, t)
}

View File

@@ -13,10 +13,8 @@ import (
*/
func init() {
input.InitEffect(input.EffectType.Skill, 90, &Effect90{
EffectNode: node.EffectNode{},
})
input.InitEffect(input.EffectType.Skill, 90, &Effect90{})
input.InitEffect(input.EffectType.Skill, 53, &Effect90{})
}
type Effect90 struct {

View File

@@ -113,11 +113,11 @@ func (f *FightC) copyskill(t *action.SelectSkillAction) *info.SkillEntity {
oldskill.(*info.SkillEntity).Rand = f.rand //拷贝后随机数丢失
return oldskill.(*info.SkillEntity)
}
func (f *FightC) copypet(t *info.BattlePetEntity) *info.BattlePetEntity {
func (f *FightC) copypet(t *model.PetInfo) *model.PetInfo {
oldskill, _ := deepcopy.Anything(t) //备份技能
// oldskill.(*info.BattlePetEntity).Rand = f.rand //拷贝后随机数丢失
return oldskill.(*info.BattlePetEntity)
return oldskill.(*model.PetInfo)
}
//回合有先手方和后手方,同时有攻击方和被攻击方
@@ -128,6 +128,20 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
// 神罗、圣华登场时魂免“登场时xx”等效果
//阿枫的效果也在这里判断
var oldpet [2]*model.PetInfo
var oldpettype [2]int
oldpet[0], oldpet[1] = f.copypet(f.Our.CurrentPet.Info), f.copypet(f.Opp.CurrentPet.Info)
oldpettype[0], oldpettype[1] = f.Our.CurrentPet.PType, f.Opp.CurrentPet.PType
fmt.Println("开始时对方最大体力值", f.Opp.CurrentPet.Info.MaxHp)
defer func() {
f.Our.CurrentPet.Info, f.Opp.CurrentPet.Info = oldpet[0], oldpet[1] //还原精灵信息
f.Our.CurrentPet.PType, f.Opp.CurrentPet.PType = oldpettype[0], oldpettype[1] //还原精灵属性
fmt.Println("结束时对方最大体力值", f.Opp.CurrentPet.Info.MaxHp)
}()
f.Our.Exec(func(t input.Effect) bool { //回合开始前
//结算状态
@@ -142,6 +156,32 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
})
// 伤害值
if fattack != nil { //如果首技能是空的,说明都空过了
if fattack.GetPlayerID() == f.ownerID {
//是否miss都应该施加解析effect
f.Our.Parseskill(fattack) //解析到临时数据
f.Opp.Parseskill(sattack) //解析到临时数据
} else {
f.Opp.Parseskill(fattack)
f.Our.Parseskill(sattack)
}
}
f.Our.Exec(func(t input.Effect) bool { //回合开始前
//结算状态
t.Compare_Pre(fattack, sattack) //先结算技能的优先级
return true
})
f.Opp.Exec(func(t input.Effect) bool { //回合开始前
//结算状态
t.Compare_Pre(fattack, sattack) //先结算技能的优先级
return true
})
// 根据攻击方归属设置当前战斗的主/次攻击方属性
if fattack != nil {
if fattack.GetPlayerID() == f.ownerID {
@@ -157,29 +197,6 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
f.First.ResetAttackValue()
f.Second.ResetAttackValue()
if fattack != nil {
//是否miss都应该施加解析effect
f.First.Parseskill(f.Second, fattack) //解析到临时数据
f.First.Exec(func(t input.Effect) bool { //回合开始前
//结算状态
t.Compare_Pre(fattack, sattack) //先结算技能的优先级
return true
})
}
if sattack != nil {
f.Second.Parseskill(f.Second, sattack) //解析到临时数据
f.Second.Exec(func(t input.Effect) bool { //回合开始前
//结算状态
t.Compare_Pre(fattack, sattack) //先结算技能的优先级
return true
})
}
if fattack != nil && sattack != nil {
switch {

View File

@@ -26,7 +26,7 @@ type BattlePetEntity struct {
xmlres.PetInfo
Info *model.PetInfo //通过偏移赋值
//*input.Input
PType int
statusConditions sync.Map // key: StatusCondition, value: int (剩余回合)
Skills [4]*SkillEntity // 技能槽最多4个技能
//Status StatusDict //精灵的状态
@@ -89,23 +89,25 @@ func (u *BattlePetEntity) GetMaxHP() decimal.Decimal {
}
func (u *BattlePetEntity) Type() *element.ElementCombination {
// 1. 遍历宠物配置查找对应元素类型ID
var typeID int
found := false
for _, v := range xmlres.PetMAP {
if v.ID == int(u.Info.ID) {
typeID = v.Type
found = true
break
if u.PType == 0 {
for _, v := range xmlres.PetMAP {
if v.ID == int(u.Info.ID) {
u.PType = v.Type
found = true
break
}
}
}
// 2. 未找到配置时,默认使用"普通8"
if !found {
typeID = 8
u.PType = 8
}
// 3. 从预加载的组合池中获取实例(无需创建,直接读取)
combo, err := element.Calculator.GetCombination(typeID)
combo, err := element.Calculator.GetCombination(u.PType)
if err != nil {
// 极端情况typeID无效强制使用默认普通属性
// (从池中获取默认值,确保实例一致性)

View File

@@ -125,7 +125,7 @@ type WeakenedS struct {
// 定义战斗状态枚举
var PetStatus = enum.New[struct {
NULL EnumPetStatus `enum:"-1"`
NULL EnumPetStatus `enum:"255"`
Paralysis EnumPetStatus `enum:"0"` // 麻痹
Poisoned EnumPetStatus `enum:"1"` // 中毒
Burned EnumPetStatus `enum:"2"` // 烧伤

View File

@@ -36,6 +36,9 @@ func Geteffect(etype EnumEffectType, id int) Effect {
//todo 获取前GetEffect
eff := deep.MustCopy(ret)
if eff.ID() >= int(EffectType.Status) && eff.ID() < int(EffectType.Sub) {
eff.CanStack(true) //状态类不能被覆盖,只能无限叠加
}
return eff
//todo 获取后GetEffect
@@ -162,19 +165,14 @@ func (our *Input) AddEffect(in *Input, e Effect) Effect {
v.Alive() && //如果之前的效果还存活
equalInts(v.Args(), e.Args()) { //如果层数可以叠加或者是无限层数
if v.MaxStack() == 0 { //说明进行了替换
if v.CanStack() { //说明进行了替换
v.Alive(false) //不允许叠层,取消效果
e.Duration(utils.Max(e.Duration(), v.Duration()))
return v //这里把V替换掉了
} else {
///e.Alive(false) //取消之前效果
if v.Stack() <= v.MaxStack() { //如果小于最大叠层,状态可以叠层
v.Stack(v.Stack() + e.Stack()) //获取到当前叠层数然后叠加
//这里直接返回,不再继续执行后续效果,因为这里是可以叠加的效果
//v.Duration(e.Duration()) //回合数覆盖
}
v.Stack(v.Stack() + e.Stack()) //获取到当前叠层数然后叠加
//这里直接返回,不再继续执行后续效果,因为这里是可以叠加的效果
//v.Duration(e.Duration()) //回合数覆盖
v.Duration(utils.Max(e.Duration(), v.Duration()))
return nil
// c.Effects = append(c.Effects, e)

View File

@@ -147,7 +147,10 @@ func (our *Input) GetStatusBonus() float64 {
}
// 解析并 施加effect
func (our *Input) Parseskill(defender *Input, skill *action.SelectSkillAction) {
func (our *Input) Parseskill(skill *action.SelectSkillAction) {
if skill == nil {
return
}
our.EffectCache = make([]Effect, 0) //先把上一回合数据清空,但是应该把本身延续类效果集成过来
our.Effect_Lost = make([]Effect, 0)

View File

@@ -59,7 +59,7 @@ type Effect interface {
Hit(...bool) bool
Alive(...bool) bool
Stack(...int) int
MaxStack(...int) int
CanStack(...bool) bool
//Owner(...bool) bool // 技能属主,比如寄生和镇魂歌,属主是对方)
GetInput() *Input

View File

@@ -14,7 +14,7 @@ type EffectNode struct {
Input *input.Input
stacks int // 当前层数
id int
maxStack int // 最大叠加层数 ,正常都是不允许叠加的,除了衰弱特殊效果 ,异常和能力的叠层
canStack bool // 最大叠加层数 ,正常都是不允许叠加的,除了衰弱特殊效果 ,异常和能力的叠层
SideEffectArgs []int // 附加效果参数
// owner bool //是否作用自身
Success bool // 是否执行成功 成功XXX失败XXX
@@ -69,12 +69,12 @@ func (e *EffectNode) Hit(t ...bool) bool {
return e.hit
}
func (e *EffectNode) MaxStack(t ...int) int {
func (e *EffectNode) CanStack(t ...bool) bool {
if len(t) > 0 {
e.maxStack = t[0]
e.canStack = t[0]
}
return e.maxStack
return e.canStack
}
func (e *EffectNode) Duration(t ...int) int {