feat: add native Tomie adapter and strict import compatibility checks
This commit is contained in:
1 parent
c8d83b3fe7
commit
57a4edcbfd
45 files changed
+2985
-71
No files matched your search
@@ -15,7 +15,7 @@ function blob(manifest: Manifest, path: string) {
|
||||
return `/api/versions/${manifest.versionId}/files/${f.hash}`;
|
||||
}
|
||||
|
||||
function saveApi(versionId: number): SaveApi {
|
||||
function saveApi<T = SaveData>(versionId: number): SaveApi<T> {
|
||||
const base = `/api/versions/${versionId}/saves`;
|
||||
return {
|
||||
list: () => getJSON(base),
|
||||
@@ -23,7 +23,7 @@ function saveApi(versionId: number): SaveApi {
|
||||
const r = await fetch(`${base}/${slot}`);
|
||||
if (r.status === 404) return null;
|
||||
if (!r.ok) throw new Error(`${r.status}`);
|
||||
return (await r.json()).data as SaveData;
|
||||
return (await r.json()).data as T;
|
||||
},
|
||||
async put(slot, title, data) {
|
||||
const r = await fetch(`${base}/${slot}`, {
|
||||
@@ -39,8 +39,10 @@ function saveApi(versionId: number): SaveApi {
|
||||
};
|
||||
}
|
||||
|
||||
export interface PlayerHandle { destroy(): void }
|
||||
|
||||
export async function launch(root: HTMLElement, versionId: number, onExit: () => void,
|
||||
onProgress: (text: string) => void): Promise<Engine> {
|
||||
onProgress: (text: string) => void): Promise<PlayerHandle> {
|
||||
onProgress("Загрузка манифеста");
|
||||
const manifest = await getJSON<Manifest>(`/api/versions/${versionId}/manifest`);
|
||||
const game = await getJSON<GameData>(blob(manifest, "game.json"));
|
||||
@@ -53,6 +55,39 @@ export async function launch(root: HTMLElement, versionId: number, onExit: () =>
|
||||
Promise.all(game.scripts.map((s) => getJSON<ScriptModule>(blob(manifest, s)))),
|
||||
loadTranslator(game, manifest, lang),
|
||||
]);
|
||||
if (game.runtime === "tomie/1") {
|
||||
if (!game.tomie) throw new Error("Не найдены исходные настройки Tomie");
|
||||
const { TomiePlayer } = await import("./tomie-player");
|
||||
const scripts: import("./nani-runtime").NaniPackage["scripts"] = Object.create(null);
|
||||
const resources: NonNullable<GameData["naniResources"]> = Object.create(null);
|
||||
for (const module of modules) {
|
||||
if (!module.nani) {
|
||||
for (const node of module.labels.flatMap((label) => label.body)) {
|
||||
const record = node.resource;
|
||||
if (!record || typeof node.what !== "string" || typeof record.collection !== "string" || typeof record.key !== "string") continue;
|
||||
const collection = resources[record.collection] ??= Object.create(null);
|
||||
(collection[record.key] ??= []).push({ ...record, tid: node.tid, text: node.what });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const nodes = module.labels.flatMap((label) => label.body);
|
||||
const commands = nodes.map((node) => {
|
||||
const command = structuredClone(node.command);
|
||||
if (node.textField && typeof node.what === "string") command.args[node.textField] = { template: node.what };
|
||||
command.tid = node.tid ?? command.tid; return command;
|
||||
});
|
||||
const labels = module.nani.label_ids ? Object.fromEntries(Object.entries(module.nani.label_ids).map(([label, id]) => {
|
||||
const index = id ? nodes.findIndex((node) => node.id === id) : nodes.length;
|
||||
if (index < 0) throw new Error("Не найдена команда метки Naninovel: " + label);
|
||||
return [label, index];
|
||||
})) : module.nani.labels;
|
||||
scripts[module.nani.name] = { commands, labels, label_depths: module.nani.label_depths };
|
||||
}
|
||||
if (url.get("at")) throw new Error("Предпросмотр Tomie с отдельной строки ещё не поддерживается. Откройте игру с начала.");
|
||||
const player = new TomiePlayer(root, { ...game.tomie, scripts, resources }, manifest,
|
||||
saveApi<import("./tomie-player").TomieSave>(versionId), tr, onExit);
|
||||
try { player.start(); return player; } catch (error) { player.destroy(); throw error; }
|
||||
}
|
||||
const engine = new Engine(root, game, manifest, modules, saveApi(versionId),
|
||||
`pgvn:persistent:${versionId}`, onExit, tr,
|
||||
(code) => loadTranslator(game, manifest, code));
|
||||
|
||||
Reference in new issue
Block a user