"use strict"; //! {"require": ["libiframe"]} let isClient = imports.terminal != null; if (isClient) { require("libiframe").iframe({ source: ` document.documentElement.setAttribute("style", "width: 100%; height: 100%; margin: 0; padding: 0"); document.body.setAttribute("style", "display: flex; flex-direction: column; height: 100%; width: 100%; margin: 0; padding: 0"); document.body.style.color = '#fff'; document.body.style.backgroundColor = '#000'; var canvas = document.createElement("canvas"); canvas.setAttribute("style", "flex: 1 1; image-rendering: pixelated"); canvas.width = 128; canvas.height = 128; document.body.append(canvas); var buttonBar = document.createElement("div"); buttonBar.setAttribute("style", "flex: 0 1 1in; display: flex; flex-direction: row"); var buttons = ["<", "^", "v", ">", "Fire!"]; for (var i = 0; i < buttons.length; i++) { var button = document.createElement("input"); button.setAttribute("type", "button"); button.setAttribute("style", "flex: 1 1"); button.value = buttons[i]; button.onclick = buttonClicked.bind(null, buttons[i]); buttonBar.append(button); } document.body.append(buttonBar); var buttonCallback; function buttonClicked(button) { if (buttonCallback) { buttonCallback(button); } } var kExportNames = [ "beginPath", "fill", "save", "restore", "scale", "rotate", "translate", "transform", "arc", "moveTo", "lineTo", "fillRect", "fillText", "stroke", ]; var context = canvas.getContext("2d"); var exports = {}; for (var i = 0; i < kExportNames.length; i++) { exports[kExportNames[i]] = context[kExportNames[i]].bind(context); } exports.setButtonCallback = function(callback) { buttonCallback = callback; }; exports.setFillStyle = function(style) { context.fillStyle = style; }; exports.setStrokeStyle = function(style) { context.strokeStyle = style; }; exports.setLineWidth = function(width) { context.lineWidth = width; }; rpc.export(exports); `, style: ` border: 0; `}).then(initialize).catch(terminal.print); function initialize(iframe) { iframe.setButtonCallback(click); draw(iframe); core.register("onMessage", onMessage.bind(null, iframe)); core.getService("server").then(function(server) { server.postMessage({ready: true}); }); } async function click(button) { (await core.getService("server")).postMessage({button: button}); } function draw(iframe) { iframe.setFillStyle('#222'); iframe.fillRect(0, 0, 640, 480); } function drawTank(iframe, x, y, color, angle) { iframe.save(); iframe.setLineWidth(1); iframe.setFillStyle(color); iframe.setStrokeStyle(color); iframe.translate(x, y); iframe.beginPath(); iframe.arc(0, 0, 4, Math.PI, 2 * Math.PI); iframe.fill(); iframe.beginPath(); var radians = Math.PI + angle * Math.PI / 180.0; iframe.moveTo(Math.cos(radians) * 2, Math.sin(radians) * 2 - 0.5); iframe.lineTo(Math.cos(radians) * 10, Math.sin(radians) * 10 - 0.5); iframe.stroke(); iframe.restore(); } function onMessage(iframe, sender, message) { iframe.setFillStyle('#222'); iframe.fillRect(0, 0, 640, 480); for (let i = 0; i < message.users.length; i++) { var tank = message.users[i]; drawTank(iframe, tank.position.x, tank.position.y, tank.user == core.user.index ? '#f00' : '#0f0', tank.angle); } iframe.setFillStyle('#0f0'); for (let i = 0; i < message.terrain.length; i++) { let h = message.terrain[i]; iframe.fillRect(i, 128 - h, i + 1, h); } } } else { let users = {}; let terrain = new Array(128); let updating = false; function startUpdating() { if (!updating) { setTimeout(100, update); updating = true; } } function update() { updating = false; for (let u of Object.values(users)) { user.position.y++; updating = true; } broadcastUsers(); if (updating) { setTimeout(100, update); } } function initializeTerrain() { for (let i = 0; i < terrain.length; i++) { terrain[i] = 32; } } function broadcastUsers() { core.broadcast({ users: Object.keys(users).map(x => ({user: x, position: users[x].position, angle: users[x].angle})), terrain: terrain, }); } function placeNewUser() { let positions = Object.keys(users).map(x => users[x].position.x); var best = 128 / 2; var bestDistance = -1; if (positions.length) { for (var i = 10; i < 118; i++) { var thisDistance = Math.min.apply(null, positions.map(x => Math.abs(i - x))); if (thisDistance > bestDistance) { bestDistance = thisDistance; best = i; } } } return {x: best, y: 10, d: bestDistance}; } initializeTerrain(); core.register("onMessage", function(sender, message) { if (message.ready) { users[sender.index] = { user: sender, angle: 0, velocity: 100, position: placeNewUser(), }; broadcastUsers(); startUpdating(); } else if (message.button) { switch (message.button) { case "<": users[sender.index].angle = Math.round(Math.max(users[sender.index].angle - 5.0, 0.0)); break; case ">": users[sender.index].angle = Math.round(Math.min(users[sender.index].angle + 5.0, 180.0)); break; case "^": users[sender.index].velocity++; break; case "v": users[sender.index].velocity--; break; } broadcastUsers(); } }); core.register("onSessionEnd", function(user) { delete users[user.index]; broadcastUsers(); }); }