Fix nurse shark and remove mid-battle choice.

This commit is contained in:
Greyson Parrelli
2026-07-24 10:12:17 -04:00
parent 094f38593e
commit 0fe0757e18
14 changed files with 84 additions and 326 deletions
+32 -41
View File
@@ -2,11 +2,11 @@ package game
import "testing"
// --- Nurse Shark: the mid-battle decision channel ---
// --- Nurse Shark ---
// Nurse Shark suspends the battle to ask how many Trumpets to spend, then
// resumes and throws two rocks per Trumpet spent.
func TestNurseSharkSuspendsAndResumes(t *testing.T) {
// Nurse Shark automatically spends every available Trumpet (up to 3), throwing
// two rocks per Trumpet spent.
func TestNurseSharkSpendsAllTrumpets(t *testing.T) {
g, _, _ := testGame(t)
g.RollDie = func() int { return 2 } // each rock deals 2
res := forceBattle(t, g,
@@ -14,25 +14,9 @@ func TestNurseSharkSuspendsAndResumes(t *testing.T) {
[]Card{g.pet("Big", 4), g.pet("Tank", 6)},
)
// Nyala trades with Big (banking 2 Trumpets), then Nurse Shark enters and
// the battle suspends on its choice.
if g.PendingBattle == nil {
t.Fatal("expected the battle to suspend on Nurse Shark's choice")
}
if g.PendingBattle.Seat != 0 || g.PendingBattle.Max != 2 || g.PendingBattle.Kind != "nurseShark" {
t.Fatalf("unexpected pending decision: %+v", g.PendingBattle)
}
if res.WinnerSeat != -1 {
t.Fatalf("a suspended battle has no winner yet, got %d", res.WinnerSeat)
}
// Spend both Trumpets: 4 rocks (2+2+2+2 = 8) kill the 6-power Tank.
if err := g.BattleChoose(g.Players[0].ID, 2); err != nil {
t.Fatal(err)
}
if g.PendingBattle != nil {
t.Fatalf("battle should have completed: %+v", g.PendingBattle)
}
// spends both: 4 rocks (2+2+2+2 = 8) kill the 6-power Tank.
spends := 0
for _, ev := range eventsOfType(g.Battle, "trumpet") {
for _, ev := range eventsOfType(res, "trumpet") {
if ev.Count < 0 {
spends++
}
@@ -40,44 +24,51 @@ func TestNurseSharkSuspendsAndResumes(t *testing.T) {
if spends != 1 {
t.Fatalf("expected one trumpet spend, got %d", spends)
}
rocks := eventsOfType(g.Battle, "rock")
rocks := eventsOfType(res, "rock")
if len(rocks) != 1 || len(rocks[0].Dice) != 4 || !rocks[0].TargetDied {
t.Fatalf("Nurse Shark should throw 4 rocks (2 per Trumpet) and kill Tank: %+v", rocks)
}
if g.Battle.WinnerSeat != 0 {
t.Fatalf("seat 0 should win, got %d", g.Battle.WinnerSeat)
if res.WinnerSeat != 0 {
t.Fatalf("seat 0 should win, got %d", res.WinnerSeat)
}
}
// Choosing to spend zero Trumpets throws no rocks.
func TestNurseSharkSpendZero(t *testing.T) {
// The spend is capped at 3 Trumpets even when more are banked.
func TestNurseSharkCapsAtThree(t *testing.T) {
g, _, _ := testGame(t)
g.RollDie = func() int { return 2 }
forceBattle(t, g,
[]Card{g.goldenPet(t, "Nyala"), g.goldenPet(t, "Nurse Shark")},
[]Card{g.pet("Big", 4), g.pet("Tank", 6)},
g.RollDie = func() int { return 1 } // each rock deals 1
g.Players[0].PendingTrumpets = 5 // seed a pool larger than the cap
res := forceBattle(t, g,
[]Card{g.goldenPet(t, "Nurse Shark")},
[]Card{g.pet("Wall", 20)},
)
if g.PendingBattle == nil {
t.Fatal("expected a pending decision")
var spend *BattleEvent
for i, ev := range res.Events {
if ev.Type == "trumpet" && ev.Count < 0 {
spend = &res.Events[i]
}
}
if err := g.BattleChoose(g.Players[0].ID, 0); err != nil {
t.Fatal(err)
if spend == nil || spend.Count != -3 {
t.Fatalf("Nurse Shark should spend exactly 3 Trumpets: %+v", eventsOfType(res, "trumpet"))
}
if len(eventsOfType(g.Battle, "rock")) != 0 {
t.Fatal("spending zero Trumpets should throw no rocks")
rocks := eventsOfType(res, "rock")
if len(rocks) != 1 || len(rocks[0].Dice) != 6 {
t.Fatalf("spending 3 Trumpets should throw 6 rocks: %+v", rocks)
}
}
// With no Trumpets, Nurse Shark's choice is trivial (max 0) and the rollout
// path auto-resolves without suspending.
func TestNurseSharkAutoResolvesInRollout(t *testing.T) {
// With no Trumpets, Nurse Shark throws no rocks and the battle still resolves.
func TestNurseSharkNoTrumpets(t *testing.T) {
res := SimulateBattle(1, 0,
[]Card{cardWithName("Nurse Shark", 3, []Effect{{Trigger: TriggerPlay, Action: ActionSpendRocks, Count: 3}})},
[]Card{{ID: "x", Kind: KindPet, Name: "Foe", Power: 2}},
func() int { return 2 },
)
if res == nil || res.WinnerSeat < -1 {
t.Fatalf("rollout should complete without suspending: %+v", res)
t.Fatalf("battle should resolve: %+v", res)
}
if len(eventsOfType(res, "rock")) != 0 {
t.Fatal("with no Trumpets, Nurse Shark should throw no rocks")
}
}