feat: 实现乱舞效果并完善战斗输入上下文
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
This commit is contained in:
84
logic/service/fight/boss/NewSeIdx_504.go
Normal file
84
logic/service/fight/boss/NewSeIdx_504.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package effect
|
||||
|
||||
import (
|
||||
"blazing/logic/service/fight/info"
|
||||
"blazing/logic/service/fight/input"
|
||||
|
||||
"github.com/alpacahq/alpacadecimal"
|
||||
)
|
||||
|
||||
// 504. 乱舞:群体攻击提升{0}%的攻击伤害并增加一个攻击目标
|
||||
// 说明:
|
||||
// 1) 作为 NewSel(魂印/特性)实现,避免影响技能侧的 Effect504 旧逻辑。
|
||||
// 2) 目前战斗主链仍是单目标红伤,额外目标以“追加一次同等红伤”方式落地。
|
||||
type NewSel504 struct {
|
||||
NewSel0
|
||||
}
|
||||
|
||||
func (e *NewSel504) isOwnerActive() bool {
|
||||
our := e.Ctx().Our
|
||||
if our == nil || our.CurrentPet() == nil {
|
||||
return false
|
||||
}
|
||||
return e.ID().GetCatchTime() == our.CurrentPet().Info.CatchTime
|
||||
}
|
||||
|
||||
func (e *NewSel504) Damage_Mul(zone *info.DamageZone) bool {
|
||||
if !e.isOwnerActive() || zone == nil || zone.Type != info.DamageType.Red {
|
||||
return true
|
||||
}
|
||||
if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS {
|
||||
return true
|
||||
}
|
||||
if len(e.Args()) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
percent := e.Args()[0]
|
||||
if percent.Cmp(alpacadecimal.Zero) <= 0 {
|
||||
return true
|
||||
}
|
||||
zone.Damage = zone.Damage.Mul(alpacadecimal.NewFromInt(100).Add(percent)).Div(alpacadecimal.NewFromInt(100))
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *NewSel504) Action_end() bool {
|
||||
if !e.isOwnerActive() {
|
||||
return true
|
||||
}
|
||||
if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().SkillEntity.AttackTime == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
our := e.Ctx().Our
|
||||
if our == nil || our.SumDamage.Cmp(alpacadecimal.Zero) <= 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
currentTarget := e.OpponentInput()
|
||||
var extraTarget *input.Input
|
||||
e.ForEachOpponentSlot(func(target *input.Input) bool {
|
||||
if target == nil || target == currentTarget {
|
||||
return true
|
||||
}
|
||||
pet := target.CurrentPet()
|
||||
if pet == nil || !pet.Alive() {
|
||||
return true
|
||||
}
|
||||
extraTarget = target
|
||||
return false
|
||||
})
|
||||
if extraTarget == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
extraTarget.Damage(our, &info.DamageZone{
|
||||
Type: info.DamageType.Red,
|
||||
Damage: our.SumDamage,
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
func init() {
|
||||
input.InitEffect(input.EffectType.NewSel, 504, &NewSel504{})
|
||||
}
|
||||
@@ -15,8 +15,11 @@ func (e *Effect453) Skill_Use() bool {
|
||||
ispwoer := false
|
||||
for i, v := range e.OpponentInput().Prop[:] {
|
||||
if v > 0 {
|
||||
e.CarrierInput().SetProp(e.OpponentInput(), int8(i), 0)
|
||||
ispwoer = true
|
||||
|
||||
if e.OpponentInput().SetProp(e.OpponentInput(), int8(i), 0) {
|
||||
ispwoer = true
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package effect
|
||||
|
||||
import (
|
||||
"blazing/logic/service/fight/input"
|
||||
"blazing/logic/service/fight/node"
|
||||
|
||||
"github.com/alpacahq/alpacadecimal"
|
||||
)
|
||||
|
||||
// {
|
||||
// "id": 529,
|
||||
// "argsNum": 0,
|
||||
// "info": "使自身体力百分比与对手体力百分比对调"
|
||||
// },
|
||||
type Effect529 struct {
|
||||
node.EffectNode
|
||||
}
|
||||
|
||||
func (e *Effect529) OnSkill() bool {
|
||||
|
||||
// 计算自身当前体力百分比
|
||||
ourCurrentHP := e.Ctx().Our.CurPet.Info.Hp
|
||||
ourMaxHP := e.Ctx().Our.CurPet.GetMaxHP().IntPart()
|
||||
ourHPPercentage := alpacadecimal.NewFromInt(ourCurrentHP).Div(alpacadecimal.NewFromInt(ourMaxHP))
|
||||
|
||||
// 计算对手当前体力百分比
|
||||
oppCurrentHP := e.Ctx().Opp.CurPet.Info.Hp
|
||||
oppMaxHP := e.Ctx().Opp.CurPet.GetMaxHP().IntPart()
|
||||
oppHPPercentage := alpacadecimal.NewFromInt(int64(oppCurrentHP)).Div(alpacadecimal.NewFromInt(oppMaxHP))
|
||||
|
||||
// 计算新的体力值(按百分比对调)
|
||||
newOurHP := alpacadecimal.NewFromInt(ourMaxHP).Mul(oppHPPercentage).IntPart()
|
||||
newOppHP := alpacadecimal.NewFromInt(oppMaxHP).Mul(ourHPPercentage).IntPart()
|
||||
|
||||
// 设置新的体力值
|
||||
e.Ctx().Our.CurPet.Info.Hp = int(newOurHP)
|
||||
e.Ctx().Opp.CurPet.Info.Hp = int(newOppHP)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func init() {
|
||||
input.InitEffect(input.EffectType.Skill, 529, &Effect529{})
|
||||
}
|
||||
@@ -152,8 +152,7 @@ func (f *FightC) buildNoteUseSkillOutboundInfo() info.NoteUseSkillOutboundInfo {
|
||||
result := info.NoteUseSkillOutboundInfo{}
|
||||
result.FirstAttackInfo = append(result.FirstAttackInfo, f.collectAttackValues(f.Our)...)
|
||||
result.SecondAttackInfo = append(result.SecondAttackInfo, f.collectAttackValues(f.Opp)...)
|
||||
result.FirstAttackInfoLen = uint32(len(result.FirstAttackInfo))
|
||||
result.SecondAttackInfoLen = uint32(len(result.SecondAttackInfo))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -3,18 +3,40 @@ package input
|
||||
import "blazing/logic/service/fight/info"
|
||||
|
||||
type LegacySides struct {
|
||||
Our *Input // 兼容旧 effect:当前执行侧
|
||||
|
||||
Opp *Input // 兼容旧 effect:当前执行侧的对位
|
||||
// Our 兼容旧 effect:当前执行侧(历史语义里的“我方”)。
|
||||
// 使用场景:
|
||||
// 1) 旧 effect 仍通过 Ctx().Our/Ctx().Opp 取双方输入。
|
||||
// 2) Source/Carrier/Target 未设置时作为默认回退。
|
||||
Our *Input
|
||||
|
||||
// Opp 兼容旧 effect:当前执行侧的对位(历史语义里的“对手”)。
|
||||
// 使用场景:
|
||||
// 1) 单目标旧链路下,直接作为“对手输入”。
|
||||
// 2) Source/Carrier/Target 语义未接入的旧 effect。
|
||||
Opp *Input
|
||||
}
|
||||
|
||||
type EffectBinding struct {
|
||||
Source *Input // effect 绑定的输入源,等价于 e.GetInput()
|
||||
// Source effect 来源输入(谁施加/创建了这个 effect),通常等价于 e.GetInput()。
|
||||
// 该值一般在 effect 生命周期内保持不变。
|
||||
// 使用场景:
|
||||
// 1) 伤害归属/效果归属判定(谁是施法者)。
|
||||
// 2) A 给 B 挂效果后,后续仍需要识别“来源是 A”。
|
||||
Source *Input
|
||||
|
||||
Carrier *Input // 当前持有该 effect 的输入源
|
||||
// Carrier 当前持有效果的输入(effect 实际挂载在哪个输入上)。
|
||||
// 例如 A 给 B 挂 debuff 时:Source=A,Carrier=B。
|
||||
// 使用场景:
|
||||
// 1) 回合开始/结束、切换、状态结算时,按“持有者”触发。
|
||||
// 2) 需要区分“是谁挂的”(Source) 与 “挂在谁身上”(Carrier)。
|
||||
Carrier *Input
|
||||
|
||||
Target *Input // 当前 effect 实际作用/挂载的输入源
|
||||
// Target 本次结算的实际作用目标输入(瞬时语义,可在链式/群体结算中变化)。
|
||||
// 为空时通常回退到 Carrier 或 LegacySides.Our。
|
||||
// 使用场景:
|
||||
// 1) 链式/群体/溅射:同一个 effect 逐个作用不同目标。
|
||||
// 2) 重定向/反弹:效果仍由原 Carrier 持有,但本次作用对象发生变化。
|
||||
Target *Input
|
||||
}
|
||||
|
||||
type Ctx struct {
|
||||
@@ -36,6 +58,10 @@ func (c *Ctx) Self() *Input {
|
||||
return c.Our
|
||||
}
|
||||
|
||||
// Receiver 返回本次结算的接收方输入:Target > Carrier > Our。
|
||||
// 使用场景:
|
||||
// 1) 写 effect 时不关心目标来源,只取“当前实际受影响者”。
|
||||
// 2) 统一兼容单目标与群体/重定向结算链路。
|
||||
func (c *Ctx) Receiver() *Input {
|
||||
if c == nil {
|
||||
return nil
|
||||
|
||||
@@ -51,6 +51,7 @@ func (e *EffectNode) GetInput() *input.Input {
|
||||
|
||||
}
|
||||
|
||||
// SourceInput 返回 effect 的来源输入(优先 Ctx.Source,回退到绑定输入)。
|
||||
func (e *EffectNode) SourceInput() *input.Input {
|
||||
if e.Ctx().Source != nil {
|
||||
return e.Ctx().Source
|
||||
@@ -58,6 +59,7 @@ func (e *EffectNode) SourceInput() *input.Input {
|
||||
return e.Input
|
||||
}
|
||||
|
||||
// CarrierInput 返回当前持有该 effect 的输入(优先 Ctx.Carrier)。
|
||||
func (e *EffectNode) CarrierInput() *input.Input {
|
||||
if e.Ctx().Carrier != nil {
|
||||
return e.Ctx().Carrier
|
||||
@@ -65,6 +67,7 @@ func (e *EffectNode) CarrierInput() *input.Input {
|
||||
return e.Ctx().Our
|
||||
}
|
||||
|
||||
// TargetInput 返回当前 effect 作用目标输入(优先 Ctx.Target)。
|
||||
func (e *EffectNode) TargetInput() *input.Input {
|
||||
if e.Ctx().Target != nil {
|
||||
return e.Ctx().Target
|
||||
@@ -72,10 +75,12 @@ func (e *EffectNode) TargetInput() *input.Input {
|
||||
return e.Ctx().Opp
|
||||
}
|
||||
|
||||
// OpponentInput 返回当前上下文中的对位输入(兼容旧链路)。
|
||||
func (e *EffectNode) OpponentInput() *input.Input {
|
||||
return e.Ctx().Opp
|
||||
}
|
||||
|
||||
// SourcePet 返回来源输入当前出战精灵。
|
||||
func (e *EffectNode) SourcePet() *info.BattlePetEntity {
|
||||
in := e.SourceInput()
|
||||
if in == nil {
|
||||
@@ -84,6 +89,7 @@ func (e *EffectNode) SourcePet() *info.BattlePetEntity {
|
||||
return in.CurrentPet()
|
||||
}
|
||||
|
||||
// CarrierPet 返回持有效果输入当前出战精灵。
|
||||
func (e *EffectNode) CarrierPet() *info.BattlePetEntity {
|
||||
in := e.CarrierInput()
|
||||
if in == nil {
|
||||
@@ -92,6 +98,7 @@ func (e *EffectNode) CarrierPet() *info.BattlePetEntity {
|
||||
return in.CurrentPet()
|
||||
}
|
||||
|
||||
// TargetPet 返回目标输入当前出战精灵。
|
||||
func (e *EffectNode) TargetPet() *info.BattlePetEntity {
|
||||
in := e.TargetInput()
|
||||
if in == nil {
|
||||
@@ -100,6 +107,7 @@ func (e *EffectNode) TargetPet() *info.BattlePetEntity {
|
||||
return in.CurrentPet()
|
||||
}
|
||||
|
||||
// OpponentPet 返回对位输入当前出战精灵。
|
||||
func (e *EffectNode) OpponentPet() *info.BattlePetEntity {
|
||||
in := e.OpponentInput()
|
||||
if in == nil {
|
||||
@@ -108,6 +116,8 @@ func (e *EffectNode) OpponentPet() *info.BattlePetEntity {
|
||||
return in.CurrentPet()
|
||||
}
|
||||
|
||||
// ForEachOpponentSlot 遍历对面全部站位;回调返回 false 时提前停止。
|
||||
// 无组队视图时回退到单目标 OpponentInput。
|
||||
func (e *EffectNode) ForEachOpponentSlot(fn func(*input.Input) bool) {
|
||||
if fn == nil {
|
||||
return
|
||||
@@ -136,6 +146,8 @@ func (e *EffectNode) ForEachOpponentSlot(fn func(*input.Input) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// ForEachCarrierBenchPet 遍历持有效果方当前站位的后备精灵(不含当前出战)。
|
||||
// 回调返回 false 时提前停止。
|
||||
func (e *EffectNode) ForEachCarrierBenchPet(fn func(*info.BattlePetEntity) bool) {
|
||||
if fn == nil {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user