aboutsummaryrefslogtreecommitdiff
path: root/go/game/state.go
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-01-31 00:16:53 +0100
committerFlorian Fischer <florian.fischer@muhq.space>2025-01-27 16:43:45 +0100
commite6a1bb4ba06361abdc2cf1bed4041fe3013b188b (patch)
treec329a9d8d071bd2a5e3d6b5bb531da67ece2b227 /go/game/state.go
parent24cea3f7c1e3679ba19ed1d49674e1228eb50ce1 (diff)
downloadmuhqs-game-e6a1bb4ba06361abdc2cf1bed4041fe3013b188b.tar.gz
muhqs-game-e6a1bb4ba06361abdc2cf1bed4041fe3013b188b.zip
intermediate commit
Diffstat (limited to 'go/game/state.go')
-rw-r--r--go/game/state.go60
1 files changed, 36 insertions, 24 deletions
diff --git a/go/game/state.go b/go/game/state.go
index 97283be4..d0a516c9 100644
--- a/go/game/state.go
+++ b/go/game/state.go
@@ -15,6 +15,7 @@ import (
type State struct {
Decks []*Deck
Stores []*Store
+ Exile *PileOfCardsBase
Map *Map
Stack *Stack
Hands []*Hand
@@ -31,6 +32,7 @@ type State struct {
func NewState() *State {
s := &State{}
s.Stack = NewStack(s)
+ s.Exile = NewPileOfCards()
s.Rand = rand.New(rand.NewSource(time.Now().UnixNano()))
return s
}
@@ -122,12 +124,12 @@ func (s *State) IsValidMove(a *MoveAction) error {
if !slices.Contains(u.MoveRangeTiles(s.Map), tile) {
return fmt.Errorf("Unit %s at %s can not move to %s",
- u.Card.Name, u.Tile.Position.String(), tile.Position.String())
+ u.card.Name, u.tile.Position.String(), tile.Position.String())
}
if tile.Permanent != nil {
return fmt.Errorf("Unit %s at %s can not move to occupied %s",
- u.Card.Name, u.Tile.Position.String(), tile.Position.String())
+ u.card.Name, u.tile.Position.String(), tile.Position.String())
}
return nil
@@ -139,17 +141,21 @@ func (s *State) IsValidAttack(a *AttackAction) error {
}
unit := a.Source.(Unit)
+ if unit.Attack == INVALID_ATTACK() {
+ return fmt.Errorf("%s can not attack", unit.card.Name)
+ }
+
target := a.Targets.(Permanent)
if !target.Attackable() {
- return fmt.Errorf("Target %s is not attackable")
+ return fmt.Errorf("Target %s is not attackable", target.Tile().Position.String())
}
- if !IsPositionInRange(unit.Tile.Position, target.GetTile().Position, unit.Attack.Range) {
- return fmt.Errorf("Move actions can only be declared during one's actions phase")
+ if !IsPositionInRange(unit.tile.Position, target.Tile().Position, unit.Attack.Range) {
+ return fmt.Errorf("Attack target on %s is not in attack range %d of %s on %s",
+ target.Tile().Position.String(), unit.Attack.Range, unit.card.Name,
+ unit.tile.Position.String())
}
- // TODO: check move
-
return nil
}
@@ -169,8 +175,8 @@ func (s *State) IsValidBuy(a *BuyAction) error {
controlsUnitOnStore := false
for _, tile := range storeTiles {
controlsUnitOnStore = controlsUnitOnStore || (tile.Permanent != nil &&
- tile.Permanent.GetCard().Type == CardTypes.Unit &&
- tile.Permanent.GetController() == a.player)
+ tile.Permanent.Card().Type == CardTypes.Unit &&
+ tile.Permanent.Controller() == a.player)
}
if !controlsUnitOnStore {
return fmt.Errorf("%s' controls no unit on a store containing %s",
@@ -209,11 +215,14 @@ func (s *State) PushAction(a Action) {
func (s *State) DeclareAction(a Action) {
var err error = nil
- switch a.(type) {
+ switch a := a.(type) {
case *BuyAction:
if s.ActivePhase != Phases.BuyPhase {
err = fmt.Errorf("Cards can only be bought during one's buy phase")
}
+ if !a.card.IsBuyable() {
+ err = fmt.Errorf("Card %s is not buyable", a.card.Name)
+ }
case *FreeAction:
default:
if !s.Stack.IsEmpty() {
@@ -239,7 +248,7 @@ func (s *State) AllPassing(skipFirst bool) bool {
p.Ctrl.SendNotification(NewPriorityNotification())
a := p.Ctrl.RecvAction()
switch a.(type) {
- case *PassPriorityAction:
+ case *PassPriority:
log.Printf("%s passed %d/%d\n", p.Name, i+1, nPlayers)
default:
s.DeclareAction(a)
@@ -284,9 +293,9 @@ func (s *State) AddNewArtifact(card *Card, pos Position, owner *Player) {
func (s *State) AddNewPermFromPath(cardPath string, pos Position, owner *Player) {
card := NewCard(cardPath)
switch card.Type {
- case UnitType:
+ case CardTypes.Unit:
s.AddNewUnit(card, pos, owner)
- case ArtifactType:
+ case CardTypes.Artifact:
s.AddNewArtifact(card, pos, owner)
}
}
@@ -319,7 +328,7 @@ func (s *State) DestroyPermanent(p Permanent) {
case *Artifact:
s.Artifacts = removePtr(s.Artifacts, p)
}
- p.GetOwner().DiscardPile.AddCard(p.GetCard())
+ p.Owner().DiscardPile.AddCard(p.Card())
}
func (s *State) Fight(p1, p2 Permanent) {
@@ -349,22 +358,21 @@ func (s *State) FilterUnits(filter func(*Unit) bool) []*Unit {
func (s *State) OwnUnits(player *Player) []*Unit {
return s.FilterUnits(func(u *Unit) bool {
- return u.GetController() == player
+ return u.Controller() == player
})
}
func (s *State) EnemyUnits(player *Player) []*Unit {
return s.FilterUnits(func(u *Unit) bool {
- // TODO: support coop / teams
- return u.GetController() != player
+ return u.Controller().IsEnemy(player)
})
}
func (s *State) ResolvePlay(p *Player, c *Card, targets interface{}) {
switch c.Type {
- case UnitType:
+ case CardTypes.Unit:
s.AddNewUnit(c, targets.(Position), p)
- case ArtifactType:
+ case CardTypes.Artifact:
s.AddNewArtifact(c, targets.(Position), p)
default:
log.Printf("Resolving unhandled card type: %s\n", c.Type.String())
@@ -419,12 +427,16 @@ func (s *State) AvailableSpawnTiles(p *Player, c *Card) []*Tile {
log.Fatalf("AvailableSpawnTiles called for %s", c.Type.String())
}
- var candidates []*Tile
- if c.hasMovementEffect("swimming") {
- candidates = s.WaterSpawnTiles(p)
- } else {
- candidates = s.SpawnTiles(p)
+ candidates := c.Impl.spawnTiles(s, p)
+ if candidates == nil {
+ // Default spawn tiles
+ if c.hasPlacementConstrain("swimming") {
+ candidates = s.WaterSpawnTiles(p)
+ } else {
+ candidates = s.SpawnTiles(p)
+ }
}
+
tiles := []*Tile{}
for _, candidate := range candidates {