feat: expand ISAC content modeling
Validate and deploy production / validate (push) Canceled after 0s
Validate and deploy production / deploy (push) Canceled after 0s
Validate and deploy staging / validate (push) Successful in 28s
Validate and deploy staging / deploy (push) Successful in 1m3s

This commit is contained in:
jb
2026-08-03 01:23:20 -04:00
parent d9924a95b9
commit 57068b61cd
90 changed files with 9635 additions and 1270 deletions
+114 -6
View File
@@ -7,6 +7,10 @@ export type LoadoutCatalog = {
brands: any[];
weapons: any[];
talents: any[];
skillChipProfile?: {
primaryOptions: any[];
secondaryOptions: any[];
};
chipSets: any[];
protocols: any[];
specializations: any[];
@@ -14,6 +18,20 @@ export type LoadoutCatalog = {
export type LoadoutSummary = {
stats: Array<{ id: string; value: number; sourceCount: number }>;
abilityStats: Array<{
abilityId: string;
stats: Array<{
id: string;
value: number;
sourceCount: number;
sources: Array<{
source: string;
chipIndex: number;
chipSetId: string;
value: number;
}>;
}>;
}>;
activeBonuses: any[];
equippedTalents: any[];
};
@@ -139,6 +157,55 @@ export function summarizeLoadout(
const brandCounts = new Map<string, number>();
const activeBonuses: any[] = [];
const equippedTalents: any[] = [];
const abilityValues = new Map<string, Map<string, number>>();
const abilitySources = new Map<
string,
Map<
string,
Array<{
source: string;
chipIndex: number;
chipSetId: string;
value: number;
}>
>
>();
const addAbilityStat = (
abilityId: string,
statId: string,
value: unknown,
chipIndex: number,
chipSetId: string,
) => {
if (
!abilityId ||
!statId ||
!catalogAttribute(catalog, statId) ||
!Number.isFinite(Number(value))
)
return;
if (!abilityValues.has(abilityId)) abilityValues.set(abilityId, new Map());
const valuesForAbility = abilityValues.get(abilityId)!;
valuesForAbility.set(
statId,
applyAttributeValue(
catalog,
valuesForAbility.get(statId) ?? 0,
statId,
Number(value),
),
);
if (!abilitySources.has(abilityId))
abilitySources.set(abilityId, new Map());
const sourcesForAbility = abilitySources.get(abilityId)!;
if (!sourcesForAbility.has(statId)) sourcesForAbility.set(statId, []);
sourcesForAbility.get(statId)!.push({
source: `Skill Chip ${chipIndex + 1} primary`,
chipIndex,
chipSetId,
value: Number(value),
});
};
for (const slot of gearSlots) {
const selection = draft.gear[slot];
@@ -259,17 +326,36 @@ export function summarizeLoadout(
string,
{ count: number; highEnd: number; item: any }
>();
const validChipRoll = (
kind: 'primaryOptions' | 'secondaryOptions',
quality: string,
roll: { statId: string; value: number },
) => {
if (!catalog.skillChipProfile) return true;
const range = catalog.skillChipProfile[kind].find(
(option) => option.statId === roll.statId,
)?.ranges?.[quality];
return (
range &&
roll.value >= Number(range.min) &&
roll.value <= Number(range.max)
);
};
for (let index = 0; index < 9; index += 1) {
const selection = draft.chips[index];
const item = catalog.chipSets.find((entry) => entry.id === selection.setId);
if (!item) continue;
add(
selection.primary.statId,
selection.primary.value,
`Skill Chip ${index + 1} primary`,
);
if (validChipRoll('primaryOptions', selection.quality, selection.primary))
addAbilityStat(
String(item.specialization_ability_id ?? ''),
selection.primary.statId,
selection.primary.value,
index,
String(item.id),
);
for (const roll of selection.secondary)
add(roll.statId, roll.value, `Skill Chip ${index + 1} secondary`);
if (validChipRoll('secondaryOptions', selection.quality, roll))
add(roll.statId, roll.value, `Skill Chip ${index + 1} secondary`);
const group = chipGroups.get(item.id) ?? { count: 0, highEnd: 0, item };
group.count += 1;
if (selection.quality === 'High-end') group.highEnd += 1;
@@ -390,6 +476,28 @@ export function summarizeLoadout(
catalogAttribute(catalog, right.id)?.name ?? '',
),
),
abilityStats: [...abilityValues].map(([abilityId, abilityStatValues]) => ({
abilityId,
stats: [...abilityStatValues]
.map(([id, value]) => {
const statSources = abilitySources.get(abilityId)?.get(id) ?? [];
return {
id,
value,
sourceCount: statSources.length,
sources: statSources,
};
})
.sort(
(left, right) =>
(catalogAttribute(catalog, left.id)?.category ?? '').localeCompare(
catalogAttribute(catalog, right.id)?.category ?? '',
) ||
(catalogAttribute(catalog, left.id)?.name ?? '').localeCompare(
catalogAttribute(catalog, right.id)?.name ?? '',
),
),
})),
activeBonuses,
equippedTalents,
};