diff --git a/examples/chess/config.js b/examples/chess/config.js new file mode 100644 index 00000000..d50f71be --- /dev/null +++ b/examples/chess/config.js @@ -0,0 +1,9 @@ +// Chess game configuration for Moth framework +return { + title: "Chess", + resolution: { width: 480, height: 480 }, + internal_resolution: { width: 480, height: 480 }, + fps: 60, + clearColor: [22/255, 120/255, 194/255, 1], + mode: 'stretch' // No letterboxing for chess +}; \ No newline at end of file diff --git a/scripts/modules/moth.js b/scripts/modules/moth.js new file mode 100644 index 00000000..558b335a --- /dev/null +++ b/scripts/modules/moth.js @@ -0,0 +1,140 @@ +/** + * Moth Game Framework + * Higher-level game development framework built on top of Prosperon + */ + +var os = use('os'); +var io = use('io'); +var render = use('render'); +var draw2d = use('draw2d'); +var actor = use('actor'); +var gameConfig = {}; +var gameDir = ""; + +// Framework initialization +function initialize(dir) { + gameDir = dir; + + // Load configuration if it exists + var configPath = `${dir}/config.js`; + if (io.exists(configPath)) { + gameConfig = use(configPath); + } + + // Set up default config values + gameConfig.resolution = gameConfig.resolution || { width: 640, height: 480 }; + gameConfig.internal_resolution = gameConfig.internal_resolution || gameConfig.resolution; + gameConfig.title = gameConfig.title || "Moth Game"; + gameConfig.fps = gameConfig.fps || 60; + gameConfig.clearColor = gameConfig.clearColor || [0, 0, 0, 1]; + + // Initialize render system + render.initialize({ + width: gameConfig.resolution.width, + height: gameConfig.resolution.height, + resolution_x: gameConfig.internal_resolution.width, + resolution_y: gameConfig.internal_resolution.height, + mode: gameConfig.mode || 'letterbox' + }); + + // Set up default camera + gameConfig.camera = gameConfig.camera || { + size: [gameConfig.internal_resolution.width, gameConfig.internal_resolution.height], + transform: os.make_transform(), + fov: 50, + near_z: 0, + far_z: 1000, + surface: undefined, + viewport: {x: 0, y: 0, width: 1, height: 1}, + ortho: true, + anchor: [0, 0] + }; + + // Set window title + prosperon.window.title = gameConfig.title; + + // Set up IO actor subscription + var ioguy = { + __ACTORDATA__: { + id: os.ioactor() + } + }; + + $_.send(ioguy, { + type: "subscribe", + actor: $_ + }); + + // Set up automatic receiver for input events + $_.receiver(function(e) { + // Handle quit + if (e.type === 'quit') { + os.exit(); + } + + // Forward all events to prosperon's dispatch system + prosperon.dispatch(e.type, e); + }); + + // Start the game loop + startGameLoop(); + + // Load the game's main.js + + var mainPath = `${dir}/main.js`; + if (io.exists(mainPath)) { + // Spawn the main game actor + actor.spawn(mainPath); + } else { + console.error(`No main.js found in ${dir}`); + } +} + +// Main game loop +function startGameLoop() { + var last = os.now(); + var fpsTimer = 0; + var fpsCount = 0; + var targetFrameTime = 1 / gameConfig.fps; + + function loop() { + var now = os.now(); + var dt = now - last; + last = now; + + // Dispatch update event + prosperon.dispatch('update', dt); + + // Clear and set up rendering + render.clear(gameConfig.clearColor); + render.camera(gameConfig.camera); + + // Dispatch draw event + prosperon.dispatch('draw'); + + // Present the frame + render.present(); + + // FPS counter + fpsTimer += dt; + fpsCount++; + if (fpsTimer >= 0.5) { + // Only update if the title wasn't changed by the game + if (prosperon.window.title === gameConfig.title) { + prosperon.window.title = `${gameConfig.title} - FPS ${(fpsCount / fpsTimer).toFixed(1)}`; + } + fpsTimer = fpsCount = 0; + } + + // Schedule next frame + $_.delay(loop, Math.max(0, targetFrameTime - (os.now() - now))); + } + + loop(); +} + +// Public API +return { + initialize: initialize, + config: gameConfig +}; \ No newline at end of file