aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-04-03 16:47:42 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2025-01-27 16:43:53 +0100
commit011b95e8db31f1306eafb575ed12bed4e2fc1ca3 (patch)
tree44b007c1324e99e26014bab45faefa755c6ab38d
parent4d33d8b637211aaa3e0adfda68e363e64f90623f (diff)
downloadmuhqs-game-011b95e8db31f1306eafb575ed12bed4e2fc1ca3.tar.gz
muhqs-game-011b95e8db31f1306eafb575ed12bed4e2fc1ca3.zip
implement equipment with next unit play option
Also make more state methods private.
-rw-r--r--go/TODO1
-rw-r--r--go/game/action.go15
-rw-r--r--go/game/areaEffect_test.go6
-rw-r--r--go/game/cardImplementations.go6
-rw-r--r--go/game/equipment.go12
-rw-r--r--go/game/kraken.go2
-rw-r--r--go/game/state.go84
-rw-r--r--go/game/targets_test.go4
-rw-r--r--go/game/unit_test.go6
9 files changed, 82 insertions, 54 deletions
diff --git a/go/TODO b/go/TODO
index 2f43d6a9..a4f57d26 100644
--- a/go/TODO
+++ b/go/TODO
@@ -1,6 +1,5 @@
* Fix data race between UI and game loop (possible big state lock)
* implement representation of next target and current seelction in prompt
-* implement equipment play equipped to next unit
* implement equipment actions
* drop
* implement Pile UI hint
diff --git a/go/game/action.go b/go/game/action.go
index 271f5f51..fbabb06b 100644
--- a/go/game/action.go
+++ b/go/game/action.go
@@ -127,7 +127,10 @@ type PlayAction struct {
ActionBase
}
-var permanentPlayActionTarget = TargetDesc{"available spawn tile", "1"}
+var (
+ permanentPlayActionTarget = TargetDesc{"available spawn tile", "1"}
+ equipmentPlayActionTarget = TargetDesc{"available spawn tile", "?"}
+)
func NewPlayAction(p *Player, c *Card, args ...interface{}) *PlayAction {
a := &PlayAction{ActionBase{
@@ -155,7 +158,11 @@ func NewPlayAction(p *Player, c *Card, args ...interface{}) *PlayAction {
s := p.gameState
if c.IsPermanent() {
- a.targets = newTargets(newTarget(s, permanentPlayActionTarget, a))
+ if c.Type == CardTypes.Equipment {
+ a.targets = newTargets(newTarget(s, equipmentPlayActionTarget, a))
+ } else {
+ a.targets = newTargets(newTarget(s, permanentPlayActionTarget, a))
+ }
} else if targetDesc := c.Impl.playTargets(); targetDesc != INVALID_TARGET_DESC {
a.targets = newTargets(newTarget(s, targetDesc, a))
} else {
@@ -175,6 +182,10 @@ func (a *PlayAction) String() string {
return fmt.Sprintf(" %s play %s", p.Name, a.Card.Name)
}
+ if a.Card.Type == CardTypes.Equipment && len(a.Target().sel) == 0 {
+ return fmt.Sprintf("%s play %s@next unit", p.Name, a.Card.Name)
+ }
+
return fmt.Sprintf("%s play %s@%v", p.Name, a.Card.Name, a.targets)
}
diff --git a/go/game/areaEffect_test.go b/go/game/areaEffect_test.go
index 8adca76a..ab058f8a 100644
--- a/go/game/areaEffect_test.go
+++ b/go/game/areaEffect_test.go
@@ -33,7 +33,7 @@ symbols:
))
palisade := NewUnit(NewCard("base/archer"), t0_0, p)
- s.AddPermanent(palisade)
+ s.addPermanent(palisade)
if !entered0_0 || palisade.Tile() != t0_0 || t0_0.Permanent != palisade {
t.Fatalf("palisade did not enter tile %v", t0_0)
}
@@ -45,7 +45,7 @@ symbols:
entered0_0, left0_0 = false, false
archer := NewUnit(NewCard("base/archer"), t0_0, p)
- s.AddPermanent(archer)
+ s.addPermanent(archer)
if !entered0_0 || archer.Tile() != t0_0 || t0_0.Permanent != archer {
t.Fatalf("archer did not enter tile %v", t0_0)
}
@@ -65,5 +65,5 @@ symbols:
}
sword := newEquipmentFromPath("base/sword", t, p)
- s.AddPermanent(sword)
+ s.addPermanent(sword)
}
diff --git a/go/game/cardImplementations.go b/go/game/cardImplementations.go
index e33906f6..b015ace8 100644
--- a/go/game/cardImplementations.go
+++ b/go/game/cardImplementations.go
@@ -140,7 +140,7 @@ func (*recruiterImpl) fullActions(u *Unit) []*FullAction {
u := a.Source().(*Unit)
return func(s *State) {
t := a.Target().sel[0].(*Tile)
- s.AddNewUnit(NewCard("base/recruit"), t.Position, u.Controller())
+ s.addNewUnit(NewCard("base/recruit"), t.Position, u.Controller())
}
}
@@ -370,7 +370,7 @@ func (*fisherImpl) fullActions(u *Unit) []*FullAction {
u := a.Source().(*Unit)
return func(s *State) {
t := a.Target().sel[0].(*Tile)
- s.AddNewArtifact(NewCard("nautics/fish_trap"), t.Position, u.Controller())
+ s.addNewArtifact(NewCard("nautics/fish_trap"), t.Position, u.Controller())
}
}
a := newFullAction(u, resolvePrototype, "create fish trap")
@@ -537,7 +537,7 @@ func (*giganticHailImpl) onPlay(s *State, _ *Card, kraken *Player, _ *Targets) {
}
choice := s.Rand.Intn(choices - 1)
- s.AddNewArtifact(NewCard("kraken/ice_berg"), emptyTiles[choice].Position, kraken)
+ s.addNewArtifact(NewCard("kraken/ice_berg"), emptyTiles[choice].Position, kraken)
emptyTiles[choice] = emptyTiles[choices-1]
choices--
}
diff --git a/go/game/equipment.go b/go/game/equipment.go
index 9e9860e8..32bb358c 100644
--- a/go/game/equipment.go
+++ b/go/game/equipment.go
@@ -16,11 +16,13 @@ func newEquipment(card *Card, containing interface{}, owner *Player) *Equipment
card.Values["durability"].(int),
}
- switch containing := containing.(type) {
- case *Tile:
- e.tile = containing
- case *Unit:
- addPermanentToPile(containing, e)
+ if containing != nil {
+ switch containing := containing.(type) {
+ case *Tile:
+ e.tile = containing
+ case *Unit:
+ addPermanentToPile(containing, e)
+ }
}
return e
diff --git a/go/game/kraken.go b/go/game/kraken.go
index 8c82fc9d..e5db4649 100644
--- a/go/game/kraken.go
+++ b/go/game/kraken.go
@@ -39,7 +39,7 @@ func NewKraken(id int, s *State) *Player {
log.Fatal("No kraken tile found in Map")
}
- s.AddPermanent(NewUnitFromPath("kraken/the_kraken", kraken_tile[0], kraken))
+ s.addPermanent(NewUnitFromPath("kraken/the_kraken", kraken_tile[0], kraken))
addKrakenControl(kraken)
diff --git a/go/game/state.go b/go/game/state.go
index 604e4530..87e1ee3d 100644
--- a/go/game/state.go
+++ b/go/game/state.go
@@ -12,20 +12,21 @@ import (
)
type State struct {
- Decks []*Deck
- Stores []*Store
- Exile *PileOfCardsBase
- Map *Map
- Stack *Stack
- Hands []*Hand
- DiscardPiles []*DiscardPile
- Players []*Player
- ActivePlayer *Player
- ActivePhase PhaseType
- Permanents []Permanent
- Units []*Unit
- Rand *rand.Rand
- EotEffects []effect
+ Decks []*Deck
+ Stores []*Store
+ Exile *PileOfCardsBase
+ Map *Map
+ Stack *Stack
+ Hands []*Hand
+ DiscardPiles []*DiscardPile
+ Players []*Player
+ ActivePlayer *Player
+ ActivePhase PhaseType
+ Permanents []Permanent
+ Units []*Unit
+ Rand *rand.Rand
+ EotEffects []effect
+ outstandingEquipment []*Card
}
func NewState() *State {
@@ -114,6 +115,11 @@ func (s *State) IsValidPlay(a *PlayAction) error {
}
if a.Card.IsPermanent() {
+ // Equipments may be played not on a spawn tile but equiped to the next played unit
+ if a.Card.Type == CardTypes.Equipment && len(a.Target().sel) == 0 {
+ return nil
+ }
+
spawn := a.Target().sel[0].(*Tile)
if !slices.Contains(s.AvailableSpawnTiles(a.Source().(*Player), a.Card), spawn) {
return fmt.Errorf("Spawn %v is not a valid spawn tile for %s",
@@ -354,7 +360,7 @@ func (s *State) broadcastNotification(n PlayerNotification) {
}
}
-func (s *State) AddPermanent(perm Permanent) {
+func (s *State) addPermanent(perm Permanent) {
s.Permanents = append(s.Permanents, perm)
switch p := perm.(type) {
case *Unit:
@@ -366,26 +372,22 @@ func (s *State) AddPermanent(perm Permanent) {
}
}
-func (s *State) AddNewUnit(card *Card, pos Position, owner *Player) {
- s.AddPermanent(NewUnit(card, s.Map.TileAt(pos), owner))
-}
-
-func (s *State) AddNewArtifact(card *Card, pos Position, owner *Player) {
- s.AddPermanent(NewArtifact(card, s.Map.TileAt(pos), owner))
+func (s *State) addNewUnit(card *Card, pos Position, owner *Player) *Unit {
+ u := NewUnit(card, s.Map.TileAt(pos), owner)
+ s.addPermanent(u)
+ return u
}
-func (s *State) addNewEquipment(card *Card, pos Position, owner *Player) {
- s.AddPermanent(newEquipment(card, s.Map.TileAt(pos), owner))
+func (s *State) addNewArtifact(card *Card, pos Position, owner *Player) *Artifact {
+ a := NewArtifact(card, s.Map.TileAt(pos), owner)
+ s.addPermanent(a)
+ return a
}
-func (s *State) AddNewPermFromPath(cardPath string, pos Position, owner *Player) {
- card := NewCard(cardPath)
- switch card.Type {
- case CardTypes.Unit:
- s.AddNewUnit(card, pos, owner)
- case CardTypes.Artifact:
- s.AddNewArtifact(card, pos, owner)
- }
+func (s *State) addNewEquipment(card *Card, pos Position, owner *Player) *Equipment {
+ e := newEquipment(card, s.Map.TileAt(pos), owner)
+ s.addPermanent(e)
+ return e
}
func (s *State) removePermanent(p Permanent) {
@@ -470,12 +472,21 @@ func (s *State) resolvePlay(p *Player, c *Card, targets *Targets) {
switch c.Type {
case CardTypes.Unit:
tile := targets.ts[0].sel[0].(*Tile)
- s.AddNewUnit(c, tile.Position, p)
+ u := s.addNewUnit(c, tile.Position, p)
+ if len(s.outstandingEquipment) > 0 {
+ for _, e := range s.outstandingEquipment {
+ u.equip(newEquipment(e, nil, p))
+ }
+ s.outstandingEquipment = nil
+ }
case CardTypes.Artifact:
tile := targets.ts[0].sel[0].(*Tile)
- s.AddNewArtifact(c, tile.Position, p)
+ s.addNewArtifact(c, tile.Position, p)
case CardTypes.Equipment:
- if tile, ok := targets.ts[0].sel[0].(*Tile); ok {
+ if len(targets.ts[0].sel) == 0 {
+ s.outstandingEquipment = append(s.outstandingEquipment, c)
+ } else {
+ tile, _ := targets.ts[0].sel[0].(*Tile)
s.addNewEquipment(c, tile.Position, p)
}
case CardTypes.Spell:
@@ -584,6 +595,11 @@ func (s *State) addEotEffect(e effect) {
}
func (s *State) endOfTurn() {
+ // Move all outstanding equipment cards to the discard pile
+ for _, e := range s.outstandingEquipment {
+ s.ActivePlayer.DiscardPile.AddCard(e)
+ }
+
for _, e := range s.EotEffects {
e.end(s)
}
diff --git a/go/game/targets_test.go b/go/game/targets_test.go
index be1cdad9..fd93ee95 100644
--- a/go/game/targets_test.go
+++ b/go/game/targets_test.go
@@ -25,10 +25,10 @@ symbols:
p := s.Players[0]
pioneer := NewUnit(NewCard("base/pioneer"), s.Map.TileAt(Position{1, 1}), p)
- s.AddPermanent(pioneer)
+ s.addPermanent(pioneer)
sword := newEquipmentFromPath("base/sword", s.Map.TileAt(Position{0, 1}), p)
- s.AddPermanent(sword)
+ s.addPermanent(sword)
fa := pioneer.FullActions[0]
targets := fa.Targets()
diff --git a/go/game/unit_test.go b/go/game/unit_test.go
index 4cfcdfb9..deaeb5f6 100644
--- a/go/game/unit_test.go
+++ b/go/game/unit_test.go
@@ -25,13 +25,13 @@ symbols:
p := s.Players[0]
f := NewUnit(NewCard("nautics/fisher"), s.Map.TileAt(Position{2, 2}), p)
- s.AddPermanent(f)
+ s.addPermanent(f)
a := NewUnit(NewCard("base/archer"), s.Map.TileAt(Position{1, 1}), p)
- s.AddPermanent(a)
+ s.addPermanent(a)
sword := newEquipmentFromPath("base/sword", s.Map.TileAt(Position{0, 1}), p)
- s.AddPermanent(sword)
+ s.addPermanent(sword)
movePermanent(a, f.Tile())
if len(f.Pile()) != 1 {