diff options
| author | LLLL Colonq <llll@colonq> | 2026-08-18 00:08:28 -0400 |
|---|---|---|
| committer | LLLL Colonq <llll@colonq> | 2026-08-18 00:08:28 -0400 |
| commit | 32a62cfcb925225359a5c90761b795f2f737ebd2 (patch) | |
| tree | 95c2261964803b19f2d39cb35d8d514ed6e5a6f4 /2026/games_submissions/doceys/runtime.js | |
| parent | de99c5f7636a952025ea13de02bafd25bcb6bdec (diff) | |
Re-populate games
Diffstat (limited to '2026/games_submissions/doceys/runtime.js')
| -rw-r--r-- | 2026/games_submissions/doceys/runtime.js | 297 |
1 files changed, 0 insertions, 297 deletions
diff --git a/2026/games_submissions/doceys/runtime.js b/2026/games_submissions/doceys/runtime.js deleted file mode 100644 index 2d13bb1..0000000 --- a/2026/games_submissions/doceys/runtime.js +++ /dev/null @@ -1,297 +0,0 @@ -// Is the game running standalone or in the harness -let is_standalone = false; -// A global reference of the WASM’s memory area so that we can look up pointers -let allocated; -// Global Canvas Object -let canvas; -// Global Canvas Context -let ctx; -// Current Mouse Position in Canvas coordinates -let mouse_position = { - x: -1, - y: -1, -}; -let mouse_is_down = false; -// Global image cache -const image_cache = new Map(); - -function init_canvas() { - canvas = document.getElementById("game_canvas"); - ctx = canvas.getContext("2d"); - - canvas.addEventListener("mousemove", (event) => { - const rect = canvas.getBoundingClientRect(); - - // Mouse position relative to the canvas - const relative_x = (event.clientX - rect.left) / rect.width; - const relative_y = (event.clientY - rect.top) / rect.height; - - // Internal pixel position - mouse_position.x = Math.round(relative_x * canvas.width); - mouse_position.y = Math.round(relative_y * canvas.height); - }); - - canvas.onmousedown = (_) => { - mouse_is_down = true; - }; - canvas.onmouseup = (event) => { - mouse_is_down = false; - }; -} - -// These are all the functions that we declared as "#foreign" in our Jai code. -// They let you interact with the JS and DOM world from within Jai. -// If you forget to implement one, the Proxy below will log a nice error. -const exported_js_functions = { - play_sound: (file_ptr, volume) => { - volume = Number(volume); - const file = js_string_from_jai_string(file_ptr); - const audio_element = new Audio(file); - audio_element.volume = volume; - audio_element.play(); - }, - - signal_done: (win) => { - window.parent.postMessage({op: "done", win}); - }, - - draw_rect: (x, y, width, height, color_ptr) => { - // Needed to convert Jai's BigInts to numbers - [x, y, width, height] = [Number(x), Number(y), Number(width), Number(height)]; - const {r, g, b} = rgb_from_jai_color(color_ptr); - - ctx.fillStyle = `rgb(${r} ${g} ${b})`; - ctx.fillRect(x, y, width, height); - }, - - draw_image: (x, y, width, height, image_ptr) => { - [x, y, width, height] = [Number(x), Number(y), Number(width), Number(height)]; - const ptr = Number(image_ptr); - - const memory_view = new DataView(allocated.buffer); - const count = Number(memory_view.getBigInt64(ptr, true)); - const data_ptr = memory_view.getUint32(ptr + 8, true); - - if (count <= 0 || data_ptr === 0) return; - - // Load image from pointer if it isn't in the cache already - if (!image_cache.has(data_ptr)) { - // Create image and add it to the cache - const image_buffer = new Uint8Array(allocated.buffer, data_ptr, count); - const image_blob = new Blob([image_buffer], { type: "image/png" }); - const blob_url = URL.createObjectURL(image_blob); - - const image = new Image(); - image.src = blob_url; - image.is_ready = false; - image.onload = () => { - image.is_ready = true; - URL.revokeObjectURL(blob_url); - }; - - image_cache.set(data_ptr, image); - } - - const image = image_cache.get(data_ptr); - if (image && image.is_ready) { - ctx.drawImage(image, x, y, width, height); - } - }, - - draw_text: (x, y, font_size, text_ptr, color_ptr, max_width) => { - [x, y, font_size, max_width] = [Number(x), Number(y), Number(font_size), Number(max_width)]; - const text = js_string_from_jai_string(text_ptr); - const {r, g, b} = rgb_from_jai_color(color_ptr); - - ctx.fillStyle = `rgb(${r} ${g} ${b})`; - ctx.font = font_size + "px serif"; - ctx.fillText(text, x, y, max_width); - }, - - get_text_width: (font_size, text_ptr) => { - ctx.font = Number(font_size) + "px serif"; - const text_metrics = ctx.measureText(js_string_from_jai_string(text_ptr)); - return text_metrics.width; - }, - - clear_rect: (x, y, width, height) => { - [x, y, width, height] = [Number(x), Number(y), Number(width), Number(height)]; - ctx.clearRect(x, y, width, height); - }, - - clear_canvas: () => { - ctx.clearRect(0, 0, canvas.width, canvas.height); - }, - - // This function "returns" a Struct like the following: - // struct { width: s64; height: s64; } - get_canvas_size_struct: (struct_ptr) => { - const ptr = Number(struct_ptr); - - const memory_view = new DataView(allocated.buffer); - memory_view.setBigInt64(ptr + 0, BigInt(canvas.width), true); - memory_view.setBigInt64(ptr + 8, BigInt(canvas.height), true); - }, - - // Same "return" as with "get_canvas_size_struct" - get_mouse_position: (struct_ptr) => { - const ptr = Number(struct_ptr); - - const memory_view = new DataView(allocated.buffer); - memory_view.setBigInt64(ptr + 0, BigInt(mouse_position.x), true); - memory_view.setBigInt64(ptr + 8, BigInt(mouse_position.y), true); - }, - is_mouse_down: () => { - return mouse_is_down; - }, - - // These two need to be implemented for print/write_string and the debugger - wasm_write_string: (s_count, s_data, to_standard_error) => { - const string = js_string_from_jai_string_length(s_data, s_count); - write_to_console_log(string, to_standard_error); - }, - wasm_debug_break: () => { - debugger; - }, -} - -// Create the environment for the WASM file, -// which includes the exported JS functions for the WASM: -const imports = { - "env": new Proxy(exported_js_functions, { - get(target, prop, receiver) { - if (target.hasOwnProperty(prop)) { - return target[prop]; - } - - return () => { throw new Error("Missing function: " + prop); }; - }, - }), -} - -let last_time_ms = 0; -// Runs the exported frame function at 60 FPS -function frame(timestamp_ms) { - const delta_time_ms = timestamp_ms - last_time_ms; - last_time_ms = timestamp_ms; - - if (wasm_instance) { - wasm_instance.exports.frame(delta_time_ms, timestamp_ms); - } - - requestAnimationFrame(frame); -} - -function init_message_listener() { - window.addEventListener("message", (event) => { - switch (event.data.op) { - case "start": - wasm_instance.exports.on_signal_start(BigInt(event.data.difficulty)); - window.parent.postMessage({ op: "started", verb: "Catch MrGreen" }); - break; - default: - console.log(`Unexpected event: ${event}`); - break; - } - }); -} - -// Load the WASM file we compiled and start the game. -WebAssembly.instantiateStreaming(fetch("main.wasm"), imports).then( - (obj) => { - wasm_instance = obj.instance; - allocated = obj.instance.exports.memory; - // Running standalone, outside the Jam harness - is_standalone = window.self === window.top; - - init_canvas(); - init_message_listener(); - - if (is_standalone) { - const restart_button = document.getElementById("game_restart"); - restart_button.addEventListener("click", (event) => { - wasm_instance.exports.main(0, BigInt(0)); - }); - restart_button.style.visibility = "visible"; - - wasm_instance.exports.main(0, BigInt(0)); - } - - window.parent.postMessage({ op: "ready" }); - requestAnimationFrame(frame); - } -); - -const text_decoder = new TextDecoder("utf-8"); -function js_string_from_jai_string_length(pointer, length) { - const u8 = new Uint8Array(allocated.buffer) - const bytes = u8.subarray(Number(pointer), Number(pointer) + Number(length)); - return text_decoder.decode(bytes); -} - -function js_string_from_jai_string(pointer) { - const ptr = Number(pointer); - - const memory_view = new DataView(allocated.buffer); - const length = Number(memory_view.getBigInt64(ptr, true)); - const data_ptr = memory_view.getUint32(ptr + 8, true); - - const u8 = new Uint8Array(allocated.buffer); - const bytes = u8.subarray(Number(data_ptr), Number(data_ptr) + Number(length)); - - return text_decoder.decode(bytes); -} - -function rgb_from_jai_color(color_ptr) { - const ptr = Number(color_ptr); - const memory_view = new DataView(allocated.buffer); - - // Read the colors from the pointer - const r = memory_view.getUint8(ptr + 0); - const g = memory_view.getUint8(ptr + 1); - const b = memory_view.getUint8(ptr + 2); - - return {r, g, b}; -} - -// console.log and console.error always add newlines so we need to buffer the output from write_string -// to simulate a more basic I/O behavior. We’ll flush it after a certain time so that you still -// see the last line if you forget to terminate it with a newline for some reason. -let console_buffer = ""; -let console_buffer_is_standard_error; -let console_timeout; -const FLUSH_CONSOLE_AFTER_MS = 3; - -function write_to_console_log(str, to_standard_error) { - if (console_buffer && console_buffer_is_standard_error != to_standard_error) { - flush_buffer(); - } - - console_buffer_is_standard_error = to_standard_error; - const lines = str.split("\n"); - for (let i = 0; i < lines.length - 1; i++) { - console_buffer += lines[i]; - flush_buffer(); - } - - console_buffer += lines[lines.length - 1]; - - clearTimeout(console_timeout); - if (console_buffer) { - console_timeout = setTimeout(() => { - flush_buffer(); - }, FLUSH_CONSOLE_AFTER_MS); - } - - function flush_buffer() { - if (!console_buffer) return; - - if (console_buffer_is_standard_error) { - console.error(console_buffer); - } else { - console.log(console_buffer); - } - - console_buffer = ""; - } -} |
