From be8703dc5139e4754535b3275eab74c0f28eb119 Mon Sep 17 00:00:00 2001 From: Quintine <15965533+Quintine@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:05:08 +1100 Subject: [PATCH] Connect printed Diplomat defenses and Ecaz Tanked Duke Cunning --- components/diplomat-defense.tsx | 21 +- components/nexus-ecaz-duke.tsx | 4 +- docs/CURRENT_STATUS.md | 16 ++ docs/DIPLOMAT_DEFENSE.md | 84 ++++++-- docs/NEXUS_ECAZ_RULES.md | 76 +++++-- docs/RULE_DECISIONS.md | 6 +- game/battle-resolution-quote.ts | 5 +- game/diplomat-defense.ts | 74 +++++-- game/duke-vidal.ts | 27 ++- game/engine.ts | 16 +- game/nexus-ecaz-duke.ts | 17 +- game/reference.ts | 6 +- tests/diplomat-defense-controls.test.tsx | 163 +++++++++------ tests/diplomat-defense.test.ts | 256 ++++++++++++++++++++++- tests/nexus-ecaz-duke-controls.test.ts | 52 ++--- tests/nexus-ecaz-duke-engine.test.ts | 2 +- tests/nexus-ecaz-duke-quote.test.ts | 168 ++++++++++++++- 17 files changed, 811 insertions(+), 182 deletions(-) diff --git a/components/diplomat-defense.tsx b/components/diplomat-defense.tsx index b67d9f54..0331186f 100644 --- a/components/diplomat-defense.tsx +++ b/components/diplomat-defense.tsx @@ -1,7 +1,8 @@ 'use client'; import type { Action, GameView } from '@/game/engine'; -import { battleCardLabel } from '@/game/battle-cards'; +import { isPortableSnooper } from '@/game/battle-cards'; +import { copiedDefenseKind, diplomatDefenseLabel } from '@/game/diplomat-defense'; import { Button } from './ui/button'; export function DiplomatDefense({ @@ -20,7 +21,16 @@ export function DiplomatDefense({ const visibleCards = [...(game.battle?.cards ?? []), ...(me?.hand ?? [])]; const card = (id: string) => visibleCards.find((entry) => entry.id === id); const source = card(decision.source); - const copiedDefense = source ? battleCardLabel(source.kind) : 'defense'; + const copiedKind = + game.battle?.diplomatDefense?.kind ?? copiedDefenseKind(source); + const copiedDefense = copiedKind + ? diplomatDefenseLabel(copiedKind) + : 'defense'; + const interpretedRole = + copiedKind === 'shieldSnooper' || + copiedKind === 'weirdingWay' || + copiedKind === 'chemistry' || + isPortableSnooper(source); return (

@@ -34,6 +44,13 @@ export function DiplomatDefense({ must be discarded after this battle. Decline to proceed without a copied defense.

+ {interpretedRole && ( +

+ First-version interpretation: copy the defense role already used by + the opponent, not its activation, late-play opportunity or requirement + for another card in your own plan. +

+ )} {decision.cards.map((id) => (