'use strict';
const WASM_PATH = "<path to .wasm>"
const FONT_SCALE_MAGIC = 0.65;
const GLFW_MAP = {
"Space": 32,
"Quote": 39,
"Comma": 44,
"Minus": 45,
"Period": 46,
"Slash": 47,
"Digit0": 48,
"Digit1": 49,
"Digit2": 50,
"Digit3": 51,
"Digit4": 52,
"Digit5": 53,
"Digit6": 54,
"Digit7": 55,
"Digit8": 56,
"Digit9": 57,
"Semicolon": 59,
"Equal": 61,
"KeyA": 65,
"KeyB": 66,
"KeyC": 67,
"KeyD": 68,
"KeyE": 69,
"KeyF": 70,
"KeyG": 71,
"KeyH": 72,
"KeyI": 73,
"KeyJ": 74,
"KeyK": 75,
"KeyL": 76,
"KeyM": 77,
"KeyN": 78,
"KeyO": 79,
"KeyP": 80,
"KeyQ": 81,
"KeyR": 82,
"KeyS": 83,
"KeyT": 84,
"KeyU": 85,
"KeyV": 86,
"KeyW": 87,
"KeyX": 88,
"KeyY": 89,
"KeyZ": 90,
"BracketLeft": 91,
"Backslash": 92,
"BracketRight": 93,
"Backquote": 96,
"Escape": 256,
"Enter": 257,
"Tab": 258,
"Backspace": 259,
"Insert": 260,
"Delete": 261,
"ArrowRight": 262,
"ArrowLeft": 263,
"ArrowDown": 264,
"ArrowUp": 265,
"PageUp": 266,
"PageDown": 267,
"Home": 268,
"End": 269,
"CapsLock": 280,
"ScrollLock": 281,
"NumLock": 282,
"PrintScreen": 283,
"Pause": 284,
"F1": 290,
"F2": 291,
"F3": 292,
"F4": 293,
"F5": 294,
"F6": 295,
"F7": 296,
"F8": 297,
"F9": 298,
"F10": 299,
"F11": 300,
"F12": 301,
"F13": 302,
"F14": 303,
"F15": 304,
"F16": 305,
"F17": 306,
"F18": 307,
"F19": 308,
"F20": 309,
"F21": 310,
"F22": 311,
"F23": 312,
"F24": 313,
"F25": 314,
"NumPad0": 320,
"NumPad1": 321,
"NumPad2": 322,
"NumPad3": 323,
"NumPad4": 324,
"NumPad5": 325,
"NumPad6": 326,
"NumPad7": 327,
"NumPad8": 328,
"NumPad9": 329,
"NumpadDecimal": 330,
"NumpadDivide": 331,
"NumpadMultiply": 332,
"NumpadSubtract": 333,
"NumpadAdd": 334,
"NumpadEnter": 335,
"NumpadEqual": 336,
"ShiftLeft": 340,
"ControlLeft" : 341,
"AltLeft": 342,
"MetaLeft": 343,
"ShiftRight": 344,
"ControlRight": 345,
"AltRight": 346,
"MetaRight": 347,
"ContextMenu": 348,
}
function cstrlen(mem, ptr) {
let len = 0;
while (mem[ptr] != 0) {
len++;
ptr++;
}
return len;
}
function cstr_by_ptr(mem_buffer, ptr) {
const mem = new Uint8Array(mem_buffer);
const len = cstrlen(mem, ptr);
const bytes = new Uint8Array(mem_buffer, ptr, len);
return new TextDecoder().decode(bytes);
}
function color_hex_unpacked(r, g, b, a) {
r = r.toString(16).padStart(2, '0');
g = g.toString(16).padStart(2, '0');
b = b.toString(16).padStart(2, '0');
a = a.toString(16).padStart(2, '0');
return "#"+r+g+b+a;
}
function getColorFromMemory(buffer, color_ptr) {
const [r, g, b, a] = new Uint8Array(buffer, color_ptr, 4);
return color_hex_unpacked(r, g, b, a);
}
function make_environment(...envs) {
return new Proxy(envs, {
get(target, prop, receiver) {
for (let env of envs) if (env.hasOwnProperty(prop)) return env[prop];
return (...args) => {console.error("NOT IMPLEMENTED: "+prop, args)}
}
});
}
let prev_pressed_key = new Set();
let curr_pressed_key = new Set();
const keyDown = (e) => {
e.preventDefault();
curr_pressed_key.add(GLFW_MAP[e.code]);
}
const keyUp = (e) => {
e.preventDefault();
curr_pressed_key.delete(GLFW_MAP[e.code]);
}
const game = document.getElementById("game");
const ctx = game.getContext("2d");
let images = []
let wasm = undefined;
let dt = undefined;
let wf = undefined;
let quit = undefined;
let prev = undefined;
let targetFPS = undefined;
const GetFPS = () => 1.0 / dt;
WebAssembly.instantiateStreaming(fetch(WASM_PATH), {
"env": make_environment({
InitWindow: (w, h, t) => {
game.width = w;
game.height = h;
const buffer = wf.memory.buffer;
document.title = cstr_by_ptr(buffer, t);
},
BeginDrawing: () => {},
SetExitKey: () => {},
CloseWindow: () => {},
EndDrawing: () => {
prev_pressed_key.clear();
prev_pressed_key = new Set(curr_pressed_key);
},
IsKeyReleased: (key) => prev_pressed_key.has(key) && !curr_pressed_key.has(key),
IsKeyDown: (key) => curr_pressed_key.has(key),
ClearBackground: (color_ptr) => {
const buffer = wf.memory.buffer;
const color = getColorFromMemory(buffer, color_ptr);
ctx.fillStyle = color;
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
},
MeasureText: (text_ptr, fontSize) => {
const buffer = wasm.instance.exports.memory.buffer;
const text = cstr_by_ptr(buffer, text_ptr);
fontSize *= FONT_SCALE_MAGIC;
ctx.font = `${fontSize}px grixel`;
return ctx.measureText(text).width;
},
DrawText: (text_ptr, posX, posY, fontSize, color_ptr) => {
const buffer = wf.memory.buffer;
const text = cstr_by_ptr(buffer, text_ptr);
const color = getColorFromMemory(buffer, color_ptr);
fontSize *= FONT_SCALE_MAGIC;
ctx.fillStyle = color;
ctx.font = `${fontSize}px grixel`;
const lines = text.split('\n');
for (var i = 0; i < lines.length; i++) {
ctx.fillText(lines[i], posX, posY + fontSize + (i * fontSize));
}
},
DrawRectangle: (posX, posY, width, height, color_ptr) => {
const buffer = wf.memory.buffer;
const color = getColorFromMemory(buffer, color_ptr);
ctx.fillStyle = color;
ctx.fillRect(posX, posY, width, height);
},
DrawLine: (startPosX, startPosY, endPosX, endPosY, color_ptr) => {
const buffer = wf.memory.buffer;
const color = getColorFromMemory(buffer, color_ptr);
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(startPosX, startPosY);
ctx.lineTo(endPosX, endPosY);
ctx.strokeStyle = color;
ctx.stroke();
},
DrawRectangleV: (position_ptr, size_ptr, color_ptr) => {
const buffer = wf.memory.buffer;
const [x, y] = new Float32Array(buffer, position_ptr, 2);
const [width, height] = new Float32Array(buffer, size_ptr, 2);
const color = getColorFromMemory(buffer, color_ptr);
ctx.fillStyle = color;
ctx.fillRect(x, y, width, height);
},
DrawCircle: (centerX, centerY, radius, color_ptr) => {
const buffer = wf.memory.buffer;
const color = getColorFromMemory(buffer, color_ptr);
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, 0);
ctx.fill();
},
DrawRectangleRec: (rec_ptr, color_ptr) => {
const buffer = wf.memory.buffer;
const [x, y, w, h] = new Float32Array(buffer, rec_ptr, 4);
const color = getColorFromMemory(buffer, color_ptr);
ctx.fillStyle = color;
ctx.fillRect(x, y, w, h);
},
LoadTexture: (result_ptr, file_path_ptr) => {
const buffer = wf.memory.buffer;
const file_path = cstr_by_ptr(buffer, file_path_ptr);
let result = new Uint32Array(buffer, result_ptr, 5)
let img = new Image();
img.src = file_path;
images.push(img);
img.onload = () => {
images.push(img);
result[0] = images.indexOf(img);
result[1] = img.width; result[2] = img.height; result[3] = 1; result[4] = 7; };
return result;
},
UnloadTexture: () => {},
GetScreenWidth: () => ctx.canvas.width,
GetScreenHeight: () => ctx.canvas.height,
GetFrameTime: () => Math.min(dt, 1.0 / targetFPS),
IsWindowResized: () => false,
WindowShouldClose: () => false,
SetTargetFPS: (x) => targetFPS = x,
GetFPS: () => GetFPS(),
DrawFPS: (x, y) => {
const fontSize = 50.0 * FONT_SCALE_MAGIC;
const fps = GetFPS();
let color = "lime"; if ((fps < 30) && (fps >= 15)) color = "orange"; else if (fps < 15) color = "red"; ctx.fillStyle = "green";
ctx.font = `${fontSize}px grixel`;
ctx.fillText(targetFPS, x, y + fontSize);
},
alert: (ptr) => {
let msg = cstr_by_ptr(ptr);
console.log(msg);
window.alert(msg);
}
})
}).then(w => {
wasm = w;
wf = w.instance.exports;
console.log(w);
window.addEventListener("keydown", keyDown);
window.addEventListener("keyup", keyUp);
let state = wf.game_init();
const next = (timestamp) => {
if (quit) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
window.removeEventListener("keydown", keyDown);
return;
}
dt = (timestamp - prev)/1000.0;
prev = timestamp;
wf.game_frame(state);
window.requestAnimationFrame(next);
};
window.requestAnimationFrame((timestamp) => {
prev = timestamp;
window.requestAnimationFrame(next);
});
}).catch((err) => {
console.log(err);
console.log('update WASM_PATH in `main.js` bruv!');
});