// ==UserScript==
// @name IMSOLO.PRO WEB
// @namespace https://imsolo.pro/
// @version 1.0.2
// @description Авто-транслитерация кириллических символов в чате
// @author Зуенко Михаил
// @include https://imsolo.pro/web/
// @run-at document-start
// ==/UserScript==
document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Eat cells smaller than you and don't get eaten by the bigger ones, as an MMO">
<meta name="keywords" content="agario, agar, io, cell, cells, virus, bacteria, blob, game, games, web game, html5, fun, flash">
<meta name="robots" content="index, follow">
<meta name="viewport" content="minimal-ui, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>IMSOLO.PRO WEB</title>
<link id="favicon" rel="icon" type="image/png" href="assets/img/favicon.png">
<link href="https://fonts.googleapis.com/css?family=Ubuntu:700" rel="stylesheet" type="text/css">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
<link href="assets/css/index.css" rel="stylesheet">
<link href="assets/css/gallery.css" rel="stylesheet">
<script src="assets/js/quadtree.js"></script>
<script>
(function() {
"use strict";
if (typeof WebSocket === 'undefined' || typeof DataView === 'undefined' ||
typeof ArrayBuffer === 'undefined' || typeof Uint8Array === 'undefined') {
alert('Your browser does not support required features, please update your browser or get a new one.');
window.stop();
}
function byId(id) {return document.getElementById(id);}
function byClass(clss, parent) {return (parent || document).getElementsByClassName(clss);}
function Sound(src, volume, maximum) {
this.src = src;
this.volume = typeof volume == "number" ? volume : 0.5;
this.maximum = typeof maximum == "number" ? maximum : Infinity;
this.elms = [];
}
Sound.prototype.play = function(vol) {
if (typeof vol == "number") this.volume = vol;
var toPlay;
for (var i = 0; i < this.elms.length; i++) {
var elm = this.elms[i];
if (elm.paused) {
toPlay = elm;
break;
}
}
if (!toPlay) toPlay = this.add();
toPlay.volume = this.volume;
toPlay.play();
};
Sound.prototype.add = function() {
if (this.elms.length >= this.maximum) {
return this.elms[0];
}
var elm = new Audio(this.src);
this.elms.push(elm);
return elm;
};
var LOAD_START = Date.now();
Array.prototype.remove = function(a) {
var i = this.indexOf(a);
if (i !== -1) this.splice(i, 1);
return i !== -1;
};
Element.prototype.hide = function() {
this.style.display = "none";
if (this.style.opacity == 1) this.style.opacity = 0;
};
Element.prototype.show = function(seconds) {
this.style.display = "";
var that = this;
if (seconds) {
this.style.transition = "opacity " + seconds + "s ease 0s";
setTimeout(function() {
that.style.opacity = 1;
}, 20);
}
};
if (!Array.prototype.includes) {
Array.prototype.includes = function(val) {
for (var i = 0; i < this.length; i++) {
if (this[i] === val) return true;
}
return false;
};
}
(function() {
var ctxProto = CanvasRenderingContext2D.prototype;
if (ctxProto.resetTransform) return;
ctxProto.resetTransform = function() {
this.setTransform(1, 0, 0, 1, 0, 0);
};
})();
function bytesToHex(r, g, b) {
return "#" + (1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1);
}
function colorToBytes(color) {
var c = color.slice(1);
if (c.length === 3) c = c.split("").map(function(a) {return a + a});
if (c.length !== 6) throw new Error("invalid color " + color);
var v = parseInt(c, 16);
return {
r: v >>> 16 & 255,
g: v >>> 8 & 255,
b: v & 255
};
}
function darkenColor(color) {
var a = colorToBytes(color);
return bytesToHex(a.r * .9, a.g * .9, a.b * .9);
}
function cleanupObject(object) {
for (var i in object)
delete object[i];
}
var __buf = new DataView(new ArrayBuffer(8));
function Writer(littleEndian) {
this._e = littleEndian;
this.reset();
return this;
}
Writer.prototype = {
writer: true,
reset: function(littleEndian) {
this._b = [];
this._o = 0;
},
setUint8: function(a) {
if (a >= 0 && a < 256) this._b.push(a);
return this;
},
setInt8: function(a) {
if (a >= -128 && a < 128) this._b.push(a);
return this;
},
setUint16: function(a) {
__buf.setUint16(0, a, this._e);
this._move(2);
return this;
},
setInt16: function(a) {
__buf.setInt16(0, a, this._e);
this._move(2);
return this;
},
setUint32: function(a) {
__buf.setUint32(0, a, this._e);
this._move(4);
return this;
},
setInt32: function(a) {
__buf.setInt32(0, a, this._e);
this._move(4);
return this;
},
setFloat32: function(a) {
__buf.setFloat32(0, a, this._e);
this._move(4);
return this;
},
setFloat64: function(a) {
__buf.setFloat64(0, a, this._e);
this._move(8);
return this;
},
_move: function(b) {
for (var i = 0; i < b; i++) this._b.push(__buf.getUint8(i));
},
setStringUTF8: function(s) {
var bytesStr = unescape(encodeURIComponent(s));
for (var i = 0, l = bytesStr.length; i < l; i++) this._b.push(bytesStr.charCodeAt(i));
this._b.push(0);
return this;
},
build: function() {
return new Uint8Array(this._b);
}
};
function Reader(view, offset, littleEndian) {
this._e = littleEndian;
if (view) this.repurpose(view, offset);
}
Reader.prototype = {
reader: true,
repurpose: function(view, offset) {
this.view = view;
this._o = offset || 0;
},
getUint8: function() {
return this.view.getUint8(this._o++, this._e);
},
getInt8: function() {
return this.view.getInt8(this._o++, this._e);
},
getUint16: function() {
return this.view.getUint16((this._o += 2) - 2, this._e);
},
getInt16: function() {
return this.view.getInt16((this._o += 2) - 2, this._e);
},
getUint32: function() {
return this.view.getUint32((this._o += 4) - 4, this._e);
},
getInt32: function() {
return this.view.getInt32((this._o += 4) - 4, this._e);
},
getFloat32: function() {
return this.view.getFloat32((this._o += 4) - 4, this._e);
},
getFloat64: function() {
return this.view.getFloat64((this._o += 8) - 8, this._e);
},
getStringUTF8: function() {
var s = "", b;
while ((b = this.view.getUint8(this._o++)) !== 0) s += String.fromCharCode(b);
return decodeURIComponent(escape(s));
}
};
var log = {
verbosity: 2,
error: function() {
if (log.verbosity > 0) console.error.apply(null, arguments);
},
warn: function() {
if (log.verbosity > 1) console.warn.apply(null, arguments);
},
info: function() {
if (log.verbosity > 2) console.info.apply(null, arguments);
},
debug: function() {
if (log.verbosity > 3) console.debug.apply(null, arguments);
}
};
var wsUrl = null,
SKIN_URL = "./skins/",
YTSKIN_URL = "./youtuberskins/",
USE_HTTPS = "https:" == window.location.protocol,
EMPTY_NAME = "An unnamed cell",
QUADTREE_MAX_POINTS = 32,
CELL_POINTS_MIN = 5,
CELL_POINTS_MAX = 120,
VIRUS_POINTS = 100,
PI_2 = Math.PI * 2,
SEND_254 = new Uint8Array([254, 6, 0, 0, 0]),
SEND_255 = new Uint8Array([255, 1, 0, 0, 0]),
UINT8_CACHE = {
1: new Uint8Array([1]),
17: new Uint8Array([17]),
21: new Uint8Array([21]),
18: new Uint8Array([18]),
19: new Uint8Array([19]),
22: new Uint8Array([22]),
23: new Uint8Array([23]),
24: new Uint8Array([24]),
25: new Uint8Array([25]),
254: new Uint8Array([254])
},
KEY_TO_CODE = {
32: UINT8_CACHE[17],
87: UINT8_CACHE[21],
81: UINT8_CACHE[18],
69: UINT8_CACHE[22],
82: UINT8_CACHE[23],
84: UINT8_CACHE[24],
80: UINT8_CACHE[25]
},
IE_KEYS = {
"spacebar": " ",
"esc": "escape"
};
function wsCleanup() {
if (!ws) return;
log.debug("ws cleanup trigger");
ws.onopen = null;
ws.onmessage = null;
ws.close();
ws = null;
}
function wsInit(url) {
if (ws) {
log.debug("ws init on existing conn");
wsCleanup();
}
byId("connecting").show(0.5);
ws = new WebSocket("ws" + (USE_HTTPS ? "s" : "") + "://" + (wsUrl = url));
ws.binaryType = "arraybuffer";
ws.onopen = wsOpen;
ws.onmessage = wsMessage;
ws.onerror = wsError;
ws.onclose = wsClose;
}
function wsOpen() {
disconnectDelay = 1000;
byId("connecting").hide();
wsSend(SEND_254);
wsSend(SEND_255);
}
function wsError(error) {
log.warn(error);
}
function wsClose(e) {
log.debug("ws disconnected " + e.code + " '" + e.reason + "'");
wsCleanup();
gameReset();
setTimeout(function() {
if (ws && ws.readyState === 1) return;
wsInit(wsUrl);
}, disconnectDelay *= 1.5);
}
function wsSend(data) {
if (!ws) return;
if (ws.readyState !== 1) return;
if (data.build) ws.send(data.build());
else ws.send(data);
}
function wsMessage(data) {
syncUpdStamp = Date.now();
var reader = new Reader(new DataView(data.data), 0, true);
var packetId = reader.getUint8();
switch (packetId) {
case 0x10: // update nodes
var killer, killed, id, node, x, y, s, flags, cell,
updColor, updName, updSkin, count, color, name, skin;
// consume records
count = reader.getUint16();
for (var i = 0; i < count; i++) {
killer = reader.getUint32();
killed = reader.getUint32();
if (!cells.byId.hasOwnProperty(killer) || !cells.byId.hasOwnProperty(killed))
continue;
if (settings.playSounds && cells.mine.includes(killer)) {
(cells.byId[killed].s < 20 ? pelletSound : eatSound).play(parseFloat(soundsVolume.value));
}
cells.byId[killed].destroy(killer);
}
// update records
while (true) {
id = reader.getUint32();
if (id === 0) break;
x = reader.getInt32();
y = reader.getInt32();
s = reader.getUint16();
flags = reader.getUint8();
updColor = !!(flags & 0x02);
updSkin = !!(flags & 0x04);
updName = !!(flags & 0x08);
color = updColor ? bytesToHex(reader.getUint8(), reader.getUint8(), reader.getUint8()) : null;
skin = updSkin ? reader.getStringUTF8() : null;
name = updName ? reader.getStringUTF8() : null;
if (cells.byId.hasOwnProperty(id)) {
cell = cells.byId[id];
cell.update(syncUpdStamp);
cell.updated = syncUpdStamp;
cell.ox = cell.x;
cell.oy = cell.y;
cell.os = cell.s;
cell.nx = x;
cell.ny = y;
cell.ns = s;
if (color) cell.setColor(color);
if (name) cell.setName(name);
if (skin) cell.setSkin(skin);
} else {
cell = new Cell(id, x, y, s, name, color, skin, flags);
cells.byId[id] = cell;
cells.list.push(cell);
}
}
// dissapear records
count = reader.getUint16();
for (i = 0; i < count; i++) {
killed = reader.getUint32();
if (cells.byId.hasOwnProperty(killed) && !cells.byId[killed].destroyed)
cells.byId[killed].destroy(null);
}
break;
case 0x11: // update pos
camera.target.x = reader.getFloat32();
camera.target.y = reader.getFloat32();
camera.target.scale = reader.getFloat32();
camera.target.scale *= camera.viewportScale;
camera.target.scale *= camera.userZoom;
break;
case 0x12: // clear all
for (var i in cells.byId)
cells.byId[i].destroy(null);
case 0x14: // clear my cells
cells.mine = [];
break;
case 0x15: // draw line
log.warn("got packet 0x15 (draw line) which is unsupported");
break;
case 0x20: // new cell
cells.mine.push(reader.getUint32());
break;
case 0x30: // text list
leaderboard.items = [];
leaderboard.type = "text";
var count = reader.getUint32();
for (i = 0; i < count; ++i)
leaderboard.items.push(reader.getStringUTF8());
drawLeaderboard();
break;
case 0x31: // ffa list
leaderboard.items = [];
leaderboard.type = "ffa";
var count = reader.getUint32();
for (i = 0; i < count; ++i) {
var isMe = !!reader.getUint32();
var name = reader.getStringUTF8();
leaderboard.items.push({
me: isMe,
name: Cell.prototype.parseName(name).name || EMPTY_NAME
});
}
drawLeaderboard();
break;
case 0x32: // pie chart
leaderboard.items = [];
leaderboard.type = "pie";
var count = reader.getUint32();
for (i = 0; i < count; ++i)
leaderboard.items.push(reader.getFloat32());
drawLeaderboard();
break;
case 0x40: // set border
border.left = reader.getFloat64();
border.top = reader.getFloat64();
border.right = reader.getFloat64();
border.bottom = reader.getFloat64();
border.width = border.right - border.left;
border.height = border.bottom - border.top;
border.centerX = (border.left + border.right) / 2;
border.centerY = (border.top + border.bottom) / 2;
if (data.data.byteLength === 33) break;
if (!mapCenterSet) {
mapCenterSet = true;
camera.x = camera.target.x = border.centerX;
camera.y = camera.target.y = border.centerY;
camera.scale = camera.target.scale = 1;
}
reader.getUint32(); // game type
if (!/MultiOgar|OgarII/.test(reader.getStringUTF8()) || stats.pingLoopId) break;
stats.pingLoopId = setInterval(function() {
wsSend(UINT8_CACHE[254]);
stats.pingLoopStamp = Date.now();
}, 2000);
break;
case 0x63: // chat message
var flags = reader.getUint8();
var color = bytesToHex(reader.getUint8(), reader.getUint8(), reader.getUint8());
var name = reader.getStringUTF8();
name = Cell.prototype.parseName(name).name || EMPTY_NAME;
var message = reader.getStringUTF8();
var server = !!(flags & 0x80),
admin = !!(flags & 0x40),
mod = !!(flags & 0x20);
if (server && name !== "SERVER") name = "[SERVER] " + name;
if (admin) name = "[ADMIN] " + name;
if (mod) name = "[MOD] " + name;
var wait = Math.max(3000, 1000 + message.length * 150);
chat.waitUntil = syncUpdStamp - chat.waitUntil > 1000 ? syncUpdStamp + wait : chat.waitUntil + wait;
chat.messages.push({
server: server,
admin: admin,
mod: mod,
color: color,
name: name,
message: message,
time: syncUpdStamp
});
if (settings.showChat) drawChat();
break;
case 0xFE: // server stat
stats.info = JSON.parse(reader.getStringUTF8());
stats.latency = syncUpdStamp - stats.pingLoopStamp;
drawStats();
break;
default:
// invalid packet
wsCleanup();
break;
}
}
function sendMouseMove(x, y) {
var writer = new Writer(true);
writer.setUint8(0x10);
writer.setUint32(x);
writer.setUint32(y);
writer._b.push(0, 0, 0, 0);
wsSend(writer);
}
function sendPlay(name) {
var writer = new Writer(true);
writer.setUint8(0x00);
writer.setStringUTF8(name);
wsSend(writer);
}
function sendChat(text) {
var writer = new Writer();
writer.setUint8(0x63);
writer.setUint8(0);
if(wsUrl=="imsolo.pro:4101")writer.setStringUTF8(text.replace(/\\u0401/g,"YO").replace(/\\u0419/g,"I").replace(/\\u0426/g,"TS").replace(/\\u0423/g,"U").replace(/\\u041A/g,"K").replace(/\\u0415/g,"E").replace(/\\u041D/g,"N").replace(/\\u0413/g,"G").replace(/\\u0428/g,"SH").replace(/\\u0429/g,"SCH").replace(/\\u0417/g,"Z").replace(/\\u0425/g,"H").replace(/\\u042A/g,"").replace(/\\u0451/g,"yo").replace(/\\u0439/g,"i").replace(/\\u0446/g,"ts").replace(/\\u0443/g,"u").replace(/\\u043A/g,"k").replace(/\\u0435/g,"e").replace(/\\u043D/g,"n").replace(/\\u0433/g,"g").replace(/\\u0448/g,"sh").replace(/\\u0449/g,"sch").replace(/\\u0437/g,"z").replace(/\\u0445/g,"h").replace(/\\u044A/g,"'").replace(/\\u0424/g,"F").replace(/\\u042B/g,"I").replace(/\\u0412/g,"V").replace(/\\u0410/g,"a").replace(/\\u041F/g,"P").replace(/\\u0420/g,"R").replace(/\\u041E/g,"O").replace(/\\u041B/g,"L").replace(/\\u0414/g,"D").replace(/\\u0416/g,"ZH").replace(/\\u042D/g,"E").replace(/\\u0444/g,"f").replace(/\\u044B/g,"i").replace(/\\u0432/g,"v").replace(/\\u0430/g,"a").replace(/\\u043F/g,"p").replace(/\\u0440/g,"r").replace(/\\u043E/g,"o").replace(/\\u043B/g,"l").replace(/\\u0434/g,"d").replace(/\\u0436/g,"zh").replace(/\\u044D/g,"e").replace(/\\u042F/g,"Ya").replace(/\\u0427/g,"CH").replace(/\\u0421/g,"S").replace(/\\u041C/g,"M").replace(/\\u0418/g,"I").replace(/\\u0422/g,"T").replace(/\\u042C/g,"'").replace(/\\u0411/g,"B").replace(/\\u042E/g,"YU").replace(/\\u044F/g,"ya").replace(/\\u0447/g,"ch").replace(/\\u0441/g,"s").replace(/\\u043C/g,"m").replace(/\\u0438/g,"i").replace(/\\u0442/g,"t").replace(/\\u044C/g,"'").replace(/\\u0431/g,"b").replace(/\\u044E/g,"yu"));
else writer.setStringUTF8(text);
wsSend(writer);
}
function gameReset() {
cleanupObject(cells);
cleanupObject(border);
cleanupObject(leaderboard);
cleanupObject(chat);
cleanupObject(stats);
chat.messages = [];
leaderboard.items = [];
cells.mine = [];
cells.byId = { };
cells.list = [];
camera.x = camera.y = camera.target.x = camera.target.y = 0;
camera.scale = camera.target.scale = 1;
mapCenterSet = false;
}
var cells = Object.create({
mine: [],
byId: { },
list: [],
});
var border = Object.create({
left: -2000,
right: 2000,
top: -2000,
bottom: 2000,
width: 4000,
height: 4000,
centerX: -1,
centerY: -1
});
var leaderboard = Object.create({
type: NaN,
items: null,
canvas: document.createElement("canvas"),
teams: ["#F33", "#3F3", "#33F"]
});
var chat = Object.create({
messages: [],
waitUntil: 0,
canvas: document.createElement("canvas"),
visible: false,
});
var stats = Object.create({
fps: 0,
latency: NaN,
supports: null,
info: null,
pingLoopId: NaN,
pingLoopStamp: null,
canvas: document.createElement("canvas"),
visible: false,
score: NaN,
maxScore: 0
});
var ws = null;
var wsUrl = null;
var disconnectDelay = 1000;
var syncUpdStamp = Date.now();
var syncAppStamp = Date.now();
var mainCanvas = null;
var mainCtx = null;
var soundsVolume;
var knownSkins = { };
var ytknownSkins = { };
var loadedSkins = { };
var loadedytSkins = { };
var escOverlayShown = false;
var isTyping = false;
var chatBox = null;
var mapCenterSet = false;
var minionControlled = false;
var camera = {
x: 0,
y: 0,
target: {
x: 0,
y: 0,
scale: 1
},
viewportScale: 1,
userZoom: 1,
sizeScale: 1,
scale: 1
};
var mouseX = NaN;
var mouseY = NaN;
var skinList = [];
var youtuberskinList = [];
var macroCooldown = 0 / 7;
var macroCooldown2 = 0 / 7;
var macroCooldown2 = 40;
var macroCooldown3 = 50;
var macroCooldown4 = 40;
var macroCooldown5 = 50;
var macroIntervalID;
var macroIntervalEJECT;
var quadtree;
var settings = {
nick: "",
skin: "",
gamemode: "",
showSkins: true,
showNames: true,
darkTheme: false,
showColor: true,
showMass: false,
_showChat: true,
get showChat() {
return this._showChat;
},
set showChat(a) {
var chat = byId("chat_textbox");
a ? chat.show() : chat.hide();
this._showChat = a;
},
showMinimap: true,
showPosition: false,
showBorder: false,
showGrid: true,
playSounds: false,
soundsVolume: 0.5,
moreZoom: false,
fillSkin: true,
backgroundSectors: false,
jellyPhysics: true
};
var pressed = {
32: false,
87: false,
69: false,
82: false,
84: false,
80: false,
81: false,
27: false
};
var eatSound = new Sound("./assets/sound/eat.mp3", 0.5, 10);
var pelletSound = new Sound("./assets/sound/pellet.mp3", 0.5, 10);
request("skinList.txt", function(data) {
var skins = data.split(",");
var stamp = Date.now();
for (var i = 0; i < skins.length; i++)
knownSkins[skins[i]] = stamp;
for (var i in knownSkins)
if (knownSkins[i] !== stamp) delete knownSkins[i];
});
request("youtuberskinList.txt", function(data) {
var skins = data.split(",");
var stamp = Date.now();
for (var i = 0; i < skins.length; i++)
ytknownSkins[skins[i]] = stamp;
for (var i in ytknownSkins)
if (ytknownSkins[i] !== stamp) delete ytknownSkins[i];
});
function hideESCOverlay() {
escOverlayShown = false;
byId("overlays").hide();
}
function showESCOverlay() {
escOverlayShown = true;
byId("overlays").show(0.5);
};
function toCamera(ctx) {
ctx.translate(mainCanvas.width / 2, mainCanvas.height / 2);
scaleForth(ctx);
ctx.translate(-camera.x, -camera.y);
}
function scaleForth(ctx) {
ctx.scale(camera.scale, camera.scale);
}
function scaleBack(ctx) {
ctx.scale(1 / camera.scale, 1 / camera.scale);
}
function fromCamera(ctx) {
ctx.translate(camera.x, camera.y);
scaleBack(ctx);
ctx.translate(-mainCanvas.width / 2, -mainCanvas.height / 2);
}
function initSetting(id, elm) {
function simpleAssignListen(id, elm, prop) {
if (settings[id] !== "") elm[prop] = settings[id];
elm.addEventListener("change", function() {
requestAnimationFrame(function() {
settings[id] = elm[prop];
});
});
}
switch (elm.tagName.toLowerCase()) {
case "input":
switch (elm.type.toLowerCase()) {
case "range":
case "text":
simpleAssignListen(id, elm, "value");
break;
case "checkbox":
simpleAssignListen(id, elm, "checked");
break;
}
break;
case "select":
simpleAssignListen(id, elm, "value");
break;
}
}
function loadSettings() {
var text = localStorage.getItem("settings");
var obj = text ? JSON.parse(text) : settings;
for (var prop in settings) {
var elm = byId(prop.charAt(0) === "_" ? prop.slice(1) : prop);
if (elm) {
if(obj.hasOwnProperty(prop)) settings[prop] = obj[prop];
initSetting(prop, elm);
} else log.info("setting " + prop + " not loaded because there is no element for it.");
}
}
function storeSettings() {
localStorage.setItem("settings", JSON.stringify(settings));
}
function request(url, callback, method, type) {
if (!method) method = "GET";
if (!type) type = "text";
var req = new XMLHttpRequest();
req.onload = function() {
callback(this.response);
};
req.open(method, url);
req.responseType = type;
req.send();
}
function buildGallery() {
var c = "";
var sortedKeys = Object.keys(knownSkins).sort();
for (var i = 0; i < sortedKeys.length; i++) {
var name = sortedKeys[i];
c += '<li class="skin" onclick="changeSkin(\\'' + name + '\\')">';
c += '<img class="circular" src="./skins/' + name + '.png">';
c += '<h4 class="skinName">' + name + '</h4>';
c += '</li>';
}
byId("gallery-body").innerHTML = '<ul id="skinsUL">' + c + '</ul>';
}
function showYoutuberSkins() {
var c = "";
var sortedKeys = Object.keys(ytknownSkins).sort();
for (var i = 0; i < sortedKeys.length; i++) {
var name = sortedKeys[i];
c += '<li class="skin" onclick="changeSkin(\\'' + name + '\\')">';
c += '<img class="circular" src="./youtuberskins/' + name + '.png">';
c += '<h4 class="skinName">' + name + '</h4>';
c += '</li>';
}
byId("gallery-yt-body").innerHTML = '<ul id="skinsUL">' + c + '</ul>';
}
function buildSettingsWindow() {
// byId("settings-window").innerHTML = '<label class="control control-checkbox">Show skins<input id="showSkins" type="checkbox" /><div class="control_indicator"></div></label>';
}
function drawChat() {
if (chat.messages.length === 0 && settings.showChat)
return chat.visible = false;
chat.visible = true;
var canvas = chat.canvas;
var ctx = canvas.getContext("2d");
var latestMessages = chat.messages.slice(-10);
var lines = [];
for (var i = 0, len = latestMessages.length; i < len; i++)
lines.push([
{
text: latestMessages[i].name,
color: latestMessages[i].color
}, {
text: " " + latestMessages[i].message,
color: settings.darkTheme ? "#FFF" : "#000"
}
]);
var width = 0;
var height = 400;
for (var i = 0; i < len; i++) {
var thisLineWidth = 0;
var complexes = lines[i];
for (var j = 0; j < complexes.length; j++) {
ctx.font = "18px Ubuntu";
complexes[j].width = ctx.measureText(complexes[j].text).width;
thisLineWidth += complexes[j].width;
}
width = Math.max(thisLineWidth, width);
}
canvas.width = width;
canvas.height = height;
for (var i = 0; i < len; i++) {
width = 0;
var complexes = lines[i];
for (var j = 0; j < complexes.length; j++) {
ctx.font = "18px Ubuntu";
ctx.fillStyle = complexes[j].color;
ctx.fillText(complexes[j].text, width, 20 * (1 + i));
width += complexes[j].width;
}
}
}
function drawStats() {
if (!stats.info) return stats.visible = false;
stats.visible = true;
var canvas = stats.canvas;
var ctx = canvas.getContext("2d");
ctx.font = "14px Ubuntu";
var width = 0;
for (var i = 0; i < rows.length; i++)
width = Math.max(width, 2 + ctx.measureText(rows[i]).width + 2);
canvas.width = width;
canvas.height = rows.length * (14 + 2);
ctx.font = "14px Ubuntu";
ctx.fillStyle = settings.darkTheme ? "#AAA" : "#555";
ctx.textBaseline = "top";
for (var i = 0; i < rows.length; i++)
ctx.fillText(rows[i], 2, -1 + i * (14 + 2));
ctx.textBaseline = "bottom";
for (var i = 0; i < rows.length; i++)
ctx.fillText(rows[i], 2, -1 + i * (14 + 2));
}
function drawPosition() {
if(border.centerX !== 0 || border.centerY !== 0 || !settings.showPosition) return;
var width = 200 * (border.width / border.height);
var height = 40 * (border.height / border.width);
var beginX = mainCanvas.width / camera.viewportScale - width;
var beginY = mainCanvas.height / camera.viewportScale - height;
if (settings.showMinimap) {
mainCtx.font = "15px Ubuntu";
beginX += width / 2 - 1;
beginY = beginY - 194 * border.height / border.width;
mainCtx.textAlign = "right";
mainCtx.fillStyle = settings.darkTheme ? "#AAA" : "#555";
mainCtx.fillText("X: " + ~~camera.x + ", Y: " + ~~camera.y, beginX + width / 2, beginY + height / 2);
} else {
mainCtx.fillStyle = "#000";
mainCtx.globalAlpha = 0.4;
mainCtx.fillRect(beginX, beginY, width, height);
mainCtx.globalAlpha = 1;
drawRaw(mainCtx, beginX + width / 2, beginY + height / 2, "X: " + ~~camera.x + ", Y: " + ~~camera.y);
}
}
function prettyPrintTime(seconds) {
seconds = ~~seconds;
var minutes = ~~(seconds / 60);
if (minutes < 1) return "<1 min";
var hours = ~~(minutes / 60);
if (hours < 1) return minutes + "min";
var days = ~~(hours / 24);
if (days < 1) return hours + "h";
return days + "d";
}
function drawLeaderboard() {
if (leaderboard.type === NaN) return leaderboard.visible = false;
if (!settings.showNames || leaderboard.items.length === 0)
return leaderboard.visible = false;
leaderboard.visible = true;
var canvas = leaderboard.canvas;
var ctx = canvas.getContext("2d");
var len = leaderboard.items.length;
canvas.width = 200;
canvas.height = leaderboard.type !== "pie" ? 60 + 24 * len : 240;
ctx.globalAlpha = .4;
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 200, canvas.height);
ctx.globalAlpha = 1;
ctx.fillStyle = "#FFF";
ctx.font = "30px Ubuntu";
ctx.fillText("Leaderboard", 100 - ctx.measureText("Leaderboard").width / 2, 40);
if (leaderboard.type === "pie") {
var last = 0;
for (var i = 0; i < len; i++) {
ctx.fillStyle = leaderboard.teams[i];
ctx.beginPath();
ctx.moveTo(100, 140);
ctx.arc(100, 140, 80, last, (last += leaderboard.items[i] * PI_2), false);
ctx.closePath();
ctx.fill();
}
} else {
var text, isMe = false, w, start;
ctx.font = "20px Ubuntu";
for (var i = 0; i < len; i++) {
if (leaderboard.type === "text")
text = leaderboard.items[i];
else
text = leaderboard.items[i].name,
isMe = leaderboard.items[i].me;
ctx.fillStyle = isMe ? "#FAA" : "#FFF";
if (leaderboard.type === "ffa")
text = (i + 1) + ". " + text;
var start = ((w = ctx.measureText(text).width) > 200) ? 2 : 100 - w * 0.5;
ctx.fillText(text, start, 70 + 24 * i);
}
}
}
function drawGrid() {
mainCtx.save();
mainCtx.lineWidth = 1;
mainCtx.strokeStyle = settings.darkTheme ? "#AAA" : "#000";
mainCtx.globalAlpha = 0.2;
var step = 50,
i,
cW = mainCanvas.width / camera.scale,
cH = mainCanvas.height / camera.scale,
startLeft = (-camera.x + cW / 2) % step,
startTop = (-camera.y + cH / 2) % step;
scaleForth(mainCtx);
mainCtx.beginPath();
for (i = startLeft; i < cW; i += step) {
mainCtx.moveTo(i, 0);
mainCtx.lineTo(i, cH);
}
for (i = startTop; i < cH; i += step) {
mainCtx.moveTo(0, i);
mainCtx.lineTo(cW, i);
}
mainCtx.stroke();
mainCtx.restore();
}
function drawBackgroundSectors() {
if (border === undefined || border.width === undefined) return;
mainCtx.save();
var sectorCount = 5;
var sectorNames = ["ABCDE", "12345"];
var w = border.width / sectorCount;
var h = border.height / sectorCount;
toCamera(mainCtx);
mainCtx.fillStyle = settings.darkTheme ? "#666" : "#DDD";
mainCtx.textBaseline = "middle";
mainCtx.textAlign = "center";
mainCtx.font = (w / 3 | 0) + "px Ubuntu";
for (var y = 0; y < sectorCount; ++y) {
for (var x = 0; x < sectorCount; ++x) {
var str = sectorNames[0][x] + sectorNames[1][y];
var dx = (x + 0.5) * w + border.left;
var dy = (y + 0.5) * h + border.top;
mainCtx.fillText(str, dx, dy);
}
}
mainCtx.restore();
}
function drawMinimap() {
if (border.centerX !== 0 || border.centerY !== 0 || !settings.showMinimap) return;
mainCtx.save();
mainCtx.resetTransform();
var targetSize = 200;
var borderAR = border.width / border.height; // aspect ratio
var width = targetSize * borderAR * camera.viewportScale;
var height = targetSize / borderAR * camera.viewportScale;
var beginX = mainCanvas.width - width;
var beginY = mainCanvas.height - height;
mainCtx.fillStyle = "#000";
mainCtx.globalAlpha = 0.4;
mainCtx.fillRect(beginX, beginY, width, height);
mainCtx.globalAlpha = 1;
var sectorCount = 5;
var sectorNames = ["ABCDE", "12345"];
var sectorWidth = width / sectorCount;
var sectorHeight = height / sectorCount;
var sectorNameSize = Math.min(sectorWidth, sectorHeight) / 3;
mainCtx.fillStyle = settings.darkTheme ? "#666" : "#DDD";
mainCtx.textBaseline = "middle";
mainCtx.textAlign = "center";
mainCtx.font = sectorNameSize + "px Ubuntu";
for (var i = 0; i < sectorCount; i++) {
var x = (i + 0.5) * sectorWidth;
for (var j = 0; j < sectorCount; j++) {
var y = (j + 0.5) * sectorHeight;
mainCtx.fillText(sectorNames[0][i] + sectorNames[1][j], beginX + x, beginY + y);
}
}
var xScaler = width / border.width;
var yScaler = height / border.height;
var halfWidth = border.width / 2;
var halfHeight = border.height / 2;
var myPosX = beginX + (camera.x + halfWidth) * xScaler;
var myPosY = beginY + (camera.y + halfHeight) * yScaler;
var xIndex = (myPosX - beginX) / sectorWidth | 0;
var yIndex = (myPosY - beginY) / sectorHeight | 0;
var lightX = beginX + xIndex * sectorWidth;
var lightY = beginY + yIndex * sectorHeight;
mainCtx.fillStyle = "yellow";
mainCtx.globalAlpha = 0.3;
mainCtx.fillRect(lightX, lightY, sectorWidth, sectorHeight);
mainCtx.globalAlpha = 1;
mainCtx.beginPath();
if (cells.mine.length) {
for (var i = 0; i < cells.mine.length; i++) {
var cell = cells.byId[cells.mine[i]];
if (cell) {
mainCtx.fillStyle = cell.color; // repeat assignment of same color is OK
var x = beginX + (cell.x + halfWidth) * xScaler;
var y = beginY + (cell.y + halfHeight) * yScaler;
var r = Math.max(cell.s, 200) * (xScaler + yScaler) / 2;
mainCtx.moveTo(x + r, y);
mainCtx.arc(x, y, r, 0, PI_2);
}
}
} else {
mainCtx.fillStyle = "#FAA";
mainCtx.arc(myPosX, myPosY, 5, 0, PI_2);
}
mainCtx.fill();
// draw name above user's pos if they have a cell on the screen
var cell = null;
for (var i = 0, l = cells.mine.length; i < l; i++)
if (cells.byId.hasOwnProperty(cells.mine[i])) {
cell = cells.byId[cells.mine[i]];
break;
}
if (cell !== null) {
mainCtx.fillStyle = settings.darkTheme ? "#DDD" : "#222";
var textSize = sectorNameSize;
mainCtx.font = textSize + "px Ubuntu";
mainCtx.fillText(cell.name || EMPTY_NAME, myPosX, myPosY - 7 - textSize / 2);
}
mainCtx.restore();
}
function drawBorders() {
if(!settings.showBorder) return;
mainCtx.strokeStyle = '#0000ff';
mainCtx.lineWidth = 6;
mainCtx.lineCap = "round";
mainCtx.lineJoin = "round";
mainCtx.beginPath();
mainCtx.moveTo(border.left, border.top);
mainCtx.lineTo(border.right, border.top);
mainCtx.lineTo(border.right, border.bottom);
mainCtx.lineTo(border.left, border.bottom);
mainCtx.closePath();
mainCtx.stroke();
};
function drawGame() {
stats.fps += (1000 / Math.max(Date.now() - syncAppStamp, 1) - stats.fps) / 10;
syncAppStamp = Date.now();
var drawList = cells.list.slice(0).sort(cellSort);
for (var i = 0, l = drawList.length; i < l; i++)
drawList[i].update(syncAppStamp);
cameraUpdate();
if (settings.jellyPhysics) {
updateQuadtree();
for (var i = 0, l = drawList.length; i < l; ++i) {
var cell = drawList[i];
cell.updateNumPoints();
cell.movePoints();
}
}
mainCtx.save();
mainCtx.fillStyle = settings.darkTheme ? "#111" : "#F2FBFF";
mainCtx.fillRect(0, 0, mainCanvas.width, mainCanvas.height);
if (settings.showGrid) drawGrid();
if (settings.backgroundSectors) drawBackgroundSectors();
toCamera(mainCtx);
drawBorders();
for (var i = 0, l = drawList.length; i < l; i++)
drawList[i].draw(mainCtx);
fromCamera(mainCtx);
quadtree = null;
mainCtx.scale(camera.viewportScale, camera.viewportScale);
var height = 1015;
mainCtx.fillStyle = settings.darkTheme ? "#FFF" : "#000";
mainCtx.textBaseline = "top";
if (!isNaN(stats.score)) {
mainCtx.font = "30px Ubuntu";
mainCtx.fillText("Score: " + stats.score, 2, height);
height += 30;
}
mainCtx.font = "20px Ubuntu";
if (stats.visible)
mainCtx.drawImage(stats.canvas, 2, height);
if (leaderboard.visible)
mainCtx.drawImage(
leaderboard.canvas,
mainCanvas.width / camera.viewportScale - 10 - leaderboard.canvas.width,
10);
if (settings.showChat && (chat.visible || isTyping)) {
// mainCtx.globalAlpha = isTyping ? 1 : Math.max(1000 - syncAppStamp + chat.waitUntil, 0) / 1000;
mainCtx.textBaseline = "bottom";
mainCtx.drawImage(
chat.canvas,
3 / camera.viewportScale,
// 5 / camera.viewportScale, - old value
(chat.canvas.height ) / camera.viewportScale - chat.canvas.height
//(mainCanvas.height - 10) / camera.viewportScale - chat.canvas.height - old value
);
mainCtx.globalAlpha = 1;
}
drawMinimap();
drawPosition();
mainCtx.restore();
if (minionControlled) {
mainCtx.save();
mainCtx.font = "12px Ubuntu";
mainCtx.textAlign = "center";
mainCtx.textBaseline = "hanging";
mainCtx.fillStyle = "#eea236";
var text = "You are controlling a minion, press Q to switch back.";
mainCtx.fillText(text, mainCanvas.width / 2, 5);
mainCtx.restore();
}
cacheCleanup();
window.requestAnimationFrame(drawGame);
}
function cellSort(a, b) {
return a.s === b.s ? a.id - b.id : a.s - b.s;
}
function cameraUpdate() {
var myCells = [];
for (var i = 0; i < cells.mine.length; i++)
if (cells.byId.hasOwnProperty(cells.mine[i]))
myCells.push(cells.byId[cells.mine[i]]);
if (myCells.length > 0) {
var x = 0,
y = 0,
s = 0,
score = 0;
for (var i = 0, l = myCells.length; i < l; i++) {
var cell = myCells[i];
score += ~~(cell.ns * cell.ns / 100);
x += cell.x;
y += cell.y;
s += cell.s;
}
camera.target.x = x / l;
camera.target.y = y / l;
camera.sizeScale = Math.pow(Math.min(64 / s, 1), 0.4);
camera.target.scale = camera.sizeScale;
camera.target.scale *= camera.viewportScale * camera.userZoom;
camera.x = (camera.target.x + camera.x) / 2;
camera.y = (camera.target.y + camera.y) / 2;
stats.score = score;
stats.maxScore = Math.max(stats.maxScore, score);
} else {
stats.score = NaN;
stats.maxScore = 0;
camera.x += (camera.target.x - camera.x) / 20;
camera.y += (camera.target.y - camera.y) / 20;
}
camera.scale += (camera.target.scale - camera.scale) / 9;
}
function sqDist(a, b) {
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
function updateQuadtree() {
var w = 1920 / camera.sizeScale;
var h = 1080 / camera.sizeScale;
var x = (camera.x - w / 2);
var y = (camera.y - h / 2);
quadtree = new PointQuadTree(x, y, w, h, QUADTREE_MAX_POINTS);
for (var i = 0; i < cells.list.length; ++i) {
var cell = cells.list[i];
for (var n = 0; n < cell.points.length; ++n) {
quadtree.insert(cell.points[n]);
}
}
}
function Cell(id, x, y, s, name, color, skin, flags) {
this.id = id;
this.x = this.nx = this.ox = x;
this.y = this.ny = this.oy = y;
this.s = this.ns = this.os = s;
this.setColor(color);
this.setName(name);
this.setSkin(skin);
this.jagged = flags & 0x01 || flags & 0x10;
this.ejected = !!(flags & 0x20);
this.born = syncUpdStamp;
this.points = [];
this.pointsVel = [];
}
Cell.prototype = {
destroyed: false,
id: 0, diedBy: 0,
ox: 0, x: 0, nx: 0,
oy: 0, y: 0, ny: 0,
os: 0, s: 0, ns: 0,
nameSize: 0, drawNameSize: 0,
color: "#FFF", sColor: "#E5E5E5",
skin: null, jagged: false,
born: null, updated: null, dead: null, // timestamps
destroy: function(killerId) {
delete cells.byId[this.id];
if (cells.mine.remove(this.id) && cells.mine.length === 0)
showESCOverlay();
this.destroyed = true;
this.dead = syncUpdStamp;
if (killerId && !this.diedBy) {
this.diedBy = killerId;
this.updated = syncUpdStamp;
}
},
update: function(relativeTime) {
var dt = (relativeTime - this.updated) / 120;
dt = Math.max(Math.min(dt, 1), 0);
if (this.destroyed && Date.now() > this.dead + 200)
cells.list.remove(this);
else if (this.diedBy && cells.byId.hasOwnProperty(this.diedBy)) {
this.nx = cells.byId[this.diedBy].x;
this.ny = cells.byId[this.diedBy].y;
}
this.x = this.ox + (this.nx - this.ox) * dt;
this.y = this.oy + (this.ny - this.oy) * dt;
this.s = this.os + (this.ns - this.os) * dt;
this.nameSize = ~~(~~(Math.max(~~(0.3 * this.ns), 24)) / 3) * 3;
this.drawNameSize = ~~(~~(Math.max(~~(0.3 * this.s), 24)) / 3) * 3;
},
updateNumPoints: function() {
var numPoints = this.s * camera.scale | 0;
numPoints = Math.max(numPoints, CELL_POINTS_MIN);
numPoints = Math.min(numPoints, CELL_POINTS_MAX);
if (this.jagged) numPoints = VIRUS_POINTS;
while (this.points.length > numPoints) {
var i = Math.random() * this.points.length | 0;
this.points.splice(i, 1);
this.pointsVel.splice(i, 1);
}
if (this.points.length == 0 && numPoints != 0) {
this.points.push({
x: this.x,
y: this.y,
rl: this.s,
parent: this
});
this.pointsVel.push(Math.random() - 0.5);
}
while (this.points.length < numPoints) {
var i = Math.random() * this.points.length | 0;
var point = this.points[i];
var vel = this.pointsVel[i];
this.points.splice(i, 0, {
x: point.x,
y: point.y,
rl: point.rl,
parent: this
});
this.pointsVel.splice(i, 0, vel);
}
},
movePoints: function() {
var pointsVel = this.pointsVel.slice();
var len = this.points.length;
for (var i = 0; i < len; ++i) {
var prevVel = pointsVel[(i - 1 + len) % len];
var nextVel = pointsVel[(i + 1) % len];
var newVel = (this.pointsVel[i] + Math.random() - 0.5) * 0.7;
newVel = Math.max(Math.min(newVel, 10), -10);
this.pointsVel[i] = (prevVel + nextVel + 8 * newVel) / 10;
}
for (var i = 0; i < len; ++i) {
var curP = this.points[i];
var curRl = curP.rl;
var prevRl = this.points[(i - 1 + len) % len].rl;
var nextRl = this.points[(i + 1) % len].rl;
var self = this;
var affected = quadtree.some({
x: curP.x - 5,
y: curP.y - 5,
w: 10,
h: 10
}, function(item) {
return item.parent != self && sqDist(item, curP) <= 25;
});
if (!affected &&
(curP.x < border.left || curP.y < border.top ||
curP.x > border.right || curP.y > border.bottom))
{
affected = true;
}
if (affected) {
this.pointsVel[i] = Math.min(this.pointsVel[i], 0);
this.pointsVel[i] -= 1;
}
curRl += this.pointsVel[i];
curRl = Math.max(curRl, 0);
curRl = (9 * curRl + this.s) / 10;
curP.rl = (prevRl + nextRl + 8 * curRl) / 10;
var angle = 2 * Math.PI * i / len;
var rl = curP.rl;
if (this.jagged && i % 2 == 0) {
rl += 5;
}
curP.x = this.x + Math.cos(angle) * rl;
curP.y = this.y + Math.sin(angle) * rl;
}
},
parseName: function(value) { // static method
value = value || "";
var nameAndSkin = /^(?:\\{([^}]*)\\})?([^]*)/.exec(value);
return {
name: nameAndSkin[2].trim(),
skin: (nameAndSkin[1] || "").trim() || nameAndSkin[2]
};
},
setName: function(name) {
var nameAndSkin = Cell.prototype.parseName(name);
this.name = nameAndSkin.name;
this.setSkin(nameAndSkin.skin);
this.setytSkin(nameAndSkin.skin);
},
setSkin: function(value) {
this.skin = (value && value[0] === "%" ? value.slice(1) : value) || this.skin;
if (this.skin === null || !knownSkins.hasOwnProperty(this.skin) || loadedSkins[this.skin])
return;
loadedSkins[this.skin] = new Image();
loadedSkins[this.skin].src = SKIN_URL + this.skin + ".png";
},
setytSkin: function(value) {
this.skin = (value && value[0] === "%" ? value.slice(1) : value) || this.skin;
if (this.skin === null || !ytknownSkins.hasOwnProperty(this.skin) || loadedytSkins[this.skin])
return;
loadedytSkins[this.skin] = new Image();
loadedytSkins[this.skin].src = YTSKIN_URL + this.skin + ".png";
},
setColor: function(value) {
if (!value) { log.warn("got no color"); return; }
this.color = value;
this.sColor = darkenColor(value);
},
draw: function(ctx) {
ctx.save();
this.drawShape(ctx);
this.drawText(ctx);
ctx.restore();
},
drawShape: function(ctx) {
ctx.fillStyle = settings.showColor ? this.color : Cell.prototype.color;
ctx.strokeStyle = settings.showColor ? this.sColor : Cell.prototype.sColor;
ctx.lineWidth = Math.max(~~(this.s / 0), 0);
if (this.s > 20)
this.s -= ctx.lineWidth / 2;
let biggestPointValue = 0;
ctx.beginPath();
if (this.jagged) ctx.lineJoin = "miter";
if (settings.jellyPhysics && this.points.length) {
var point = this.points[0];
ctx.moveTo(point.x, point.y);
for (var i = 0; i < this.points.length; ++i) {
var point = this.points[i];
ctx.lineTo(point.x, point.y);
if(this.points[i].rl > biggestPointValue) biggestPointValue = this.points[i].rl;
}
} else if (this.jagged) {
var pointCount = 120;
var incremental = PI_2 / pointCount;
ctx.moveTo(this.x, this.y + this.s + 3);
for (var i = 1; i < pointCount; i++) {
var angle = i * incremental;
var dist = this.s - 3 + (i % 2 === 0) * 6;
ctx.lineTo(
this.x + dist * Math.sin(angle),
this.y + dist * Math.cos(angle)
)
}
ctx.lineTo(this.x, this.y + this.s + 3);
} else ctx.arc(this.x, this.y, this.s, 0, PI_2, false);
ctx.closePath();
if (this.destroyed)
ctx.globalAlpha = Math.max(120 - Date.now() + this.dead, 0) / 120;
else ctx.globalAlpha = Math.min(Date.now() - this.born, 120) / 120;
if (settings.fillSkin) ctx.fill();
if (settings.showSkins && this.skin) {
var skin = loadedSkins[this.skin] || loadedytSkins[this.skin];
if (skin && skin.complete && skin.width && skin.height) {
ctx.save();
ctx.clip();
scaleBack(ctx);
var sScaled = (settings.jellyPhysics ? biggestPointValue : this.s) * camera.scale;
ctx.drawImage(skin,
this.x * camera.scale - sScaled,
this.y * camera.scale - sScaled,
sScaled *= 2, sScaled);
scaleForth(ctx);
ctx.restore();
}
} else if (!settings.fillSkin) ctx.fill();
if (this.s > 20) {
ctx.stroke();
this.s += ctx.lineWidth / 2;
}
},
drawText: function(ctx) {
if (this.s < 20 || this.jagged) return;
var y = this.y;
if (this.name && settings.showNames) {
drawText(ctx, false, this.x, this.y, this.nameSize, this.drawNameSize, this.name);
y += Math.max(this.s / 4.5, this.nameSize / 1.5);
}
if (settings.showMass && (cells.mine.indexOf(this.id) !== -1 || cells.mine.length === 0)) {
var mass = (~~(this.s * this.s / 100)).toString();
drawText(ctx, true, this.x, y, this.nameSize / 2, this.drawNameSize / 2, mass);
}
}
};
function cacheCleanup() {
for (var i in cachedNames) {
for (var j in cachedNames[i])
if (syncAppStamp - cachedNames[i][j].accessTime >= 5000)
delete cachedNames[i][j];
if (cachedNames[i] === { }) delete cachedNames[i];
}
for (var i in cachedMass)
if (syncAppStamp - cachedMass[i].accessTime >= 5000)
delete cachedMass[i];
}
// 2-var draw-stay cache
var cachedNames = { };
var cachedMass = { };
function drawTextOnto(canvas, ctx, text, size) {
ctx.font = size + "px Ubuntu";
ctx.lineWidth = Math.max(~~(size / 10), 2);
canvas.width = ctx.measureText(text).width + 2 * ctx.lineWidth;
canvas.height = 4 * size;
ctx.font = size + "px Ubuntu";
ctx.lineWidth = Math.max(~~(size / 10), 2);
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillStyle = "#FFF"
ctx.strokeStyle = "#000";
ctx.translate(canvas.width / 2, 2 * size);
(ctx.lineWidth !== 1) && ctx.strokeText(text, 0, 0);
ctx.fillText(text, 0, 0);
}
function drawRaw(ctx, x, y, text, size) {
ctx.font = size + "px Ubuntu";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.lineWidth = Math.max(~~(size / 10), 2);
ctx.fillStyle = "#FFF"
ctx.strokeStyle = "#000";
(ctx.lineWidth !== 1) && ctx.strokeText(text, x, y);
ctx.fillText(text, x, y);
ctx.restore();
}
function newNameCache(value, size) {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
drawTextOnto(canvas, ctx, value, size);
cachedNames[value] = cachedNames[value] || { };
cachedNames[value][size] = {
width: canvas.width,
height: canvas.height,
canvas: canvas,
value: value,
size: size,
accessTime: syncAppStamp
};
return cachedNames[value][size];
}
function newMassCache(size) {
var canvases = {
"0": { }, "1": { }, "2": { }, "3": { }, "4": { },
"5": { }, "6": { }, "7": { }, "8": { }, "9": { }
};
for (var value in canvases) {
var canvas = canvases[value].canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
drawTextOnto(canvas, ctx, value, size);
canvases[value].canvas = canvas;
canvases[value].width = canvas.width;
canvases[value].height = canvas.height;
}
cachedMass[size] = {
canvases: canvases,
size: size,
lineWidth: Math.max(~~(size / 10), 2),
accessTime: syncAppStamp
};
return cachedMass[size];
}
function toleranceTest(a, b, tolerance) {
return (a - tolerance) <= b && b <= (a + tolerance);
}
function getNameCache(value, size) {
if (!cachedNames[value]) return newNameCache(value, size);
var sizes = Object.keys(cachedNames[value]);
for (var i = 0, l = sizes.length; i < l; i++)
if (toleranceTest(size, sizes[i], size / 4))
return cachedNames[value][sizes[i]];
return newNameCache(value, size);
}
function getMassCache(size) {
var sizes = Object.keys(cachedMass);
for (var i = 0, l = sizes.length; i < l; i++)
if (toleranceTest(size, sizes[i], size / 4))
return cachedMass[sizes[i]];
return newMassCache(size);
}
function drawText(ctx, isMass, x, y, size, drawSize, value) {
ctx.save();
if (size > 500) return drawRaw(ctx, x, y, value, drawSize);
ctx.imageSmoothingQuality = "high";
if (isMass) {
var cache = getMassCache(size);
cache.accessTime = syncAppStamp;
var canvases = cache.canvases;
var correctionScale = drawSize / cache.size;
// calculate width
var width = 0;
for (var i = 0; i < value.length; i++)
width += canvases[value[i]].width - 2 * cache.lineWidth;
ctx.scale(correctionScale, correctionScale);
x /= correctionScale;
y /= correctionScale;
x -= width / 2;
for (var i = 0; i < value.length; i++) {
var item = canvases[value[i]];
ctx.drawImage(item.canvas, x, y - item.height / 2);
x += item.width - 2 * cache.lineWidth;
}
} else {
var cache = getNameCache(value, size);
cache.accessTime = syncAppStamp;
var canvas = cache.canvas;
var correctionScale = drawSize / cache.size;
ctx.scale(correctionScale, correctionScale);
x /= correctionScale;
y /= correctionScale;
ctx.drawImage(canvas, x - canvas.width / 2, y - canvas.height / 2);
}
ctx.restore();
}
function keydown(event) {
var key = event.keyCode;
if (key == 13) {//enter
if (escOverlayShown || !settings.showChat) return;
if (isTyping) {
chatBox.blur();
var text = chatBox.value;
if (text.length > 0) sendChat(text);
chatBox.value = "";
} else chatBox.focus();
} else if (pressed[key]) {
return;
} else if (key == 27) {//escape
pressed[key] = true;
escOverlayShown ? hideESCOverlay() : showESCOverlay();
} else {
if (isTyping || escOverlayShown) return;
if(pressed.hasOwnProperty(key))pressed[key] = true;
var code = KEY_TO_CODE[key];
if (code !== undefined) wsSend(code);
if (key == 87) {//w
macroIntervalID = setInterval(function() {
wsSend(code);
}, macroCooldown);
console.log(key,macroIntervalID);
}
if (key == 82) {//r
macroIntervalEJECT = setInterval(function() {
wsSend(code);
}, macroCooldown);
}
if (key == 81) minionControlled = !minionControlled;//q
if(key == 70) {//f
wsSend(UINT8_CACHE[17]);
setTimeout(function(){
wsSend(UINT8_CACHE[17]);
}, macroCooldown2);
setTimeout(function(){
wsSend(UINT8_CACHE[17]);
}, macroCooldown2*2);
setTimeout(function(){
wsSend(UINT8_CACHE[17]);
}, macroCooldown2*3);
setTimeout(function(){
wsSend(UINT8_CACHE[17]);
}, macroCooldown2*4);
}
if(key == 68) {//d
wsSend(UINT8_CACHE[17]);
setTimeout(function(){
wsSend(UINT8_CACHE[17]);
}, macroCooldown3*2);
}
if(key == 88) {//x
wsSend(UINT8_CACHE[22]);
setTimeout(function(){
wsSend(UINT8_CACHE[22]);
}, macroCooldown5*2);
}
if(key == 67) {//c
wsSend(UINT8_CACHE[22]);
setTimeout(function(){
wsSend(UINT8_CACHE[22]);
}, macroCooldown2);
setTimeout(function(){
wsSend(UINT8_CACHE[22]);
}, macroCooldown2*2);
setTimeout(function(){
wsSend(UINT8_CACHE[22]);
}, macroCooldown2*3);
setTimeout(function(){
wsSend(UINT8_CACHE[22]);
}, macroCooldown2*4);
}
}
}
function keyup(e) {
var key = e.keyCode;
console.log(key,pressed);
if(pressed.hasOwnProperty(key))pressed[key] = false;
if (key == 81) wsSend(UINT8_CACHE[19]);//q
if (key == 87) clearInterval(macroIntervalID);//w
if (key == 82) clearInterval(macroIntervalEJECT);//r
}
function handleScroll(event) {
if (event.target !== mainCanvas) return;
camera.userZoom *= event.deltaY > 0 ? 0.8 : 1.2;
camera.userZoom = Math.max(camera.userZoom, settings.moreZoom ? 0.1 : 1);
camera.userZoom = Math.min(camera.userZoom, 4);
}
function init() {
mainCanvas = document.getElementById("canvas");
mainCtx = mainCanvas.getContext("2d");
chatBox = byId("chat_textbox");
soundsVolume = byId("soundsVolume");
mainCanvas.focus();
loadSettings();
window.addEventListener("beforeunload", storeSettings);
document.addEventListener("wheel", handleScroll, {passive: true});
byId("play-btn").addEventListener("click", function() {
if (settings.skin) {
sendPlay("{" + settings.skin + "}" + settings.nick);
} else {
sendPlay(settings.nick);
}
hideESCOverlay();
});
window.onkeydown = keydown;
window.onkeyup = keyup;
chatBox.onblur = function() {
isTyping = false;
drawChat();
};
chatBox.onfocus = function() {
isTyping = true;
drawChat();
};
mainCanvas.onmousemove = function(event) {
mouseX = event.clientX;
mouseY = event.clientY;
};
setInterval(function() {
sendMouseMove(
(mouseX - mainCanvas.width / 2) / camera.scale + camera.x,
(mouseY - mainCanvas.height / 2) / camera.scale + camera.y
);
}, 40);
window.onresize = function() {
var width = mainCanvas.width = window.innerWidth;
var height = mainCanvas.height = window.innerHeight;
camera.viewportScale = Math.max(width / 1920, height / 1080);
};
window.onresize();
var mobileStuff = byId("mobileStuff");
var touchpad = byId("touchpad");
var touchCircle = byId("touchCircle");
var touchSize = .2;
var touched = false;
var touchmove = function(event) {
var touch = event.touches[0];
var width = innerWidth * touchSize;
var height = innerHeight * touchSize;
if (touch.pageX < width && touch.pageY > innerHeight - height) {
mouseX = innerWidth / 2 + (touch.pageX - width / 2) * innerWidth / width;
mouseY = innerHeight / 2 + (touch.pageY - (innerHeight - height / 2)) * innerHeight / height;
} else {
mouseX = touch.pageX;
mouseY = touch.pageY;
}
var r = innerWidth * .02;
touchCircle.style.left = mouseX - r + "px";
touchCircle.style.top = mouseY - r + "px";
};
window.addEventListener("touchmove", touchmove);
window.addEventListener("touchstart", function(event) {
if (!touched) {
touched = true;
mobileStuff.show();
}
if (event.target.id == "splitBtn") {
wsSend(UINT8_CACHE[17]);
} else if (event.target.id == "ejectBtn") {
wsSend(UINT8_CACHE[21]);
} else if (event.target.id == "splitBotBtn") {
wsSend(UINT8_CACHE[22]);
} else if (event.target.id == "ejectBotBtn") {
wsSend(UINT8_CACHE[23]);
} else {
touchmove(event);
}
touchCircle.show();
});
window.addEventListener("touchend", function(event) {
if (event.touches.length === 0) {
touchCircle.hide();
}
});
gameReset();
showESCOverlay();
if (window.location.search) {
var div = /ip=([\\w\\W]+):([0-9]+)/.exec(window.location.search.slice(1))
if (div) wsInit(div[1] + ":" + div[2]);
}
window.setserver(byId("gamemode").value);
window.requestAnimationFrame(drawGame);
log.info("init done in " + (Date.now() - LOAD_START) + "ms");
}
window.setserver = function(arg) {
if (wsUrl === arg) return;
wsInit(arg);
};
window.spectate = function(a) {
wsSend(UINT8_CACHE[1]);
stats.maxScore = 0;
hideESCOverlay();
};
window.changeSkin = function(a) {
byId("skin").value = a;
settings.skin = a;
byId("gallery").hide();
byId("gallery-yt").hide();
};
window.openSkinsList = function() {
if (byId("gallery-body").innerHTML == "") buildGallery();
byId("gallery").show(0.5);
};
window.showYoutuberSkins = function() {
if (byId("gallery-yt-body").innerHTML == "") showYoutuberSkins();
byId("gallery-yt").show(0.5);
};
window.showSettings = function() {
if (byId("settings-window-body").innerHTML == "") buildSettingsWindow();
byId("settings-window").show(0.5);
};
window.addEventListener("DOMContentLoaded", init);
})();
</script>
<script src="assets/js/skinhandler.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
</head>
<body onload="setserver('imsolo.pro:4101');skinHandler()">
<div id="gallery" onclick="if (event.target == this) this.hide()" style="display: none;">
<div id="gallery-content">
<div id="gallery-header">Free Skins - Gallery</div>
<button id="gallery-youtubers" class="youtuberSkinsBtn-gallery" onclick="showYoutuberSkins()">Youtuber Skins - Gallery</button>
<div id="gallery-body"></div>
</div>
</div>
<div id="gallery-yt" onclick="if (event.target == this) this.hide()" style="display: none;">
<div id="gallery-yt-content">
<div id="gallery-yt-header">Youtuber Skins</div>
<div id="gallery-yt-body"></div>
</div>
</div>
<div id="settings-window" onclick="if (event.target == this) this.hide()" style="display: none;">
<div id="settings-window-content">
<div id="settings-window-header">Imsolo Web Settings</div>
<div id="settings-window-body">
<div>
<label class="control control-checkbox">Show skins<input id="showSkins" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Show Names<input id="showNames" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Dark Theme<input id="darkTheme" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Show Colors<input id="showColor" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Show Mass<input id="showMass" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Show Chat<input id="showChat" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Show Minimap<input id="showMinimap" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Show Position<input id="showPosition" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Show Map & Cell Borders<input id="showBorder" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Show Grid<input id="showGrid" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Enable Zoomout<input id="moreZoom" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Fill Skins<input id="fillSkin" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Show Background Sectors<input id="backgroundSectors" type="checkbox" /><div class="control_indicator"></div></label>
<label class="control control-checkbox">Enable Smooth Animations<input id="jellyPhysics" type="checkbox" /><div class="control_indicator"></div></label>
</div>
</div>
</div>
</div>
<div id="overlays" style="display: none;">
<div id="helloDialog">
<div class="form-group">
<h2 id="title">Imsolo.pro Web</h2>
</div>
<div id="play-style-menu" class="play-style-menu-div">
<input id="nick" class="form-control zindex" placeholder="Nickname" maxlength="17">
<button id="play-btn" class="play-btn-pc"><span>Play</span></button>
</div>
<!-- <p class="newBadge">Savaging players can result in a permanent ban.</p> -->
<button id="show-settings-menu-btn" class="btn btn-play btn-primary btn-needs-server btn-info glyphicon glyphicon-cog" onclick="showSettings()"></button>
<button id="spectate-btn" onclick="spectate()" class="btn btn-warning btn-spectate btn-needs-server glyphicon glyphicon-eye-open"></button>
<button id="gallery-btn" onclick="openSkinsList()" class="btn btn-play btn-primary btn-needs-server btn-info glyphicon glyphicon-picture"></button>
<div style="text-align:center;" class="form-group">
<input id="skin" class="form-control" placeholder="Skin Name">
</div>
<div id="settings" class="checkbox">
<table style="width:100%">
<div style="margin: 6px;" class="control-group">
</table>
</div>
<hr>
<div id="footer">
<p>if you want to play on the Original 2015, 2016 and 2017 clients, click here:</p>
<a href="/2015/">
<button id="2015-btn" class="other-gamemode-versions button-client-select">2015</button>
</a>
<a href="/2016/">
<button id="2016-btn" class="other-gamemode-versions button-client-select">2016</button>
</a>
<a href="/2017/">
<button id="2017-btn" class="other-gamemode-versions button-client-select">2017</button>
</a>
</div>
<div id="rightOptions" class="rightOptions">
<div id="spacegmd">
<br>
<br>
</div>
<div style="margin: 6px;" id="gamemode">
<div>
<!-- <a href="https://fr.surveymonkey.com/r/YMMCTX3">
<button id="2015-btn" class="other-gamemode-versions button-client-select whiteborder">Do you enjoy beta party mode? Click here to help us improving it! Submit your feedback :)</button>
</a><br> -->
<button class="button-gamemodes-imsolo-style-newmode button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:2110')"><span class="newBadge">NEW</span> BETA PARTY MODE V2<img class="img-gamemodes-imsolo-megasplit" src="assets/img/party.png" /></button>
</div>
<center>
<table>
<tbody>
<div class="gamemodesDisplay">
<button class="button-gamemodes-imsolo-style-megasplit button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4110')">PARTY MEGASPLIT<img class="img-gamemodes-imsolo-megasplit" src="assets/img/megasplit.png" /></button>
<button class="button-gamemodes-imsolo-style-megasplit button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:2108')">BETA MEGASPLIT<!-- <span class="newBadge">NEW</span> --><img class="img-gamemodes-imsolo-megasplit" src="assets/img/megasplit.png" /></button>
<button class="button-gamemodes-imsolo-style-megasplit button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:2104')">BETA PARTY MODE<img class="img-gamemodes-imsolo-megasplit" src="assets/img/party.png" /></button>
</div>
<tr>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4101')">PARTY MODE<img class="img-gamemodes-imsolo" src="assets/img/party.png" /></button>
</td>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:2107')">INSTANT MERGING<img class="img-gamemodes-imsolo" src="assets/img/instant.png" /></button>
</td>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4107')">PARTY SELFEED<img class="img-gamemodes-imsolo" src="assets/img/selfeed.png" /></button>
</td>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4108')">CRAZY SELFEED<img class="img-gamemodes-imsolo" src="assets/img/crazyselfeed.png" /></button>
</td>
</tr>
<tr>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4102')">1V1#1 FFA TOURNEY<img class="img-gamemodes-imsolo" src="assets/img/1v1.png" /></button>
</td>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4104')">1V1#2 FFA TOURNEY<img class="img-gamemodes-imsolo" src="assets/img/1v1.png" /></button>
</td>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4105')">1V1#3 FFA TOURNEY<img class="img-gamemodes-imsolo" src="assets/img/1v1.png" /></button>
</td>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4111')">1V1#1 EXP TOURNEY<img class="img-gamemodes-imsolo" src="assets/img/1v1megasplit.png" /></button>
</td>
</tr>
<tr>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4112')">1V1#2 EXP TOURNEY<img class="img-gamemodes-imsolo" src="assets/img/1v1megasplit.png" /></button>
</td>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4113')">1V1#3 EXP TOURNEY<img class="img-gamemodes-imsolo" src="assets/img/1v1megasplit.png" /></button>
</td>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4103')">2v2 TOURNEY<img class="img-gamemodes-imsolo" src="assets/img/2v2.png" /></button>
</td>
<td>
<button class="button-gamemodes-imsolo-style button-gamemodes-select whiteborder" onclick="setserver('imsolo.pro:4109')">2v2V2 TOURNEY<img class="img-gamemodes-imsolo" src="assets/img/2v2v2.png" /></button>
</td>
</tr>
</tbody>
</table>
</center>
</ul>
</div>
</div>
<div id="leftOptions" class="leftOptions">
<div id="settings" class="checkbox">
<table style="width:100%">
<div style="margin: 6px;" class="control-group">
</table>
</script-->
<div id="showSettingsDiv" class="div-border-menu control-group">
<br>
<h4>imsolo.pro/web - Changelog</h4>
<br>
<p><span class="newBadge">NEW UPDATE :</span> Global Game Update</p>
<p>----------------</p>
<p>- Fixed Squared Skins Glitch</p>
<p>----------------</p>
<p><span class="newBadge">NEW UPDATE :</span> Beta Party Mode</p>
<p>----------------</p>
<p>- Bots less big to avoid 200k servers</p>
<p>- Increased mass boosters amount</p>
<p>----------------</p>
<p></p>
<p></p>
<p>Join the discord to give some feedback :)</p>
<div id="instructions">
<hr class="hrMenu">
<center>
<span class="text-muted">
Press <b>D</b> for macro doublesplit<br>
Press <b>F</b> for macro sixteen<br>
<br>
Press <b>X</b> for bot doublesplit<br>
Press <b>C</b> for bot sixteen<br>
</span>
</center>
</div>
</div>
</div>
</div>
</div>
</center>
</div>
</div>
</div>
</div>
<div id="connecting">
<div id="connecting-content">
<h2>Connecting</h2>
<p> If you cannot connect to the servers, check if you have some anti virus or firewall blocking the connection.</p>
</div>
</div>
<div id="mobileStuff" style="display: none;">
<div id="touchpad"></div>
<div id="touchCircle" style="display: none;"></div>
<img src="./assets/img/split.png" id="splitBtn">
<img src="./assets/img/eject.png" id="ejectBtn">
<img src="./assets/img/splitBot.png" id="splitBotBtn">
<img src="./assets/img/ejectBot.png" id="ejectBotBtn">
</div>
<canvas id="canvas" class="chat-style" width="800" height="600"></canvas>
<input type="text" id="chat_textbox" placeholder="Press enter to chat" maxlength="50">
<div style="font-family:'Ubuntu'"> </div>
</body>
</html>
`);
document.close();