import { CompanionState, GameState } from '../types'; import { MAX_COMPANIONS } from './npcInteractions'; function upsertCompanion(list: CompanionState[], companion: CompanionState) { const next = [...list]; const existingIndex = next.findIndex(item => item.npcId === companion.npcId); if (existingIndex >= 0) { next[existingIndex] = companion; return next; } next.push(companion); return next; } function removeCompanion(list: CompanionState[], npcId: string) { return list.filter(item => item.npcId !== npcId); } export function getRecruitedNpcIds(state: Pick) { return new Set([ ...state.companions.map(companion => companion.npcId), ...state.roster.map(companion => companion.npcId), ]); } export function normalizeRoster(roster: CompanionState[], activeCompanions: CompanionState[]) { const activeIds = new Set(activeCompanions.map(companion => companion.npcId)); return roster .filter(companion => !activeIds.has(companion.npcId)) .reduce((next, companion) => upsertCompanion(next, companion), []); } export function benchActiveCompanion(state: GameState, npcId: string) { const activeCompanion = state.companions.find(companion => companion.npcId === npcId); if (!activeCompanion) return state; return { ...state, companions: state.companions.filter(companion => companion.npcId !== npcId), roster: upsertCompanion(state.roster, activeCompanion), }; } export function activateRosterCompanion(state: GameState, npcId: string, swapNpcId?: string | null) { const reserveCompanion = state.roster.find(companion => companion.npcId === npcId); if (!reserveCompanion) return state; if (state.companions.some(companion => companion.npcId === npcId)) { return { ...state, roster: removeCompanion(state.roster, npcId), }; } if (state.companions.length < MAX_COMPANIONS) { return { ...state, companions: [...state.companions, reserveCompanion], roster: removeCompanion(state.roster, npcId), }; } if (!swapNpcId) return state; const swapIndex = state.companions.findIndex(companion => companion.npcId === swapNpcId); if (swapIndex < 0) return state; const swappedOut = state.companions[swapIndex]; if (!swappedOut) { return state; } const nextCompanions = [...state.companions]; nextCompanions[swapIndex] = reserveCompanion; return { ...state, companions: nextCompanions, roster: upsertCompanion(removeCompanion(state.roster, npcId), swappedOut), }; } export function recruitCompanionToParty( state: GameState, companion: CompanionState, replacedNpcId?: string | null, ) { const nextReserve = removeCompanion(state.roster, companion.npcId); if (!replacedNpcId && state.companions.length < MAX_COMPANIONS) { return { ...state, companions: [...state.companions, companion], roster: nextReserve, }; } if (!replacedNpcId) { return { ...state, companions: state.companions.slice(0, MAX_COMPANIONS), roster: normalizeRoster(nextReserve, state.companions), }; } const replaceIndex = state.companions.findIndex(item => item.npcId === replacedNpcId); if (replaceIndex < 0) { return { ...state, companions: [...state.companions, companion].slice(0, MAX_COMPANIONS), roster: nextReserve, }; } const replacedCompanion = state.companions[replaceIndex]; if (!replacedCompanion) { return state; } const nextCompanions = [...state.companions]; nextCompanions[replaceIndex] = companion; return { ...state, companions: nextCompanions, roster: upsertCompanion(nextReserve, replacedCompanion), }; }