package game import ( "log" ) func adjustMelee(p Permanent, damage int) { if u, ok := p.(*Unit); ok && !INVALID_ATTACK(u.Attack) { u.Attack.attacks[0] += damage } } // ====== Base Set ====== type advisorImpl struct{ cardImplementationBase } func (*advisorImpl) fullActions(u *Unit) []*FullAction { resolvePrototype := func(a Action) ActionResolveFunc { u := a.Source().(*Unit) return func(s *State) { controller := u.Controller() controller.DrawN(1) cards := controller.PromptHandCardSelection(0, 1) if cards != nil { controller.Deck.MoveCard(cards[0], s.Exile) } } } return []*FullAction{NewFullAction(u, resolvePrototype, "draw 1. exile 1")} } type missionaryImpl struct{ cardImplementationBase } func (*missionaryImpl) fullActions(u *Unit) []*FullAction { resolvePrototype := func(a Action) ActionResolveFunc { u := a.Source().(*Unit) target := a.Targets().ts[0].sel[0].(*Unit) return func(s *State) { target.addMarks(UnitMarks.Faith, 2) if target.Marks(UnitMarks.Faith) > target.Card().BuyCost { target.controller = u.Controller() } } } s := u.Controller().gameState a := NewFullAction(u, resolvePrototype, "put faith ...") a.targets = newTargets(newTarget(s, newTargetDesc("unit"), a)) return []*FullAction{a} } type swordImpl struct{ cardImplementationBase } func (*swordImpl) onPile(p Permanent) { adjustMelee(p, 1) } func (*swordImpl) onUnpile(p Permanent) { adjustMelee(p, -1) } type greatSwordImpl struct{ cardImplementationBase } func (*greatSwordImpl) onPile(p Permanent) { adjustMelee(p, 2) } func (*greatSwordImpl) onUnpile(p Permanent) { adjustMelee(p, -2) } type misinformationImpl struct{ cardImplementation } func (*misinformationImpl) onPlay(s *State, c *Card, _ *Player, _ *Targets) { s.Exile.AddCard(c) } // ====== Nautics Set ====== type fishTrapAoE struct{} func (*fishTrapAoE) onEntering(p Permanent) { if p.Card().Name != "nautics/fisher" { return } fisher := p.(*Unit) fishTrapAction := func(a Action) ActionResolveFunc { u := a.Source().(*Unit) return func(s *State) { controller := u.Controller() controller.gainResource(2) } } fa := NewFullAction(fisher, fishTrapAction, "gain 2 resource") fa.tag = "fishTrapAction" fisher.FullActions = append(fisher.FullActions, fa) } func (*fishTrapAoE) onLeaving(p Permanent) { if p.Card().Name != "nautics/fisher" { return } fisher := p.(*Unit) for i, fa := range fisher.FullActions { if fa.tag != "fishTrapAction" { continue } fisher.FullActions[i] = fisher.FullActions[len(fisher.FullActions)-1] fisher.FullActions = fisher.FullActions[:len(fisher.FullActions)-1] break } } type fishTrapImpl struct { cardImplementation aoe fishTrapAoE } func (i *fishTrapImpl) onEntering(t *Tile) { s := t.Permanent.Controller().gameState for _, tile := range TilesInRange(s.Map, t.Permanent, 1) { tile.addEffect(&i.aoe) } } func (i *fishTrapImpl) onLeaving(t *Tile) { s := t.Permanent.Controller().gameState for _, tile := range TilesInRange(s.Map, t.Permanent, 1) { tile.removeEffect(&i.aoe) } } type fisherImpl struct{ cardImplementationBase } func (*fisherImpl) fullActions(u *Unit) []*FullAction { s := u.Controller().gameState resolvePrototype := func(a Action) ActionResolveFunc { u := a.Source().(*Unit) return func(s *State) { t := a.Targets().ts[0].sel[0].(*Tile) s.AddNewArtifact(NewCard("nautics/fish_trap"), t.Position, u.Controller()) } } a := NewFullAction(u, resolvePrototype, "create fish trap") a.targets = newTargets(newTarget(s, newTargetDesc("adjacent water tile"), a)) return []*FullAction{a} } type captainImpl struct{ cardImplementationBase } func (*captainImpl) onPile(containing Permanent) { // TODO: implement static effect } func (*captainImpl) onUnpile(containing Permanent) { // TODO: remove static effect } type sailorImpl struct{ cardImplementationBase } func (*sailorImpl) onPile(containing Permanent) { if u, ok := containing.(*Unit); ok && u.Movement.Swimming { u.Movement.Range += 1 } } func (*sailorImpl) onUnpile(containing Permanent) { if u, ok := containing.(*Unit); ok && u.Movement.Swimming { u.Movement.Range -= 1 } } // ====== Kraken Set ====== type tidesChangeImpl struct{ cardImplementation } func (*tidesChangeImpl) onPlay(s *State, _ *Card, _ *Player, _ *Targets) { s.Map.redistributeStoreCards(s.Rand) } type dejaVuImpl struct{ cardImplementation } func (*dejaVuImpl) onPlay(s *State, _ *Card, _ *Player, _ *Targets) { for _, p := range s.Players { if !p.IsKraken() { p.Resource = s.Map.ResourceGain } } } type devourThePoorImpl struct{ cardImplementation } func (*devourThePoorImpl) onPlay(s *State, _ *Card, kraken *Player, _ *Targets) { for _, u := range s.EnemyUnits(kraken) { if u.Card().BuyCost < 7 { s.DestroyPermanent(u) } } } type drownedSailorImpl struct { cardImplementation sailorImpl } func (i *drownedSailorImpl) onPile(containing Permanent) { i.sailorImpl.onPile(containing) } func (i *drownedSailorImpl) onUnpile(containing Permanent) { i.sailorImpl.onUnpile(containing) } func (*drownedSailorImpl) onDrop(sailor Permanent) { sailor.Controller().gameState.DestroyPermanent(sailor) } // TODO: implement death trigger // TODO: implement hook for the containing permanent on pile type flyingDutchmenImpl struct{ cardImplementation } type frostPylonAoE struct{} func (*frostPylonAoE) onEntering(Permanent) { // TODO: prevent actions and grant new full action } func (*frostPylonAoE) onLeaving(Permanent) { // TODO: remove effect } type frostPylonImpl struct { cardImplementation aoe frostPylonAoE } func (*frostPylonImpl) spawnTiles(s *State, kraken *Player) (spawns []*Tile) { for _, u := range s.EnemyUnits(kraken) { spawns = append(spawns, TilesInRange(s.Map, u, 1)...) } return } func (*frostPylonImpl) stateBasedActions(s *State, p Permanent) { if p.Marks(PermanentMarks.Crack) >= 3 { s.DestroyPermanent(p) } } func (i *frostPylonImpl) onEntering(t *Tile) { s := t.Permanent.Controller().gameState for _, tile := range TilesInRange(s.Map, t.Permanent, 1) { tile.addEffect(&i.aoe) } } func (i *frostPylonImpl) onLeaving(t *Tile) { s := t.Permanent.Controller().gameState for _, tile := range TilesInRange(s.Map, t.Permanent, 1) { tile.removeEffect(&i.aoe) } } type giganticHailImpl struct{ cardImplementation } func (*giganticHailImpl) onPlay(s *State, _ *Card, kraken *Player, _ *Targets) { emptyTiles := s.Map.FreeTiles() choices := len(emptyTiles) for i := 0; i < 3; i++ { if choices == 0 { return } choice := s.Rand.Intn(choices - 1) s.AddNewArtifact(NewCard("kraken/ice_berg"), emptyTiles[choice].Position, kraken) emptyTiles[choice] = emptyTiles[choices-1] choices-- } } type soldOutImpl struct{ cardImplementation } func (*soldOutImpl) onPlay(s *State, _ *Card, kraken *Player, _ *Targets) { nStores := len(s.Map.Stores) storePositions := make([]Position, 0, nStores) for pos := range s.Map.Stores { storePositions = append(storePositions, pos) } storePos := storePositions[s.Rand.Intn(nStores-1)] s.Map.StoreOn(storePos).MoveInto(s.Exile) storeTile := s.Map.TileAt(storePos) storeTile.neutralize() } type supriseImpl struct{ cardImplementation } func (*supriseImpl) onPlay(s *State, _ *Card, kraken *Player, _ *Targets) { kraken.Ctrl.(*KrakenControl).activeSuprise = true } type tentacleSlapImpl struct{ cardImplementation } func (*tentacleSlapImpl) onPlay(s *State, _ *Card, kraken *Player, _ *Targets) { x := 0 for _, p := range s.Players { if p.IsKraken() { continue } x += p.Hand.Size() p.discardHand() } gain := 5 - x if gain > 0 { kraken.gainResource(gain) } } type unholyCannonballImpl struct{ cardImplementation } func (*unholyCannonballImpl) onPlay(s *State, _ *Card, kraken *Player, _ *Targets) { krakenTiles := s.Map.FilterTiles(func(t *Tile) bool { return t.Raw == "the kraken" }) if len(krakenTiles) != 1 { log.Fatal("No kraken tile found in Map") } enemiesInRange2 := []*Unit{} for _, u := range s.EnemyUnits(kraken) { if u.Tile() == nil { continue } if IsPositionInRange(u.Tile().Position, krakenTiles[0].Position, 2) { enemiesInRange2 = append(enemiesInRange2, u) break } } if len(enemiesInRange2) == 0 { kraken.gainResource(5) } else { for _, u := range enemiesInRange2 { u.addMarks(UnitStates.Panic, 1) } } } func init() { cardImplementations = map[string]cardImplementation{ "base/advisor": &advisorImpl{}, "base/missionary": &missionaryImpl{}, "base/sword": &swordImpl{}, "base/greatsword": &greatSwordImpl{}, "base/misinformation": &misinformationImpl{}, "nautics/captain": &captainImpl{}, "nautics/fish_trap": &fishTrapImpl{}, "nautics/fisher": &fisherImpl{}, "nautics/sailor": &sailorImpl{}, "kraken/deja_vu!": &dejaVuImpl{}, "kraken/devour_the_poor!": &devourThePoorImpl{}, "kraken/drowned_sailor": &drownedSailorImpl{}, "kraken/flying_dutchmen": &flyingDutchmenImpl{}, "kraken/frost_pylon": &frostPylonImpl{}, "kraken/gigantic_hail": &giganticHailImpl{}, "kraken/sold_out!": &soldOutImpl{}, "kraken/suprise!": &supriseImpl{}, "kraken/tentacle_slap": &tentacleSlapImpl{}, "kraken/tides_change!": &tidesChangeImpl{}, "kraken/unholy_cannonball": &unholyCannonballImpl{}, } }