diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2022-12-27 14:29:39 +0100 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-08-20 15:46:38 +0200 |
| commit | d6b5eeaa5e639c5ab89b5e1c93075a1b765794dc (patch) | |
| tree | f87422bb91c2197f88aa8d29092592858764049d /go/game/action.go | |
| parent | 3c2ee4ee48480a28dc606878503f314ec8862d45 (diff) | |
| download | muhqs-game-d6b5eeaa5e639c5ab89b5e1c93075a1b765794dc.tar.gz muhqs-game-d6b5eeaa5e639c5ab89b5e1c93075a1b765794dc.zip | |
intermediate commit
Diffstat (limited to 'go/game/action.go')
| -rw-r--r-- | go/game/action.go | 351 |
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} +} |
