Files
gupi-osint-board/server/levelVisibility.ts

51 lines
3.0 KiB
TypeScript

import type { CaseState, DocumentExhibit, ExhibitRelation, Connection } from '../src/types.js'
function requirementsMet(document: DocumentExhibit, earnedFlags: ReadonlySet<string>) {
return (document.requiredFlags || []).every(flag => earnedFlags.has(flag))
}
export function filterLevelVisibility(full: CaseState, earnedFlags: Iterable<string>, seenDocumentIds: Iterable<string>, authorMode: boolean): CaseState {
if (authorMode) return { ...full, newlyVisibleDocumentIds: [] }
const earned = new Set(earnedFlags)
const seen = new Set(seenDocumentIds)
const visibleExhibits = full.exhibits.filter(exhibit => !exhibit.hidden && (exhibit.type !== 'document' || requirementsMet(exhibit, earned)))
const visibleIds = new Set(visibleExhibits.map(exhibit => exhibit.id))
const sanitize = (exhibit: typeof visibleExhibits[number]) => {
if (exhibit.type !== 'document') return exhibit
const { requiredFlags: _requirements, ...document } = exhibit
return document
}
return {
...full,
exhibits: visibleExhibits.map(sanitize),
relations: full.relations.filter(relation => visibleIds.has(relation.fromExhibitId) && visibleIds.has(relation.toExhibitId)),
connections: full.connections.filter(connection => visibleIds.has(connection.fromExhibitId) && visibleIds.has(connection.toExhibitId)),
newlyVisibleDocumentIds: visibleExhibits.flatMap(exhibit => exhibit.type === 'document' && !seen.has(exhibit.id) ? [exhibit.id] : []),
}
}
function appendMissingById<T extends { id: string }>(submitted: T[], preserved: T[]) {
const ids = new Set(submitted.map(item => item.id))
return [...submitted, ...preserved.filter(item => !ids.has(item.id))]
}
/** Preserve server-hidden objects during the legacy whole-board PUT used by play mode. */
export function mergePlayerStateForPersistence(full: CaseState, visible: CaseState, submitted: CaseState): CaseState {
const visibleIds = new Set(visible.exhibits.map(exhibit => exhibit.id))
const unavailableIds = new Set(full.exhibits.filter(exhibit => !visibleIds.has(exhibit.id)).map(exhibit => exhibit.id))
const fullDocuments = new Map(full.exhibits.flatMap(exhibit => exhibit.type === 'document' ? [[exhibit.id, exhibit] as const] : []))
const submittedExhibits = submitted.exhibits.map(exhibit => exhibit.type === 'document'
? { ...exhibit, requiredFlags: fullDocuments.get(exhibit.id)?.requiredFlags || exhibit.requiredFlags || [] }
: exhibit)
const preservedExhibits = full.exhibits.filter(exhibit => unavailableIds.has(exhibit.id))
const touchesUnavailable = (item: ExhibitRelation | Connection) => unavailableIds.has(item.fromExhibitId) || unavailableIds.has(item.toExhibitId)
return {
...submitted,
report: full.report,
exhibits: appendMissingById(submittedExhibits, preservedExhibits),
relations: appendMissingById(submitted.relations, full.relations.filter(touchesUnavailable)),
connections: appendMissingById(submitted.connections, full.connections.filter(touchesUnavailable)),
newlyVisibleDocumentIds: [],
}
}