Reveal tripled-in pets but keep the pick secret unless it buys.
This commit is contained in:
+29
-2
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tunable rules. The engine supports any player count >= 2; MinPlayers /
|
// Tunable rules. The engine supports any player count >= 2; MinPlayers /
|
||||||
@@ -369,6 +370,17 @@ func (g *Game) sellCards(p *Player, cardIDs []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasBuyEffect reports whether a card carries any buy-triggered ability, which
|
||||||
|
// means acquiring it does something visible to everyone.
|
||||||
|
func hasBuyEffect(c Card) bool {
|
||||||
|
for _, e := range c.Effects {
|
||||||
|
if e.Trigger == TriggerBuy {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// TradeStart spends one coin and three same-suit pets from the player's deck
|
// TradeStart spends one coin and three same-suit pets from the player's deck
|
||||||
// to reveal the top two cards of the next tier's deck. The player must then
|
// to reveal the top two cards of the next tier's deck. The player must then
|
||||||
// call TradeChoose before anything else happens.
|
// call TradeChoose before anything else happens.
|
||||||
@@ -412,7 +424,14 @@ func (g *Game) TradeStart(playerID string, cardIDs []string) error {
|
|||||||
}
|
}
|
||||||
p.Coins--
|
p.Coins--
|
||||||
p.TripledThisRound = true
|
p.TripledThisRound = true
|
||||||
g.logf(p.Seat, "🔄", "%s traded in 3 %s pets for a tier %d pick.", p.Name, suit, nextTier)
|
// The discarded trio is public — everyone sees what was given up — even
|
||||||
|
// though the pet ultimately chosen stays secret (see TradeChoose).
|
||||||
|
names := make([]string, len(traded))
|
||||||
|
for i, c := range traded {
|
||||||
|
names[i] = c.Name
|
||||||
|
}
|
||||||
|
g.logf(p.Seat, "🔄", "%s traded in %s (%s) for a tier %d pick.",
|
||||||
|
p.Name, strings.Join(names, ", "), suit, nextTier)
|
||||||
g.Pending = &PendingTrade{
|
g.Pending = &PendingTrade{
|
||||||
PlayerID: playerID,
|
PlayerID: playerID,
|
||||||
Tier: nextTier,
|
Tier: nextTier,
|
||||||
@@ -440,7 +459,15 @@ func (g *Game) TradeChoose(playerID string, pick int) error {
|
|||||||
tierIdx := g.Pending.Tier - 1
|
tierIdx := g.Pending.Tier - 1
|
||||||
g.ShopDecks[tierIdx] = append(g.ShopDecks[tierIdx], other)
|
g.ShopDecks[tierIdx] = append(g.ShopDecks[tierIdx], other)
|
||||||
g.Pending = nil
|
g.Pending = nil
|
||||||
g.logf(p.Seat, "🔄", "%s picked %s from the trade.", p.Name, chosen.Name)
|
// The pick is secret — opponents never saw the two revealed options and
|
||||||
|
// can't see the deck. But a pet with a Buy ability performs it publicly, so
|
||||||
|
// we have to reveal that pet (its effect log names it anyway).
|
||||||
|
if hasBuyEffect(chosen) {
|
||||||
|
g.logf(p.Seat, "🔄", "%s's trade pick is %s %s — its buy ability triggers.",
|
||||||
|
p.Name, article(chosen.Name), chosen.Name)
|
||||||
|
} else {
|
||||||
|
g.logf(p.Seat, "🔄", "%s keeps their trade pick hidden.", p.Name)
|
||||||
|
}
|
||||||
// Pets obtained via the Triple action trigger their Buy effects.
|
// Pets obtained via the Triple action trigger their Buy effects.
|
||||||
g.applyShopTrigger(p, chosen, TriggerBuy)
|
g.applyShopTrigger(p, chosen, TriggerBuy)
|
||||||
g.advanceShopTurn()
|
g.advanceShopTurn()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package game
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"slices"
|
"slices"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -225,6 +226,54 @@ func TestTradeTriggersTripleAndBuyEffects(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A triple makes the three discarded pets public, but keeps the chosen pet
|
||||||
|
// secret — unless it has a Buy ability, which performs publicly.
|
||||||
|
func TestTradeVisibilityHidesPickUnlessBuyEffect(t *testing.T) {
|
||||||
|
logContains := func(g *Game, sub string) bool {
|
||||||
|
for _, e := range g.Log {
|
||||||
|
if strings.Contains(e.Text, sub) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
trade := func(g *Game, pick Card) *Game {
|
||||||
|
p := current(g)
|
||||||
|
var ids []string
|
||||||
|
for range 3 {
|
||||||
|
c := g.pet("Newt", 1)
|
||||||
|
c.Suit = SuitBlue
|
||||||
|
p.Deck = append(p.Deck, c)
|
||||||
|
ids = append(ids, c.ID)
|
||||||
|
}
|
||||||
|
if err := g.TradeStart(p.ID, ids); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
g.Pending.Options[0] = pick
|
||||||
|
if err := g.TradeChoose(p.ID, 0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discarded trio is named; an effect-less pick stays hidden.
|
||||||
|
g, _, _ := testGame(t)
|
||||||
|
trade(g, g.pet("SecretPet", 3))
|
||||||
|
if !logContains(g, "Newt") {
|
||||||
|
t.Fatal("the discarded pets should be public in the log")
|
||||||
|
}
|
||||||
|
if logContains(g, "SecretPet") {
|
||||||
|
t.Fatal("a pick with no buy ability must stay hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
// A pick with a Buy ability (Otter) is revealed.
|
||||||
|
g2, _, _ := testGame(t)
|
||||||
|
trade(g2, g2.realPet(t, "Otter"))
|
||||||
|
if !logContains(g2, "Otter") {
|
||||||
|
t.Fatal("a pick with a buy ability should be revealed in the log")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuyWormAddsTwoApples(t *testing.T) {
|
func TestBuyWormAddsTwoApples(t *testing.T) {
|
||||||
g, _, _ := testGame(t)
|
g, _, _ := testGame(t)
|
||||||
p := current(g)
|
p := current(g)
|
||||||
|
|||||||
Reference in New Issue
Block a user