aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go/game/artifact.go5
-rw-r--r--go/game/attack.go2
-rw-r--r--go/game/card.go2
-rw-r--r--go/game/cardParsing.go7
-rw-r--r--go/game/permanent.go52
-rw-r--r--go/game/tile.go20
-rw-r--r--go/game/unit.go56
-rw-r--r--go/game/unit_test.go60
8 files changed, 178 insertions, 26 deletions
diff --git a/go/game/artifact.go b/go/game/artifact.go
index acbaeb74..916917d8 100644
--- a/go/game/artifact.go
+++ b/go/game/artifact.go
@@ -18,7 +18,10 @@ func NewArtifactFromPath(cardPath string, tile *Tile, owner *Player) *Artifact {
func NewArtifact(card *Card, tile *Tile, owner *Player) *Artifact {
a := &Artifact{NewPermanentBase(card, tile, owner), 0}
- tile.Permanent = a
+
+ if tile != nil {
+ tile.Permanent = a
+ }
effects := card.getEffects()
for _, e := range effects {
diff --git a/go/game/attack.go b/go/game/attack.go
index 35af7ff2..0d3bef7c 100644
--- a/go/game/attack.go
+++ b/go/game/attack.go
@@ -12,7 +12,7 @@ type Attack struct {
}
func INVALID_ATTACK(a Attack) bool {
- return a.attacks == nil
+ return a.attacks == nil || len(a.attacks) == 0
}
func (a Attack) MaxRange() int {
diff --git a/go/game/card.go b/go/game/card.go
index 23562f0c..81464cce 100644
--- a/go/game/card.go
+++ b/go/game/card.go
@@ -77,6 +77,7 @@ type cardImplementation interface {
freeActions(Permanent) []*FreeAction
onPile(Permanent)
+ onUnpile(Permanent)
onDrop(Permanent)
}
@@ -90,6 +91,7 @@ func (*cardImplementationBase) spawnTiles(*State, *Player) []*Tile { return nil
func (*cardImplementationBase) fullActions(*Unit) []*FullAction { return nil }
func (*cardImplementationBase) freeActions(Permanent) []*FreeAction { return nil }
func (*cardImplementationBase) onPile(Permanent) {}
+func (*cardImplementationBase) onUnpile(Permanent) {}
func (*cardImplementationBase) onDrop(Permanent) {}
// Map of all card implementations
diff --git a/go/game/cardParsing.go b/go/game/cardParsing.go
index ad0b6ea8..dd64c7b8 100644
--- a/go/game/cardParsing.go
+++ b/go/game/cardParsing.go
@@ -13,6 +13,7 @@ type parsedCardImplementation struct {
genFullActions func(*Unit) []*FullAction
genFreeActions func(Permanent) []*FreeAction
_onPile func(Permanent)
+ _onUnpile func(Permanent)
_onDrop func(Permanent)
}
@@ -43,6 +44,12 @@ func (impl *parsedCardImplementation) onPile(p Permanent) {
}
}
+func (impl *parsedCardImplementation) onUnpile(p Permanent) {
+ if impl._onUnpile != nil {
+ impl._onUnpile(p)
+ }
+}
+
func (impl *parsedCardImplementation) onDrop(p Permanent) {
if impl._onDrop != nil {
impl._onDrop(p)
diff --git a/go/game/permanent.go b/go/game/permanent.go
index 36c5e1ba..91aa0cda 100644
--- a/go/game/permanent.go
+++ b/go/game/permanent.go
@@ -28,19 +28,23 @@ type Permanent interface {
AvailActions() []Action
IsAvailableTile(*Tile) bool
+ SetContainingPerm(p Permanent)
AddPermanentToPile(p Permanent)
- OnPile(containing Permanent)
- OnDrop(containing Permanent)
+ RemovePermanentFromPile(p Permanent)
+ onPile(containing Permanent)
+ onUnpile(containing Permanent)
+ onDrop(containing Permanent)
}
type PermanentBase struct {
- card *Card
- tile *Tile
- damage int
- pile []Permanent
- controller *Player
- owner *Player
- tmpEffects []string
+ card *Card
+ tile *Tile
+ damage int
+ pile []Permanent
+ containingPerm Permanent
+ controller *Player
+ owner *Player
+ tmpEffects []string
}
func NewPermanentBase(card *Card, tile *Tile, owner *Player) PermanentBase {
@@ -134,13 +138,37 @@ func (p *PermanentBase) IsAvailableTile(tile *Tile) bool {
return tile.IsAvailableForCard(p.card)
}
+func (p *PermanentBase) SetContainingPerm(containing Permanent) {
+ p.containingPerm = containing
+}
+
func (p *PermanentBase) AddPermanentToPile(a Permanent) {
p.pile = append(p.pile, a)
}
-func (b *PermanentBase) OnPile(p Permanent) { p.Card().Impl.onPile(p) }
-func (b *PermanentBase) OnDrop(p Permanent) { p.Card().Impl.onDrop(p) }
+
+func (p *PermanentBase) RemovePermanentFromPile(rm Permanent) {
+ for i, piled := range p.pile {
+ if piled != rm {
+ continue
+ }
+
+ p.pile[i] = p.pile[len(p.pile)-1]
+ p.pile = p.pile[:len(p.pile)-1]
+ return
+ }
+}
+func (b *PermanentBase) onPile(p Permanent) { p.Card().Impl.onPile(p) }
+func (b *PermanentBase) onUnpile(p Permanent) { p.Card().Impl.onUnpile(p) }
+func (b *PermanentBase) onDrop(p Permanent) { p.Card().Impl.onDrop(p) }
func AddPermanentToPile(containing, p Permanent) {
+ p.SetContainingPerm(containing)
containing.AddPermanentToPile(p)
- p.OnPile(p)
+ p.onPile(p)
+}
+
+func RemovePermanentFromPile(containing, rm Permanent) {
+ containing.RemovePermanentFromPile(rm)
+ rm.onUnpile(containing)
+ rm.SetContainingPerm(nil)
}
diff --git a/go/game/tile.go b/go/game/tile.go
index ba600c66..3f9f9f5f 100644
--- a/go/game/tile.go
+++ b/go/game/tile.go
@@ -105,13 +105,27 @@ func (t *Tile) IsFree() bool {
}
func (t *Tile) IsAvailableForCard(card *Card) bool {
- if t.Water && !card.hasPlacementConstrain("swimming") {
+ if t.IsFree() {
+ if t.Water && !card.hasPlacementConstrain("swimming") {
+ return false
+ }
+ if !t.Water && card.hasPlacementConstrain("swimming") {
+ return false
+ }
+
+ return true
+ }
+
+ x, err := t.Permanent.Card().getXEffect("crew")
+ if err != nil {
return false
}
- if !t.Water && card.hasPlacementConstrain("swimming") {
+
+ if len(t.Permanent.Pile()) == x {
return false
}
- return t.IsFree()
+
+ return true
}
func (t *Tile) OnDiagonal(other *Tile) bool {
diff --git a/go/game/unit.go b/go/game/unit.go
index fba85f30..a0d6bfa4 100644
--- a/go/game/unit.go
+++ b/go/game/unit.go
@@ -125,9 +125,25 @@ func (u *Unit) onUpkeep() {
}
func (u *Unit) move(s *State, tile *Tile) {
- u.tile.Permanent = nil
- tile.Permanent = u
- u.tile = tile
+ if !u.IsAvailableTile(tile) {
+ log.Fatalf("moveing to not available tile %v", tile)
+ }
+
+ if u.tile != nil {
+ u.tile.Permanent = nil
+ } else if u.containingPerm != nil {
+ RemovePermanentFromPile(u.containingPerm, u)
+ } else {
+ log.Fatalf("unit has no current location")
+ }
+
+ if tile.Permanent == nil {
+ tile.Permanent = u
+ u.tile = tile
+ } else { // crew permanent at tile
+ AddPermanentToPile(tile.Permanent, u)
+ u.tile = nil
+ }
}
func (u *Unit) MoveRangeTiles(m *Map) []*Tile {
@@ -226,14 +242,36 @@ func (u *Unit) AvailActions() (actions []Action) {
return actions
}
-func (u *Unit) OnPile(containing Permanent) {
- if _, ok := containing.(*Unit); ok {
- // TODO: add attack
+func (u *Unit) onPile(containing Permanent) {
+ u.PermanentBase.onPile(u)
+ if baseUnit, ok := containing.(*Unit); ok {
+ baseUnitAttackRange := baseUnit.Attack.MaxRange()
+ for r, a := range u.Attack.attacks {
+ if r > baseUnitAttackRange-1 {
+ baseUnit.Attack.attacks = append(baseUnit.Attack.attacks, a)
+ } else {
+ baseUnit.Attack.attacks[r] += a
+ }
+ }
}
}
-func (u *Unit) OnDrop(containing Permanent) {
- if _, ok := containing.(*Unit); ok {
- // TODO: remove attack
+func (u *Unit) onUnpile(containing Permanent) {
+ u.PermanentBase.onUnpile(u)
+ if baseUnit, ok := containing.(*Unit); ok {
+ for r, a := range baseUnit.Attack.attacks {
+ if a == u.Attack.attacks[r] {
+ // Remove all further attacks since they must all be from the
+ // dropped unit
+ baseUnit.Attack.attacks = baseUnit.Attack.attacks[:r]
+ break
+ } else {
+ baseUnit.Attack.attacks[r] -= u.Attack.attacks[r]
+ }
+ }
}
}
+
+func (u *Unit) onDrop(containing Permanent) {
+ u.onUnpile(containing)
+}
diff --git a/go/game/unit_test.go b/go/game/unit_test.go
new file mode 100644
index 00000000..341fb350
--- /dev/null
+++ b/go/game/unit_test.go
@@ -0,0 +1,60 @@
+package game
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestCrew(t *testing.T) {
+ mapDef := `map: |1-
+ HST
+ HSF
+ TSW
+symbols:
+ T: tower
+ H: house
+ F: farm
+ S: street
+ W: deep water
+`
+ s := NewState()
+ r := strings.NewReader(mapDef)
+ s.Map, _ = readMap(r)
+ f := NewUnit(NewCard("nautics/fisher"), s.Map.TileAt(Position{2, 2}), nil)
+ a := NewUnit(NewCard("base/archer"), s.Map.TileAt(Position{1, 1}), nil)
+ sword := newEquipment(NewCard("base/sword"), s.Map.TileAt(Position{0, 1}), nil)
+
+ a.move(s, f.Tile())
+ if len(f.Pile()) != 1 {
+ t.Fatalf("fisher's pile size is not 1")
+ }
+
+ if f.Pile()[0] != a {
+ t.Fatalf("fisher's pile does not contain the archer")
+ }
+
+ if a.Tile() != nil {
+ t.Fatalf("piled archer has still a tile %v", a.Tile())
+ }
+
+ for r, d := range f.Attack.attacks {
+ if d != a.Attack.attacks[r] {
+ t.Fatalf("fisher's attack %d is not equal to the archer's attack %d at range %d",
+ d, a.Attack.attacks[r], r)
+ }
+ }
+
+ a.move(s, s.Map.TileAt(Position{1, 1}))
+ if len(f.Pile()) != 0 {
+ t.Fatalf("fisher's pile is not empty")
+ }
+
+ if !INVALID_ATTACK(f.Attack) {
+ t.Fatalf("fisher has still a valid atack")
+ }
+
+ AddPermanentToPile(a, sword)
+ if a.Attack.attacks[0] != 2 {
+ t.Fatalf("archer's mele attack is not 2")
+ }
+}