aboutsummaryrefslogtreecommitdiff
path: root/go/game
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-12-03 15:24:37 +0100
committerFlorian Fischer <florian.fischer@muhq.space>2025-08-20 15:57:08 +0200
commitcf29758fbd8bb1cc0b82a37351e2aa7ddbd2586f (patch)
tree3d357dbc1e13446f3a8aaefb05ae76b54f56cfdb /go/game
parentc84ec00541d3e18a4abe193621002055a2377af9 (diff)
downloadmuhqs-game-cf29758fbd8bb1cc0b82a37351e2aa7ddbd2586f.tar.gz
muhqs-game-cf29758fbd8bb1cc0b82a37351e2aa7ddbd2586f.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.
Diffstat (limited to 'go/game')
-rw-r--r--go/game/action.go34
-rw-r--r--go/game/playerControl.go7
-rw-r--r--go/game/state.go45
-rw-r--r--go/game/trigger.go6
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
}