aboutsummaryrefslogtreecommitdiff
path: root/go/game/action.go
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-02-08 17:50:10 +0100
committerFlorian Fischer <florian.fischer@muhq.space>2025-08-20 15:50:10 +0200
commit45df9bf319d38db916ca983b8c92bb912f5ce9ee (patch)
treed7966002dad4a20bc3ef385bd152554fe278a6df /go/game/action.go
parenteb10f6f4940ecd09fd7d881fe5e1735ff146f919 (diff)
downloadmuhqs-game-45df9bf319d38db916ca983b8c92bb912f5ce9ee.tar.gz
muhqs-game-45df9bf319d38db916ca983b8c92bb912f5ce9ee.zip
intermediate commit
Diffstat (limited to 'go/game/action.go')
-rw-r--r--go/game/action.go194
1 files changed, 109 insertions, 85 deletions
diff --git a/go/game/action.go b/go/game/action.go
index 5002f58a..77e9743f 100644
--- a/go/game/action.go
+++ b/go/game/action.go
@@ -13,6 +13,7 @@ type (
type Action interface {
Source() interface{}
Targets() *Targets
+ Target() *Target // Shortcut for to select the first target
CheckTargets(*State) error
PayCosts(s *State) bool
@@ -20,35 +21,40 @@ type Action interface {
String() string
}
-type PassPriority struct{ source *Player }
+type PassPriority struct{ player *Player }
-func (a *PassPriority) Source() interface{} { return a.source }
+func (a *PassPriority) Source() interface{} { return a.player }
func (*PassPriority) Targets() *Targets { return nil }
+func (*PassPriority) Target() *Target { return nil }
func (*PassPriority) CheckTargets(*State) error { return nil }
func (*PassPriority) PayCosts(*State) bool { return true }
func (*PassPriority) Resolve(*State) {}
func (*PassPriority) String() string { return "pass" }
func NewPassPriority(p *Player) Action { return &PassPriority{p} }
-type HandCardSelection struct{ cards []*Card }
-
-func (*HandCardSelection) Source() interface{} { return nil }
-func (*HandCardSelection) Targets() *Targets { return nil }
-func (*HandCardSelection) CheckTargets(*State) error { return nil }
-func (*HandCardSelection) PayCosts(*State) bool { return true }
-func (*HandCardSelection) Resolve(*State) {}
-func (*HandCardSelection) String() string { return "hand card selection" }
-func NewHandCardSelection(cards []*Card) Action { return &HandCardSelection{cards} }
-
-type TargetSelection struct{ targets *Targets }
+type TargetSelection struct {
+ player *Player
+ targets *Targets
+}
-func (*TargetSelection) Source() interface{} { return nil }
+func (sel *TargetSelection) Source() interface{} { return sel.player }
func (sel *TargetSelection) Targets() *Targets { return sel.targets }
+func (sel *TargetSelection) Target() *Target { return sel.targets.ts[0] }
func (sel *TargetSelection) CheckTargets(s *State) error { return sel.targets.CheckTargets(s) }
func (*TargetSelection) PayCosts(*State) bool { return true }
func (*TargetSelection) Resolve(*State) {}
func (*TargetSelection) String() string { return "target selection" }
-func NewTargetSelection(targets *Targets) Action { return &TargetSelection{targets} }
+
+func newTargetSelection(player *Player, targets *Targets) Action {
+ return &TargetSelection{player, targets}
+}
+
+func newHandCardSelection(p *Player, min, max int) Action {
+ a := &TargetSelection{player: p}
+ targetDesc := TargetDesc{"hand card", fmt.Sprintf("%d-%d", min, max)}
+ a.targets = newTargets(newTarget(p.gameState, targetDesc, a))
+ return a
+}
type ActionBase struct {
source interface{}
@@ -58,23 +64,15 @@ type ActionBase struct {
costFunc ActionCostFunc
}
-func (a *ActionBase) Source() interface{} {
- return a.source
-}
-
-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
-}
-
+func (a *ActionBase) Source() interface{} { return a.source }
+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 }
+func (a *ActionBase) Target() *Target { return a.targets.ts[0] }
func (a *ActionBase) CheckTargets(s *State) error {
+ if a.targets == nil {
+ return nil
+ }
return a.targets.CheckTargets(s)
}
@@ -82,7 +80,7 @@ type PlayAction struct {
ActionBase
}
-const permanentPlayActionTarget = "available spawn tile"
+var permanentPlayActionTarget = TargetDesc{"available spawn tile", "1"}
func NewPlayAction(p *Player, c *Card, args ...interface{}) *PlayAction {
a := &PlayAction{ActionBase{
@@ -110,9 +108,11 @@ func NewPlayAction(p *Player, c *Card, args ...interface{}) *PlayAction {
s := p.gameState
if c.IsPermanent() {
- a.targets = newTargets(s, []string{permanentPlayActionTarget}, a)
+ a.targets = newTargets(newTarget(s, permanentPlayActionTarget, a))
} else {
- a.targets = newTargets(s, []string{}, a)
+ // TODO: implement parsing targets to play a card
+ // a.targets = newTargets(s, c.Impl.playTargets(s, p), a)
+ a.targets = newTargets()
}
a.resolveFunc = func(s *State) {
@@ -124,7 +124,7 @@ func NewPlayAction(p *Player, c *Card, args ...interface{}) *PlayAction {
func (a *PlayAction) String() string {
p := a.source.(*Player)
- if a.targets.RequireTargets() {
+ if a.targets.RequireSelection() {
return fmt.Sprintf(" %s play %s", p.Name, a.Card.Name)
}
@@ -135,14 +135,15 @@ type MoveAction struct {
ActionBase
}
-const moveActionTargetDesc = "available tile"
+var moveActionTargetDesc = TargetDesc{"available tile", "1"}
func (a *MoveAction) targetTile() *Tile {
- if t, ok := a.targets.sel[0].(*Tile); ok {
- return t
+ t := a.Target()
+ if tile, ok := t.sel[0].(*Tile); ok {
+ return tile
}
- return a.targets.sel[0].(*Unit).Tile()
+ return t.sel[0].(*Unit).Tile()
}
func NewMoveAction(u *Unit) *MoveAction {
@@ -153,7 +154,7 @@ func NewMoveAction(u *Unit) *MoveAction {
},
}
- a.targets = newTargets(u.Controller().gameState, []string{moveActionTargetDesc}, a)
+ a.targets = newTargets(newTarget(u.Controller().gameState, moveActionTargetDesc, a))
a.resolveFunc = func(s *State) {
tile := a.targetTile()
@@ -173,19 +174,19 @@ func NewMoveAction(u *Unit) *MoveAction {
func (a *MoveAction) String() string {
u := a.source.(*Unit)
- if a.targets.RequireTargets() {
+ if a.targets.RequireSelection() {
return fmt.Sprintf("move %s", u.Movement.String())
}
t := a.targetTile()
- return fmt.Sprintf("%v: %v -> %v", u.Controller().Name, u.Card().Name, t.Position)
+ return fmt.Sprintf("%s -> %v", u.FmtWController(), t.Position)
}
type AttackAction struct {
ActionBase
}
-const attackActionTargetDesc = "attackable enemy permanent"
+var attackActionTargetDesc = TargetDesc{"attackable enemy permanent", "1"}
func NewAttackAction(u *Unit) *AttackAction {
a := &AttackAction{ActionBase{
@@ -193,10 +194,10 @@ func NewAttackAction(u *Unit) *AttackAction {
Card: u.Card(),
}}
- a.targets = newTargets(u.Controller().gameState, []string{attackActionTargetDesc}, a)
+ a.targets = newTargets(newTarget(u.Controller().gameState, attackActionTargetDesc, a))
a.resolveFunc = func(s *State) {
- s.Fight(u, a.targets.sel[0].(Permanent))
+ s.Fight(u, a.Target().sel[0].(Permanent))
}
a.costFunc = func(*State) bool {
@@ -212,13 +213,12 @@ func NewAttackAction(u *Unit) *AttackAction {
func (a *AttackAction) String() string {
u := a.source.(*Unit)
- if a.targets.RequireTargets() {
+ if a.targets.RequireSelection() {
return fmt.Sprintf("attack %v", u.Attack)
}
- target := a.targets.sel[0].(Permanent)
- return fmt.Sprintf("%v: %v x %v",
- u.Controller().Name, u.Card().Name, target.Tile().Position)
+ target := a.Target().sel[0].(Permanent)
+ return fmt.Sprintf("%s x %v", u.FmtWController(), target.Tile().Position)
}
type FullAction struct {
@@ -255,11 +255,11 @@ func NewFullAction(u *Unit, proto ActionFuncPrototype, desc string) *FullAction
func (a *FullAction) String() string {
u := a.source.(*Unit)
- if a.targets.RequireTargets() {
+ if a.targets == nil || a.targets.RequireSelection() {
return fmt.Sprintf("↻ %s", a.desc)
}
- return fmt.Sprintf("%s: %s ↻@%v", u.Controller().Name, u.Card().Name, a.targets)
+ return fmt.Sprintf("%s ↻@%v", u.FmtWController(), a.targets)
}
type FreeAction struct {
@@ -289,80 +289,104 @@ func NewFreeAction(p Permanent, resolveProto ActionFuncPrototype, costFunc Actio
func (a *FreeAction) String() string {
p := a.source.(Permanent)
- if a.targets.RequireTargets() {
+ if a.targets.RequireSelection() {
return fmt.Sprintf("free_action %s", a.desc)
}
return fmt.Sprintf("%v: %s free_action@%v", p.Controller(), p.Card().Name, a.targets)
}
-type BuyAction struct {
- player *Player
- card *Card
-}
+var buyActionTargetDesc = TargetDesc{"store card", "?"}
-func (a *BuyAction) Source() interface{} {
- return a.player
-}
+type BuyAction struct{ TargetSelection }
-func (a *BuyAction) Targets() *Targets {
- return newTargetsWithSel(a.player.gameState, []string{"store cards"}, a, []interface{}{a.card})
+func (a *BuyAction) card() *Card {
+ return a.Target().sel[0].(*Card)
}
func (a *BuyAction) CheckTargets(s *State) error {
+ err := a.targets.CheckTargets(s)
+ if err != nil {
+ return err
+ }
return s.isValidBuy(a)
}
func (a *BuyAction) Resolve(s *State) {
- a.player.Store.MoveCard(a.card, a.player.DiscardPile)
+ a.player.Store.MoveCard(a.card(), a.player.DiscardPile)
}
func (a *BuyAction) PayCosts(*State) bool {
- if a.card.BuyCost < 0 || a.card.BuyCost > a.player.Resource {
+ cost := a.card().BuyCost
+ if cost < 0 || cost > a.player.Resource {
return false
}
- a.player.Resource -= a.card.BuyCost
+ a.player.Resource -= cost
return true
}
-func (a *BuyAction) String() string { return fmt.Sprintf("%s buy %s", a.player.Name, a.card.Name) }
+func (a *BuyAction) String() string {
+ if !a.Targets().HasSelections() {
+ return fmt.Sprintf("%s buy", a.player.Name)
+ }
-func NewBuyAction(p *Player, c *Card) *BuyAction {
- return &BuyAction{p, c}
+ return fmt.Sprintf("%s buy %s", a.player.Name, a.card().Name)
}
-type UpkeepAction struct {
- player *Player
- unit *Unit
- pay bool
+func newBuyAction(p *Player) *BuyAction {
+ a := &BuyAction{TargetSelection{player: p}}
+ a.targets = newTargets(newTarget(p.gameState, buyActionTargetDesc, a))
+ return a
}
-func (a *UpkeepAction) Resolve(s *State) {
- if a.pay {
- a.player.Resource -= a.unit.Upkeep
- } else {
- s.DestroyPermanent(a.unit)
- }
+var upkeepActionTargetDesc = TargetDesc{"unit you controll", "*"}
+
+type UpkeepAction struct {
+ TargetSelection
}
func (a *UpkeepAction) PayCosts(*State) bool {
- if a.unit.Upkeep > a.player.Resource {
+ costs := 0
+ for _, t := range a.Target().sel {
+ u := t.(*Unit)
+ costs += u.UpkeepCost()
+ }
+
+ if a.player.Resource < costs {
return false
}
- a.player.Resource -= a.unit.Upkeep
+ a.player.Resource -= costs
return true
}
-func (a *UpkeepAction) String() string {
- if a.pay {
- return fmt.Sprintf("%s keep %v", a.player.Name, a.unit.Tile().Position)
+func (a *UpkeepAction) Resolve(*State) {
+ p := a.player
+ s := p.gameState
+ for _, i := range a.Target().sel {
+ u := i.(*Unit)
+ s.DestroyPermanent(u)
}
- return fmt.Sprintf("%s disband %v", a.player.Name, a.unit.Tile().Position)
+ // Keep and pay for the rest
+ for _, u := range s.Units {
+ if u.Controller() != p {
+ continue
+ }
+
+ p.Resource -= u.upkeep
+ u.onUpkeep()
+ }
}
-func NewUpkeepAction(p *Player, u *Unit, pay bool) *UpkeepAction {
- return &UpkeepAction{p, u, pay}
+func (a *UpkeepAction) String() string {
+ return fmt.Sprintf("upkeep disbanding: %v", a.Target().sel)
+}
+
+func newUpkeepAction(p *Player) *UpkeepAction {
+ a := &UpkeepAction{TargetSelection{player: p}}
+ a.targets = newTargets(newTarget(p.gameState, upkeepActionTargetDesc, a))
+
+ return a
}