Forced drops bank honestly; half-dead sockets confess

DROP OBJECT's forced drop hardcoded onHomeOf null and skipped the
victory check — a treasure shaken loose over a home square neither
reported the banking nor triggered scoring or the captured-treasures
clause until something else prodded the engine. It now lands like any
drop: homeOwnerAt reported, checkVictory immediate.

And the stuck-game mystery from 38YP: a deploy restart left the
browser holding a half-dead socket that never fired onclose, so the
existing reconnect loop and masthead banner never engaged and a live
game wore a frozen face. The client now pings every 15s and force-
closes a socket silent for 45s, which wakes the reconnect machinery;
the server answers pong.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
Eric Wagoner
2026-08-27 23:45:26 -04:00
co-authored by Claude Fable 5
parent 5eb5312637
commit ca0a4ef2e3
4 changed files with 58 additions and 1 deletions
+5 -1
View File
@@ -1460,8 +1460,12 @@ const CARD_EFFECTS: Record<string, AttackEffect | NeutralEffect | CounterEffect>
ctx.defender.carriedTreasureId = null;
ctx.events.push({
type: "treasureDropped", player: ctx.defender.id, treasureId: t.id,
at: ctx.defender.position, onHomeOf: null,
at: ctx.defender.position, onHomeOf: homeOwnerAt(ctx.state, ctx.defender.position),
});
// A forced drop can land on a home square: bankings and the
// captured-treasures clause take effect at once, as they would
// for a willing drop.
checkVictory(ctx.state, ctx.events);
return;
}
const idx = ctx.defender.hand.findIndex((c) => c.cardId === wanted);