Files
Dune/components/leader-skill-battle-guide.tsx
T
Quintine 06f46c6f79 Connect Leader Skill battle effects and private Rihani exchanges
Prototype Mentat/Bureaucrat score effects, automatic Sandmaster board spice, and Rihani inspection plus committed exchange. Preserve private views, physical custody, saved winner cleanup and captive/Suk sequencing. Remaining normal bands, missing skill families and combined modules remain gated.

Validation: 45 focused tests; typecheck, lint, 4465 offline tests, production build and 43 HTTP tests passed. Independent rules/privacy/persistence review completed. New isolated browser setup exercised inspection, draw, pending refresh, exchange and completed refresh. Two genuine four-profile Basic/Advanced samples completed without rejected candidates but did not trigger Rihani.

Preserved all 393 opening rooms and original seat/recovery rows (427 rooms after QA/HTTP). SQLite integrity and foreign keys passed. Server remained healthy after the reported outage; no restart or reset.

Verified source tree: c64ce9537269de94c0d785bc08b17346e826c5797352a636771e1dc6ce5e4859
2026-09-13 19:45:29 +10:00

24 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { GameView } from '@/game/engine';
import { usesSurvivingSkilledLeader } from '@/game/leader-skill-combat';
import { leaderSkillStrongholdCount } from '@/game/leader-skill-battle-board';
/** Guidance uses only the viewer's selected disc and public board occupation. */
export function LeaderSkillBattleGuide({ game, leader }: { game: GameView; leader: string }) {
const battle = game.battle;
if (!battle || ![battle.attacker, battle.defender].includes(game.me)) return null;
const assignments = game.leaderSkills?.assignments.filter((a) => a.controller === game.me) ?? [];
const opponent = game.players.find((p) => p.id === (battle.attacker === game.me ? battle.defender : battle.attacker))!;
const mentat = usesSurvivingSkilledLeader(assignments, 'mentat', leader, true);
const bureaucrat = usesSurvivingSkilledLeader(assignments, 'bureaucrat', leader, true);
const sandmaster = usesSurvivingSkilledLeader(assignments, 'sandmaster', leader, true);
const rihani = usesSurvivingSkilledLeader(assignments, 'rihani-decipherer', leader, true);
if (!mentat && !bureaucrat && !sandmaster && !rihani) return null;
return <div className="notice" aria-label="Leader Skill battle effect">
{mentat && <p>Mentat adds 2 to this leader’s battle strength if the leader survives.</p>}
{bureaucrat && <p>Bureaucrat subtracts {leaderSkillStrongholdCount(game, opponent)} from your opponent’s battle total if your leader survives. Count each occupied stronghold once, including contested strongholds. Advisors do not count.</p>}
{sandmaster && <p>If this Sandmaster survives and wins, add 3 spice to the territory’s existing spice pile automatically. No spice is added when the territory has no spice.</p>}
{rihani && <p>If this Rihani Decipherer survives and wins, you may draw two Traitors and exchange an unused old card for one of them. A native Rihani also privately inspects two deck cards first.</p>}
{(mentat || bureaucrat) && <p className="fine">These score effects resolve automatically. They do not change the leader’s printed value or spice bounty.</p>}
</div>;
}