diff options
Diffstat (limited to 'go')
| -rw-r--r-- | go/game/action.go | 6 | ||||
| -rw-r--r-- | go/game/cardImplementations.go | 16 | ||||
| -rw-r--r-- | go/game/cardImplementations_test.go | 48 | ||||
| -rw-r--r-- | go/game/events.go | 5 | ||||
| -rw-r--r-- | go/game/player.go | 20 | ||||
| -rw-r--r-- | go/game/state.go | 3 | ||||
| -rw-r--r-- | go/game/trigger.go | 24 |
7 files changed, 112 insertions, 10 deletions
diff --git a/go/game/action.go b/go/game/action.go index d82bf56a..0cc0bd0a 100644 --- a/go/game/action.go +++ b/go/game/action.go @@ -108,10 +108,8 @@ func (a *FreeDiscardAction) PayCosts(s *LocalState) bool { if a.targets.CheckTargets(s) != nil { return false } - for _, sel := range a.Target().Selection() { - card, _ := sel.(*Card) - a.player.DiscardCard(card) - } + cards := utils.InterfaceSliceToTypedSlice[*Card](a.Target().Selection()) + a.player.discardCards(cards) return true } diff --git a/go/game/cardImplementations.go b/go/game/cardImplementations.go index 1f7af599..ea07ccf4 100644 --- a/go/game/cardImplementations.go +++ b/go/game/cardImplementations.go @@ -816,6 +816,21 @@ func (*recycleImpl) onPlay(a *PlayAction) { } } +type reformerImpl struct{ cardImplementationBase } + +func (*reformerImpl) fullActions(u *Unit) []*FullAction { + fa := newFullAction(u, func(Action) ActionResolveFunc { + return func(s *LocalState) { + s.addTrigger(newOwnDiscardTrigger(u, false, true, func(s *LocalState, e Event) ActionResolveFunc { + return func(*LocalState) { + u.Controller().DrawN(len(e.affected)) + } + }, "reformer draw")) + } + }, "draw for discard") + return []*FullAction{fa} +} + func init() { cardImplementations = map[string]cardImplementation{ "base/advisor": &advisorImpl{}, @@ -869,5 +884,6 @@ func init() { "exp1/patrician": &patricianImpl{}, "exp1/power_to_the_people!": &powerToThePeopleImpl{}, "exp1/recycle!": &recycleImpl{}, + "exp1/reformer": &reformerImpl{}, } } diff --git a/go/game/cardImplementations_test.go b/go/game/cardImplementations_test.go index b3ac5811..51aab02f 100644 --- a/go/game/cardImplementations_test.go +++ b/go/game/cardImplementations_test.go @@ -346,3 +346,51 @@ func TestPowerToThePeople(t *testing.T) { t.Fatal("Not gained 4 resource") } } + +func TestReformer(t *testing.T) { + s, _, player, _ := newMockState() + + r := s.addNewUnit(NewCard("exp1/reformer"), Position{0, 0}, player) + + c1 := NewCard("base/sword") + player.Hand.AddCard(c1) + + ra := r.FullActions[0] + s.ResolveAction(ra) + s.handleTriggers() + + if len(s.triggers) != 1 { + t.Fatal("No active reformer trigger") + } + + player.discardCard(c1) + if len(s.events) != 1 { + t.Fatal("No discard event fired") + } + + s.handleTriggers() + if s.stack.IsEmpty() { + t.Fatal("reformer draw not triggered") + } + + if len(s.triggers) != 1 { + t.Fatal("Reformer trigger removed") + } + + s.stack.pop() + if player.Hand.IsEmpty() { + t.Fatal("no card drawn") + } + + player.DiscardPile.MoveCard(c1, player.Hand) + da := NewFreeDiscardAction(player) + da.Target().AddSelection(player.Hand.Cards()[0]) + da.Target().AddSelection(player.Hand.Cards()[1]) + s.declareAction(da) + s.stack.pop() + s.handleTriggers() + s.stack.pop() + if player.Hand.Size() != 2 { + t.Fatal("not two cards drawn") + } +} diff --git a/go/game/events.go b/go/game/events.go index efe5cdb5..4e2479a8 100644 --- a/go/game/events.go +++ b/go/game/events.go @@ -20,6 +20,7 @@ const ( shuffle declaredAction resolvedAction + discard ) var EventTypes = struct { @@ -33,6 +34,7 @@ var EventTypes = struct { Shuffle eventType DeclaredAction eventType ResolvedAction eventType + Discard eventType }{ Destruction: destruction, Eot: eot, @@ -44,6 +46,7 @@ var EventTypes = struct { Shuffle: shuffle, DeclaredAction: declaredAction, ResolvedAction: resolvedAction, + Discard: discard, } func (e eventType) String() string { @@ -68,6 +71,8 @@ func (e eventType) String() string { return "declaredAction" case resolvedAction: return "resolvedAction" + case discard: + return "discard" } log.Panicf("Unknown eventType: %d", e) diff --git a/go/game/player.go b/go/game/player.go index a3b5c44b..a8372040 100644 --- a/go/game/player.go +++ b/go/game/player.go @@ -198,14 +198,24 @@ func (p *Player) buyPhase() []*Player { return nil } -func (p *Player) discardHand() { - for _, c := range p.Hand.Cards() { - p.DiscardCard(c) +func (p *Player) _discardCard(c *Card) { + p.Hand.MoveCard(c, p.DiscardPile) +} + +func (p *Player) discardCard(c *Card) { + p._discardCard(c) + p.gameState.fireEvent(Event{discard, []any{p}, []any{c}}) +} + +func (p *Player) discardCards(cards []*Card) { + for _, c := range cards { + p._discardCard(c) } + p.gameState.fireEvent(Event{discard, []any{p}, utils.TypedSliceToInterfaceSlice(cards)}) } -func (p *Player) DiscardCard(c *Card) { - p.Hand.MoveCard(c, p.DiscardPile) +func (p *Player) discardHand() { + p.discardCards(p.Hand.Cards()) } func (p *Player) discardStep() { diff --git a/go/game/state.go b/go/game/state.go index 9a76a945..9baaea5f 100644 --- a/go/game/state.go +++ b/go/game/state.go @@ -411,6 +411,7 @@ func (s *LocalState) declareAction(a Action) { } func (s *LocalState) orderTriggeredActions(triggeredActions []*TriggeredAction) []Action { + // Sort triggers by their controller playerActions := make(map[*Player][]*TriggeredAction) for _, action := range triggeredActions { p := action.Controller() @@ -444,7 +445,7 @@ func (s *LocalState) orderTriggeredActions(triggeredActions []*TriggeredAction) pActions = append(pActions, a) } orderedActions = append(orderedActions, pActions...) - case *PassPriority: // Support kraken AI always sending PassPriority + case nil, *PassPriority: // Support kraken AI always sending PassPriority and mockPlayerControl for _, a := range actions { // Simply use the order the actions were triggered pActions = append(pActions, a) } diff --git a/go/game/trigger.go b/go/game/trigger.go index e5b00a9c..6ab42856 100644 --- a/go/game/trigger.go +++ b/go/game/trigger.go @@ -56,6 +56,30 @@ func newTargetedTrigger(p Permanent, singleshot bool, resolveFunc func(*LocalSta return &t } +func newDiscardTrigger(p Permanent, singleshot, singleturn bool, resolveFunc func(*LocalState, Event) ActionResolveFunc, cond func(Event) bool, desc string) Trigger { + t := triggerBase{source: p, resolveFunc: resolveFunc, desc: desc} + t.condition = func(_ *LocalState, event Event) (bool, bool) { + if event.eventType == EventTypes.Eot && singleturn { + return false, true + } + + if event.eventType != EventTypes.Discard { + return false, false + } + + c := cond(event) + remove := singleshot && c + return c, remove + } + return &t +} + +func newOwnDiscardTrigger(p Permanent, singleshot, singleturn bool, resolveFunc func(*LocalState, Event) ActionResolveFunc, desc string) Trigger { + return newDiscardTrigger(p, singleshot, singleturn, resolveFunc, func(e Event) bool { + return e.sources[0] == p.Controller() + }, desc) +} + func newDeathTrigger(p Permanent, singleshot bool, resolveFunc func(*LocalState, Event) ActionResolveFunc, permCond func(Permanent) bool, desc string) Trigger { t := triggerBase{source: p, resolveFunc: resolveFunc, desc: desc} t.condition = func(_ *LocalState, event Event) (bool, bool) { |
