208 lines
5.4 KiB
JavaScript

"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();
});
}