diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2023-02-17 14:36:06 +0100 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-08-20 15:50:11 +0200 |
| commit | 50b9a042435b324be281b78ceb87dd63fd24acba (patch) | |
| tree | 06d7436f7cb6963894ddcb767658e472474c9d67 /go | |
| parent | d52a41bdcb587abe57f7d581d260b4be948dcc2a (diff) | |
| download | muhqs-game-50b9a042435b324be281b78ceb87dd63fd24acba.tar.gz muhqs-game-50b9a042435b324be281b78ceb87dd63fd24acba.zip | |
use prompt for target selection of all actions
Diffstat (limited to 'go')
| -rw-r--r-- | go/TODO | 2 | ||||
| -rw-r--r-- | go/client/game.go | 173 | ||||
| -rw-r--r-- | go/game/targets.go | 31 | ||||
| -rw-r--r-- | go/ui/button.go | 13 | ||||
| -rw-r--r-- | go/ui/cardGrid.go | 1 | ||||
| -rw-r--r-- | go/ui/prompt.go | 8 |
6 files changed, 154 insertions, 74 deletions
@@ -1,3 +1,5 @@ +* implement different highlighting used for selection and candidates +* implement representation of next target and current seelction in prompt * implement pile dropping * implement artifcat action * move diff --git a/go/client/game.go b/go/client/game.go index 32fb5f2f..5ae2fbe8 100644 --- a/go/client/game.go +++ b/go/client/game.go @@ -26,6 +26,8 @@ const ( STATE_BAR_WIDTH = 300 POC_BUTTON_WIDTH = 150 + + AUTO_PROGRESS_PROMPT = true ) type Game struct { @@ -70,11 +72,7 @@ func newGame(app *app) *Game { "Pass", func(*ui.SimpleButton) { if g.prompt != nil { - a := g.prompt.Confirm() - g.declareAction(a) - g.RemoveWidget(g.prompt) - g.prompt = nil - + g.progressPrompt() } else if g.hasPriority { g.passPriority() } @@ -108,6 +106,11 @@ func (g *Game) addActivePlayer(name string, deckList string) *Game { return g.initPlayerUi(p) } +func (g *Game) addPrompt(action game.Action, prompt string) { + g.prompt = ui.NewPrompt(g.Height/2, g.Width, action, prompt) + g.AddWidget(g.prompt) +} + func (g *Game) getPlayer(name string) *game.Player { for _, p := range g.gameState.Players { if p.Name == name { @@ -227,7 +230,7 @@ func (g *Game) addActionChoice(perm game.Permanent, x, y int) { g.removeChoice() a := actions[c.GetChoosen(x, y)] if a.Targets() != nil && a.Targets().RequireSelection() { - g.selectedObject = a + g.addPrompt(a, fmt.Sprintf("Select targets for %v", a)) } else { g.declareAction(a) } @@ -242,7 +245,23 @@ func (g *Game) removeChoice() { g.choice = nil } -func (g *Game) AddHighlight(obj interface{}) { +func (g *Game) progressPrompt() { + a := g.prompt.Action() + targets := a.Targets() + if targets.RequireSelection() { + targets.Next() + } else { + g.declareAction(a) + g.removePrompt() + } +} + +func (g *Game) removePrompt() { + g.RemoveWidget(g.prompt) + g.prompt = nil +} + +func (g *Game) addHighlight(obj interface{}) { switch obj := obj.(type) { case ui.HandCard: g.handLayer.AddHighlightCard(obj.C) @@ -257,18 +276,27 @@ func (g *Game) AddHighlight(obj interface{}) { } } +func (g *Game) addHighlights(objs []interface{}) { + for _, obj := range objs { + g.addHighlight(obj) + } +} + func (g *Game) clearMapHighlights() { g.mapView.ClearPermanentsHighlights() g.mapView.ClearTileHighlights() g.mapView.ForceRedraw() } -func (g *Game) declareAction(a game.Action) { - g.playerCtrl.SendAction(a) +func (g *Game) clearHighlights() { g.clearMapHighlights() g.handLayer.ClearHighlights() +} + +func (g *Game) declareAction(a game.Action) { + g.playerCtrl.SendAction(a) + g.clearHighlights() g.hasPriority = false - g.hidePassButton() g.stateBar.ForceRedraw() } @@ -280,6 +308,34 @@ func (g *Game) hidePassButton() { g.RemoveWidget(g.passButton) } +func (g *Game) updatePassButton() { + if g.prompt == nil && !g.hasPriority { + g.hidePassButton() + } + + passButtonLabel := "pass" + if g.prompt != nil { + targets := g.prompt.Action().Targets() + if targets.RequireSelection() { + if !targets.Cur().HasSelection() { + g.hidePassButton() + return + } + passButtonLabel = "next" + } else { + passButtonLabel = "confirm" + } + + if AUTO_PROGRESS_PROMPT && !targets.Cur().AllowSelection() { + g.progressPrompt() + return + } + } + + g.showPassButton() + g.passButton.UpdateLabel(passButtonLabel) +} + func (g *Game) passPriority() { g.declareAction(game.NewPassPriority(g.activePlayer)) } @@ -290,7 +346,6 @@ func (g *Game) handlePlayerNotifications() { log.Println("Received", n) switch n.Notification { case game.PriorityNotification: - g.showPassButton() g.hasPriority = true case game.DeclaredActionNotification: @@ -318,10 +373,7 @@ func (g *Game) handlePlayerNotifications() { g.showStore() } } - - g.prompt = ui.NewPrompt(g.Height/2, g.Width, ctx.Action, ctx.Prompt) - g.AddWidget(g.prompt) - g.showPassButton() + g.addPrompt(ctx.Action, ctx.Prompt) } g.stateBar.ForceRedraw() @@ -348,77 +400,81 @@ func (g *Game) findObjectAt(x, y int) interface{} { return nil } -func (g *Game) handleTargetSelection(obj interface{}) { - a := g.selectedObject.(game.Action) - err := a.Targets().AddSelection(obj) - if err != nil { - log.Println("Not added", obj, "as target for", a, "because", err) - } - - if !a.Targets().RequireSelection() { - g.declareAction(a) - g.selectedObject = nil - } -} - func (g *Game) handleSelection(obj interface{}, x, y int) { if obj == nil { fmt.Printf("No object found at cursor position (%d, %d)\n", x, y) return } - if _, ok := g.selectedObject.(game.Action); ok { - g.handleTargetSelection(obj) - } else { - switch obj := obj.(type) { - case *game.Tile: - if g.storesOnMap && obj.Type == game.TileTypes.Store && g.activePlayer.KnowsStore(obj.Position) { - if g.storeView != nil { - g.hideStore() - } + switch obj := obj.(type) { + case *game.Tile: + if g.storesOnMap && obj.Type == game.TileTypes.Store && + g.activePlayer.KnowsStore(obj.Position) { - g.storeView = ui.NewPocList(x, y, g.gameState.Map.StoreOn(obj.Position)) - g.showStore() + if g.storeView != nil { + g.hideStore() } - case game.Permanent: - perm := obj - g.addPermActionChoice(perm, x, y) + g.storeView = ui.NewPocList(x, y, g.gameState.Map.StoreOn(obj.Position)) + g.showStore() + } - case ui.HandCard: - g.selectedObject = game.NewPlayAction(g.activePlayer, obj.C) - g.handLayer.HighlightCard(obj.C) + case game.Permanent: + perm := obj + g.addPermActionChoice(perm, x, y) - default: - log.Panicf("Object of type %T not handled", obj) + case ui.HandCard: + a := game.NewPlayAction(g.activePlayer, obj.C) + if a.Targets().RequireSelection() { + g.addPrompt(a, fmt.Sprintf("Select targets to play %v", obj.C.Name)) + g.handLayer.HighlightCard(obj.C) + } else { + g.declareAction(a) } + + default: + log.Panicf("Object of type %T not handled", obj) } } -func (g *Game) highlightTargets() { - a, ok := g.selectedObject.(game.Action) - if !ok { +func (g *Game) updateHighlight() { + g.clearHighlights() + if g.prompt == nil { return } - options := a.Targets().Options() - for _, option := range options { - g.AddHighlight(option) + a := g.prompt.Action() + if pa, ok := a.(*game.PlayAction); ok { + g.addHighlight(ui.HandCard{C: pa.Card}) + } + + targets := a.Targets() + if len(targets.Targets()) == 0 { + return + } + + for _, t := range targets.Targets() { + g.addHighlights(t.Selection()) + } + + target := targets.Cur() + if target.AllowSelection() { + options := target.Options() + g.addHighlights(options) } } func (g *Game) Update() error { g.handlePlayerNotifications() - g.highlightTargets() + g.updateHighlight() + g.updatePassButton() if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { x, y := ebiten.CursorPosition() obj := g.findObjectAt(x, y) if g.prompt != nil { - if err := g.prompt.Add(obj); err == nil { - g.AddHighlight(obj) - } else { + if err := g.prompt.Add(obj); err != nil { log.Println("Not added", obj, "to active prompt:", err) g.handleSelection(obj, x, y) } @@ -431,8 +487,7 @@ func (g *Game) Update() error { g.removeChoice() g.selectedObject = nil g.ResetHover() - g.clearMapHighlights() - g.handLayer.ClearHighlights() + g.clearHighlights() g.hideStore() g.RemoveWidget(g.discardPileView) if g.prompt != nil { diff --git a/go/game/targets.go b/go/game/targets.go index 75f3f53c..df522565 100644 --- a/go/game/targets.go +++ b/go/game/targets.go @@ -121,9 +121,11 @@ func (t *Target) AddSelection(sel interface{}) (err error) { return } -func (t *Target) RequireSelection() bool { return len(t.sel) < t.requirement.min } -func (t *Target) HasSelection() bool { return len(t.sel) > 0 } -func (t *Target) ClearSelection() { t.sel = []interface{}{} } +func (t *Target) RequireSelection() bool { return len(t.sel) < t.requirement.min } +func (t *Target) HasSelection() bool { return len(t.sel) > 0 } +func (t *Target) AllowSelection() bool { return len(t.sel) < t.requirement.max } +func (t *Target) ClearSelection() { t.sel = []interface{}{} } +func (t *Target) Selection() []interface{} { return t.sel } func (t *Target) Options() (options []interface{}) { candidates := t.candidates() @@ -172,7 +174,9 @@ func (t *Targets) ClearSelections() { } } func (t *Targets) Options() (options []interface{}) { return t.ts[t.idx].Options() } +func (t *Targets) Cur() *Target { return t.ts[t.idx] } func (t *Targets) Next() { t.idx++ } +func (t *Targets) Targets() []*Target { return t.ts } func (t *Targets) CheckTargets(s *State) error { for _, target := range t.ts { @@ -532,10 +536,25 @@ func (t *Target) candidates() []interface{} { return utils.TypedSliceToInterfaceSlice(t.s.Permanents) } else if strings.Contains(t.desc, "tile") { return utils.TypedSliceToInterfaceSlice(t.s.Map.AllTiles()) - } else if t.desc == "hand card" { + } else if strings.Contains(t.desc, "card") { cards := NewPileOfCards() - for _, p := range t.s.Players { - cards.AddPoc(p.Hand) + switch t.desc { + case "hand card": + for _, p := range t.s.Players { + cards.AddPoc(p.Hand) + } + case "store card": + if t.s.Map.HasStores() { + for _, store := range t.s.Map.Stores { + cards.AddPoc(store) + } + } else { + for _, p := range t.s.Players { + cards.AddPoc(p.Store) + } + } + default: + log.Panicf("Unimplemented card options for %s", t.desc) } return utils.TypedSliceToInterfaceSlice(cards.Cards()) } diff --git a/go/ui/button.go b/go/ui/button.go index eac3c2e4..50f78a3d 100644 --- a/go/ui/button.go +++ b/go/ui/button.go @@ -19,8 +19,8 @@ type SimpleButton struct { WidgetBase label string OnClick func(b *SimpleButton) - BG color.Color - FG color.Color + Bg color.Color + Fg color.Color } func NewSimpleButton(x, y, w, h int, label string, onClick func(b *SimpleButton)) *SimpleButton { @@ -32,15 +32,20 @@ func NewSimpleButton(x, y, w, h int, label string, onClick func(b *SimpleButton) func (b *SimpleButton) render() *ebiten.Image { img := ebiten.NewImage(b.Width, b.Height) - ebitenutil.DrawRect(img, float64(0), float64(0), float64(b.Width), float64(b.Height), b.BG) + ebitenutil.DrawRect(img, float64(0), float64(0), float64(b.Width), float64(b.Height), b.Bg) bounds := text.BoundString(font.Font24, b.label) x := b.Width/2 - centerTextXOffset(bounds) y := b.Height/2 - centerTextYOffset(bounds) - text.Draw(img, b.label, font.Font24, x, y, b.FG) + text.Draw(img, b.label, font.Font24, x, y, b.Fg) return img } func (b *SimpleButton) Click(x, y int) { b.OnClick(b) } + +func (b *SimpleButton) UpdateLabel(label string) { + b.label = label + b.ForceRedraw() +} diff --git a/go/ui/cardGrid.go b/go/ui/cardGrid.go index f6720ca1..1b86fe35 100644 --- a/go/ui/cardGrid.go +++ b/go/ui/cardGrid.go @@ -98,7 +98,6 @@ func (w *CardGrid) render() *ebiten.Image { op.GeoM.Translate(xOffset, yOffset) if slices.Contains(w.highlights, card) { - log.Println("Highlighting card", card.Name) op.ColorM.Scale(1, 0, 0, 0.5) } diff --git a/go/ui/prompt.go b/go/ui/prompt.go index 93c4b4cd..c0b11d94 100644 --- a/go/ui/prompt.go +++ b/go/ui/prompt.go @@ -28,6 +28,10 @@ func NewPrompt(y, width int, action game.Action, promptText string) *Prompt { return p } +func (p *Prompt) Action() game.Action { + return p.action +} + func (p *Prompt) Add(obj interface{}) error { if handCard, ok := obj.(HandCard); ok { obj = handCard.C @@ -36,10 +40,6 @@ func (p *Prompt) Add(obj interface{}) error { return p.action.Targets().AddSelection(obj) } -func (p *Prompt) Confirm() game.Action { - return p.action -} - func (p *Prompt) Height() int { return PROMPT_HEIGHT } |
