package game import "fmt" // Suit is the colored trade-in symbol printed on pet cards. Three pets of // the same suit can be traded (the "Triple" action) for a pick from the next // tier's deck. type Suit string const ( SuitRed Suit = "red" SuitBlue Suit = "blue" SuitYellow Suit = "yellow" ) // CardKind distinguishes pets from foods. type CardKind string const ( KindPet CardKind = "pet" KindFood CardKind = "food" ) // Food identifiers. const ( FoodApple = "apple" FoodHoney = "honey" FoodGarlic = "garlic" ) // EffectTrigger is when an effect fires. type EffectTrigger string const ( TriggerFaint EffectTrigger = "faint" // defeated in battle TriggerSell EffectTrigger = "sell" // sold in the shop (the discard→apple action) TriggerBuy EffectTrigger = "buy" // purchased (also fires on pets gained via Triple) TriggerPlay EffectTrigger = "play" // revealed on the stack during battle TriggerTriple EffectTrigger = "triple" // used as one of the three traded-in cards TriggerHurt EffectTrigger = "hurt" // attacked and received damage // TriggerBattlePrep fires right after the shop phase ends, as players // begin ordering their decks. TriggerBattlePrep EffectTrigger = "battlePrep" // TriggerPassive marks always-on effects (e.g. Garlic's damage // prevention); they're consulted contextually rather than fired. TriggerPassive EffectTrigger = "passive" ) // EffectAction is what the effect does. type EffectAction string const ( // ActionSummonTop puts Count cards (Effect.Card: "apple" or "bee") on // top of a deck/stack — the owner's, or the enemy's when Target is // "enemy". Battle-time. ActionSummonTop EffectAction = "summonTop" // ActionGainApple adds Count apples to the player's hand. Shop-time. ActionGainApple EffectAction = "gainApple" // ActionThrowRock rolls Count rock dice (faces: 0,0,1,1,2,2) and deals // the total to the opposing pet in play. Battle-time. ActionThrowRock EffectAction = "throwRock" // ActionEatApple gives the pet itself +Count power (as if it ate // apples), if it hasn't fainted. Battle-time. With Per set, Count is // multiplied by a battle statistic (see Effect.Per). ActionEatApple EffectAction = "eatApple" // ActionRefreshGold returns Count spent coins to the player (never // above the round's allowance). Shop-time. ActionRefreshGold EffectAction = "refreshGold" // ActionPreventDamage (passive) reduces every attack that hits this pet // by Count damage. ActionPreventDamage EffectAction = "preventDamage" // ActionRecycleApples (faint) puts up to Count of this pet's attached // apples back on top of its owner's deck. ActionRecycleApples EffectAction = "recycleApples" // ActionDelayedRocks (faint) sets the pet aside: when its owner plays // their next pet, it throws Count rocks at EACH active pet — the // enemy's and the owner's own. ActionDelayedRocks EffectAction = "delayedRocks" ) // Per multipliers for dynamic effect counts. const PerFaintedBees = "faintedBees" // × friendly bees fainted this battle // Effect is one trigger→action pair printed on a card. type Effect struct { Trigger EffectTrigger `json:"trigger"` Action EffectAction `json:"action"` Card string `json:"card,omitempty"` // summonTop: "apple" | "bee" Count int `json:"count,omitempty"` // 0 means 1 Target string `json:"target,omitempty"` // summonTop: "" (self) | "enemy" // Per multiplies Count by a battle statistic (e.g. PerFaintedBees). Per string `json:"per,omitempty"` // MinRound gates the effect to round >= MinRound (0 = always). MinRound int `json:"minRound,omitempty"` } // count normalizes the zero value to 1. func (e Effect) count() int { if e.Count <= 0 { return 1 } return e.Count } // Card is a single physical card instance. IDs are unique per game. type Card struct { ID string `json:"id"` Kind CardKind `json:"kind"` Name string `json:"name"` Tier int `json:"tier,omitempty"` Power int `json:"power,omitempty"` Suit Suit `json:"suit,omitempty"` // Effects drive the engine; EffectText is the human-readable rule shown // on the card face. Effects []Effect `json:"effects,omitempty"` EffectText string `json:"effectText,omitempty"` Food string `json:"food,omitempty"` // Perk foods (e.g. Honey) attach like any food, but a pet only benefits // from the last-applied perk. Perk bool `json:"perk,omitempty"` // Temporary cards (apples, bees) are removed from the deck after the // next battle. Temporary bool `json:"temporary,omitempty"` } func (c Card) IsPet() bool { return c.Kind == KindPet } func (c Card) IsFood() bool { return c.Kind == KindFood } // petTemplate is the printed definition of a pet. Suits lists the suit of // each physical copy in the tier deck (one card per entry). type petTemplate struct { Name string Power int Suits []Suit Effects []Effect EffectText string } // foodTemplate is the printed definition of a food card that lives in a // shop deck (e.g. Honey). type foodTemplate struct { Name string Food string Copies int Perk bool Effects []Effect EffectText string } // petTiers defines the shop decks' pets. Index 0 is tier 1 (round 1) // through index 5 for tier 6 (round 6). // // Tiers 1-2 are real card data. Tiers 3-6 are placeholders with the same // structure until their real definitions are provided. var petTiers = [MaxRounds][]petTemplate{ { // Tier 1 { Name: "Ant", Power: 1, Suits: []Suit{SuitBlue, SuitYellow}, Effects: []Effect{{Trigger: TriggerFaint, Action: ActionSummonTop, Card: "apple"}}, EffectText: "Faint: add an Apple on top of your deck", }, { Name: "Cricket", Power: 1, Suits: []Suit{SuitRed, SuitBlue}, Effects: []Effect{{Trigger: TriggerFaint, Action: ActionSummonTop, Card: "bee"}}, EffectText: "Faint: add a Bee on top of your deck", }, { Name: "Duck", Power: 2, Suits: []Suit{SuitYellow, SuitBlue}, Effects: []Effect{{Trigger: TriggerSell, Action: ActionGainApple}}, EffectText: "Sell: add 1 extra Apple to your hand", }, { Name: "Otter", Power: 1, Suits: []Suit{SuitYellow, SuitRed}, Effects: []Effect{{Trigger: TriggerBuy, Action: ActionGainApple}}, EffectText: "Buy: add an Apple to your hand", }, { Name: "Mosquito", Power: 2, Suits: []Suit{SuitRed, SuitBlue}, Effects: []Effect{{Trigger: TriggerPlay, Action: ActionThrowRock}}, EffectText: "Play: throw 1 Rock", }, { Name: "Fish", Power: 2, Suits: []Suit{SuitYellow, SuitRed}, Effects: []Effect{{Trigger: TriggerTriple, Action: ActionGainApple}}, EffectText: "Triple: add an Apple to your hand", }, }, { // Tier 2 { Name: "Worm", Power: 2, Suits: []Suit{SuitBlue, SuitYellow}, Effects: []Effect{{Trigger: TriggerBuy, Action: ActionGainApple, Count: 2}}, EffectText: "Buy: add 2 Apples to your hand", }, { Name: "Flamingo", Power: 1, Suits: []Suit{SuitRed, SuitYellow}, Effects: []Effect{{Trigger: TriggerFaint, Action: ActionSummonTop, Card: "apple", Count: 2}}, EffectText: "Faint: add 2 Apples on top of your deck", }, { Name: "Peacock", Power: 2, Suits: []Suit{SuitBlue, SuitRed}, Effects: []Effect{{Trigger: TriggerHurt, Action: ActionEatApple}}, EffectText: "Hurt: if this pet hasn't fainted, it eats 1 Apple", }, { Name: "Swan", Power: 1, Suits: []Suit{SuitRed, SuitBlue}, Effects: []Effect{{Trigger: TriggerTriple, Action: ActionRefreshGold, MinRound: 3}}, EffectText: "Triple: if it is round 3 or later, refresh a spent Gold", }, { Name: "Rat", Power: 4, Suits: []Suit{SuitRed, SuitYellow}, Effects: []Effect{{Trigger: TriggerFaint, Action: ActionSummonTop, Card: "bee", Target: "enemy"}}, EffectText: "Faint: add a Bee on top of the enemy deck", }, { Name: "Spider", Power: 2, Suits: []Suit{SuitYellow, SuitBlue}, Effects: []Effect{ {Trigger: TriggerFaint, Action: ActionSummonTop, Card: "bee"}, {Trigger: TriggerFaint, Action: ActionSummonTop, Card: "apple"}, }, EffectText: "Faint: add a Bee, then an Apple, on top of your deck", }, }, { // Tier 3 { Name: "Dog", Power: 2, Suits: []Suit{SuitYellow, SuitBlue}, Effects: []Effect{{Trigger: TriggerPlay, Action: ActionEatApple, Per: PerFaintedBees}}, EffectText: "Play: eats 1 Apple for each friendly fainted Bee", }, { Name: "Dolphin", Power: 2, Suits: []Suit{SuitBlue, SuitRed}, Effects: []Effect{{Trigger: TriggerPlay, Action: ActionThrowRock, Count: 3}}, EffectText: "Play: throw 3 Rocks", }, { Name: "Giraffe", Power: 2, Suits: []Suit{SuitRed, SuitBlue}, Effects: []Effect{{Trigger: TriggerBattlePrep, Action: ActionGainApple, Count: 2}}, EffectText: "Battle Prep: add 2 Apples to your hand", }, { Name: "Camel", Power: 3, Suits: []Suit{SuitYellow, SuitBlue}, Effects: []Effect{{Trigger: TriggerHurt, Action: ActionSummonTop, Card: "apple"}}, EffectText: "Hurt: add an Apple on top of your deck", }, { Name: "Sheep", Power: 3, Suits: []Suit{SuitYellow, SuitRed}, Effects: []Effect{{Trigger: TriggerFaint, Action: ActionSummonTop, Card: "bee", Count: 2}}, EffectText: "Faint: add 2 Bees on top of your deck", }, { Name: "Dodo", Power: 3, Suits: []Suit{SuitRed, SuitBlue}, Effects: []Effect{{Trigger: TriggerFaint, Action: ActionRecycleApples, Count: 3}}, EffectText: "Faint: put up to 3 of this pet's Apples on top of your deck", }, { Name: "Badger", Power: 3, Suits: []Suit{SuitRed, SuitYellow}, Effects: []Effect{{Trigger: TriggerFaint, Action: ActionDelayedRocks, Count: 2}}, EffectText: "Faint: set aside — when you play your next pet, throw 2 Rocks at each active pet", }, }, placeholderTier([6]string{"Skunk", "Hippo", "Bison", "Deer", "Squirrel", "Whale"}, [6]int{5, 6, 6, 4, 4, 5}), placeholderTier([6]string{"Scorpion", "Rhino", "Monkey", "Cow", "Seal", "Shark"}, [6]int{5, 7, 6, 6, 6, 7}), placeholderTier([6]string{"Leopard", "Boar", "Gorilla", "Mammoth", "Snake", "Tiger"}, [6]int{8, 9, 9, 10, 8, 9}), } // foodTiers defines the food cards mixed into each tier's shop deck. var foodTiers = [MaxRounds][]foodTemplate{ {}, // Tier 1 { // Tier 2 { Name: "Honey", Food: FoodHoney, Copies: 2, Perk: true, Effects: []Effect{{Trigger: TriggerFaint, Action: ActionSummonTop, Card: "bee"}}, EffectText: "Faint: add a Bee on top of your deck", }, }, { // Tier 3 { Name: "Garlic", Food: FoodGarlic, Copies: 2, Perk: true, Effects: []Effect{{Trigger: TriggerPassive, Action: ActionPreventDamage}}, EffectText: "Every attack that hits this pet deals 1 less damage", }, }, {}, {}, {}, } // placeholderTier mirrors tier 1's suit distribution (each suit appears on 4 // of the 12 cards) for pets whose real definitions aren't in yet. func placeholderTier(names [6]string, powers [6]int) []petTemplate { suitPairs := [6][]Suit{ {SuitBlue, SuitYellow}, {SuitRed, SuitBlue}, {SuitYellow, SuitBlue}, {SuitYellow, SuitRed}, {SuitRed, SuitBlue}, {SuitYellow, SuitRed}, } tier := make([]petTemplate, 6) for i := range names { tier[i] = petTemplate{Name: names[i], Power: powers[i], Suits: suitPairs[i]} } return tier } // newCardID mints a unique card ID within the game. func (g *Game) newCardID() string { g.NextCardID++ return fmt.Sprintf("c%d", g.NextCardID) } // buildShopDecks creates all six tier decks (unshuffled). func (g *Game) buildShopDecks() { g.ShopDecks = make([][]Card, MaxRounds) for tierIdx := range petTiers { var deck []Card for _, t := range petTiers[tierIdx] { for _, suit := range t.Suits { deck = append(deck, Card{ ID: g.newCardID(), Kind: KindPet, Name: t.Name, Tier: tierIdx + 1, Power: t.Power, Suit: suit, Effects: t.Effects, EffectText: t.EffectText, }) } } for _, f := range foodTiers[tierIdx] { for range f.Copies { deck = append(deck, Card{ ID: g.newCardID(), Kind: KindFood, Name: f.Name, Tier: tierIdx + 1, Food: f.Food, Perk: f.Perk, Effects: f.Effects, EffectText: f.EffectText, }) } } g.ShopDecks[tierIdx] = deck } } // newApple mints an apple food card. Apples are temporary: they vanish from // the deck after the next battle. func (g *Game) newApple() Card { return Card{ ID: g.newCardID(), Kind: KindFood, Name: "Apple", Food: FoodApple, Temporary: true, } } // newBee mints a bee: a temporary 1-power pet with no effect, summoned by // other pets' effects. It counts as a pet while it exists (foods can attach // to it in battle). func (g *Game) newBee() Card { return Card{ ID: g.newCardID(), Kind: KindPet, Name: "Bee", Power: 1, Temporary: true, } }