package game import ( "fmt" "log" "golang.org/x/exp/slices" ) type eventType int const ( destruction eventType = iota eot sacrifice play target etb ) var EventTypes = struct { Destruction eventType Eot eventType Sacrifice eventType Play eventType Target eventType Etb eventType }{ Destruction: destruction, Eot: eot, Sacrifice: sacrifice, Play: play, Target: target, Etb: etb, } func (e eventType) String() string { switch e { case destruction: return "destruction" case eot: return "eot" case sacrifice: return "sacrifice" case play: return "play" case target: return "target" case etb: return "etb" } log.Panicf("Unknown evenType: %d", e) return "" } type Event struct { eventType eventType sources []interface{} affected []interface{} } func (e Event) String() string { if len(e.sources) == 0 { return e.eventType.String() } return fmt.Sprintf("%v:%v", e.sources, e.eventType) } func newEotEvent() Event { return Event{eventType: eot} } func newTargetEvent(source interface{}, targets *Targets) Event { affected := make([]interface{}, 0, len(targets.ts)) for _, t := range targets.ts { affected = append(affected, t.sel...) } return Event{eventType: target, sources: []interface{}{source}, affected: affected} } type Trigger interface { Source() interface{} String() string Card() *Card trigger(*LocalState, Event) ([]*TriggeredAction, bool) } type triggerBase struct { source interface{} condition func(*LocalState, Event) (bool, bool) resolveFunc ActionResolveFunc costFunc ActionCostFunc desc string } func (t *triggerBase) Source() interface{} { return t.source } func (t *triggerBase) String() string { return t.desc } func (t *triggerBase) Card() *Card { switch t := t.source.(type) { case *Card: return t case Permanent: return t.Card() default: log.Panicf("Unhandled source type %v", t) return nil } } func (t *triggerBase) trigger(s *LocalState, e Event) ([]*TriggeredAction, bool) { triggered, remove := t.condition(s, e) if !triggered { return nil, remove } return []*TriggeredAction{newTriggeredAction(e, t, t.resolveFunc, t.costFunc)}, remove } func newTargetedTrigger(p Permanent, singleshot bool, resolveFunc ActionResolveFunc, desc string) Trigger { t := triggerBase{source: p, resolveFunc: resolveFunc, desc: desc} t.condition = func(_ *LocalState, event Event) (bool, bool) { c := event.eventType == EventTypes.Target && slices.Contains(event.affected, p.(interface{})) remove := singleshot && c return c, remove } return &t } func newDeathTrigger(p Permanent, singleshot bool, resolveFunc ActionResolveFunc, permCond func(Permanent) bool, desc string) Trigger { t := triggerBase{source: p, resolveFunc: resolveFunc, desc: desc} t.condition = func(_ *LocalState, event Event) (bool, bool) { if !(event.eventType == EventTypes.Sacrifice || event.eventType == EventTypes.Destruction) { return false, false } destroyedPerm := event.affected[0].(Permanent) c := permCond(destroyedPerm) remove := singleshot && c return c, remove } return &t } func newOwnDeathTrigger(p Permanent, resolveFunc ActionResolveFunc, desc string) Trigger { return newDeathTrigger(p, true, resolveFunc, func(destroyedPerm Permanent) bool { return p == destroyedPerm }, desc) } func newEnemyDeathTrigger(p Permanent, resolveFunc ActionResolveFunc, desc string) Trigger { return newDeathTrigger(p, true, resolveFunc, func(destroyedPerm Permanent) bool { return p.Controller().IsEnemy(destroyedPerm.Controller()) }, desc) }