Add ink integration UI and media playback
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import path from 'path';
|
||||
import { existsSync, mkdirSync, readFileSync } from 'fs';
|
||||
|
||||
export type EngineName = 'yaml' | 'ink' | 'zork' | string;
|
||||
|
||||
export interface GameMetadata {
|
||||
title: string;
|
||||
author?: string;
|
||||
subtitle?: string;
|
||||
version?: string;
|
||||
copyright?: string;
|
||||
}
|
||||
|
||||
export interface GamePaths {
|
||||
mainGameFile: string;
|
||||
inkSource?: string;
|
||||
inkCompiled?: string;
|
||||
promptDir?: string;
|
||||
music?: string;
|
||||
sfx?: string;
|
||||
images?: string;
|
||||
[key: string]: string | undefined;
|
||||
}
|
||||
|
||||
export interface GameEngineConfig {
|
||||
engine: EngineName;
|
||||
locale: 'en_US' | 'de_DE' | string;
|
||||
paths: GamePaths;
|
||||
metadata: GameMetadata;
|
||||
}
|
||||
|
||||
const PROJECT_ROOT = path.resolve(__dirname, '../..');
|
||||
|
||||
function fallbackConfig(engine: EngineName): GameEngineConfig {
|
||||
return {
|
||||
engine,
|
||||
locale: 'en_US',
|
||||
paths: {
|
||||
mainGameFile:
|
||||
engine === 'ink'
|
||||
? 'data/ink/story.ink.json'
|
||||
: engine === 'zork'
|
||||
? 'data/z-code/zork1.bin'
|
||||
: 'data/worlds/example_world.yml',
|
||||
music: 'public/music',
|
||||
sfx: 'public/sounds',
|
||||
images: 'public/images',
|
||||
},
|
||||
metadata: {
|
||||
title: 'AI Interactive Fiction',
|
||||
author: 'Generative AI',
|
||||
subtitle: 'An open-world text adventure',
|
||||
version: '1.0.0',
|
||||
copyright: '',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function projectPath(relativeOrAbsolutePath: string): string {
|
||||
return path.isAbsolute(relativeOrAbsolutePath)
|
||||
? relativeOrAbsolutePath
|
||||
: path.resolve(PROJECT_ROOT, relativeOrAbsolutePath);
|
||||
}
|
||||
|
||||
export function loadGameConfig(configPath: string, engine: EngineName): GameEngineConfig {
|
||||
const absolutePath = projectPath(configPath);
|
||||
if (!existsSync(absolutePath)) {
|
||||
console.warn(`[config] Missing ${absolutePath}; using ${engine} defaults.`);
|
||||
return fallbackConfig(engine);
|
||||
}
|
||||
|
||||
const parsed = JSON.parse(readFileSync(absolutePath, 'utf8')) as Partial<GameEngineConfig>;
|
||||
const fallback = fallbackConfig(engine);
|
||||
return {
|
||||
engine: parsed.engine ?? fallback.engine,
|
||||
locale: parsed.locale ?? fallback.locale,
|
||||
paths: {
|
||||
...fallback.paths,
|
||||
...(parsed.paths ?? {}),
|
||||
},
|
||||
metadata: {
|
||||
...fallback.metadata,
|
||||
...(parsed.metadata ?? {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function ensureConfiguredAssetDirectories(config: GameEngineConfig): void {
|
||||
const directories = [
|
||||
config.paths.music,
|
||||
config.paths.sfx,
|
||||
config.paths.images,
|
||||
config.paths.inkSource ? path.dirname(config.paths.inkSource) : undefined,
|
||||
config.paths.inkCompiled ? path.dirname(config.paths.inkCompiled) : undefined,
|
||||
config.paths.mainGameFile ? path.dirname(config.paths.mainGameFile) : undefined,
|
||||
config.paths.promptDir,
|
||||
];
|
||||
|
||||
for (const directory of directories) {
|
||||
if (!directory) continue;
|
||||
const absolutePath = projectPath(directory);
|
||||
if (!existsSync(absolutePath)) {
|
||||
mkdirSync(absolutePath, { recursive: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function clientGameConfig(config: GameEngineConfig) {
|
||||
return {
|
||||
engine: config.engine,
|
||||
locale: config.locale,
|
||||
metadata: config.metadata,
|
||||
assets: {
|
||||
music: '/music/',
|
||||
sfx: '/sounds/',
|
||||
sounds: '/sounds/',
|
||||
images: '/images/',
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user