feat: add local image enhancement and lossless mod assets
This commit is contained in:
1 parent
cc71050362
commit
f7f7f5b1b3
29 files changed
+1495
-10
No files matched your search
@@ -2,6 +2,7 @@
|
||||
|
||||
import { AudioSystem } from "./audio";
|
||||
import { VolumeStore } from "./volume-store";
|
||||
import { LiveImages } from "../imager/live";
|
||||
import { BlockingCall, JumpSignal, PyDict, Python, decodeValue, encodeValue, pyStr, truthy, userBuiltins, type Scope } from "./python";
|
||||
import { Renderer, type ResolvedImage } from "./renderer";
|
||||
import { fastPoint, interpolate, parseText, plainText, renderText, waitPoints } from "./text";
|
||||
@@ -24,6 +25,8 @@ interface LabelRef {
|
||||
}
|
||||
|
||||
export class Engine {
|
||||
private liveImages?: LiveImages;
|
||||
private imageScales = new Map<string, number>();
|
||||
private labels = new Map<string, LabelRef>();
|
||||
private anchors = new Map<string, { label: string; path: number[]; params?: [string, Expr | null][] | null }>();
|
||||
private imagesByTag = new Map<string, { attrs: string[]; key: string }[]>();
|
||||
@@ -136,6 +139,7 @@ export class Engine {
|
||||
root.append(this.container);
|
||||
this.renderer = new Renderer(this.container, w, h, {
|
||||
url: (f) => this.fileUrl(f),
|
||||
assetScale: (url) => this.imageScales.get(url) ?? 1,
|
||||
resolveRef: (name) => this.resolveImage(name.split(" "))?.def ?? null,
|
||||
evalCond: (c) => truthy(this.safeEval(c)),
|
||||
transform: (name) => game.transforms[name] ?? null,
|
||||
@@ -153,6 +157,7 @@ export class Engine {
|
||||
});
|
||||
this.audio = new AudioSystem(this.settings, (f) => this.fileUrl(f), game.channels ?? {});
|
||||
this.buildUI();
|
||||
if (manifest.versionId !== undefined) this.liveImages = new LiveImages(manifest.versionId, (message) => this.notify(`Улучшение графики: ${message}`));
|
||||
this.resizeObserver = new ResizeObserver(() => this.renderer.fit(this.container));
|
||||
this.resizeObserver.observe(this.container);
|
||||
this.renderer.fit(this.container);
|
||||
@@ -161,6 +166,7 @@ export class Engine {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.liveImages?.destroy();
|
||||
this.screens.destroy();
|
||||
this.gen++;
|
||||
this.wakeAll();
|
||||
@@ -184,7 +190,13 @@ export class Engine {
|
||||
}
|
||||
}
|
||||
const entry = logical ? this.manifest.files[logical] : undefined;
|
||||
return entry ? `/api/versions/${this.manifest.versionId}/files/${entry.hash}` : null;
|
||||
if (!entry || !logical) return null;
|
||||
const source = `/api/versions/${this.manifest.versionId}/files/${entry.hash}`;
|
||||
const improved = this.liveImages?.url(logical, entry.hash, source);
|
||||
const url = improved || source;
|
||||
const baseScale = this.game.assetScale?.[logical] ?? 1;
|
||||
this.imageScales.set(url, baseScale * (improved ? this.liveImages!.scale(improved) : 1));
|
||||
return url;
|
||||
}
|
||||
|
||||
private resolveImage(name: string[]): ResolvedImage | null {
|
||||
@@ -698,10 +710,7 @@ export class Engine {
|
||||
if (resolved) return this.renderer.displayable(resolved.def);
|
||||
const url = this.fileUrl(value);
|
||||
if (!url) return null;
|
||||
const img = new Image();
|
||||
img.src = url;
|
||||
img.draggable = false;
|
||||
return img;
|
||||
return this.renderer.displayable({ file: value });
|
||||
}
|
||||
|
||||
private screenFont(file: string): string | null {
|
||||
@@ -2008,6 +2017,21 @@ export class Engine {
|
||||
row(reading, "Скорость текста", 0, 150, 5, s.cps, (v) => (v === 0 ? "Мгновенно" : `${v} симв/с`), (v) => (s.cps = v), "reading");
|
||||
row(reading, "Задержка авто", 0.3, 4, 0.1, s.autoDelay, (v) => `${v.toFixed(1)} с`, (v) => (s.autoDelay = v), "reading");
|
||||
const display = section("Игра", "Изменения сохраняются автоматически в этом браузере.");
|
||||
const graphics = section("Графика", "Локальный генератор настраивается в меню профиля → Улучшение графики. Результат подхватывается при следующем показе картинки.");
|
||||
const imageToggle = (label: string, checked: boolean, apply: (value: boolean) => void) => {
|
||||
const row = el("label", "pg-check"); const box = el("input");
|
||||
Object.assign(box, { type: "checkbox", checked });
|
||||
box.onchange = () => apply(box.checked); row.append(box, el("span", "", label)); graphics.append(row);
|
||||
};
|
||||
imageToggle("Показывать улучшенные изображения", this.liveImages?.enabled ?? false, (value) => {
|
||||
if (!this.liveImages) return;
|
||||
this.liveImages.enabled = value;
|
||||
this.liveImages.save();
|
||||
// Refresh shown scene for immediate original/result comparison.
|
||||
const shown = this.renderer.snapshot();
|
||||
for (const image of shown) { const resolved = this.resolveImage(image.name); if (resolved) this.renderer.show(resolved, image.tag, image.at, { zorder: image.zorder }); }
|
||||
});
|
||||
imageToggle("Улучшать по ходу игры", this.liveImages?.automatic ?? false, (value) => { if (this.liveImages) { this.liveImages.automatic = value; this.liveImages.save(); } });
|
||||
const langs = this.languages();
|
||||
if (langs.length > 1) {
|
||||
const r = el("label", "pg-setting");
|
||||
|
||||
Reference in new issue
Block a user