Prototype Leader Skills setup, battle disciplines and capture
Connect all fourteen physical skills and private setup to five card-role battle modifiers, public posture, Harkonnen skilled capture, death return and optional own-leader revival through controls, legal AI and persisted room actions. Nine skill effects and combined-mode/publication acceptance remain unfinished. Captured replacement entitlement and foreign ghola integration stay guarded. Fresh publisher/designer research supports skilled-capture publicity through its unique known physical card; this is recorded as a component inference. Independent review found and fixed Voice/must-use-leader and invalid saved assignment/offer cases. A pre-existing Discovery privacy assertion confused card ID prefixes; the corrected deterministic physical-pair scenario was independently reviewed. The first broad run was 4410/4411; corrected final verification passes types/lint, all 4411 offline tests, build and 43 HTTP tests. Final durations: check 149741ms, build 6659ms, integration 28274ms. Source fingerprint: aef655ac1ebc0eb2f64294e13d3d27337167312cf8ab17568a166ee9c39f6a85. Private final report: 20260913-leader-skills/checks-final/report.json. Three seeded prototype games completed with no rejected bot candidates. Browser setup/card inspection/refresh passed. Another user-reported power outage stopped the server; backup and necessary restart restored the same private seat. All 292 games at restart and their original application records remain unchanged; 33 isolated HTTP-test rooms bring the current total to 325. No database reset. Efficiency: one shared setup/lifecycle implementation supports five connected role effects; selective existing agents handled catalog/UI/tests/research and independent reviews. One repeated broad check was required by the reproduced ID-prefix assertion failure. Aggregate goal counter rose from 65529766 to 67313673 through this checkpoint (1783907), including inherited context and coordination; this is not a billing or savings estimate. Full goal remains active.
This commit is contained in:
1 parent
369371671f
commit
6e6fecc9d4
28 files changed
+4196
-38
No files matched your search
@@ -0,0 +1,95 @@
|
||||
import type { Card } from './cards';
|
||||
import { defenseTypes, weaponTypes } from './battle-cards';
|
||||
import type { LeaderSkillId } from './leader-skill-cards';
|
||||
|
||||
export type DirectLeaderSkillId =
|
||||
| 'warmaster'
|
||||
| 'master-of-assassins'
|
||||
| 'swordmaster-of-ginaz'
|
||||
| 'killer-medic'
|
||||
| 'prana-bindu-adept';
|
||||
|
||||
export type BattleLeaderSkill = {
|
||||
skill: LeaderSkillId;
|
||||
leader: string;
|
||||
/** Ordinary assignments are publicly face up until deliberately concealed. */
|
||||
faceUp: boolean;
|
||||
/** A captured skill grants only its attached leader's lower battle effect. */
|
||||
captured: boolean;
|
||||
};
|
||||
|
||||
export type AppliedLeaderSkill = {
|
||||
skill: DirectLeaderSkillId;
|
||||
amount: 1 | 3;
|
||||
mode: 'normal' | 'skilled';
|
||||
};
|
||||
|
||||
export type LeaderSkillBattleBonus = {
|
||||
bonus: number;
|
||||
applied: AppliedLeaderSkill[];
|
||||
};
|
||||
|
||||
const directSkills = new Set<LeaderSkillId>([
|
||||
'warmaster',
|
||||
'master-of-assassins',
|
||||
'swordmaster-of-ginaz',
|
||||
'killer-medic',
|
||||
'prana-bindu-adept',
|
||||
]);
|
||||
|
||||
function isDirectSkill(skill: string): skill is DirectLeaderSkillId {
|
||||
return directSkills.has(skill as LeaderSkillId);
|
||||
}
|
||||
|
||||
function qualifies(
|
||||
skill: DirectLeaderSkillId,
|
||||
weapon: Card | undefined,
|
||||
defense: Card | undefined,
|
||||
) {
|
||||
if (skill === 'warmaster')
|
||||
return [weapon, defense].some((card) => card?.kind === 'worthless');
|
||||
if (skill === 'master-of-assassins')
|
||||
return weaponTypes(weapon).includes('poison');
|
||||
if (skill === 'swordmaster-of-ginaz')
|
||||
return weaponTypes(weapon).includes('projectile');
|
||||
if (skill === 'killer-medic')
|
||||
return defenseTypes(defense).includes('snooper');
|
||||
return defenseTypes(defense).includes('shield');
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote the five card-role strength families after weapon survival is known.
|
||||
* Printed disc strength and bounty remain outside this additive score receipt.
|
||||
*/
|
||||
export function leaderSkillBattleBonus(input: {
|
||||
assignments: readonly BattleLeaderSkill[];
|
||||
/** Cheap Hero substitutes for another leader for normal +1 training only. */
|
||||
selectedLeader: { id: string; kind: 'disc' | 'hero' } | undefined;
|
||||
weapon: Card | undefined;
|
||||
defense: Card | undefined;
|
||||
skilledLeaderSurvives: boolean;
|
||||
}): LeaderSkillBattleBonus {
|
||||
if (!input.selectedLeader) return { bonus: 0, applied: [] };
|
||||
const applied: AppliedLeaderSkill[] = [];
|
||||
for (const assignment of input.assignments) {
|
||||
if (!isDirectSkill(assignment.skill)) continue;
|
||||
if (!qualifies(assignment.skill, input.weapon, input.defense)) continue;
|
||||
if (
|
||||
input.selectedLeader.kind === 'disc' &&
|
||||
assignment.leader === input.selectedLeader.id &&
|
||||
(!assignment.faceUp || assignment.captured) &&
|
||||
input.skilledLeaderSurvives
|
||||
)
|
||||
applied.push({ skill: assignment.skill, amount: 3, mode: 'skilled' });
|
||||
else if (
|
||||
assignment.leader !== input.selectedLeader.id &&
|
||||
assignment.faceUp &&
|
||||
!assignment.captured
|
||||
)
|
||||
applied.push({ skill: assignment.skill, amount: 1, mode: 'normal' });
|
||||
}
|
||||
return {
|
||||
bonus: applied.reduce((sum, receipt) => sum + receipt.amount, 0),
|
||||
applied,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user