216 lines
4.8 KiB
Go
216 lines
4.8 KiB
Go
package effect
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"github.com/badu/bus"
|
|
"github.com/tnnmigga/enum"
|
|
)
|
|
|
|
// ========================
|
|
// BattleMode 枚举
|
|
// ========================
|
|
|
|
type EnumBattleMode string
|
|
|
|
var BattleMode = enum.New[struct {
|
|
PVE EnumBattleMode `enum:"3"`
|
|
PVP EnumBattleMode `enum:"1"`
|
|
}]()
|
|
|
|
// ========================
|
|
// Effect 框架
|
|
// ========================
|
|
|
|
type ContextType interface {
|
|
SourceID() string
|
|
}
|
|
|
|
type EffectContext struct {
|
|
Parent ContextType
|
|
Trigger EnumEffectTrigger
|
|
Container *EffectContainer
|
|
Effect *Effect
|
|
Available bool
|
|
Success bool
|
|
Done bool
|
|
}
|
|
|
|
func (c *EffectContext) Stop() { c.Done = true }
|
|
func (c *EffectContext) SourceID() string {
|
|
if c.Container != nil {
|
|
return c.Container.ID
|
|
}
|
|
return "unknown"
|
|
}
|
|
|
|
type EffectFunc func(ctx *EffectContext, next func())
|
|
|
|
type Effect struct {
|
|
ID string
|
|
Priority int
|
|
Triggers []EnumEffectTrigger
|
|
Condition func(*EffectContext) bool
|
|
Apply EffectFunc
|
|
}
|
|
|
|
type EffectContainer struct {
|
|
ID string
|
|
Effects []*Effect
|
|
Subs []*bus.Listener[*EffectContext]
|
|
Battle *Battle
|
|
Parent ContextType
|
|
}
|
|
|
|
func NewEffectContainer(id string, effects []*Effect, battle *Battle, parent ContextType) *EffectContainer {
|
|
c := &EffectContainer{
|
|
ID: id,
|
|
Effects: effects,
|
|
Battle: battle,
|
|
Parent: parent,
|
|
}
|
|
// 自动订阅 triggers
|
|
for _, e := range effects {
|
|
for _, trig := range e.Triggers {
|
|
sub := battle.GetTopic(trig).Sub(func(ctx *EffectContext) {
|
|
if ctx.Available && e.Apply != nil {
|
|
ctx.Effect = e
|
|
c.executeWithPriority([]*EffectContext{ctx})
|
|
}
|
|
})
|
|
c.Subs = append(c.Subs, sub)
|
|
}
|
|
}
|
|
return c
|
|
}
|
|
|
|
// done/next 执行队列,按优先级
|
|
func (c *EffectContainer) executeWithPriority(queue []*EffectContext) {
|
|
sort.SliceStable(queue, func(i, j int) bool {
|
|
return queue[i].Effect.Priority > queue[j].Effect.Priority
|
|
})
|
|
var runNext func(idx int)
|
|
runNext = func(idx int) {
|
|
if idx >= len(queue) {
|
|
return
|
|
}
|
|
ctx := queue[idx]
|
|
if ctx.Available {
|
|
ctx.Effect.Apply(ctx, func() {
|
|
runNext(idx + 1)
|
|
})
|
|
} else {
|
|
runNext(idx + 1)
|
|
}
|
|
}
|
|
runNext(0)
|
|
}
|
|
|
|
// ========================
|
|
// Battle
|
|
// ========================
|
|
|
|
type Battle struct {
|
|
Turn int
|
|
topics map[EnumEffectTrigger]*bus.Topic[*EffectContext]
|
|
}
|
|
|
|
func NewBattle() *Battle {
|
|
allTriggers := []EnumEffectTrigger{
|
|
EffectTrigger.OnBattleStart,
|
|
EffectTrigger.BeforeSort,
|
|
EffectTrigger.BeforeUseSkillCheck,
|
|
EffectTrigger.AfterUseSkillCheck,
|
|
EffectTrigger.BeforeMultiHit,
|
|
EffectTrigger.BeforeHit,
|
|
EffectTrigger.OnCritPreDamage,
|
|
EffectTrigger.PreDamage,
|
|
EffectTrigger.OnHit,
|
|
EffectTrigger.OnMiss,
|
|
EffectTrigger.AfterAttacked,
|
|
EffectTrigger.OnDefeat,
|
|
EffectTrigger.SkillUseEnd,
|
|
EffectTrigger.OnBeforeCalculateDamage,
|
|
EffectTrigger.OnDamage,
|
|
EffectTrigger.Shield,
|
|
EffectTrigger.PostDamage,
|
|
EffectTrigger.OnCritPostDamage,
|
|
EffectTrigger.OnTransform,
|
|
EffectTrigger.OnTransformEnd,
|
|
EffectTrigger.BeforeTransform,
|
|
EffectTrigger.AfterTransform,
|
|
EffectTrigger.TurnStart,
|
|
EffectTrigger.TurnEnd,
|
|
EffectTrigger.OnBeforeAddMark,
|
|
EffectTrigger.OnAnyMarkAdded,
|
|
EffectTrigger.OnRemoveMark,
|
|
EffectTrigger.OnMarkCreated,
|
|
EffectTrigger.OnMarkDestroy,
|
|
EffectTrigger.OnMarkDurationEnd,
|
|
EffectTrigger.OnStackBefore,
|
|
EffectTrigger.OnStack,
|
|
EffectTrigger.OnBeforeConsumeStack,
|
|
EffectTrigger.OnConsumeStack,
|
|
EffectTrigger.OnBeforeHeal,
|
|
EffectTrigger.OnHeal,
|
|
EffectTrigger.BeforeRageGain,
|
|
EffectTrigger.BeforeRageLoss,
|
|
EffectTrigger.OnRageGain,
|
|
EffectTrigger.OnRageLoss,
|
|
EffectTrigger.OnSwitchIn,
|
|
EffectTrigger.OnSwitchOut,
|
|
EffectTrigger.OnOwnerSwitchIn,
|
|
EffectTrigger.OnOwnerSwitchOut,
|
|
EffectTrigger.BeforeEffect,
|
|
EffectTrigger.AfterEffect,
|
|
}
|
|
topics := make(map[EnumEffectTrigger]*bus.Topic[*EffectContext])
|
|
for _, trig := range allTriggers {
|
|
topics[trig] = bus.NewTopic[*EffectContext]()
|
|
}
|
|
return &Battle{Turn: 0, topics: topics}
|
|
}
|
|
|
|
func (b *Battle) GetTopic(trig EnumEffectTrigger) *bus.Topic[*EffectContext] {
|
|
return b.topics[trig]
|
|
}
|
|
|
|
func (b *Battle) NextTurn(containers []*EffectContainer) {
|
|
b.Turn++
|
|
fmt.Printf("=== 回合 %d 开始 ===\n", b.Turn)
|
|
b.PublishTrigger(EffectTrigger.TurnStart, containers)
|
|
fmt.Println("=== 玩家操作阶段 ===")
|
|
b.PublishTrigger(EffectTrigger.TurnEnd, containers)
|
|
fmt.Printf("=== 回合 %d 结束 ===\n\n", b.Turn)
|
|
}
|
|
|
|
func (b *Battle) PublishTrigger(trigger EnumEffectTrigger, containers []*EffectContainer) {
|
|
for _, c := range containers {
|
|
for _, e := range c.Effects {
|
|
for _, t := range e.Triggers {
|
|
if t == trigger {
|
|
ctx := &EffectContext{
|
|
Parent: c.Parent,
|
|
Trigger: trigger,
|
|
Container: c,
|
|
Effect: e,
|
|
Available: true,
|
|
}
|
|
b.GetTopic(trigger).Pub(ctx)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ========================
|
|
// 示例
|
|
// ========================
|
|
|
|
type Player struct {
|
|
ID string
|
|
}
|
|
|
|
func (p *Player) SourceID() string { return p.ID }
|