Break a 9-clash pet deadlock by fainting both combatants.
This commit is contained in:
@@ -440,6 +440,14 @@ func (g *Game) resolveBattle() {
|
||||
// The exchange loop terminates: every clash kills at least one pet or
|
||||
// is a detected stalemate, and summons/auras are finite. The guard is
|
||||
// just insurance as effects get richer.
|
||||
//
|
||||
// clashStreak counts how many times the current pair has clashed without
|
||||
// either fainting. Some effects (e.g. eat-an-apple-when-hurt) can make two
|
||||
// pets un-killable, looping forever; if they trade blows deadlockLimit
|
||||
// times in a row, both drop so the battle can proceed. It resets whenever
|
||||
// the matchup changes (a fresh pet enters).
|
||||
const deadlockLimit = 9
|
||||
clashStreak := 0
|
||||
for range 10_000 {
|
||||
// Reveal until every side has a pet in play or runs out. Play
|
||||
// effects queue up and resolve after all reveals (simultaneous).
|
||||
@@ -525,6 +533,15 @@ func (g *Game) resolveBattle() {
|
||||
}
|
||||
}
|
||||
|
||||
// A fresh pet on either side is a new matchup, so the deadlock streak
|
||||
// starts over.
|
||||
for _, np := range newlyPlayed {
|
||||
if np {
|
||||
clashStreak = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Battle over? A side that couldn't field a pet is out; no further
|
||||
// effects resolve.
|
||||
anyOut := false
|
||||
@@ -657,9 +674,25 @@ func (g *Game) resolveBattle() {
|
||||
if dealtB > 0 && ua.hasKnockout() {
|
||||
ub.Damage = max(ub.Damage, ub.Power())
|
||||
}
|
||||
// Deadlock guard: if the pair keeps surviving, count the exchanges and
|
||||
// force a mutual knockout once they hit the limit.
|
||||
deadlocked := false
|
||||
if ua.Alive() && ub.Alive() {
|
||||
clashStreak++
|
||||
if clashStreak >= deadlockLimit {
|
||||
deadlocked = true
|
||||
ua.Damage = max(ua.Damage, ua.Power())
|
||||
ub.Damage = max(ub.Damage, ub.Power())
|
||||
}
|
||||
} else {
|
||||
clashStreak = 0
|
||||
}
|
||||
clashTxt := fmt.Sprintf("%s's %s and %s's %s trade blows.",
|
||||
pname(0), ua.Card.Name, pname(1), ub.Card.Name)
|
||||
switch da, db := !ua.Alive(), !ub.Alive(); {
|
||||
case deadlocked:
|
||||
clashTxt = fmt.Sprintf("%s's %s and %s's %s are deadlocked after %d clashes — both faint.",
|
||||
pname(0), ua.Card.Name, pname(1), ub.Card.Name, deadlockLimit)
|
||||
case da && db:
|
||||
clashTxt += " Both faint."
|
||||
case da:
|
||||
|
||||
@@ -736,3 +736,28 @@ func TestSkunkStripFaintsDebuffedPet(t *testing.T) {
|
||||
t.Fatalf("skunk should win once the stripped pet faints, got %d", res.WinnerSeat)
|
||||
}
|
||||
}
|
||||
|
||||
// Two pets that block every hit could clash forever; the deadlock rule makes
|
||||
// them both faint after 9 clashes so the battle can proceed.
|
||||
func TestBattleDeadlockBreaksAfterNineClashes(t *testing.T) {
|
||||
g, _, _ := testGame(t)
|
||||
wall := func(name string) Card {
|
||||
return Card{
|
||||
ID: g.newCardID(), Kind: KindPet, Name: name, Tier: 1, Power: 3, Suit: SuitRed,
|
||||
Effects: []Effect{{Trigger: TriggerPlay, Action: ActionShieldSelf, Count: 20}},
|
||||
}
|
||||
}
|
||||
res := forceBattle(t, g, []Card{wall("Wall")}, []Card{wall("Fort")})
|
||||
|
||||
clashes := eventsOfType(res, "clash")
|
||||
if len(clashes) != 9 {
|
||||
t.Fatalf("deadlock should resolve after 9 clashes, got %d", len(clashes))
|
||||
}
|
||||
last := clashes[len(clashes)-1]
|
||||
if !last.Died[0] || !last.Died[1] {
|
||||
t.Fatalf("the 9th clash should faint both pets: %+v", last)
|
||||
}
|
||||
if res.WinnerSeat != -1 {
|
||||
t.Fatalf("a deadlock is a draw, got winner=%d", res.WinnerSeat)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user