aboutsummaryrefslogtreecommitdiff
path: root/go/game/action.go
diff options
context:
space:
mode:
Diffstat (limited to 'go/game/action.go')
-rw-r--r--go/game/action.go303
1 files changed, 192 insertions, 111 deletions
diff --git a/go/game/action.go b/go/game/action.go
index 0f7a1294..4894d47d 100644
--- a/go/game/action.go
+++ b/go/game/action.go
@@ -4,40 +4,52 @@ import (
"fmt"
)
-type ActionPrototype interface {
- Specialize(*State, ...interface{}) Action
- String() string
-}
+type (
+ ActionCostFunc func(*State) bool
+ ActionResolveFunc func(*State)
+ ActionFuncPrototype func(Action) ActionResolveFunc
+)
type Action interface {
- Resolve(s *State)
+ Source() interface{}
+ Targets() *Targets
+
+ CheckTargets(*State) error
PayCosts(s *State) bool
+ Resolve(s *State)
String() string
}
-type PassPriority struct{ Source *Player }
+type PassPriority struct{ source *Player }
-func (*PassPriority) Resolve(*State) {}
-func (*PassPriority) PayCosts(*State) bool { return true }
-func (*PassPriority) String() string { return "pass" }
-func NewPassPriority(p *Player) Action { return &PassPriority{p} }
+func (a *PassPriority) Source() interface{} { return a.source }
+func (*PassPriority) Targets() *Targets { 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
-}
+type HandCardSelection struct{ cards []*Card }
-func (*HandCardSelection) Resolve(*State) {}
-func (*HandCardSelection) PayCosts(*State) bool { return true }
-func (*HandCardSelection) String() string { return "hand card selection" }
-func NewHandCardSelection(cards []*Card) Action { return &HandCardSelection{cards} }
+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 ActionBase struct {
- Source interface{}
+ source interface{}
Card *Card
- Targets interface{}
- resolveFunc func(s *State)
- costFunc func(s *State) bool
- desc string
+ targets *Targets
+ resolveFunc ActionResolveFunc
+ costFunc ActionCostFunc
+}
+
+func (a *ActionBase) Source() interface{} {
+ return a.source
}
func (a *ActionBase) Resolve(s *State) {
@@ -48,73 +60,91 @@ func (a *ActionBase) PayCosts(s *State) bool {
return a.costFunc(s)
}
-func (a *ActionBase) String() string {
- switch source := a.Source.(type) {
- case Permanent:
- p := source
- return fmt.Sprintf("%s: %s %s", p.Controller().Name, p.Tile().Position.String(), a.desc)
- case *Player:
- return fmt.Sprintf("%s %s", source.Name, a.desc)
- default:
- return fmt.Sprintf("%v %s", source, a.desc)
- }
+func (a *ActionBase) Targets() *Targets {
+ return a.targets
+}
+
+func (a *ActionBase) CheckTargets(s *State) error {
+ return a.targets.CheckTargets(s)
}
type PlayAction struct {
ActionBase
}
-func NewPlayAction(p *Player, c *Card, targets interface{}) Action {
- costFunc := func(*State) bool {
- cost := c.PlayCosts()
+const permanentPlayActionTarget = "available spawn tile"
- if p.Resource < cost {
- return false
+func NewPlayAction(p *Player, c *Card, args ...interface{}) *PlayAction {
+ a := &PlayAction{ActionBase{
+ source: p,
+ Card: c,
+ }}
+
+ if len(args) > 0 {
+ if costFunc, ok := args[0].(ActionCostFunc); ok {
+ a.costFunc = costFunc
}
- p.Resource -= cost
- return true
}
- return NewPlayActionWithSpecialCosts(p, c, targets, costFunc)
-}
-func NewPlayActionWithSpecialCosts(p *Player, c *Card, targets interface{},
- costFunc func(*State) bool,
-) Action {
+ if a.costFunc == nil {
+ a.costFunc = func(*State) bool {
+ cost := c.PlayCosts()
+
+ if p.Resource < cost {
+ return false
+ }
+ p.Resource -= cost
+ return true
+ }
+ }
+
+ s := p.gameState
+ if c.IsPermanent() {
+ a.targets = newTargets(s, []string{permanentPlayActionTarget}, a)
+ } else {
+ a.targets = newTargets(s, []string{}, a)
+ }
+
p.Hand.RemoveCard(c)
- resolveFunc := func(s *State) {
- s.ResolvePlay(p, c, targets)
+ a.resolveFunc = func(s *State) {
+ s.resolvePlay(p, c, a.targets)
}
- desc := fmt.Sprintf("play %s@%v", c.Name, targets)
- return &PlayAction{ActionBase{p, c, targets, resolveFunc, costFunc, desc}}
+ return a
}
-type MoveAction struct {
- ActionBase
-}
+func (a *PlayAction) String() string {
+ p := a.source.(*Player)
+ if a.targets.RequireTargets() {
+ return fmt.Sprintf(" %s play %s", p.Name, a.Card.Name)
+ }
-type MoveActionPrototype struct {
- Unit *Unit
+ return fmt.Sprintf("%s play %s@%v", p.Name, a.Card.Name, a.targets)
}
-func NewMoveActionPrototype(u *Unit) *MoveActionPrototype {
- return &MoveActionPrototype{u}
+type MoveAction struct {
+ ActionBase
}
-func (proto *MoveActionPrototype) String() string {
- return fmt.Sprintf("move %s", proto.Unit.Movement.String())
-}
+const moveActionTargetDesc = "available tile"
+
+func NewMoveAction(u *Unit) *MoveAction {
+ a := &MoveAction{
+ ActionBase{
+ source: u,
+ Card: u.Card(),
+ },
+ }
-func (proto *MoveActionPrototype) Specialize(s *State, args ...interface{}) Action {
- target := args[0].(*Tile)
- u := proto.Unit
+ a.targets = newTargets(u.Controller().gameState, []string{moveActionTargetDesc}, a)
- resolveFunc := func(s *State) {
- u.move(s, target)
+ a.resolveFunc = func(s *State) {
+ tile := a.targets.sel[0].(*Tile)
+ u.move(s, tile)
}
- costFunc := func(*State) bool {
+ a.costFunc = func(*State) bool {
if u.AvailMoveActions < 1 {
return false
}
@@ -122,35 +152,38 @@ func (proto *MoveActionPrototype) Specialize(s *State, args ...interface{}) Acti
return true
}
- desc := fmt.Sprintf("-> %s", target.Position.String())
- return &MoveAction{ActionBase{u, u.Card(), target, resolveFunc, costFunc, desc}}
+ return a
}
-type AttackAction struct {
- ActionBase
-}
+func (a *MoveAction) String() string {
+ u := a.source.(*Unit)
+ if a.targets.RequireTargets() {
+ return fmt.Sprintf("move %s", u.Movement.String())
+ }
-type AttackActionPrototype struct {
- Unit *Unit
+ t := a.targets.sel[0].(*Tile)
+ return fmt.Sprintf("%s: %s -> %s", u.Controller().Name, u.Card().Name, t.Position.String())
}
-func NewAttackActionPrototype(u *Unit) *AttackActionPrototype {
- return &AttackActionPrototype{u}
+type AttackAction struct {
+ ActionBase
}
-func (proto *AttackActionPrototype) String() string {
- return fmt.Sprintf("attack %s", proto.Unit.Attack.String())
-}
+const attackActionTargetDesc = "attackable permanent"
+
+func NewAttackAction(u *Unit) *AttackAction {
+ a := &AttackAction{ActionBase{
+ source: u,
+ Card: u.Card(),
+ }}
-func (proto *AttackActionPrototype) Specialize(s *State, args ...interface{}) Action {
- target := args[0].(Permanent)
- u := proto.Unit
+ a.targets = newTargets(u.Controller().gameState, []string{attackActionTargetDesc}, a)
- resolveFunc := func(s *State) {
- s.Fight(u, target)
+ a.resolveFunc = func(s *State) {
+ s.Fight(u, a.targets.sel[0].(Permanent))
}
- costFunc := func(*State) bool {
+ a.costFunc = func(*State) bool {
if u.AvailAttackActions < 1 {
return false
}
@@ -158,27 +191,41 @@ func (proto *AttackActionPrototype) Specialize(s *State, args ...interface{}) Ac
return true
}
- desc := fmt.Sprintf("x %s", target.Tile().Position.String())
- return &AttackAction{ActionBase{u, u.Card(), target, resolveFunc, costFunc, desc}}
+ return a
}
-type FullActionPrototype struct {
- Unit *Unit
- resolveFuncPrototype func(*State, *Unit) func(*State)
-}
+func (a *AttackAction) String() string {
+ u := a.source.(*Unit)
+ if a.targets.RequireTargets() {
+ return fmt.Sprintf("attack %s", u.Attack.String())
+ }
-func NewFullActionPrototype(u *Unit, proto func(*State, *Unit) func(*State)) *FullActionPrototype {
- return &FullActionPrototype{u, proto}
+ target := a.targets.sel[0].(Permanent)
+ return fmt.Sprintf("%s: %s x %s",
+ u.Controller().Name, u.Card().Name, target.Tile().Position.String())
}
-func (proto *FullActionPrototype) String() string {
- return "full_action"
+type FullAction struct {
+ ActionBase
+ desc string
}
-func (proto *FullActionPrototype) Specialize(s *State, args ...interface{}) Action {
- u := proto.Unit
- resolveFunc := proto.resolveFuncPrototype(s, u)
- costFunc := func(*State) bool {
+func NewFullAction(u *Unit, proto ActionFuncPrototype, desc string) *FullAction {
+ a := &FullAction{
+ ActionBase{
+ source: u,
+ Card: u.Card(),
+ },
+
+ desc,
+ }
+
+ a.resolveFunc = func(s *State) {
+ rf := proto(a)
+ rf(s)
+ }
+
+ a.costFunc = func(*State) bool {
if u.AvailAttackActions < 1 && u.AvailMoveActions < 1 {
return false
}
@@ -186,28 +233,50 @@ func (proto *FullActionPrototype) Specialize(s *State, args ...interface{}) Acti
u.AvailMoveActions--
return true
}
- desc := "full_action"
- return &ActionBase{u, u.Card(), nil, resolveFunc, costFunc, desc}
-}
-
-type FreeActionPrototype struct {
- Permanent Permanent
+ return a
}
-func (proto *FreeActionPrototype) String() string {
- return "free_action"
-}
+func (a *FullAction) String() string {
+ u := a.source.(*Unit)
+ if a.targets.RequireTargets() {
+ return fmt.Sprintf("↻ %s", a.desc)
+ }
-func (proto *FreeActionPrototype) Specialize(s *State, args ...interface{}) Action {
- p := proto.Permanent
- resolveFunc := args[0].(func(s *State))
- costFunc := args[1].(func(s *State) bool)
- desc := "free_action"
- return &FreeAction{ActionBase{p, p.Card(), nil, resolveFunc, costFunc, desc}}
+ return fmt.Sprintf("%s: %s ↻@%s", u.Controller(), u.Card().Name, a.targets)
}
type FreeAction struct {
ActionBase
+ desc string
+}
+
+func NewFreeAction(p Permanent, resolveProto ActionFuncPrototype, costFunc ActionCostFunc,
+ desc string,
+) *FreeAction {
+ a := &FreeAction{
+ ActionBase{
+ source: p,
+ Card: p.Card(),
+ costFunc: costFunc,
+ },
+ desc,
+ }
+
+ a.resolveFunc = func(s *State) {
+ rf := resolveProto(a)
+ rf(s)
+ }
+
+ return a
+}
+
+func (a *FreeAction) String() string {
+ p := a.source.(Permanent)
+ if a.targets.RequireTargets() {
+ return fmt.Sprintf("free_action %s", a.desc)
+ }
+
+ return fmt.Sprintf("%s: %s free_action@%s", p.Controller(), p.Card().Name, a.targets)
}
type BuyAction struct {
@@ -215,6 +284,18 @@ type BuyAction struct {
card *Card
}
+func (a *BuyAction) Source() interface{} {
+ return a.player
+}
+
+func (a *BuyAction) Targets() *Targets {
+ return newTargetsWithSel(a.player.gameState, []string{"store cards"}, a, []interface{}{a.card})
+}
+
+func (a *BuyAction) CheckTargets(s *State) error {
+ return s.isValidBuy(a)
+}
+
func (a *BuyAction) Resolve(s *State) {
a.player.Store.MoveCard(a.card, a.player.DiscardPile)
}