aboutsummaryrefslogtreecommitdiff
path: root/go/game/action.go
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2022-12-27 14:29:39 +0100
committerFlorian Fischer <florian.fischer@muhq.space>2025-08-20 15:46:38 +0200
commitd6b5eeaa5e639c5ab89b5e1c93075a1b765794dc (patch)
treef87422bb91c2197f88aa8d29092592858764049d /go/game/action.go
parent3c2ee4ee48480a28dc606878503f314ec8862d45 (diff)
downloadmuhqs-game-d6b5eeaa5e639c5ab89b5e1c93075a1b765794dc.tar.gz
muhqs-game-d6b5eeaa5e639c5ab89b5e1c93075a1b765794dc.zip
intermediate commit
Diffstat (limited to 'go/game/action.go')
-rw-r--r--go/game/action.go351
1 files changed, 351 insertions, 0 deletions
diff --git a/go/game/action.go b/go/game/action.go
new file mode 100644
index 00000000..4894d47d
--- /dev/null
+++ b/go/game/action.go
@@ -0,0 +1,351 @@
+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("%s: %s -> %s", u.Controller().Name, u.Card().Name, t.Position.String())
+}
+
+type AttackAction struct {
+ ActionBase
+}
+
+const attackActionTargetDesc = "attackable 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 %s", u.Attack.String())
+ }
+
+ target := a.targets.sel[0].(Permanent)
+ return fmt.Sprintf("%s: %s x %s",
+ u.Controller().Name, u.Card().Name, target.Tile().Position.String())
+}
+
+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("%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 {
+ 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 %s", a.player.Name, a.unit.Tile().Position.String())
+ }
+
+ return fmt.Sprintf("%s disband %s", a.player.Name, a.unit.Tile().Position.String())
+}
+
+func NewUpkeepAction(p *Player, u *Unit, pay bool) *UpkeepAction {
+ return &UpkeepAction{p, u, pay}
+}