diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2023-12-03 15:24:37 +0100 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-01-27 16:43:54 +0100 |
| commit | 9b71fe182e8e42ea2b7d3ba42a0f2767e5e5a246 (patch) | |
| tree | 6c7fb471efa87cd4f5f2e5c00fa067c65d10ca7e | |
| parent | 771b85c660dfd03f786aaf86ff86a9416e361235 (diff) | |
| download | muhqs-game-9b71fe182e8e42ea2b7d3ba42a0f2767e5e5a246.tar.gz muhqs-game-9b71fe182e8e42ea2b7d3ba42a0f2767e5e5a246.zip | |
prompt the user to order its triggers
All TriggeredAction are passed in the wrapper action
DeclareTriggeredActionsAction to the controller of the triggers.
It is expected to receive an ordered list back from the user
again wrapped in a DeclareTriggeredActionsAction.
The triggered actions will be declared in from first to last.
| -rw-r--r-- | go/game/action.go | 34 | ||||
| -rw-r--r-- | go/game/playerControl.go | 7 | ||||
| -rw-r--r-- | go/game/state.go | 45 | ||||
| -rw-r--r-- | go/game/trigger.go | 6 |
4 files changed, 81 insertions, 11 deletions
diff --git a/go/game/action.go b/go/game/action.go index 4f433845..a8b27314 100644 --- a/go/game/action.go +++ b/go/game/action.go @@ -2,6 +2,7 @@ package game import ( "fmt" + "log" ) type ( @@ -12,6 +13,7 @@ type ( type Action interface { Source() interface{} + Controller() *Player Targets() *Targets Target() *Target // Shortcut for to select the first target @@ -25,6 +27,7 @@ type Action interface { type PassPriority struct{ player *Player } func (a *PassPriority) Source() interface{} { return a.player } +func (a *PassPriority) Controller() *Player { return a.player } func (*PassPriority) Targets() *Targets { return nil } func (*PassPriority) Target() *Target { return nil } func (*PassPriority) NeedsActionCostChoice() bool { return false } @@ -40,6 +43,7 @@ type TargetSelection struct { } func (sel *TargetSelection) Source() interface{} { return sel.player } +func (sel *TargetSelection) Controller() *Player { return sel.player } func (sel *TargetSelection) Targets() *Targets { return sel.targets } func (sel *TargetSelection) Target() *Target { return sel.targets.ts[0] } func (*TargetSelection) NeedsActionCostChoice() bool { return false } @@ -67,7 +71,20 @@ type ActionBase struct { costFunc ActionCostFunc } -func (a *ActionBase) Source() interface{} { return a.source } +func (a *ActionBase) Source() interface{} { return a.source } +func (a *ActionBase) Controller() *Player { + switch t := a.source.(type) { + case *Player: + return t + case Permanent: + return t.Controller() + default: + log.Panicf("Unhandled source type %v", t) + return nil + } + + return nil +} func (a *ActionBase) resolve(s *State) { a.resolveFunc(s) } func (a *ActionBase) PayCosts(s *State) bool { return a.costFunc(s) } func (a *ActionBase) Targets() *Targets { return a.targets } @@ -102,6 +119,21 @@ func (a *TriggeredAction) String() string { return fmt.Sprintf("%v ! %v", a.event, a.source) } +// Wrapper action to pass all triggered actions through the PlayerControl +type DeclareTriggeredActionsAction struct { + actions []*TriggeredAction +} + +func (sel *DeclareTriggeredActionsAction) Source() interface{} { return nil } +func (sel *DeclareTriggeredActionsAction) Controller() *Player { return nil } +func (sel *DeclareTriggeredActionsAction) Targets() *Targets { return nil } +func (sel *DeclareTriggeredActionsAction) Target() *Target { return nil } +func (*DeclareTriggeredActionsAction) NeedsActionCostChoice() bool { return false } +func (sel *DeclareTriggeredActionsAction) CheckTargets(s *State) error { return nil } +func (*DeclareTriggeredActionsAction) PayCosts(*State) bool { return true } +func (*DeclareTriggeredActionsAction) resolve(*State) {} +func (*DeclareTriggeredActionsAction) String() string { return "declared triggered actions" } + type PileDropAction struct { ActionBase pile []Permanent diff --git a/go/game/playerControl.go b/go/game/playerControl.go index eea6a19b..94c8be7e 100644 --- a/go/game/playerControl.go +++ b/go/game/playerControl.go @@ -12,6 +12,7 @@ const ( ResolvedActionNotification PriorityNotification TargetSelectionPrompt + DeclareTriggeredActionsPrompt ) func (n PlayerNotificationType) String() string { @@ -24,6 +25,8 @@ func (n PlayerNotificationType) String() string { return "PriorityNotification" case TargetSelectionPrompt: return "TargetSelectionPrompt" + case DeclareTriggeredActionsPrompt: + return "DeclareTriggeredActionsPrompt" default: log.Panicf("Unhandled notification %d", n) return "" @@ -86,6 +89,10 @@ func newHandCardSelectionPrompt(p *Player, min, max int) PlayerNotification { return newTargetSelectionPrompt(a, desc) } +func newDeclareTriggeredActionsPrompt(triggeredActions []*TriggeredAction) PlayerNotification { + return PlayerNotification{DeclareTriggeredActionsPrompt, triggeredActions, nil} +} + type PlayerControl interface { Player() *Player RecvAction() Action diff --git a/go/game/state.go b/go/game/state.go index 6bb3dbf0..36dc3e77 100644 --- a/go/game/state.go +++ b/go/game/state.go @@ -106,7 +106,7 @@ func (s *State) removeTriggers(triggers []Trigger) { } func (s *State) handleTriggers() { - var actions []Action + var actions []*TriggeredAction var triggersToRemove []Trigger for _, e := range s.events { @@ -126,9 +126,11 @@ func (s *State) handleTriggers() { s.removeTriggers(triggersToRemove) - orderedActions := s.orderTriggeredActions(actions) - for _, a := range orderedActions { - s.declareAction(a) + if len(actions) > 0 { + orderedActions := s.orderTriggeredActions(actions) + for _, a := range orderedActions { + s.declareAction(a) + } } } @@ -360,9 +362,38 @@ func (s *State) declareAction(a Action) { s.broadcastNotification(newDeclaredActionNotification(a, err)) } -func (s *State) orderTriggeredActions(actions []Action) []Action { - // TODO: implement ordering - return actions +func (s *State) orderTriggeredActions(triggeredActions []*TriggeredAction) []Action { + playerActions := make(map[*Player][]*TriggeredAction) + for _, action := range triggeredActions { + p := action.Controller() + if actions, found := playerActions[p]; found { + playerActions[p] = append(actions, action) + } else { + playerActions[p] = []*TriggeredAction{action} + } + } + + orderedActions := []Action{} + nPlayers := len(s.Players) + for i := 0; i < nPlayers; i++ { + p := s.Players[(i+s.ActivePlayer.Id-1)%nPlayers] + + actions := playerActions[p] + p.Ctrl.SendNotification(newDeclareTriggeredActionsPrompt(actions)) + + switch a := p.Ctrl.RecvAction().(type) { + case *DeclareTriggeredActionsAction: + pActions := make([]Action, 0, len(a.actions)) + for _, a := range a.actions { + pActions = append(pActions, a) + } + orderedActions = append(orderedActions, pActions...) + default: + log.Panicf("Unexpected action type %T after DeclareTriggerPrompt", a) + } + } + + return orderedActions } func (s *State) allPassing(skipFirst bool) bool { diff --git a/go/game/trigger.go b/go/game/trigger.go index 39901d71..8fe4ae89 100644 --- a/go/game/trigger.go +++ b/go/game/trigger.go @@ -61,7 +61,7 @@ func newEotEvent() Event { type Trigger interface { Card() *Card String() string - trigger(*State, Event) ([]Action, bool) + trigger(*State, Event) ([]*TriggeredAction, bool) } type triggerBase struct { @@ -80,11 +80,11 @@ func (t *triggerBase) String() string { return t.desc } -func (t *triggerBase) trigger(s *State, e Event) ([]Action, bool) { +func (t *triggerBase) trigger(s *State, e Event) ([]*TriggeredAction, bool) { triggered, remove := t.condition(s, e) if !triggered { return nil, remove } - return []Action{newTriggeredAction(e, t, t.resolveFunc, t.costFunc)}, remove + return []*TriggeredAction{newTriggeredAction(e, t, t.resolveFunc, t.costFunc)}, remove } |
