/** * What the scene view shows of each placeable type: its symbol, its name, and for a collider its * outline. It is saved with the project, so whoever opens it sees the same scene, and a project * written before there was such a field reads as the defaults. */ import { hasSceneIcon, type PlaceableAssetType } from './AssetTypes'; export type VisibilityPart = 'symbol' | 'label' | 'shape'; export type TypeVisibility = Record; export type SceneVisibility = Record; /** Every placeable type, in the order its name sorts – Behavior to Tilemap. */ export const VISIBILITY_TYPES: readonly PlaceableAssetType[] = [ 'behavior', 'camera', 'clip', 'collider', 'container', 'particles', 'rigidbody', 'sprite', 'text', 'tilemap', ]; /** * The switches a type has. Only a type with a symbol can hide it, every type can show its name, * and only what collides has an outline to hide: a collider its shape, a tilemap the outline of * its solid cells – the map's art stays. A sprite without its picture would be nothing at all. */ export function visibilityParts(type: PlaceableAssetType): VisibilityPart[] { const parts: VisibilityPart[] = hasSceneIcon(type) ? ['symbol', 'label'] : ['label']; if (type === 'collider' || type === 'tilemap' || type === 'clip') parts.push('shape'); return parts; } /** What a type shows until it is switched: all of it, except a name nothing showed before. */ export function defaultVisibility(type: PlaceableAssetType): TypeVisibility { return { symbol: true, label: hasSceneIcon(type), shape: true }; } export function defaultSceneVisibility(): SceneVisibility { return Object.fromEntries( VISIBILITY_TYPES.map((type) => [type, defaultVisibility(type)]), ) as SceneVisibility; } /** Read off untrusted JSON: every type and part that is missing or not a boolean is the default. */ export function sceneVisibilityOf(raw: unknown): SceneVisibility { const stored = (raw as { sceneVisibility?: unknown } | null)?.sceneVisibility; const out = defaultSceneVisibility(); if (!stored || typeof stored !== 'object') return out; for (const type of VISIBILITY_TYPES) { const entry = (stored as Record)[type]; if (!entry || typeof entry !== 'object') continue; for (const part of Object.keys(out[type]) as VisibilityPart[]) { const value = (entry as Record)[part]; if (typeof value === 'boolean') out[type][part] = value; } } return out; } /** Whether the scene view draws `part` of an object of `type`; a type without that part never does. */ export function shows(visibility: SceneVisibility, type: string, part: VisibilityPart): boolean { const entry = (visibility as Partial>)[type]; if (!entry) return part !== 'label'; if (!visibilityParts(type as PlaceableAssetType).includes(part)) return part === 'shape'; return entry[part]; }