Synthesize per-level rewards for the Character card

Character XP has no Rewards column, but every level grants skill points
and/or intel points. Synthesize a "Level Reward" perk per level (e.g.
"+1 Skill Point · +10 Intel Points") so the Character card uses the same
unlocked-perks UI as the spec cards. The card header reads "Level Rewards"
instead of "Perks Unlocked" via a new perksLabel prop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-05-23 07:50:20 -04:00
parent 616a84ec5e
commit 84b3f6a061
4 changed files with 1423 additions and 202 deletions

File diff suppressed because it is too large Load diff

View file

@ -300,6 +300,8 @@ const specMeta: Record<SpecId, { name: string; sym: string }> = {
:table="charXp" :table="charXp"
:level="build.character.level" :level="build.character.level"
:xp-into="build.character.xpInto" :xp-into="build.character.xpInto"
show-perks
perks-label="Level Rewards"
@update:level="(n) => (build.character.level = n)" @update:level="(n) => (build.character.level = n)"
@update:xp-into="(n) => (build.character.xpInto = n)" @update:xp-into="(n) => (build.character.xpInto = n)"
/> />

View file

@ -10,6 +10,7 @@ const props = defineProps<{
xpInto: number; xpInto: number;
maxLevel?: number; maxLevel?: number;
showPerks?: boolean; showPerks?: boolean;
perksLabel?: string;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@ -114,7 +115,7 @@ function fmt(n: number): string {
<div v-if="showPerks && allPerks.length" class="perks-block"> <div v-if="showPerks && allPerks.length" class="perks-block">
<div class="perks-head"> <div class="perks-head">
<span>Perks Unlocked</span> <span>{{ perksLabel || 'Perks Unlocked' }}</span>
<span class="count"> <span class="count">
{{ unlockedPerks.length }} / {{ allPerks.length }} {{ unlockedPerks.length }} / {{ allPerks.length }}
</span> </span>

View file

@ -293,7 +293,9 @@ def extract_skill_tree(path: Path, class_id: str, class_name: str) -> dict:
def main(): def main():
manifest = {"xp": {}, "factions": {}, "skills": []} manifest = {"xp": {}, "factions": {}, "skills": []}
# Character XP (200 levels, 6 value columns) # Character XP (200 levels, 6 value columns). Character XP has no
# "Rewards" column, but every level grants skill / intel points — we
# synthesize a per-level "Level Reward" perk so the UI can show them.
char_xp = extract_xp_table( char_xp = extract_xp_table(
SAMPLE / "Character XP Table - Dune Awakening.html", SAMPLE / "Character XP Table - Dune Awakening.html",
[ [
@ -305,6 +307,22 @@ def main():
"totalIntelPoints", "totalIntelPoints",
], ],
) )
for row in char_xp["rows"]:
sp = row.get("skillPoints", 0) or 0
ip = row.get("intelPoints", 0) or 0
if sp == 0 and ip == 0:
continue
parts = []
if sp > 0:
parts.append(f"+{sp} Skill Point" + ("s" if sp > 1 else ""))
if ip > 0:
parts.append(f"+{ip} Intel Point" + ("s" if ip > 1 else ""))
row["perks"] = [
{
"name": "Level Reward",
"effect": " · ".join(parts),
}
]
(OUT / "character-xp.json").write_text(json.dumps(char_xp, indent=2)) (OUT / "character-xp.json").write_text(json.dumps(char_xp, indent=2))
manifest["xp"]["character"] = "character-xp.json" manifest["xp"]["character"] = "character-xp.json"