export const js_load_asset = (url) => {
fetch(url)
.then(data => {
return data.arrayBuffer();
})
.then(res => {
wasm_on_asset_loaded(url, new Uint8Array(res));
})
.catch(error => {
console.error(`Failed to load asset: ${url}`, error);
});
};
const utils = {};
utils.loop = update => {
let lastTime = performance.now();
const frameDuration = 1000 / 60;
const loopFunction = function(currentTime) {
const deltaTime = currentTime - lastTime;
if (deltaTime >= frameDuration) {
const times = Math.floor(deltaTime / frameDuration);
update((times * frameDuration) * 0.001); lastTime += times * frameDuration;
}
requestAnimationFrame(loopFunction);
};
requestAnimationFrame(loopFunction);
};
import init, {PixelGame, wasm_init_pixel_assets, wasm_set_app_data, wasm_on_asset_loaded} from "./pkg/pixel.js";
const wasm = await init();
const symbolMapResponse = await fetch("assets/pix/layered_symbol_map.json");
const symbolMapText = await symbolMapResponse.text();
const symbolMap = JSON.parse(symbolMapText);
const layerSize = symbolMap.layer_size;
const layerFiles = symbolMap.layer_files;
console.log(`[DEBUG] Loading ${layerFiles.length} layers (${layerSize}x${layerSize} each)`);
const bytesPerLayer = layerSize * layerSize * 4;
const allLayerData = new Uint8Array(bytesPerLayer * layerFiles.length);
await Promise.all(layerFiles.map(async (file, i) => {
const resp = await fetch(`assets/pix/${file}`);
const blob = await resp.blob();
const bitmap = await createImageBitmap(blob);
let cv, cx;
if (typeof OffscreenCanvas !== 'undefined') {
cv = new OffscreenCanvas(bitmap.width, bitmap.height);
cx = cv.getContext("2d");
} else {
cv = document.createElement("canvas");
cv.width = bitmap.width;
cv.height = bitmap.height;
cx = cv.getContext("2d");
}
cx.drawImage(bitmap, 0, 0);
const pixels = cx.getImageData(0, 0, bitmap.width, bitmap.height).data;
allLayerData.set(pixels, i * bytesPerLayer);
console.log(`[DEBUG] Layer ${i} loaded: ${bitmap.width}x${bitmap.height}`);
}));
console.log(`[DEBUG] All layers loaded: ${allLayerData.length} bytes total`);
wasm_init_pixel_assets("pixel_game", layerSize, layerFiles.length, allLayerData, symbolMapText);
const dataUrl = new URLSearchParams(window.location.search).get('data');
if (dataUrl) {
try {
const dataResponse = await fetch(dataUrl);
if (dataResponse.ok) {
wasm_set_app_data(await dataResponse.text());
} else {
console.warn(`Failed to load app data: ${dataUrl} (${dataResponse.status})`);
}
} catch (e) {
console.warn('Failed to load app data:', e);
}
}
const sg = PixelGame.new();
await sg.init_from_cache();
const canvasSize = sg.get_canvas_size();
const gameCanvas = document.getElementById("canvas");
gameCanvas.width = canvasSize[0];
gameCanvas.height = canvasSize[1];
function fitCanvasToWindow() {
const scaleX = window.innerWidth / canvasSize[0];
const scaleY = window.innerHeight / canvasSize[1];
const scale = Math.min(scaleX, scaleY);
const displayW = Math.floor(canvasSize[0] * scale);
const displayH = Math.floor(canvasSize[1] * scale);
gameCanvas.style.width = displayW + "px";
gameCanvas.style.height = displayH + "px";
gameCanvas.style.left = Math.floor((window.innerWidth - displayW) / 2) + "px";
gameCanvas.style.top = Math.floor((window.innerHeight - displayH) / 2) + "px";
}
fitCanvasToWindow();
window.addEventListener("resize", fitCanvasToWindow);
console.log(`Canvas: ${canvasSize[0]}x${canvasSize[1]} (WGPU), scaled to fit window`)
window.onkeydown = (e) => {
sg.key_event(0, e);
if (["Space","ArrowLeft","ArrowRight","ArrowUp","ArrowDown","PageUp","PageDown","Home","End"].includes(e.code)) {
e.preventDefault();
}
};
window.onmouseup = (e) => { sg.key_event(1, e); };
window.onmousedown = (e) => { sg.key_event(2, e); };
window.onmousemove = (e) => { sg.key_event(3, e); };
utils.loop(function(timeStep) {
sg.tick(timeStep);
return true;
});