You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

75 lines
2.5 KiB
TypeScript

import { Assets, Texture } from 'pixi.js';
import {
fetchGameTextureManifest,
tryResolveGameAssetFile,
type GameTextureManifest,
} from './resolveGameAssetUrl';
import { getRequiredSpriteKeys } from './spriteMapping';
export class GameSpriteRegistry {
private _manifest: GameTextureManifest | null = null;
private _textures = new Map<string, Texture>();
private _ready = false;
private _buildFallbackManifest(keys: string[]): GameTextureManifest {
const textures: Record<string, { file: string; kind: string }> = {};
for (const key of keys) {
let file: string | null = null;
if (key.startsWith('terrain.')) file = `tiles/${key}.png`;
else if (key.startsWith('prop.')) file = `prop/${key}.png`;
else if (key.startsWith('building.')) file = `building/${key}.png`;
else if (key.startsWith('enemy.')) file = `enemies/${key}.png`;
else if (key.startsWith('npc.') || key.startsWith('hero.')) file = `characters/${key}.png`;
if (!file) continue;
textures[key] = { file, kind: 'fallback' };
}
return {
version: 0,
note: 'Fallback manifest (generated at runtime).',
textures,
};
}
get ready(): boolean {
return this._ready;
}
get manifest(): GameTextureManifest | null {
return this._manifest;
}
async loadAll(): Promise<void> {
const requiredKeys = getRequiredSpriteKeys();
try {
this._manifest = await fetchGameTextureManifest();
} catch (error) {
console.warn('[Assets] Manifest load failed, using fallback manifest.', error);
this._manifest = this._buildFallbackManifest(requiredKeys);
}
for (const key of requiredKeys) {
if (!this._manifest.textures[key]) {
console.warn(`[Assets] Missing manifest entry for sprite key: ${key}`);
}
}
const entries = Object.entries(this._manifest.textures);
await Promise.all(
entries.map(async ([key, entry]) => {
const url = tryResolveGameAssetFile(entry.file);
if (!url) {
console.warn(`[Assets] Missing sprite file in bundle: ${entry.file}`);
return;
}
const texture = await Assets.load<Texture>(url);
// Pixel-art tiles/props: nearest filtering avoids subpixel seam artifacts on tile borders.
texture.source.scaleMode = 'nearest';
this._textures.set(key, texture);
}),
);
this._ready = true;
}
getTexture(key: string): Texture | null {
return this._textures.get(key) ?? null;
}
}