diff --git a/core.cm b/core.cm index 621118a0..77ba5fe5 100644 --- a/core.cm +++ b/core.cm @@ -13,6 +13,9 @@ // Headless mode (no window, no input, no render, no audio): // core.start({ headless: true, update: function(dt) { ... } }) +var io = use('cellfs') +io.mount('/Users/john/work/prosperon') + var time_mod = use('time') var core = {} diff --git a/examples/chess/board_view.cm b/examples/chess/board_view.cm new file mode 100644 index 00000000..07a845e9 --- /dev/null +++ b/examples/chess/board_view.cm @@ -0,0 +1,143 @@ +var shape = use('shape2d') +var text2d = use('text2d') + +var S = 60 +var GW = S * 8 +var GH = S * 8 + +var light_color = {r: 0.93, g: 0.93, b: 0.85, a: 1} +var dark_color = {r: 0.45, g: 0.55, b: 0.35, a: 1} +var select_color = {r: 1, g: 0.84, b: 0, a: 1} +var valid_color = {r: 0.6, g: 0.8, b: 0.4, a: 1} +var hover_color = {r: 0.8, g: 0.85, b: 0.6, a: 1} + +var piece_letter = { + king: "K", queen: "Q", rook: "R", + bishop: "B", knight: "N", pawn: "P" +} +var white_text = {r: 1, g: 1, b: 1, a: 1} +var black_text = {r: 0.1, g: 0.1, b: 0.1, a: 1} + +var board_shapes = [] +var piece_labels = {} +var status_label = null +var next_id = 0 + +function init(grid) { + var bx = 0, by = 0, row = null, col = null + for (by = 0; by < 8; by++) { + row = [] + for (bx = 0; bx < 8; bx++) { + col = ((bx + by) & 1) ? dark_color : light_color + push(row, shape.rect({ + pos: {x: bx * S + S / 2, y: by * S + S / 2}, + width: S, height: S, + fill: {r: col.r, g: col.g, b: col.b, a: col.a}, + plane: 'game', layer: 0 + })) + } + push(board_shapes, row) + } + + next_id = 0 + grid.each(function(p) { + next_id++ + p._id = next_id + piece_labels[text(next_id)] = text2d({ + text: piece_letter[p.kind], + pos: {x: p.coord[0] * S + S / 2, y: p.coord[1] * S + S / 2}, + size: 36, + color: (p.colour == 'white') ? white_text : black_text, + plane: 'game', layer: 1 + }) + }) + + status_label = text2d({ + text: "White's turn", pos: {x: 10, y: GH - 20}, + plane: 'hud', size: 14, color: {r: 1, g: 1, b: 1, a: 1} + }) +} + +function sync_pieces(grid) { + var keys = array(piece_labels) + var i = 0 + for (i = 0; i < length(keys); i++) { + piece_labels[keys[i]].visible = false + } + grid.each(function(p) { + var lbl = piece_labels[text(p._id)] + if (!lbl) return + lbl.pos.x = p.coord[0] * S + S / 2 + lbl.pos.y = p.coord[1] * S + S / 2 + lbl.visible = true + }) +} + +function rebuild_pieces(grid) { + var keys = array(piece_labels) + var i = 0 + for (i = 0; i < length(keys); i++) { + piece_labels[keys[i]].visible = false + } + grid.each(function(p) { + var key = text(p._id) + var lbl = piece_labels[key] + if (!lbl) { + lbl = text2d({ + text: piece_letter[p.kind], + pos: {x: p.coord[0] * S + S / 2, y: p.coord[1] * S + S / 2}, + size: 36, + color: (p.colour == 'white') ? white_text : black_text, + plane: 'game', layer: 1 + }) + piece_labels[key] = lbl + } + lbl.pos.x = p.coord[0] * S + S / 2 + lbl.pos.y = p.coord[1] * S + S / 2 + lbl.text = piece_letter[p.kind] + lbl.visible = true + }) +} + +function highlight(selectPos, validMoves, hover_gx, hover_gy) { + var x = 0, y = 0, col = null + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x++) { + col = ((x + y) & 1) ? dark_color : light_color + board_shapes[y][x].fill = {r: col.r, g: col.g, b: col.b, a: col.a} + } + } + + if (hover_gx >= 0 && hover_gx < 8 && hover_gy >= 0 && hover_gy < 8) { + board_shapes[hover_gy][hover_gx].fill = { + r: hover_color.r, g: hover_color.g, b: hover_color.b, a: hover_color.a + } + } + + if (!selectPos) return + + board_shapes[selectPos[1]][selectPos[0]].fill = { + r: select_color.r, g: select_color.g, b: select_color.b, a: select_color.a + } + + var i = 0, m = null + for (i = 0; i < length(validMoves); i++) { + m = validMoves[i] + board_shapes[m[1]][m[0]].fill = { + r: valid_color.r, g: valid_color.g, b: valid_color.b, a: valid_color.a + } + } +} + +function set_status(msg) { + status_label.text = msg +} + +return { + S: S, GW: GW, GH: GH, + init: init, + sync_pieces: sync_pieces, + rebuild_pieces: rebuild_pieces, + highlight: highlight, + set_status: set_status +} diff --git a/examples/chess/chess.ce b/examples/chess/chess.ce index 882ad1b1..2a23406a 100644 --- a/examples/chess/chess.ce +++ b/examples/chess/chess.ce @@ -2,16 +2,21 @@ var core = use('core') var camera = use('camera') var compositor = use('compositor') var input = use('input') -var shape = use('shape2d') -var text2d = use('text2d') var Grid = use('examples/chess/grid') var MovementSystem = use('examples/chess/movement') var startingPos = use('examples/chess/pieces').startingPosition var rules = use('examples/chess/rules') +var board_view = use('examples/chess/board_view') -var S = 60 -var GW = S * 8, GH = S * 8 +var grid = Grid(8, 8) +grid.width = 8 +grid.height = 8 +var mover = MovementSystem(grid, rules) +startingPos(grid) +board_view.init(grid) + +var S = board_view.S, GW = board_view.GW, GH = board_view.GH var move_history = [] var game_cam = camera.make({width: GW, height: GH, pos: {x: GW / 2, y: GH / 2}}) @@ -24,119 +29,19 @@ input.configure({ } }) -// Build board -var grid = Grid(8, 8) -grid.width = 8 -grid.height = 8 -var mover = MovementSystem(grid, rules) -startingPos(grid) - -// Board squares (shape2d) -var light_color = {r: 0.93, g: 0.93, b: 0.85, a: 1} -var dark_color = {r: 0.45, g: 0.55, b: 0.35, a: 1} -var select_color = {r: 1, g: 0.84, b: 0, a: 1} -var valid_color = {r: 0.6, g: 0.8, b: 0.4, a: 1} - -var board_shapes = [] -var bx = 0, by = 0, row = null, col = null -for (by = 0; by < 8; by++) { - row = [] - for (bx = 0; bx < 8; bx++) { - col = ((bx + by) & 1) ? dark_color : light_color - push(row, shape.rect({ - pos: {x: bx * S + S / 2, y: by * S + S / 2}, - width: S, height: S, - fill: {r: col.r, g: col.g, b: col.b, a: col.a}, - plane: 'game', layer: 0 - })) - } - push(board_shapes, row) -} - -// Piece letter abbreviations -var piece_letter = { - king: "K", queen: "Q", rook: "R", - bishop: "B", knight: "N", pawn: "P" -} -var white_color = {r: 1, g: 1, b: 1, a: 1} -var black_color = {r: 0.1, g: 0.1, b: 0.1, a: 1} - -// Piece labels — one per piece, keyed by piece id -var piece_labels = {} -var piece_id = 0 -grid.each(function(p) { - piece_id++ - p._id = piece_id - piece_labels[text(piece_id)] = text2d({ - text: piece_letter[p.kind], - pos: {x: p.coord[0] * S + S / 2, y: p.coord[1] * S + S / 2}, - size: 36, - color: (p.colour == 'white') ? white_color : black_color, - plane: 'game', layer: 1 - }) -}) - -// Selection state var selectPos = null var validMoves = [] - -// Mouse position in grid coords var hover_gx = -1, hover_gy = -1 -// Status text -var status_label = text2d({ - text: "White's turn", pos: {x: 10, y: GH - 20}, - plane: 'hud', size: 14, color: {r: 1, g: 1, b: 1, a: 1} -}) - -function update_status() { - status_label.text = mover.turn + "'s turn" -} - -function reset_board_colors() { - var x = 0, y = 0, col = null - for (y = 0; y < 8; y++) { - for (x = 0; x < 8; x++) { - col = ((x + y) & 1) ? dark_color : light_color - board_shapes[y][x].fill = {r: col.r, g: col.g, b: col.b, a: col.a} - } - } -} - -var hover_color = {r: 0.8, g: 0.85, b: 0.6, a: 1} - -function highlight_selection() { - reset_board_colors() - - // Hover highlight - if (hover_gx >= 0 && hover_gx < 8 && hover_gy >= 0 && hover_gy < 8) { - board_shapes[hover_gy][hover_gx].fill = { - r: hover_color.r, g: hover_color.g, b: hover_color.b, a: hover_color.a - } - } - - if (!selectPos) return - - // Highlight selected square - board_shapes[selectPos[1]][selectPos[0]].fill = { - r: select_color.r, g: select_color.g, b: select_color.b, a: select_color.a - } - - // Highlight valid moves - var i = 0, m = null - for (i = 0; i < length(validMoves); i++) { - m = validMoves[i] - board_shapes[m[1]][m[0]].fill = { - r: valid_color.r, g: valid_color.g, b: valid_color.b, a: valid_color.a - } - } +var file_letters = "abcdefgh" +function sq_name(x, y) { + return text(file_letters, x, x + 1) + text(y + 1) } function compute_valid_moves(from) { validMoves = [] var piece = grid.at(from)[0] if (!piece) return - var x = 0, y = 0, to = null, dest = null for (y = 0; y < 8; y++) { for (x = 0; x < 8; x++) { @@ -149,22 +54,6 @@ function compute_valid_moves(from) { } } -function sync_piece_sprites() { - var keys = array(piece_labels) - var i = 0 - for (i = 0; i < length(keys); i++) { - piece_labels[keys[i]].visible = false - } - grid.each(function(p) { - var lbl = piece_labels[text(p._id)] - if (!lbl) return - lbl.pos.x = p.coord[0] * S + S / 2 - lbl.pos.y = p.coord[1] * S + S / 2 - lbl.visible = true - }) -} - -// Input handler var game_input = { on_input: function(action, data) { var clicked = null, cell = null, is_valid = false, i = 0, src_piece = null @@ -173,7 +62,7 @@ var game_input = { if (action == 'cancel') { selectPos = null validMoves = [] - highlight_selection() + board_view.highlight(selectPos, validMoves, hover_gx, hover_gy) return } @@ -182,9 +71,7 @@ var game_input = { cell = grid.at(clicked) if (selectPos) { - // Try to move is_valid = false - i = 0 for (i = 0; i < length(validMoves); i++) { if (validMoves[i][0] == clicked[0] && validMoves[i][1] == clicked[1]) { is_valid = true @@ -197,13 +84,12 @@ var game_input = { if (src_piece && mover.tryMove(src_piece, clicked)) { move_history[] = sq_name(selectPos[0], selectPos[1]) + "-" + sq_name(clicked[0], clicked[1]) log.chess("move " + sq_name(selectPos[0], selectPos[1]) + "-" + sq_name(clicked[0], clicked[1]) + " turn=" + mover.turn) - sync_piece_sprites() - update_status() + board_view.sync_pieces(grid) + board_view.set_status(mover.turn + "'s turn") } selectPos = null validMoves = [] } else if (length(cell) && cell[0].colour == mover.turn) { - // Select different piece selectPos = clicked compute_valid_moves(selectPos) } else { @@ -211,13 +97,12 @@ var game_input = { validMoves = [] } } else { - // Select piece if (length(cell) && cell[0].colour == mover.turn) { selectPos = clicked compute_valid_moves(selectPos) } } - highlight_selection() + board_view.highlight(selectPos, validMoves, hover_gx, hover_gy) } } } @@ -226,11 +111,6 @@ input.player1().possess(game_input) // --- Probe endpoints for AI play --- var probe = use('probe') -var file_letters = "abcdefgh" -function sq_name(x, y) { - return text(file_letters, x, x + 1) + text(y + 1) -} - function piece_char(p) { var chars = {king: "K", queen: "Q", rook: "R", bishop: "B", knight: "N", pawn: "P"} var c = chars[p.kind] @@ -275,8 +155,8 @@ probe.register("chess", { if (mover.tryMove(piece, to)) { move_history[] = sq_name(fx, fy) + "-" + sq_name(tx, ty) log.chess("move " + sq_name(fx, fy) + "-" + sq_name(tx, ty) + " turn=" + mover.turn) - sync_piece_sprites() - update_status() + board_view.sync_pieces(grid) + board_view.set_status(mover.turn + "'s turn") return {ok: true, move: sq_name(fx, fy) + "-" + sq_name(tx, ty)} } return {ok: false, error: "move rejected"} @@ -297,7 +177,6 @@ core.start({ input: function(ev) { var wp = null if (ev.type == 'mouse_motion') { - // Convert pixel coords to grid coords via camera wp = game_cam.window_to_world(ev.pos[0], ev.pos[1]) if (wp) { hover_gx = floor(wp.x / S) @@ -307,7 +186,7 @@ core.start({ }, update: function(dt) { - highlight_selection() + board_view.highlight(selectPos, validMoves, hover_gx, hover_gy) }, render: function() { diff --git a/examples/chess/client.ce b/examples/chess/client.ce new file mode 100644 index 00000000..41d306f8 --- /dev/null +++ b/examples/chess/client.ce @@ -0,0 +1,166 @@ +var core = use('core') +var camera = use('camera') +var compositor = use('compositor') +var input = use('input') + +var Grid = use('examples/chess/grid') +var rules = use('examples/chess/rules') +var board_view = use('examples/chess/board_view') +var game_state = use('examples/chess/game_state') + +var grid = Grid(8, 8) +grid.width = 8 +grid.height = 8 +board_view.init(grid) + +var S = board_view.S, GW = board_view.GW, GH = board_view.GH + +var game_cam = camera.make({width: GW, height: GH, pos: {x: GW / 2, y: GH / 2}}) +var hud_cam = camera.make({width: GW, height: GH, pos: {x: GW / 2, y: GH / 2}}) + +input.configure({ + action_map: { + select: ['mouse_button_left'], + cancel: ['escape', 'mouse_button_right'] + } +}) + +// Network state +var my_color = null +var server_actor = null +var current_turn = "white" + +// Selection state (declared early for $receiver access) +var selectPos = null +var validMoves = [] +var hover_gx = -1, hover_gy = -1 + +board_view.set_status("Connecting to host...") + +$contact(function(server) { + if (server) { + server_actor = server + log.console("Connected to host!") + board_view.set_status("Connected, waiting for game...") + } else { + log.error("Failed to connect to host") + board_view.set_status("Connection failed!") + } +}, {address: "127.0.0.1", port: 7777}) + +$receiver(function(msg) { + if (msg.type == "assign") { + my_color = msg.color + log.console("Assigned color: " + my_color) + board_view.set_status("Playing as " + my_color) + } else if (msg.type == "state") { + current_turn = game_state.deserialize(msg, grid, board_view) + board_view.set_status(current_turn + "'s turn") + selectPos = null + validMoves = [] + } else if (msg.type == "result") { + if (!msg.ok) { + board_view.set_status("Move rejected: " + msg.error) + } + } +}) + +// Local input — client plays black +function compute_valid_moves(from) { + validMoves = [] + var piece = grid.at(from)[0] + if (!piece) return + var x = 0, y = 0, to = null, dest = null + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x++) { + to = [x, y] + dest = grid.at(to) + if (length(dest) && dest[0].colour == piece.colour) continue + if (rules.canMove(piece, from, to, grid)) + push(validMoves, to) + } + } +} + +var game_input = { + on_input: function(action, data) { + var clicked = null, cell = null, is_valid = false, i = 0 + if (!data.pressed) return + if (!my_color || current_turn != my_color) return + + if (action == 'cancel') { + selectPos = null + validMoves = [] + board_view.highlight(selectPos, validMoves, hover_gx, hover_gy) + return + } + + if (action == 'select' && hover_gx >= 0 && hover_gx < 8 && hover_gy >= 0 && hover_gy < 8) { + clicked = [hover_gx, hover_gy] + cell = grid.at(clicked) + + if (selectPos) { + is_valid = false + for (i = 0; i < length(validMoves); i++) { + if (validMoves[i][0] == clicked[0] && validMoves[i][1] == clicked[1]) { + is_valid = true + break + } + } + + if (is_valid) { + // Send move to server — don't apply locally + if (server_actor) { + send(server_actor, {type: "move", fx: selectPos[0], fy: selectPos[1], tx: clicked[0], ty: clicked[1]}) + } + selectPos = null + validMoves = [] + } else if (length(cell) && cell[0].colour == my_color) { + selectPos = clicked + compute_valid_moves(selectPos) + } else { + selectPos = null + validMoves = [] + } + } else { + if (length(cell) && cell[0].colour == my_color) { + selectPos = clicked + compute_valid_moves(selectPos) + } + } + board_view.highlight(selectPos, validMoves, hover_gx, hover_gy) + } + } +} +input.player1().possess(game_input) + +var comp_config = { + clear: {r: 0.15, g: 0.15, b: 0.2, a: 1}, + planes: [ + {name: 'game', camera: game_cam, resolution: {width: GW, height: GH}, presentation: 'letterbox'}, + {name: 'hud', camera: hud_cam, resolution: {width: GW, height: GH}, presentation: 'stretch'} + ] +} + +core.start({ + width: 640, height: 640, title: "Chess - Client (Black)", + + input: function(ev) { + var wp = null + if (ev.type == 'mouse_motion') { + wp = game_cam.window_to_world(ev.pos[0], ev.pos[1]) + if (wp) { + hover_gx = floor(wp.x / S) + hover_gy = floor(wp.y / S) + } + } + }, + + update: function(dt) { + board_view.highlight(selectPos, validMoves, hover_gx, hover_gy) + }, + + render: function() { + return compositor.execute(compositor.compile(comp_config)) + } +}) diff --git a/examples/chess/game_state.cm b/examples/chess/game_state.cm new file mode 100644 index 00000000..a21d3e5c --- /dev/null +++ b/examples/chess/game_state.cm @@ -0,0 +1,31 @@ +var Piece = use('examples/chess/pieces').Piece + +function serialize(grid, turn) { + var pieces = [] + grid.each(function(p) { + pieces[] = {kind: p.kind, colour: p.colour, x: p.coord[0], y: p.coord[1], id: p._id} + }) + return {type: "state", turn: turn, pieces: pieces} +} + +function deserialize(state, grid, board_view) { + var x = 0, y = 0 + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x++) { + grid.cells[y][x] = [] + } + } + + var i = 0, pd = null, p = null + for (i = 0; i < length(state.pieces); i++) { + pd = state.pieces[i] + p = Piece(pd.kind, pd.colour) + p._id = pd.id + grid.add(p, [pd.x, pd.y]) + } + + board_view.rebuild_pieces(grid) + return state.turn +} + +return {serialize: serialize, deserialize: deserialize} diff --git a/examples/chess/host.ce b/examples/chess/host.ce new file mode 100644 index 00000000..c3c9e106 --- /dev/null +++ b/examples/chess/host.ce @@ -0,0 +1,184 @@ +var core = use('core') +var camera = use('camera') +var compositor = use('compositor') +var input = use('input') + +var Grid = use('examples/chess/grid') +var MovementSystem = use('examples/chess/movement') +var pieces = use('examples/chess/pieces') +var rules = use('examples/chess/rules') +var board_view = use('examples/chess/board_view') +var game_state = use('examples/chess/game_state') + +var grid = Grid(8, 8) +grid.width = 8 +grid.height = 8 +var mover = MovementSystem(grid, rules) +pieces.startingPosition(grid) +board_view.init(grid) + +var S = board_view.S, GW = board_view.GW, GH = board_view.GH + +var game_cam = camera.make({width: GW, height: GH, pos: {x: GW / 2, y: GH / 2}}) +var hud_cam = camera.make({width: GW, height: GH, pos: {x: GW / 2, y: GH / 2}}) + +input.configure({ + action_map: { + select: ['mouse_button_left'], + cancel: ['escape', 'mouse_button_right'] + } +}) + +// Network state +var client_actor = null + +board_view.set_status("Waiting for client on port 7777...") + +$portal(function(connection) { + log.console("Client connected!") + client_actor = connection + send(client_actor, {type: "assign", color: "black"}) + send(client_actor, game_state.serialize(grid, mover.turn)) + board_view.set_status("White's turn - Game started!") +}, 7777) + +$receiver(function(msg) { + var from = null, to = null, cell = null, piece = null + if (msg.type == "move") { + from = [msg.fx, msg.fy] + to = [msg.tx, msg.ty] + cell = grid.at(from) + if (!length(cell)) { + send(client_actor, {type: "result", ok: false, error: "no piece"}) + return + } + piece = cell[0] + if (piece.colour != "black") { + send(client_actor, {type: "result", ok: false, error: "not your piece"}) + return + } + if (mover.turn != "black") { + send(client_actor, {type: "result", ok: false, error: "not your turn"}) + return + } + if (!rules.canMove(piece, from, to, grid)) { + send(client_actor, {type: "result", ok: false, error: "illegal move"}) + return + } + if (mover.tryMove(piece, to)) { + board_view.sync_pieces(grid) + board_view.set_status(mover.turn + "'s turn") + send(client_actor, game_state.serialize(grid, mover.turn)) + } else { + send(client_actor, {type: "result", ok: false, error: "move rejected"}) + } + } +}) + +// Local input — host plays white +var selectPos = null +var validMoves = [] +var hover_gx = -1, hover_gy = -1 + +function compute_valid_moves(from) { + validMoves = [] + var piece = grid.at(from)[0] + if (!piece) return + var x = 0, y = 0, to = null, dest = null + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x++) { + to = [x, y] + dest = grid.at(to) + if (length(dest) && dest[0].colour == piece.colour) continue + if (rules.canMove(piece, from, to, grid)) + push(validMoves, to) + } + } +} + +var game_input = { + on_input: function(action, data) { + var clicked = null, cell = null, is_valid = false, i = 0, src_piece = null + if (!data.pressed) return + if (mover.turn != 'white') return + + if (action == 'cancel') { + selectPos = null + validMoves = [] + board_view.highlight(selectPos, validMoves, hover_gx, hover_gy) + return + } + + if (action == 'select' && hover_gx >= 0 && hover_gx < 8 && hover_gy >= 0 && hover_gy < 8) { + clicked = [hover_gx, hover_gy] + cell = grid.at(clicked) + + if (selectPos) { + is_valid = false + for (i = 0; i < length(validMoves); i++) { + if (validMoves[i][0] == clicked[0] && validMoves[i][1] == clicked[1]) { + is_valid = true + break + } + } + + if (is_valid) { + src_piece = grid.at(selectPos)[0] + if (src_piece && mover.tryMove(src_piece, clicked)) { + board_view.sync_pieces(grid) + board_view.set_status(mover.turn + "'s turn") + if (client_actor) { + send(client_actor, game_state.serialize(grid, mover.turn)) + } + } + selectPos = null + validMoves = [] + } else if (length(cell) && cell[0].colour == 'white') { + selectPos = clicked + compute_valid_moves(selectPos) + } else { + selectPos = null + validMoves = [] + } + } else { + if (length(cell) && cell[0].colour == 'white') { + selectPos = clicked + compute_valid_moves(selectPos) + } + } + board_view.highlight(selectPos, validMoves, hover_gx, hover_gy) + } + } +} +input.player1().possess(game_input) + +var comp_config = { + clear: {r: 0.15, g: 0.15, b: 0.2, a: 1}, + planes: [ + {name: 'game', camera: game_cam, resolution: {width: GW, height: GH}, presentation: 'letterbox'}, + {name: 'hud', camera: hud_cam, resolution: {width: GW, height: GH}, presentation: 'stretch'} + ] +} + +core.start({ + width: 640, height: 640, title: "Chess - Host (White)", + + input: function(ev) { + var wp = null + if (ev.type == 'mouse_motion') { + wp = game_cam.window_to_world(ev.pos[0], ev.pos[1]) + if (wp) { + hover_gx = floor(wp.x / S) + hover_gy = floor(wp.y / S) + } + } + }, + + update: function(dt) { + board_view.highlight(selectPos, validMoves, hover_gx, hover_gy) + }, + + render: function() { + return compositor.execute(compositor.compile(comp_config)) + } +}) diff --git a/sdl_gpu.cm b/sdl_gpu.cm index 188a119c..fd0d5d2b 100644 --- a/sdl_gpu.cm +++ b/sdl_gpu.cm @@ -6,7 +6,7 @@ var video = use('sdl3/video') var gpu_mod = use('sdl3/gpu') var blob_mod = use('blob') -var io = use('fd') +var io = use('cellfs') var png = use('image/png') var qoi = use('image/qoi') var gif = use('image/gif') diff --git a/staef.c b/staef.c index 735bf28a..b101a445 100644 --- a/staef.c +++ b/staef.c @@ -9,8 +9,7 @@ #include #include #include -#include - +#include #define MSDF_IMPLEMENTATION #include "msdf.h" @@ -681,25 +680,28 @@ QJSCLASS(font,) static void attach_font_texture(JSContext *js, JSValue *pret, font *f) { JS_FRAME(js); JS_ROOT(obj, *pret); + JS_ROOT(texData, JS_NULL); + JSValue tmp; if (f->pixels) { - JS_ROOT(texData, JS_NewObject(js)); + texData.val = JS_NewObject(js); JS_SetPropertyStr(js, texData.val, "width", JS_NewInt32(js, f->atlas_size)); JS_SetPropertyStr(js, texData.val, "height", JS_NewInt32(js, f->atlas_size)); - JS_SetPropertyStr(js, texData.val, "format", JS_NewString(js, "rgba8")); + tmp = JS_NewString(js, "rgba8"); + JS_SetPropertyStr(js, texData.val, "format", tmp); size_t byte_size = f->atlas_size * f->atlas_size * 4; - JS_SetPropertyStr(js, texData.val, "pixels", js_new_blob_stoned_copy(js, f->pixels, byte_size)); + tmp = js_new_blob_stoned_copy(js, f->pixels, byte_size); + JS_SetPropertyStr(js, texData.val, "pixels", tmp); JS_SetPropertyStr(js, obj.val, "texture", texData.val); } - // Add mode string const char *mode_str = "bitmap"; if (f->mode == FONT_MODE_SDF) mode_str = "sdf"; else if (f->mode == FONT_MODE_MSDF) mode_str = "msdf"; - JS_SetPropertyStr(js, obj.val, "mode", JS_NewString(js, mode_str)); + tmp = JS_NewString(js, mode_str); + JS_SetPropertyStr(js, obj.val, "mode", tmp); - // Add range_px and sharpness for SDF/MSDF fonts JS_SetPropertyStr(js, obj.val, "range_px", JS_NewFloat64(js, f->range_px)); JS_SetPropertyStr(js, obj.val, "sharpness", JS_NewFloat64(js, f->sharpness)); *pret = obj.val;