Add pets for tiers 1-3.
This commit is contained in:
+306
-54
@@ -2,19 +2,17 @@ package game
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Suit is the trade-in symbol printed on pet cards. Three pets of the same
|
||||
// suit can be traded for a pick of the next tier's deck.
|
||||
// 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 (
|
||||
SuitSun Suit = "sun"
|
||||
SuitMoon Suit = "moon"
|
||||
SuitStar Suit = "star"
|
||||
SuitLeaf Suit = "leaf"
|
||||
SuitRed Suit = "red"
|
||||
SuitBlue Suit = "blue"
|
||||
SuitYellow Suit = "yellow"
|
||||
)
|
||||
|
||||
var suits = []Suit{SuitSun, SuitMoon, SuitStar, SuitLeaf}
|
||||
|
||||
// CardKind distinguishes pets from foods.
|
||||
type CardKind string
|
||||
|
||||
@@ -23,63 +21,286 @@ const (
|
||||
KindFood CardKind = "food"
|
||||
)
|
||||
|
||||
// Food identifiers. Only apples exist for now; more foods come later.
|
||||
const FoodApple = "apple"
|
||||
// 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"`
|
||||
Tier int `json:"tier,omitempty"`
|
||||
Power int `json:"power,omitempty"`
|
||||
Suit Suit `json:"suit,omitempty"`
|
||||
// Effect is display text for the pet's ability. Effects are not yet
|
||||
// implemented mechanically; the field keeps card data forward-compatible.
|
||||
Effect string `json:"effect,omitempty"`
|
||||
Food string `json:"food,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; each template appears as
|
||||
// multiple card copies in its tier's shop deck.
|
||||
// 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
|
||||
Name string
|
||||
Power int
|
||||
Suits []Suit
|
||||
Effects []Effect
|
||||
EffectText string
|
||||
}
|
||||
|
||||
const copiesPerPet = 2
|
||||
// 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. Index 0 is tier 1 (round 1) through
|
||||
// index 5 for tier 6 (round 6). Suits are assigned round-robin per tier so
|
||||
// every tier contains every suit.
|
||||
// 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
|
||||
{"Ant", 1}, {"Cricket", 1}, {"Fish", 2}, {"Horse", 1},
|
||||
{"Beaver", 2}, {"Otter", 1}, {"Pig", 3}, {"Mosquito", 2},
|
||||
{
|
||||
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
|
||||
{"Crab", 3}, {"Swan", 2}, {"Hedgehog", 3}, {"Peacock", 4},
|
||||
{"Flamingo", 3}, {"Rat", 2}, {"Shrimp", 2}, {"Spider", 3},
|
||||
{
|
||||
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
|
||||
{"Dog", 4}, {"Badger", 4}, {"Camel", 3}, {"Giraffe", 3},
|
||||
{"Kangaroo", 4}, {"Ox", 5}, {"Rabbit", 3}, {"Sheep", 4},
|
||||
{
|
||||
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",
|
||||
},
|
||||
},
|
||||
{ // Tier 4
|
||||
{"Skunk", 5}, {"Hippo", 6}, {"Bison", 6}, {"Deer", 4},
|
||||
{"Squirrel", 4}, {"Whale", 5}, {"Worm", 4}, {"Penguin", 5},
|
||||
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 5
|
||||
{"Scorpion", 5}, {"Rhino", 7}, {"Monkey", 6}, {"Cow", 6},
|
||||
{"Seal", 6}, {"Shark", 7}, {"Turkey", 5}, {"Crocodile", 8},
|
||||
},
|
||||
{ // Tier 6
|
||||
{"Leopard", 8}, {"Boar", 9}, {"Fly", 7}, {"Gorilla", 9},
|
||||
{"Mammoth", 10}, {"Snake", 8}, {"Tiger", 9}, {"Dragon", 10},
|
||||
{ // 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.
|
||||
@@ -91,17 +312,33 @@ func (g *Game) newCardID() string {
|
||||
// buildShopDecks creates all six tier decks (unshuffled).
|
||||
func (g *Game) buildShopDecks() {
|
||||
g.ShopDecks = make([][]Card, MaxRounds)
|
||||
for tierIdx, templates := range petTiers {
|
||||
deck := make([]Card, 0, len(templates)*copiesPerPet)
|
||||
for i, t := range templates {
|
||||
for range copiesPerPet {
|
||||
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: suits[i%len(suits)],
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -109,12 +346,27 @@ func (g *Game) buildShopDecks() {
|
||||
}
|
||||
}
|
||||
|
||||
// newApple mints an apple food card (from discarding, etc.).
|
||||
// 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,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user