124 lines
3.2 KiB
TypeScript
124 lines
3.2 KiB
TypeScript
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;
|
|
language?: 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: '',
|
|
language: 'en_US',
|
|
},
|
|
};
|
|
}
|
|
|
|
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 ?? {}),
|
|
language: parsed.metadata?.language ?? parsed.locale ?? fallback.metadata.language,
|
|
},
|
|
};
|
|
}
|
|
|
|
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/',
|
|
},
|
|
};
|
|
}
|