Files
cell/tests/moth.js

223 lines
5.3 KiB
JavaScript

/**
* Moth Game Framework
* Higher-level game development framework built on top of Prosperon
*/
var os = use('os');
var io = use('io');
var transform = use('transform');
var rasterize = use('rasterize');
var video_actor = use('sdl_video')
var window
var render
var gameactor
$_.start(e => {
if (gameactor) return
gameactor = e.actor
loop()
}, 'tests/prosperon.js')
send(video_actor, {
kind: "window",
op:"create"
}, e => {
if (e.error) {
console.error(e.error)
os.exit(1)
}
window = e.id
send(video_actor,{
kind:"window",
op:"makeRenderer",
id:window
}, e => {
if (e.error) {
console.error(e.error)
os.exit(1)
}
render = e.id
console.log(`Created window and renderer id ${render}`)
})
})
var last = os.now()
// Engine state
var camera = {
x: 0,
y: 0,
scale: 1,
rotation: 0
}
// Convert high-level draw commands to low-level renderer commands
function translate_draw_commands(commands) {
var renderer_commands = []
commands.forEach(function(cmd) {
if (cmd.material && cmd.material.color) {
renderer_commands.push({
op: "set",
prop: "drawColor",
value: cmd.material.color
})
}
switch(cmd.cmd) {
case "draw_rect":
// Handle rectangles with optional rounding and thickness
if (cmd.opt && cmd.opt.radius && cmd.opt.radius > 0) {
// Rounded rectangle
var thickness = (cmd.opt.thickness === 0) ? 0 : (cmd.opt.thickness || 1)
var raster_result = rasterize.round_rect(cmd.rect, cmd.opt.radius, thickness)
if (raster_result.type === 'rect') {
renderer_commands.push({
op: "fillRect",
data: {rect: raster_result.data}
})
} else if (raster_result.type === 'rects') {
// SDL video expects 'rects' operation, not 'fillRects'
raster_result.data.forEach(function(rect) {
renderer_commands.push({
op: "fillRect",
data: {rect: rect}
})
})
}
} else if (cmd.opt && cmd.opt.thickness && cmd.opt.thickness > 0) {
// Outlined rectangle
var raster_result = rasterize.outline_rect(cmd.rect, cmd.opt.thickness)
if (raster_result.type === 'rect') {
renderer_commands.push({
op: "fillRect",
data: {rect: raster_result.data}
})
} else if (raster_result.type === 'rects') {
// SDL video expects 'rects' operation with array
renderer_commands.push({
op: "rects",
data: {rects: raster_result.data}
})
}
} else {
// Filled rectangle
renderer_commands.push({
op: "fillRect",
data: {rect: cmd.rect}
})
}
break
case "draw_circle":
case "draw_ellipse":
break
// Rasterize ellipse to points or rects
var radii = cmd.radii || [cmd.radius, cmd.radius]
var raster_result = rasterize.ellipse(cmd.pos, radii, cmd.opt || {})
if (raster_result.type === 'points') {
renderer_commands.push({
op: "point",
data: {points: raster_result.data}
})
} else if (raster_result.type === 'rects') {
// Use 'rects' operation for multiple rectangles
renderer_commands.push({
op: "rects",
data: {rects: raster_result.data}
})
}
break
case "draw_line":
renderer_commands.push({
op: "line",
data: {points: cmd.points}
})
break
case "draw_point":
renderer_commands.push({
op: "point",
data: {points: [cmd.pos]}
})
break
case "draw_image":
// TODO: Handle image loading and texture management
renderer_commands.push({
op: "texture",
data: cmd
})
break
case "draw_text":
// Use debugText for now
renderer_commands.push({
op: "debugText",
data: {
pos: cmd.pos || {x: 0, y: 0},
text: cmd.text || ""
}
})
break
}
})
return renderer_commands
}
function loop()
{
os.frame()
var now = os.now()
var dt = now - last
last = now
// Update the game
send(gameactor, {kind:'update', dt:dt}, e => {
// Get draw commands from game
send(gameactor, {kind:'draw'}, draw_commands => {
var batch_commands = []
batch_commands.push({
op: "set",
prop: "drawColor",
value: [0.1,0.1,0.15,1]
})
// Clear the screen
batch_commands.push({
op: "clear"
})
if (draw_commands && draw_commands.length > 0) {
var renderer_commands = translate_draw_commands(draw_commands)
batch_commands = batch_commands.concat(renderer_commands)
}
batch_commands.push({
op: "present"
})
send(video_actor, {
kind: "renderer",
id: render,
op: "batch",
data: batch_commands
}, _ => {
var diff = os.now() - now
$_.delay(loop,1/240)
})
})
})
}