From e6a1bb4ba06361abdc2cf1bed4041fe3013b188b Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Tue, 31 Jan 2023 00:16:53 +0100 Subject: intermediate commit --- go/game/state.go | 60 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 24 deletions(-) (limited to 'go/game/state.go') 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 { -- cgit v1.2.3