From 3d36d1d0254b8aeecc889a2a49cbcced458cbe9c Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Sat, 25 Feb 2023 23:08:47 +0100 Subject: intermediate commit * Implement move artifact action * Fix widget update memory leak * update the highlights not during on each frame * do not update the unchanged label of a button * Allow to exit the game by pressing 'q' * Implement some cards from the magic set * Improve permanent formatting * Some tweaks to UnitAI code --- go/game/cardImplementations.go | 204 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 197 insertions(+), 7 deletions(-) (limited to 'go/game/cardImplementations.go') diff --git a/go/game/cardImplementations.go b/go/game/cardImplementations.go index 1c4d07a5..66c77b18 100644 --- a/go/game/cardImplementations.go +++ b/go/game/cardImplementations.go @@ -2,6 +2,8 @@ package game import ( "log" + + "muhq.space/muhqs-game/go/utils" ) func adjustMelee(p Permanent, damage int) { @@ -21,6 +23,35 @@ func adjustMelee(p Permanent, damage int) { } } +func adjustAttack(p Permanent, damage int) { + u, ok := p.(*Unit) + if !ok { + return + } + + if INVALID_ATTACK(u.Attack) { + u.Attack.attacks = append(u.Attack.attacks, damage) + } else { + // TODO: remove 0 attacks + for i := range u.Attack.attacks { + u.Attack.attacks[i] += damage + } + } +} + +func adjustMovement(p Permanent, delta int) { + u, ok := p.(*Unit) + if !ok { + return + } + + if u.Movement == INVALID_MOVEMENT() && delta > 0 { + u.Movement.Range = delta + } else { + u.Movement.Range += delta + } +} + func adjustHealth(p Permanent, delta int) { if u, ok := p.(*Unit); ok { u.Health += delta @@ -39,7 +70,7 @@ func (*advisorImpl) fullActions(u *Unit) []*FullAction { controller.DrawN(1) cards := controller.PromptHandCardSelection(0, 1) if cards != nil { - controller.Deck.MoveCard(cards[0], s.Exile) + controller.Hand.MoveCard(cards[0], s.Exile) } } } @@ -172,11 +203,59 @@ func (*wormtongueImpl) fullActions(u *Unit) []*FullAction { // ====== Magic Set ====== +type attackImpl struct{ cardImplementationBase } + +func (*attackImpl) playTargets() TargetDesc { + return TargetDesc{"unit", "0-2"} +} + +func (*attackImpl) onPlay(s *State, c *Card, p *Player, t *Targets) { + sel := t.Cur().sel + switch len(sel) { + case 1: + s.addEotEffect(newAttackModificationEffect(sel[0].(*Unit), 2)) + case 2: + s.addEotEffect(newAttackModificationEffect(sel[0].(*Unit), 1)) + s.addEotEffect(newAttackModificationEffect(sel[1].(*Unit), 1)) + } +} + +type healImpl struct{ cardImplementationBase } + +func (*healImpl) playTargets() TargetDesc { + return newTargetDesc("unit") +} + +func (*healImpl) onPlay(s *State, c *Card, p *Player, t *Targets) { + u := t.Cur().sel[0].(*Unit) + // TODO: prompt damage or poison choice + u.adjustDamage(-2) +} + type dieImpl struct{ cardImplementationBase } -func (*dieImpl) playTargets() *TargetDesc { - desc := newTargetDesc("unit") - return &desc +func (*dieImpl) playTargets() TargetDesc { + return newTargetDesc("unit") +} + +type moreImpl struct{ cardImplementationBase } + +func (*moreImpl) onPlay(s *State, c *Card, p *Player, t *Targets) { + p.DrawN(2) +} + +type mineImpl struct{ cardImplementationBase } + +func (*mineImpl) onPlay(s *State, c *Card, p *Player, t *Targets) { + stolen := 0 + for _, player := range s.Players { + if p == player || !p.IsEnemy(player) { + continue + } + player.reduceResource(2) + stolen += 2 + } + p.gainResource(stolen) } func (*dieImpl) onPlay(s *State, c *Card, p *Player, t *Targets) { @@ -189,6 +268,67 @@ func (*ritualImpl) onPlay(s *State, c *Card, p *Player, t *Targets) { p.gainResource(3) } +type rushImpl struct{ cardImplementationBase } + +func (*rushImpl) playTargets() TargetDesc { + return TargetDesc{"unit", "0-2"} +} + +func (*rushImpl) onPlay(s *State, c *Card, p *Player, t *Targets) { + sel := t.Cur().sel + switch len(sel) { + case 1: + s.addEotEffect(newMovementModificationEffect(sel[0].(*Unit), 2)) + case 2: + s.addEotEffect(newMovementModificationEffect(sel[0].(*Unit), 1)) + s.addEotEffect(newMovementModificationEffect(sel[1].(*Unit), 1)) + } +} + +type selectImpl struct{ cardImplementationBase } + +func (*selectImpl) onPlay(s *State, c *Card, p *Player, t *Targets) { + p.DrawN(1) + cards := p.PromptHandCardSelection(0, 1) + if cards != nil { + p.Hand.MoveCard(cards[0], s.Exile) + } +} + +type shroudImpl struct{ cardImplementationBase } + +func (*shroudImpl) playTargets() TargetDesc { + return newTargetDesc("unit") +} + +func (*shroudImpl) onPlay(s *State, c *Card, p *Player, t *Targets) { + u := t.Cur().sel[0].(*Unit) + s.addEotEffect(newTmpEffect(u, "shroud")) +} + +type switchImpl struct{ cardImplementationBase } + +func (*switchImpl) playTargets() TargetDesc { + return TargetDesc{"permanent", "2"} +} + +func (*switchImpl) onPlay(s *State, c *Card, p *Player, t *Targets) { + perms := utils.InterfaceSliceToTypedSlice[Permanent](t.Cur().sel) + s.switchPermanents(perms[0], perms[1]) +} + +type transmuteImpl struct{ cardImplementationBase } + +func (*transmuteImpl) playTargets() TargetDesc { + return newTargetDesc("store card") +} + +func (*transmuteImpl) onPlay(s *State, c *Card, p *Player, t *Targets) { + storeCard := t.Cur().sel[0].(*Card) + p.Store.MoveCard(storeCard, p.Hand) + s.Exile.AddCard(c) +} + // ====== Nautics Set ====== var fishTrapAoE areaEffect = newGrantFullActionEffect("nautics/fisher", @@ -290,6 +430,40 @@ func (*devourThePoorImpl) onPlay(s *State, _ *Card, kraken *Player, _ *Targets) } } +type dolphinImpl struct{ cardImplementationBase } + +func (*dolphinImpl) fullActions(u *Unit) []*FullAction { + s := u.Controller().gameState + resolvePrototype := func(a Action) ActionResolveFunc { + return func(s *State) { + t := a.Target().sel[0].(*Unit) + + // 1. +1 Attack + // 2. +1 Armor + // 3. +2 Movement + // 4. +1 Action + // 5. +2 Pierce + // 6. All + choice := s.Rand.Intn(5) + 1 + switch choice { + case 1: + s.addEotEffect(newAttackModificationEffect(t, 1)) + case 2: + case 3: + s.addEotEffect(newMovementModificationEffect(t, 1)) + case 4: + case 5: + case 6: + } + + // TODO: implement eot effects + } + } + a := newFullAction(u, resolvePrototype, "random dolphin buff") + a.targets = newTargets(newTarget(s, newTargetDesc("adjacent akkied unit"), a)) + return []*FullAction{a} +} + type drownedSailorImpl struct { cardImplementation sailorImpl @@ -348,7 +522,11 @@ func (i *frostPylonImpl) onLeaving(t *Tile) { } } -type giganticHailImpl struct{ cardImplementation } +type giganticHailImpl struct { + cardImplementation +} + +func (*giganticHailImpl) playTargets() TargetDesc { return INVALID_TARGET_DESC } func (*giganticHailImpl) onPlay(s *State, _ *Card, kraken *Player, _ *Targets) { emptyTiles := s.Map.FreeTiles() @@ -408,6 +586,8 @@ func (*tentacleSlapImpl) onPlay(s *State, _ *Card, kraken *Player, _ *Targets) { type unholyCannonballImpl struct{ cardImplementation } +func (*unholyCannonballImpl) playTargets() TargetDesc { return INVALID_TARGET_DESC } + 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 { @@ -449,8 +629,17 @@ func init() { "base/tower_shield": &towerShieldImpl{}, "base/wormtongue": &wormtongueImpl{}, - "magic/die!": &dieImpl{}, - "magic/ritual!": &ritualImpl{}, + "magic/attack!": &attackImpl{}, + "magic/die!": &dieImpl{}, + "magic/heal!": &healImpl{}, + "magic/more!": &moreImpl{}, + "magic/mine!": &mineImpl{}, + "magic/ritual!": &ritualImpl{}, + "magic/rush!": &rushImpl{}, + "magic/select!": &selectImpl{}, + "magic/shroud!": &shroudImpl{}, + "magic/switch!": &switchImpl{}, + "magic/transmute!": &transmuteImpl{}, "nautics/captain": &captainImpl{}, "nautics/fish_trap": &fishTrapImpl{aoe: fishTrapAoE}, @@ -459,6 +648,7 @@ func init() { "kraken/deja_vu!": &dejaVuImpl{}, "kraken/devour_the_poor!": &devourThePoorImpl{}, + "kraken/dolphin": &dolphinImpl{}, "kraken/drowned_sailor": &drownedSailorImpl{}, "kraken/flying_dutchmen": &flyingDutchmenImpl{}, "kraken/frost_pylon": &frostPylonImpl{}, -- cgit v1.2.3