package game import ( "fmt" ) type ( ActionCostFunc func(*State) bool ActionResolveFunc func(*State) ActionFuncPrototype func(Action) ActionResolveFunc ) type Action interface { Source() interface{} Targets() *Targets CheckTargets(*State) error PayCosts(s *State) bool Resolve(s *State) String() string } type PassPriority struct{ source *Player } 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 } 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{} Card *Card targets *Targets resolveFunc ActionResolveFunc 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) CheckTargets(s *State) error { return a.targets.CheckTargets(s) } type PlayAction struct { ActionBase } const permanentPlayActionTarget = "available spawn tile" 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 } } 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) a.resolveFunc = func(s *State) { s.resolvePlay(p, c, a.targets) } return a } func (a *PlayAction) String() string { p := a.source.(*Player) if a.targets.RequireTargets() { return fmt.Sprintf(" %s play %s", p.Name, a.Card.Name) } return fmt.Sprintf("%s play %s@%v", p.Name, a.Card.Name, a.targets) } type MoveAction struct { ActionBase } const moveActionTargetDesc = "available tile" func NewMoveAction(u *Unit) *MoveAction { a := &MoveAction{ ActionBase{ source: u, Card: u.Card(), }, } a.targets = newTargets(u.Controller().gameState, []string{moveActionTargetDesc}, a) a.resolveFunc = func(s *State) { tile := a.targets.sel[0].(*Tile) u.move(s, tile) } a.costFunc = func(*State) bool { if u.AvailMoveActions < 1 { return false } u.AvailMoveActions-- return true } return a } func (a *MoveAction) String() string { u := a.source.(*Unit) if a.targets.RequireTargets() { return fmt.Sprintf("move %s", u.Movement.String()) } t := a.targets.sel[0].(*Tile) return fmt.Sprintf("%v: %v -> %v", u.Controller().Name, u.Card().Name, t.Position) } type AttackAction struct { ActionBase } const attackActionTargetDesc = "attackable enemy permanent" func NewAttackAction(u *Unit) *AttackAction { a := &AttackAction{ActionBase{ source: u, Card: u.Card(), }} a.targets = newTargets(u.Controller().gameState, []string{attackActionTargetDesc}, a) a.resolveFunc = func(s *State) { s.Fight(u, a.targets.sel[0].(Permanent)) } a.costFunc = func(*State) bool { if u.AvailAttackActions < 1 { return false } u.AvailAttackActions-- return true } return a } func (a *AttackAction) String() string { u := a.source.(*Unit) if a.targets.RequireTargets() { 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) } type FullAction struct { ActionBase desc string } 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 } u.AvailAttackActions-- u.AvailMoveActions-- return true } return a } func (a *FullAction) String() string { u := a.source.(*Unit) if a.targets.RequireTargets() { return fmt.Sprintf("↻ %s", a.desc) } return fmt.Sprintf("%v: %s ↻@%v", 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("%v: %s free_action@%v", p.Controller(), p.Card().Name, a.targets) } type BuyAction struct { player *Player 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) } func (a *BuyAction) PayCosts(*State) bool { if a.card.BuyCost < 0 || a.card.BuyCost > a.player.Resource { return false } a.player.Resource -= a.card.BuyCost return true } func (a *BuyAction) String() string { return fmt.Sprintf("%s buy %s", a.player.Name, a.card.Name) } func NewBuyAction(p *Player, c *Card) *BuyAction { return &BuyAction{p, c} } type UpkeepAction struct { player *Player unit *Unit pay bool } func (a *UpkeepAction) Resolve(s *State) { if a.pay { a.player.Resource -= a.unit.Upkeep } else { s.DestroyPermanent(a.unit) } } func (a *UpkeepAction) PayCosts(*State) bool { if a.unit.Upkeep > a.player.Resource { return false } a.player.Resource -= a.unit.Upkeep return true } func (a *UpkeepAction) String() string { if a.pay { return fmt.Sprintf("%s keep %v", a.player.Name, a.unit.Tile().Position) } return fmt.Sprintf("%s disband %v", a.player.Name, a.unit.Tile().Position) } func NewUpkeepAction(p *Player, u *Unit, pay bool) *UpkeepAction { return &UpkeepAction{p, u, pay} }