log.console("paladin game starting") var time = use('time') var random = use('random').random var core = use('core') var sprite = use('sprite') var text2d = use('text2d') var particles2d = use('particles2d') var compositor = use('compositor') var film2d = use('film2d') var tilemap2d = use('tilemap2d') var clay = use('clay') var sprites = [] // Keep references for cleanup var emitters = [] // Particle emitters var bunnies = [] // Bouncing bunnies for Y-sort test // Cameras var world_camera = { pos: {x: 2.5, y: 2.5}, width: 5, height: 5, anchor: {x: 0.5, y: 0.5} } var hud_camera = { pos: {x: 0, y: 0}, width: 640, height: 360, anchor: {x: 0, y: 0} } // Group effects definition - THIS IS THE KEY PART var group_effects = { 'bloom': { effects: [{type: 'bloom', threshold: 0.1, intensity: 5, blur_passes: 1}] }, 'masked_fire': { effects: [ {type: 'bloom', threshold: 0.1, intensity: 5, blur_passes: 0}, {type: 'mask', channel: 'alpha', mask_group: 'mask_text'} ] }, 'corner_fire': { effects: [{type: 'mask', channel: 'alpha', mask_group: 'corner_mask'}] } } // Compositor config - pure data var compositor_config = { clear: {r: 0, g: 0, b: 0, a: 1}, planes: [ { name: 'world', plane: 'world', camera: world_camera, resolution: {width: 500, height: 500}, presentation: 'letterbox', clear: {r: 0.1, g: 0.1, b: 0.9, a: 1} }, { name: 'hud', plane: 'hud', camera: hud_camera, resolution: {width: 640, height: 360}, presentation: 'integer_scale', clear: {r: 0, g: 0, b: 0, a: 0}, layer_sort: { '100': 'y' // Bunny layer uses Y-sort } }, { name: 'ui', plane: 'ui', camera: hud_camera, resolution: {width: 640, height: 360}, presentation: 'integer_scale', clear: null } ], group_effects } function init_game() { // Cleanup old sprites var i = 0 for (i = 0; i < length(sprites); i++) sprites[i].destroy() sprites = [] emitters = [] bunnies = [] // World sprites // Background Tilemap var tiles = [] var x = 0 var y = 0 for (x = 0; x < 5; x++) { tiles[x] = [] for (y = 0; y < 5; y++) { tiles[x][y] = 'examples/tiles/terrain_dirt_cloud' } } sprites[] = tilemap2d({ plane: 'world', layer: 0, tiles: tiles, tile_width: 1, tile_height: 1, offset_x: 0, offset_y: 0 }) sprites[] = sprite({ plane: 'world', layer: 10, image: 'examples/enemies/saw_a', pos: {x: 1, y: 1}, anchor_x: 0.5, }) // HUD sprites sprites[] = text2d({ plane: 'hud', groups: ['mask_text'], // Effect routing only layer: 50, text: 'PALADIN', pos: {x: 640/2 - 270, y: 360/2}, font: 'examples/fonts/dos', size: 150 }) var masked_fire = particles2d.create({ plane: 'hud', groups: ['masked_fire'], // Effect routing only layer: 40, image: 'examples/tiles/fireball', width: 40, height: 40 }) sprites[] = masked_fire emitters[] = particles2d.emitters.create({ pos: {x: 320, y: 180}, spawn_area: {width: 500, height: 200}, velocity: {x: 0, y: 50}, velocity_var: {x: 30, y: 30}, life: 2, rate: 60, scale: 3, scale_var: 0.3, color: {r: 1, g: 0.6, b: 0.2}, handle: masked_fire }) sprites[] = sprite({ plane: 'hud', groups: ['corner_mask'], // Effect routing only layer: 60, image: 'examples/tiles/fireball', pos: {x: 600, y: 40}, width: 50, height: 50, anchor_x: 0.5, anchor_y: 0.5 }) var corner_fire = particles2d.create({ plane: 'hud', groups: ['corner_fire'], // Effect routing only layer: 70, image: 'examples/tiles/fireball', width: 25, height: 25 }) sprites[] = corner_fire emitters[] = particles2d.emitters.create({ pos: {x: 600, y: 40}, spawn_area: {width: 30, height: 30}, velocity: {x: 0, y: 30}, velocity_var: {x: 20, y: 20}, life: 1.5, rate: 10, scale: 1, scale_var: 0.2, color: {r: 1, g: 0.5, b: 0.1}, handle: corner_fire }) var bloom_fire = particles2d.create({ plane: 'hud', groups: ['bloom'], // Effect routing only layer: 80, image: 'examples/tiles/fireball', width: 48, height: 48 }) sprites[] = bloom_fire emitters[] = particles2d.emitters.create({ pos: {x: 100, y: 100}, spawn_area: {width: 50, height: 50}, velocity: {x: 100, y: 0}, velocity_var: {x: 40, y: 40}, life: 2.5, rate: 12, scale: 1.2, scale_var: 0.4, color: {r: 1, g: 0.8, b: 0.3}, handle: bloom_fire }) // Bouncing bunnies - exactly two, big, bottom of screen, Y-sort layer 100 var bunny = null for (i = 0; i < 2; i++) { bunny = sprite({ plane: 'hud', layer: 100, // This layer has Y-sort enabled image: 'examples/bunny', pos: {x: 320 + (i == 0 ? -20 : 20), y: 340}, width: 96, height: 96, anchor_x: 0.5, }) bunny._base_y = 340 bunny._t = i * 0.7 // phase offset so they don’t match bunny._amp = 240 // bounce height in pixels bunny._speed = 1 * random() // bounce speed sprites[] = bunny bunnies[] = bunny } log.console("World initialized - " + text(length(film2d.query({}))) + " drawables") } var json = use('json') var last_time = 0 function update(dt) { // Update all particle emitters var i = 0 for (i = 0; i < length(emitters); i++) particles2d.emitters.update(emitters[i], dt) // Update bouncing bunnies // Update bouncing bunnies (vertical bob only) var b = null var cycle = null var u = null for (i = 0; i < length(bunnies); i++) { b = bunnies[i] b._t += dt // Bob between base_y and base_y - amp using triangle wave cycle = (b._t * b._speed) % 2 u = cycle < 1 ? cycle : 2 - cycle b.pos.y = b._base_y - u * b._amp // Optional: if your Y-sort uses an explicit key, keep it in sync // b.y_sort_key = b.pos.y } } function render() { // 1. Build Clay UI var clay_tree = clay.layout(function() { clay.vstack({gap: 10, padding: 20, align: 'center'}, function() { clay.text("Clay UI Sample", {font_size: 24, color: {r:1,g:1,b:0}}) clay.image('examples/tiles/key_blue', {size:{width:32,height:32}, color: {r:1,g:0,b:0}}) clay.container({size: [200, 100], background_color: {r:0.2, g:0.2, b:0.2, a:0.8}, padding: 10}, function() { clay.vstack(function() { clay.text("List Item 1") clay.text("List Item 2") clay.text("List Item 3") }) }) }) }, {width: 640, height: 360}) // Assign clay drawables to UI plane compositor_config.planes[2].drawables = clay_tree.drawables compositor_config.planes[2].clear = {r: 0, g: 0, b: 0, a: 0} var plan = compositor.compile(compositor_config) return compositor.execute(plan) } init_game() core.start({ width: 1280, height: 720, title: "Paladin - Data Oriented", framerate: 60, update: update, render: render, editor: function(ui) { } })