diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2023-02-07 00:32:39 +0100 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2023-02-07 00:32:39 +0100 |
| commit | 6bd6936e1aae2fb8d2f5fc95d3c8dcdbe799242d (patch) | |
| tree | 861f7a977c2cd3f015c65c14b5257013650ca71e | |
| parent | 7ebfd783e9b2fbc788dc54b57185f7f41b257f2a (diff) | |
| download | muhqs-game-6bd6936e1aae2fb8d2f5fc95d3c8dcdbe799242d.tar.gz muhqs-game-6bd6936e1aae2fb8d2f5fc95d3c8dcdbe799242d.zip | |
intermediate commit
adding area effects and hooks for permanents entering and leaving tiles
| -rw-r--r-- | go/TODO | 11 | ||||
| -rw-r--r-- | go/game/ai.go | 1 | ||||
| -rw-r--r-- | go/game/areaEffect.go | 27 | ||||
| -rw-r--r-- | go/game/areaEffect_test.go | 69 | ||||
| -rw-r--r-- | go/game/artifact.go | 4 | ||||
| -rw-r--r-- | go/game/card.go | 4 | ||||
| -rw-r--r-- | go/game/cardParsing.go | 40 | ||||
| -rw-r--r-- | go/game/equipment.go | 1 | ||||
| -rw-r--r-- | go/game/permanent.go | 20 | ||||
| -rw-r--r-- | go/game/state.go | 20 | ||||
| -rw-r--r-- | go/game/tile.go | 19 | ||||
| -rw-r--r-- | go/game/unit.go | 12 | ||||
| -rw-r--r-- | go/game/unit_test.go | 6 |
13 files changed, 200 insertions, 34 deletions
diff --git a/go/TODO b/go/TODO new file mode 100644 index 00000000..590bb641 --- /dev/null +++ b/go/TODO @@ -0,0 +1,11 @@ +* implement upkeep action +* implement target selection for triggers +* implement Pile UI hint +* implement spell target parsing / selection +* implement triggers +* implement area of effects +* implement draft +* implement game log +* finish AIs + * shy ai + * target-oriented diff --git a/go/game/ai.go b/go/game/ai.go index 181f6dc1..daeeff4f 100644 --- a/go/game/ai.go +++ b/go/game/ai.go @@ -317,6 +317,7 @@ func WanderingAI(ai *UnitAI, x int) { break } } + // 2. if ai.u.AvailMoveActions > 0 { moveToRandomTile(ai) diff --git a/go/game/areaEffect.go b/go/game/areaEffect.go new file mode 100644 index 00000000..8991bc3c --- /dev/null +++ b/go/game/areaEffect.go @@ -0,0 +1,27 @@ +package game + +type areaEffect interface { + onEntering(Permanent) + onLeaving(Permanent) +} + +type dynamicAreaEffect struct { + _onEntering func(Permanent) + _onLeaving func(Permanent) +} + +func newDynamicAreaEffect(enter func(Permanent), leave func(Permanent)) *dynamicAreaEffect { + return &dynamicAreaEffect{enter, leave} +} + +func (e *dynamicAreaEffect) onEntering(p Permanent) { + if e._onEntering != nil { + e._onEntering(p) + } +} + +func (e *dynamicAreaEffect) onLeaving(p Permanent) { + if e._onLeaving != nil { + e._onLeaving(p) + } +} diff --git a/go/game/areaEffect_test.go b/go/game/areaEffect_test.go new file mode 100644 index 00000000..4f55bffd --- /dev/null +++ b/go/game/areaEffect_test.go @@ -0,0 +1,69 @@ +package game + +import ( + "strings" + "testing" +) + +func TestEnteringLeaving(t *testing.T) { + mapDef := `map: |1- + HH +symbols: + H: house +` + s := NewState() + r := strings.NewReader(mapDef) + s.Map, _ = readMap(r) + + s.AddNewPlayer("player", NewDeck()) + p := s.Players[0] + + t0_0 := s.Map.TileAt(Position{0, 0}) + entered0_0, left0_0 := false, false + t0_0.effects = append(t0_0.effects, newDynamicAreaEffect( + func(Permanent) { entered0_0 = true }, + func(Permanent) { left0_0 = true }, + )) + + t0_1 := s.Map.TileAt(Position{1, 0}) + entered0_1, left0_1 := false, false + t0_1.effects = append(t0_1.effects, newDynamicAreaEffect( + func(Permanent) { entered0_1 = true }, + func(Permanent) { left0_1 = true }, + )) + + palisade := NewUnit(NewCard("base/archer"), t0_0, p) + s.AddPermanent(palisade) + if !entered0_0 || palisade.Tile() != t0_0 || t0_0.Permanent != palisade { + t.Fatalf("palisade did not enter tile %v", t0_0) + } + + s.DestroyPermanent(palisade) + if !left0_0 || palisade.Tile() != nil || t0_0.Permanent != nil { + t.Fatalf("destroyed palisade did not leave tile %v", t0_0) + } + + entered0_0, left0_0 = false, false + archer := NewUnit(NewCard("base/archer"), t0_0, p) + s.AddPermanent(archer) + if !entered0_0 || archer.Tile() != t0_0 || t0_0.Permanent != archer { + t.Fatalf("archer did not enter tile %v", t0_0) + } + + archer.move(s, t0_1) + if !left0_0 || t0_0.Permanent != nil { + t.Fatalf("moved archer did not leave tile %v", t0_0) + } + + if !entered0_1 || archer.Tile() != t0_1 || t0_1.Permanent != archer { + t.Fatalf("moved archer did not leave tile %v", t0_1) + } + + s.DestroyPermanent(archer) + if !left0_1 || archer.Tile() != nil || t0_1.Permanent != nil { + t.Fatalf("destroyed archer did not leave tile %v", t0_1) + } + + sword := newEquipmentFromPath("base/sword", t, p) + s.AddPermanent(sword) +} diff --git a/go/game/artifact.go b/go/game/artifact.go index 916917d8..50fa2653 100644 --- a/go/game/artifact.go +++ b/go/game/artifact.go @@ -19,10 +19,6 @@ 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} - if tile != nil { - tile.Permanent = a - } - effects := card.getEffects() for _, e := range effects { if strings.HasPrefix(e, "Solid") { diff --git a/go/game/card.go b/go/game/card.go index b1d913a8..8b4cd889 100644 --- a/go/game/card.go +++ b/go/game/card.go @@ -76,6 +76,8 @@ type cardImplementation interface { fullActions(*Unit) []*FullAction freeActions(Permanent) []*FreeAction + onEntering(*Tile) + onLeaving(*Tile) onPlay(*State, *Player, *Targets) onPile(Permanent) onUnpile(Permanent) @@ -91,6 +93,8 @@ type cardImplementationBase struct{} func (*cardImplementationBase) spawnTiles(*State, *Player) []*Tile { return nil } func (*cardImplementationBase) fullActions(*Unit) []*FullAction { return nil } func (*cardImplementationBase) freeActions(Permanent) []*FreeAction { return nil } +func (*cardImplementationBase) onEntering(*Tile) {} +func (*cardImplementationBase) onLeaving(*Tile) {} func (*cardImplementationBase) onPlay(*State, *Player, *Targets) {} func (*cardImplementationBase) onPile(Permanent) {} func (*cardImplementationBase) onUnpile(Permanent) {} diff --git a/go/game/cardParsing.go b/go/game/cardParsing.go index 9fcb48aa..df78ade6 100644 --- a/go/game/cardParsing.go +++ b/go/game/cardParsing.go @@ -7,64 +7,78 @@ import ( "strings" ) -type parsedCardImplementation struct { +type dynamicCardImplementation struct { card *Card spawnTilesFunc func(*State, *Player) []*Tile genFullActions func(*Unit) []*FullAction genFreeActions func(Permanent) []*FreeAction + _onEntering func(*Tile) + _onLeaving func(*Tile) _onPlay func(*State, *Player, *Targets) _onPile func(Permanent) _onUnpile func(Permanent) _onDrop func(Permanent) } -func (impl *parsedCardImplementation) spawnTiles(s *State, p *Player) []*Tile { +func (impl *dynamicCardImplementation) spawnTiles(s *State, p *Player) []*Tile { if impl.spawnTilesFunc != nil { return impl.spawnTilesFunc(s, p) } return nil } -func (impl *parsedCardImplementation) fullActions(u *Unit) []*FullAction { +func (impl *dynamicCardImplementation) fullActions(u *Unit) []*FullAction { if impl.genFullActions != nil { return impl.genFullActions(u) } return nil } -func (impl *parsedCardImplementation) freeActions(p Permanent) []*FreeAction { +func (impl *dynamicCardImplementation) freeActions(p Permanent) []*FreeAction { if impl.genFreeActions != nil { return impl.genFreeActions(p) } return nil } -func (impl *parsedCardImplementation) onPlay(s *State, p *Player, t *Targets) { +func (impl *dynamicCardImplementation) onEntering(t *Tile) { + if impl._onEntering != nil { + impl._onEntering(t) + } +} + +func (impl *dynamicCardImplementation) onLeaving(t *Tile) { + if impl._onLeaving != nil { + impl._onLeaving(t) + } +} + +func (impl *dynamicCardImplementation) onPlay(s *State, p *Player, t *Targets) { if impl._onPlay != nil { impl._onPlay(s, p, t) } } -func (impl *parsedCardImplementation) onPile(p Permanent) { +func (impl *dynamicCardImplementation) onPile(p Permanent) { if impl._onPile != nil { impl._onPile(p) } } -func (impl *parsedCardImplementation) onUnpile(p Permanent) { +func (impl *dynamicCardImplementation) onUnpile(p Permanent) { if impl._onUnpile != nil { impl._onUnpile(p) } } -func (impl *parsedCardImplementation) onDrop(p Permanent) { +func (impl *dynamicCardImplementation) onDrop(p Permanent) { if impl._onDrop != nil { impl._onDrop(p) } } -func newParsedCardImplementation(c *Card) *parsedCardImplementation { - impl := &parsedCardImplementation{card: c} +func newParsedCardImplementation(c *Card) *dynamicCardImplementation { + impl := &dynamicCardImplementation{card: c} for _, effect := range c.getEffects() { impl.parseEffect(effect) } @@ -76,13 +90,13 @@ func newParsedCardImplementation(c *Card) *parsedCardImplementation { return impl } -func (impl *parsedCardImplementation) parseEffect(effect string) { +func (impl *dynamicCardImplementation) parseEffect(effect string) { if strings.Contains(effect, "can be played") { impl.parsePlayModification(effect) } } -func (impl *parsedCardImplementation) parseFullAction(fullAction string) { +func (impl *dynamicCardImplementation) parseFullAction(fullAction string) { var resolveProtos []ActionFuncPrototype gainRessourceRegex := regexp.MustCompile(`gain [1-9][0-9]* resource`) @@ -190,7 +204,7 @@ func parseLocation(location string) func(interface{}, *State) []*Tile { return nil } -func (impl *parsedCardImplementation) parsePlayModification(effect string) { +func (impl *dynamicCardImplementation) parsePlayModification(effect string) { tokens := strings.SplitN(effect, " can be played ", 2) log.Println(tokens) what, how := tokens[0], tokens[1] diff --git a/go/game/equipment.go b/go/game/equipment.go index af06fdb1..89821458 100644 --- a/go/game/equipment.go +++ b/go/game/equipment.go @@ -19,7 +19,6 @@ func newEquipment(card *Card, containing interface{}, owner *Player) *Equipment switch containing := containing.(type) { case *Tile: e.tile = containing - containing.Permanent = e case *Unit: AddPermanentToPile(containing, e) } diff --git a/go/game/permanent.go b/go/game/permanent.go index 67c39e0b..596e3c69 100644 --- a/go/game/permanent.go +++ b/go/game/permanent.go @@ -12,6 +12,7 @@ import ( type Permanent interface { Card() *Card Tile() *Tile + SetTile(*Tile) Damage() int Pile() []Permanent Controller() *Player @@ -68,6 +69,10 @@ func (p *PermanentBase) Tile() *Tile { return p.tile } +func (p *PermanentBase) SetTile(t *Tile) { + p.tile = t +} + func (p *PermanentBase) Damage() int { return p.damage } @@ -161,6 +166,7 @@ func (p *PermanentBase) RemovePermanentFromPile(rm Permanent) { return } } + func (b *PermanentBase) onPile(p Permanent) { b.Card().Impl.onPile(p) } func (b *PermanentBase) onUnpile(p Permanent) { b.Card().Impl.onUnpile(p) } func (b *PermanentBase) onDrop(p Permanent) { @@ -179,3 +185,17 @@ func RemovePermanentFromPile(containing, rm Permanent) { rm.onUnpile(containing) rm.SetContainingPerm(nil) } + +func enterTile(p Permanent, t *Tile) { + p.SetTile(t) + p.Card().Impl.onEntering(t) + t.entering(p) +} + +func leaveTile(p Permanent) { + if p.Tile() != nil { + p.Card().Impl.onLeaving(p.Tile()) + p.Tile().leaving(p) + p.SetTile(nil) + } +} diff --git a/go/game/state.go b/go/game/state.go index 87e2133a..01cee403 100644 --- a/go/game/state.go +++ b/go/game/state.go @@ -24,7 +24,6 @@ type State struct { ActivePhase PhaseType Permanents []Permanent Units []*Unit - Artifacts []*Artifact Rand *rand.Rand } @@ -288,8 +287,10 @@ func (s *State) AddPermanent(perm Permanent) { switch p := perm.(type) { case *Unit: s.Units = append(s.Units, p) - case *Artifact: - s.Artifacts = append(s.Artifacts, p) + } + + if perm.Tile() != nil { + enterTile(perm, perm.Tile()) } } @@ -301,6 +302,10 @@ func (s *State) AddNewArtifact(card *Card, pos Position, owner *Player) { s.AddPermanent(NewArtifact(card, s.Map.TileAt(pos), owner)) } +func (s *State) addNewEquipment(card *Card, pos Position, owner *Player) { + s.AddPermanent(newEquipment(card, s.Map.TileAt(pos), owner)) +} + func (s *State) AddNewPermFromPath(cardPath string, pos Position, owner *Player) { card := NewCard(cardPath) switch card.Type { @@ -320,7 +325,7 @@ func (s *State) removePermanent(p Permanent) { } } -func removePtr[P *Unit | *Artifact](collection []P, ptr P) []P { +func removePtr[P *Unit](collection []P, ptr P) []P { for i, p := range collection { if p == ptr { collection[i] = collection[len(collection)-1] @@ -331,13 +336,12 @@ func removePtr[P *Unit | *Artifact](collection []P, ptr P) []P { } func (s *State) DestroyPermanent(p Permanent) { + leaveTile(p) s.removePermanent(p) switch p := p.(type) { case *Unit: s.Units = removePtr(s.Units, p) - case *Artifact: - s.Artifacts = removePtr(s.Artifacts, p) } p.Owner().DiscardPile.AddCard(p.Card()) } @@ -381,6 +385,10 @@ func (s *State) resolvePlay(p *Player, c *Card, targets *Targets) { case CardTypes.Artifact: tile := targets.sel[0].(*Tile) s.AddNewArtifact(c, tile.Position, p) + case CardTypes.Equipment: + if tile, ok := targets.sel[0].(*Tile); ok { + s.addNewEquipment(c, tile.Position, p) + } case CardTypes.Spell: c.Impl.onPlay(s, p, targets) default: diff --git a/go/game/tile.go b/go/game/tile.go index 3f9f9f5f..a7bf2b26 100644 --- a/go/game/tile.go +++ b/go/game/tile.go @@ -77,6 +77,7 @@ type Tile struct { Type TileType Water bool Raw string + effects []areaEffect } func (t *Tile) String() string { @@ -84,7 +85,7 @@ func (t *Tile) String() string { } func INVALID_TILE() Tile { - return Tile{INVALID_POSITION(), nil, 0, false, ""} + return Tile{Position: INVALID_POSITION()} } func NewTileFromString(t string, pos Position) (Tile, error) { @@ -97,7 +98,7 @@ func NewTileFromString(t string, pos Position) (Tile, error) { } } water := strings.Contains(tile, "water") - return Tile{pos, nil, tileType, water, t}, nil + return Tile{Position: pos, Type: tileType, Water: water, Raw: t}, nil } func (t *Tile) IsFree() bool { @@ -135,3 +136,17 @@ func (t *Tile) OnDiagonal(other *Tile) bool { p1.X == p2.X+1 && p1.Y == p2.Y+1 || p1.X == p2.X-1 && p1.Y == p2.Y+1 } + +func (t *Tile) entering(p Permanent) { + t.Permanent = p + for _, effect := range t.effects { + effect.onEntering(p) + } +} + +func (t *Tile) leaving(p Permanent) { + t.Permanent = nil + for _, effect := range t.effects { + effect.onLeaving(p) + } +} diff --git a/go/game/unit.go b/go/game/unit.go index d38d156d..82aa8528 100644 --- a/go/game/unit.go +++ b/go/game/unit.go @@ -107,7 +107,6 @@ func NewUnit(card *Card, tile *Tile, owner *Player) *Unit { u.FullActions = card.Impl.fullActions(u) u.FreeActions = card.Impl.freeActions(u) - tile.Permanent = u return u } @@ -126,20 +125,17 @@ func (u *Unit) onUpkeep() { func (u *Unit) move(s *State, tile *Tile) { if !u.IsAvailableTile(tile) { - log.Fatalf("moveing to not available tile %v", tile) + log.Fatalf("moving to not available tile %v", tile) } - if u.tile != nil { - u.tile.Permanent = nil - } else if u.containingPerm != nil { + if u.containingPerm != nil { RemovePermanentFromPile(u.containingPerm, u) } else { - log.Fatalf("unit has no current location") + leaveTile(u) } if tile.Permanent == nil { - tile.Permanent = u - u.tile = tile + enterTile(u, tile) } else { // crew permanent at tile AddPermanentToPile(tile.Permanent, u) u.tile = nil diff --git a/go/game/unit_test.go b/go/game/unit_test.go index 0f16176c..53b585d8 100644 --- a/go/game/unit_test.go +++ b/go/game/unit_test.go @@ -20,9 +20,15 @@ symbols: s := NewState() r := strings.NewReader(mapDef) s.Map, _ = readMap(r) + f := NewUnit(NewCard("nautics/fisher"), s.Map.TileAt(Position{2, 2}), nil) + s.AddPermanent(f) + a := NewUnit(NewCard("base/archer"), s.Map.TileAt(Position{1, 1}), nil) + s.AddPermanent(a) + sword := newEquipmentFromPath("base/sword", s.Map.TileAt(Position{0, 1}), nil) + s.AddPermanent(sword) a.move(s, f.Tile()) if len(f.Pile()) != 1 { |
