// Pure skill-progression module (spec §6): XP accrual, level→power, titles. No lib/db imports — operates on plain SkillRow data (Task 12's crew_skills table); the frontend imports it for the CREW-tab display. import type { SkillName } from './crew-costs' import { POWER_BANDS, XP_THRESHOLDS } from './crew-costs' export interface SkillRow { saveId: string crewId: string skill: SkillName level: number xp: number } export type CrewIdentity = { name: string; role: string } // Highest level 0–5 with xp ≥ XP_THRESHOLDS[level−1]; thresholds [5,10,15,20,25], cap 5. export function levelFromXp(xp: number): number { let level = 0 for (let l = 1; l <= XP_THRESHOLDS.length; l++) if (xp >= XP_THRESHOLDS[l - 1]) level = l return level } // L0–2 → 1 · L3–4 → 2 · L5 → 3 (direct lookup into crew-costs POWER_BANDS). export function powerForLevel(level: number): number { return POWER_BANDS[level] } // From the member's HIGHEST skill level: ≥5 → "MASTER ", ≥3 → "SENIOR ", below → null. export function titleFor(member: CrewIdentity, skillRows: SkillRow[]): string | null { const highest = skillRows.reduce((max, r) => Math.max(max, r.level), 0) if (highest >= 5) return `MASTER ${member.role}` if (highest >= 3) return `SENIOR ${member.role}` return null } // +1 XP to every listed member's row for `skill`; returns updated rows + any level-up events. An event // fires when the gained skill steps into a titled band (3 → SENIOR, 5 → MASTER), e.g. "BEN OKONKWO is now a SENIOR PILOT — NAVIGATION L3" (title from the member's HIGHEST skill). export function grantXp( skillRows: SkillRow[], crewIds: string[], skill: SkillName, members: Record, ): { rows: SkillRow[]; events: string[] } { const rows = skillRows.map((r) => r.skill === skill && crewIds.includes(r.crewId) ? { ...r, xp: r.xp + 1, level: levelFromXp(r.xp + 1) } : r, ) const events: string[] = [] for (const crewId of crewIds) { const member = members[crewId] if (!member) continue const newLevel = rows.find((r) => r.crewId === crewId && r.skill === skill)?.level ?? 0 if (newLevel === 3 || newLevel === 5) { const title = titleFor(member, rows.filter((r) => r.crewId === crewId)) if (title) events.push(`${member.name} is now a ${title} — ${skill} L${newLevel}`) } } return { rows, events } }