mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
- i18n scanner: lastError sink, no false sinks on defaults and ternaries - shared src/i18n/marker.js for modules that only mark strings - wind field messages move to wind/fields.js; CITY_POIS imports from cityPois.js - drop review IDs, change history and stale references from comments and docs Co-Authored-By: Claude Opus 5.5 <[email protected]>
362 lines
10 KiB
JavaScript
362 lines
10 KiB
JavaScript
import { SOURCE_PATH_BEAT_IDS } from '../data/bhoteKoshiShotPaths.js';
|
|
|
|
/**
|
|
* Nepal evidence expansion. Coordinates and camera framing come from
|
|
* the source event pack; existing authored cameras remain owned by the Scene.
|
|
*/
|
|
const ADDITIONAL_SHOTS = [
|
|
{
|
|
id: 'second-landslide',
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Second landslide',
|
|
lat: 28.399234,
|
|
lon: 85.535806,
|
|
alt: 10145,
|
|
heading: 224,
|
|
pitch: -38,
|
|
hold: 11,
|
|
},
|
|
{
|
|
id: 'dhunche',
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Dhunche',
|
|
lat: 28.161431,
|
|
lon: 85.324278,
|
|
alt: 6212,
|
|
heading: 208,
|
|
pitch: -39,
|
|
hold: 16,
|
|
},
|
|
{
|
|
id: 'mailung-upper-trishuli',
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Mailung / Upper Trishuli bridge',
|
|
lat: 28.140642,
|
|
lon: 85.265996,
|
|
alt: 6353,
|
|
heading: 211,
|
|
pitch: -40,
|
|
hold: 13,
|
|
},
|
|
{
|
|
id: 'mailung-bazzar',
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Mailung Bazzar, Dandaguan',
|
|
lat: 28.126526,
|
|
lon: 85.248027,
|
|
alt: 6331,
|
|
heading: 212,
|
|
pitch: -40,
|
|
hold: 15,
|
|
},
|
|
{
|
|
id: 'dandagaun',
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Dandagaun, Rasuwa',
|
|
lat: 28.124424,
|
|
lon: 85.245154,
|
|
alt: 6311,
|
|
heading: 214,
|
|
pitch: -40,
|
|
hold: 15,
|
|
},
|
|
{
|
|
id: 'dandagaun-viewpoint',
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Dandagaun viewpoint restaurant',
|
|
lat: 28.086663,
|
|
lon: 85.229914,
|
|
alt: 6740,
|
|
heading: 215,
|
|
pitch: -41,
|
|
hold: 13,
|
|
},
|
|
{
|
|
id: 'betrawati-bazaar',
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Betrawati Bazaar',
|
|
lat: 28.029827,
|
|
lon: 85.227694,
|
|
alt: 7034,
|
|
heading: 216,
|
|
pitch: -41,
|
|
hold: 12,
|
|
},
|
|
{
|
|
id: 'bhainse',
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhainse',
|
|
lat: 28.023087,
|
|
lon: 85.229449,
|
|
alt: 8051,
|
|
heading: 219,
|
|
pitch: -41,
|
|
hold: 13,
|
|
},
|
|
{
|
|
id: 'devighat-taadi-khola-bridge',
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Devighat / Taadi Khola Bridge',
|
|
lat: 27.921132,
|
|
lon: 85.177113,
|
|
alt: 9056,
|
|
heading: 224,
|
|
pitch: -42,
|
|
hold: 13,
|
|
},
|
|
{
|
|
id: 'charaudi',
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Charaudi',
|
|
lat: 27.865095,
|
|
lon: 84.838424,
|
|
alt: 10347,
|
|
heading: 232,
|
|
pitch: -42,
|
|
hold: 11,
|
|
},
|
|
{
|
|
id: 'final-view',
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Final view',
|
|
lat: 28.0834,
|
|
lon: 85.206,
|
|
alt: 113520,
|
|
heading: 64,
|
|
pitch: -83,
|
|
roll: 174,
|
|
duration: 4,
|
|
hold: 11,
|
|
locatorPresentation: 'bhote-koshi-incident-places',
|
|
},
|
|
];
|
|
const EVIDENCE_ORDER = [
|
|
'immediate-collapse-viewpoint',
|
|
'debris-dammed-lake',
|
|
'second-landslide',
|
|
'gyirong-border-gate',
|
|
'timure-cluster',
|
|
'syabru-besi',
|
|
'dhunche',
|
|
'mailung-upper-trishuli',
|
|
'mailung-bazzar',
|
|
'dandagaun',
|
|
'dandagaun-viewpoint',
|
|
'betrawati-bazaar',
|
|
'bhainse',
|
|
'bidur-trishuli-bridge',
|
|
'devighat-taadi-khola-bridge',
|
|
'charaudi',
|
|
'final-view',
|
|
];
|
|
|
|
// Per-reach motion timing keeps the schematic front ahead of the fixed 4.2s
|
|
// camera flights without pretending short urban reaches and long valley runs
|
|
// should advance at one global speed.
|
|
const PATH_DURATION_BY_BEAT = Object.freeze({
|
|
'debris-dammed-lake': 4.8,
|
|
'second-landslide': 6.5,
|
|
'timure-cluster': 2.1,
|
|
'syabru-besi': 2.8,
|
|
dhunche: 2.5,
|
|
'mailung-upper-trishuli': 2.9,
|
|
'mailung-bazzar': 2.1,
|
|
dandagaun: 1.2,
|
|
'dandagaun-viewpoint': 2.5,
|
|
'betrawati-bazaar': 2.9,
|
|
bhainse: 1.5,
|
|
'bidur-trishuli-bridge': 2.6,
|
|
});
|
|
|
|
/** Extend the six-beat Nepal pack without replacing existing authored shots. */
|
|
export function expandNepalEvidencePack(base) {
|
|
const layerId = base.requiredSourcePackLayerId;
|
|
const extraShots = ADDITIONAL_SHOTS.map(
|
|
({ id, hold, duration = 4.2, locatorPresentation, ...camera }) => ({
|
|
...camera,
|
|
roll: Number.isFinite(Number(camera.roll)) ? Number(camera.roll) : 0,
|
|
duration,
|
|
hold,
|
|
mapStack: 'photoreal',
|
|
layers: {
|
|
[layerId]: {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: id,
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
...(locatorPresentation
|
|
? {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: locatorPresentation },
|
|
},
|
|
}
|
|
: {}),
|
|
},
|
|
}),
|
|
);
|
|
const byBeat = new Map(
|
|
[...base.cameraPath, ...extraShots].map((shot) => [
|
|
shot.layers[layerId].params.beatId,
|
|
shot,
|
|
]),
|
|
);
|
|
const cameraPath = EVIDENCE_ORDER.map((id) => byBeat.get(id));
|
|
const runtimeControlsByBeat = { ...base.runtimeControlsByBeat };
|
|
const cameraLedPathBeats = new Set([
|
|
'timure-cluster',
|
|
'syabru-besi',
|
|
...SOURCE_PATH_BEAT_IDS.slice(2),
|
|
]);
|
|
const mediaLedPathBeats = new Set(['debris-dammed-lake', 'second-landslide']);
|
|
const finalSourcedPathBeatId = SOURCE_PATH_BEAT_IDS.at(-1);
|
|
const postSourceHistoryBeats = new Set([
|
|
'devighat-taadi-khola-bridge',
|
|
'charaudi',
|
|
'final-view',
|
|
]);
|
|
for (const shot of cameraPath) {
|
|
const id = shot.layers[layerId].params.beatId;
|
|
const isFinalView = id === 'final-view';
|
|
const authoredPathDurationSec = PATH_DURATION_BY_BEAT[id];
|
|
runtimeControlsByBeat[id] = {
|
|
...runtimeControlsByBeat[id],
|
|
evidenceMediaAutoplay: !isFinalView,
|
|
...(isFinalView
|
|
? {}
|
|
: {
|
|
mediaPlaybackHoldSec: id === 'mailung-bazzar' ? 7 : 6,
|
|
mediaExitDurationSec: 0.65,
|
|
}),
|
|
deferEvidenceUntilCameraSettled: !isFinalView,
|
|
evidenceRevealDurationSec: 0.9,
|
|
evidencePath: SOURCE_PATH_BEAT_IDS.includes(id)
|
|
? 'source'
|
|
: ['gyirong-border-gate', 'timure-cluster', 'syabru-besi'].includes(id)
|
|
? 'comparison'
|
|
: 'none',
|
|
...(cameraLedPathBeats.has(id)
|
|
? {
|
|
evidencePathDuringCamera: true,
|
|
evidencePathPersistent: true,
|
|
...(authoredPathDurationSec
|
|
? { evidencePathTravelDurationSec: authoredPathDurationSec }
|
|
: {}),
|
|
}
|
|
: {}),
|
|
...(mediaLedPathBeats.has(id)
|
|
? {
|
|
evidencePathDuringMedia: true,
|
|
evidencePathPersistent: true,
|
|
evidencePathStartDelaySec: 0,
|
|
}
|
|
: {}),
|
|
};
|
|
if (SOURCE_PATH_BEAT_IDS.includes(id)) {
|
|
runtimeControlsByBeat[id] = {
|
|
...runtimeControlsByBeat[id],
|
|
evidenceHoldCard: true,
|
|
evidencePathHistory: true,
|
|
evidencePathDurationSec:
|
|
authoredPathDurationSec || Math.max(4, shot.hold),
|
|
minimumHoldSec: Math.max(4, shot.hold),
|
|
};
|
|
}
|
|
if (SOURCE_PATH_BEAT_IDS.slice(2).includes(id)) {
|
|
runtimeControlsByBeat[id] = {
|
|
...runtimeControlsByBeat[id],
|
|
evidencePathHistory: true,
|
|
evidencePathElevation: 'source',
|
|
};
|
|
}
|
|
if (postSourceHistoryBeats.has(id)) {
|
|
runtimeControlsByBeat[id] = {
|
|
...runtimeControlsByBeat[id],
|
|
evidencePath: 'history',
|
|
evidencePathHistory: true,
|
|
evidencePathHistoryBeatId: finalSourcedPathBeatId,
|
|
evidencePathPersistent: true,
|
|
evidencePathElevation: 'source',
|
|
};
|
|
}
|
|
const pathDurationSec =
|
|
Number(runtimeControlsByBeat[id].evidencePathDurationSec) ||
|
|
(!cameraLedPathBeats.has(id) &&
|
|
runtimeControlsByBeat[id].evidenceSequence === true
|
|
? Number(runtimeControlsByBeat[id].evidenceSequenceDurationSec) || 0
|
|
: 0);
|
|
const mediaPresentationSec = isFinalView
|
|
? 0
|
|
: (id === 'mailung-bazzar' ? 7 : 6) + 0.65;
|
|
const mediaAndPathHoldSec =
|
|
pathDurationSec > 0 && !cameraLedPathBeats.has(id)
|
|
? mediaLedPathBeats.has(id)
|
|
? Math.max(mediaPresentationSec, pathDurationSec)
|
|
: mediaPresentationSec + pathDurationSec
|
|
: mediaPresentationSec;
|
|
runtimeControlsByBeat[id] = {
|
|
...runtimeControlsByBeat[id],
|
|
minimumHoldSec:
|
|
id === 'mailung-bazzar'
|
|
? mediaAndPathHoldSec
|
|
: Math.max(
|
|
Number(runtimeControlsByBeat[id].minimumHoldSec) || 0,
|
|
mediaAndPathHoldSec,
|
|
),
|
|
};
|
|
}
|
|
// Keep the downstream sequence on its sourced river profile. Coarse Google
|
|
// mesh samples can contain cliff-height spikes that reverse apparent motion.
|
|
// Preserve each authored readable hold after the media-first timing floor.
|
|
for (const [id, seconds, sequenceSeconds = seconds] of [
|
|
['immediate-collapse-viewpoint', 10],
|
|
['gyirong-border-gate', 8, 2.5],
|
|
['timure-cluster', 12],
|
|
['syabru-besi', 11],
|
|
]) {
|
|
runtimeControlsByBeat[id] = {
|
|
...runtimeControlsByBeat[id],
|
|
evidenceHoldCard: true,
|
|
evidenceSequenceDurationSec: sequenceSeconds,
|
|
minimumHoldSec: Math.max(
|
|
Number(runtimeControlsByBeat[id].minimumHoldSec) || 0,
|
|
seconds,
|
|
),
|
|
};
|
|
}
|
|
const finalView = byBeat.get('final-view');
|
|
return Object.freeze({
|
|
...base,
|
|
version: 18,
|
|
cameraPath,
|
|
runtimeControlsByBeat,
|
|
// Clip trims must also replace older saved holds, not just lower a floor.
|
|
runtimeHoldSecByBeat: { 'mailung-bazzar': 7 + 0.65 },
|
|
expansionFromVersion: 17,
|
|
previousRequiredSourcePackBeatIds: EVIDENCE_ORDER.slice(0, -1),
|
|
previousRequiredSourcePackBeatIdVariants: [
|
|
base.requiredSourcePackBeatIds,
|
|
EVIDENCE_ORDER.slice(0, -1),
|
|
],
|
|
requiredSourcePackBeatIds: EVIDENCE_ORDER,
|
|
requiredShotTitles: [
|
|
...base.requiredShotTitles.slice(0, -base.cameraPath.length),
|
|
...cameraPath.map(({ title }) => title),
|
|
],
|
|
adoptExistingShotTitles: ['Final view'],
|
|
shotPatches: [
|
|
...(base.shotPatches || []),
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Final view',
|
|
layers: finalView.layers,
|
|
},
|
|
],
|
|
photorealSurfaceBeatIds: EVIDENCE_ORDER,
|
|
});
|
|
}
|