Skip to content

Instantly share code, notes, and snippets.

@nabbynz
Last active February 28, 2024 23:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nabbynz/5cc0b6e10e4024a888bc7da4a902deaf to your computer and use it in GitHub Desktop.
Save nabbynz/5cc0b6e10e4024a888bc7da4a902deaf to your computer and use it in GitHub Desktop.
TagPro Performance Enhancer
// ==UserScript==
// @name TagPro Performance Enhancer
// @description Tries to make the game smoother by reducing screen draws. Mostly useful when "WebGL Rendering" is disabled.
// @version 3.0.2
// @match https://*.koalabeast.com/game
// @match https://*.koalabeast.com/game?*
// @updateURL https://gist.github.com/nabbynz/5cc0b6e10e4024a888bc7da4a902deaf/raw/TagPro_Performance_Enhancer.user.js
// @downloadURL https://gist.github.com/nabbynz/5cc0b6e10e4024a888bc7da4a902deaf/raw/TagPro_Performance_Enhancer.user.js
// @grant none
// @author nabby
// ==/UserScript==
console.log('START: ' + GM_info.script.name + ' (v' + GM_info.script.version + ' by ' + GM_info.script.author + ')');
// OPTIONS...
const replaceExplosions = true; // don't create a new PIXI.graphics for every explosion/ball pop frame, instead we just create it once and scale it as needed (only when particles are disabled).
const replaceUpdateCamera = true; // skips some math if "Viewport Scaling" is disabled
const skipPlayerDraws = true; // skips player draws if they're outside our viewport. Other small optimizations.
const modify_UI = true; // change the way the Score, Timer & FPS/Ping/Loss sprites are updated (by only updating the text when it changes we can save 90-99% redraws)
const matchBallColors = true; // try and match the ball colors when drawing the score and team names (needs modify_UI true).
const disableInteractivity = false; // if true PIXI's Interaction Manager will be disabled/removed (so you won't be able to click on a ball to report it, and special events like eggball may not work either)
const useTimerHundredths = true; // this is not a performance enhancement, it just fits nicely here (it may actually be slightly slower)
const disableForEvents = true; // this probably won't work anymore
'use strict';
/* eslint-env jquery */
/* globals tagpro, tagproConfig, PIXI */
/* eslint-disable no-multi-spaces */
let isEvent = false; // won't work on tagpro-vcr because the event scripts have been renamed for some reason
let isHalloween = false;
let isBirthday = false;
let isEaster = false;
let isEgg = false;
let scriptSrcs = $('script[src]').toArray();
let scriptNames = ['halloween', 'birthday', 'easter', 'egg', 'pirates']; // disable for Halloween, Birthday, Easter/Eggball & Pirates events
for (let scriptName of scriptNames) {
if (scriptSrcs.some(e => e.src.includes(scriptName))) { //eg: https://static.koalabeast.com/events/easter-2017.js
isEvent = true;
if (scriptName === 'halloween') isHalloween = true;
if (scriptName === 'birthday' || scriptName === 'pirates') isBirthday = true;
if (scriptName === 'easter') isEaster = true;
if (scriptName === 'egg') isEgg = true;
break;
}
}
if (!disableForEvents || disableForEvents && !isEvent) {
tagpro.ready(function() {
let tr = tagpro.renderer;
/*****************************************************************************/
if (replaceExplosions) { // && !(isEaster || isEgg)
tr.createExplosion = function(x, y) {
if (tr.options.disableParticles) {
let explosion = new PIXI.Graphics();
explosion.tagpro = {
started: performance.now(),
length: 150,
x: x + 20,
y: y + 20,
size: 5 * 40,
};
//explosion.beginFill(0xFFFF00).drawCircle(0, 0, explosion.tagpro.size).beginFill(0xFFAA00).drawCircle(0, 0, explosion.tagpro.size * 0.4);
explosion.lineStyle(3, 0xFFFF00, 0.5).drawCircle(0, 0, explosion.tagpro.size).beginFill(0xFFAA00).drawCircle(0, 0, explosion.tagpro.size * 0.4);
tr.layers.foreground.addChild(explosion);
tr.explosions.push(explosion);
} else {
let emitter = new PIXI.particles.Emitter(
tr.layers.foreground,
[tr.particleTexture],
tagpro.particleDefinitions.explosion
);
emitter.updateSpawnPos(x, y);
tr.emitters.push(emitter);
}
};
tr.drawBallPop = function(x, y, team, color) {
let explosion = new PIXI.Graphics();
explosion.tagpro = {
started: performance.now(),
length: 150,
x: x,
y: y,
size: 1.5 * 40,
color: (color ? Number('0x' + color) : (team === 1 ? 0xED4135 : (team === 2 ? 0x0F49CB : 0xFFFF00))),
};
//explosion.beginFill(explosion.tagpro.color).drawCircle(0, 0, explosion.tagpro.size); //uncomment for normal circle
explosion.beginFill(explosion.tagpro.color).drawStar(0, 0, 5, explosion.tagpro.size + 20, explosion.tagpro.size / 3, 0); //uncomment for 5-point star
tr.layers.foreground.addChild(explosion);
tr.explosions.push(explosion);
};
tr.updateExplosions = function() {
// draw the graphic once (in tr.createExplosion), then adjust the scale (much faster)
if (!tr.options.disableParticles) {
return;
}
let now = performance.now();
tr.explosions.slice().forEach(function(explosion) {
let duration = now - explosion.tagpro.started;
if (duration >= explosion.tagpro.length) {
//tr.layers.foreground.removeChild(explosion);
tr.explosions.splice(tr.explosions.indexOf(explosion), 1);
explosion.destroy(true);
return;
}
let elapsed = duration / explosion.tagpro.length;
explosion.alpha = 1 - elapsed;
explosion.scale.set(elapsed, elapsed);
explosion.position.set(explosion.tagpro.x, explosion.tagpro.y);
});
};
} // if (replaceExplosions)
/*****************************************************************************/
if (replaceUpdateCamera && !(isEaster || isEgg)) { // && !(isEaster || isEgg) // this breaks eggball click outside viewport???
tr.centerContainerToPoint = function(x, y) {
if (tr.options.disableViewportScaling) {
let resizeScaleFactor = tr.options.disableViewportScaling ? 1 : +(this.vpHeight / tr.canvas_height).toFixed(2);
tr.gameContainer.x = this.vpWidth / 2 - (x / tagpro.zoom * resizeScaleFactor);
tr.gameContainer.y = this.vpHeight / 2 - (y / tagpro.zoom * resizeScaleFactor);
} else {
tr.gameContainer.x = 640 - x;
tr.gameContainer.y = 400 - y;
}
};
tr.updateCameraPosition = function(player) {
//if (player.sprite.x !== -1000 && player.sprite.y !== -1000) {
if (player.visible) {
tr.centerContainerToPoint(player.sprite.x + 20, player.sprite.y + 20); //this is +19 in production, but it should be +20 (otherwise the ball is 1px off-center :)
}
};
tr.updateCameraZoom = function() {
let resizeScaleFactor = tr.options.disableViewportScaling ? 1 : this.vpHeight / tr.canvas_height;
if (tagpro.spectator) {
tagpro.zoom = tagpro.zoom + tagpro.zooming > 0.1 ? tagpro.zoom + tagpro.zooming : tagpro.zoom;
} else {
tagpro.zoom = 1;
}
if (resizeScaleFactor === 1 && tagpro.zoom === 1 && tr.gameContainer.scale.x === 1 && tr.gameContainer.scale.y === 1) {
return; //skip all the below if the zoom is 1
}
let scale = new PIXI.Point(1.0 / tagpro.zoom * resizeScaleFactor, 1.0 / tagpro.zoom * resizeScaleFactor);
let inverseScale = new PIXI.Point(tagpro.zoom, tagpro.zoom);
let newX = 19 - (tagpro.zoom * 19);
tr.gameContainer.scale = scale;
for (let id in tagpro.players) {
if (!tagpro.players.hasOwnProperty(id)) {
continue;
}
let player = tagpro.players[id];
if (player.sprites) {
let info = player.sprites.info; //name, degrees, flair, flag
info.x = newX;
info.scale = inverseScale;
let visibility = tagpro.zoom < tr.zoomThreshold;
//if (player.sprites.flair) {
// player.sprites.flair.visible = visibility;
//}
if (player.sprites.degrees) {
player.sprites.degrees.visible = visibility;
}
}
}
};
} //if (replaceUpdateCamera)
/*****************************************************************************/
if (skipPlayerDraws && !(isEaster || isEgg)) { // && !(isEaster || isEgg)
tr.updatePlayerVisibility = function(player) {
//not needed due to player.visible being set directly
};
tr.updateDeathStatus = function(player, drawPos) { //Uses .visible instead of moving the sprite offscreen
// if (player.toUpdate) {
// // drawPos.x = -1000;
// // drawPos.y = -1000;
// // player.sprite.x = -1000;
// // player.sprite.y = -1000;
// player.visible = false;
// player.toUpdate = false;
// } else {
// player.visible = true;
// }
};
tr.updateGrip = function(player) { //Attaches the sprite to the player (rather than tr.layers.foreground) so we don't have to update it's position every tick. Uses .visible instead of add/removing each time
if (!player.sprites.grip) {
player.sprites.grip = tagpro.tiles.draw(player.sprites.ball, "grip", {x: 0, y: 23}, 17, 17);
}
player.sprites.grip.visible = player.grip;
};
tr.updateTagpro = function(player) { //Uses .visible instead of add/removing each time
if (!player.sprites.tagproTint) {
player.sprites.tagproTint = new PIXI.Graphics();
player.sprites.tagproTint.lineStyle(2, 0x00FF00).beginFill(0x00FF00, 0.1).drawCircle(20, 20, 19);
player.sprites.tagproTint.alpha = 0.8; //Lowers the opacity a bit
player.sprites.tagproTint.anchor = { x: 0.5, y: 0.5 };
player.sprites.ball.addChild(player.sprites.tagproTint);
}
player.sprites.tagproTint.visible = player.tagpro;
if (!tr.options.disableParticles) {
if (player.tagpro && !player.sprites.tagproSparks) {
player.sprites.tagproSparks = new PIXI.particles.Emitter(player.sprites.ball, [tr.particleFireTexture], tagpro.particleDefinitions.tagproSparks);
player.sprites.tagproSparks.player = player.id;
tr.emitters.push(player.sprites.tagproSparks);
} else if (!player.tagpro && player.sprites.tagproSparks) {
player.sprites.tagproSparks.emit = false;
tr.emitters.splice(tr.emitters.indexOf(player.sprites.tagproSparks), 1);
player.sprites.tagproSparks.destroy();
player.sprites.tagproSparks.cleanup();
player.sprites.tagproSparks = null;
}
}
};
tr.updateRollingBomb = function(player) { //Uses .visible instead of add/removing each time
if (!player.sprites.bomb) {
player.sprites.bomb = new PIXI.Graphics();
player.sprites.bomb.beginFill(0xFFFF00, 0.75).drawCircle(20, 20, 20);
player.sprites.bomb.anchor = { x: 0.5, y: 0.5 };
player.sprites.ball.addChild(player.sprites.bomb);
}
if (player.bomb) {
player.sprites.bomb.alpha = Math.abs(0.75 * Math.sin(performance.now() / 150));
player.sprites.bomb.visible = true;
} else {
player.sprites.bomb.visible = false;
}
if (!tr.options.disableParticles) {
if (player.bomb && !player.sprites.rollingBomb) {
player.sprites.rollingBomb = new PIXI.particles.Emitter(player.sprites.ball, [tr.particleTexture], tagpro.particleDefinitions.rollingBomb);
tr.emitters.push(player.sprites.rollingBomb);
} else if (!player.bomb && player.sprites.rollingBomb) {
player.sprites.rollingBomb.emit = false;
tr.emitters.splice(tr.emitters.indexOf(player.sprites.rollingBomb), 1);
player.sprites.rollingBomb.destroy();
//player.sprites.rollingBomb.cleanup();
player.sprites.rollingBomb = null;
}
}
};
tr.drawDegree = function(player) { //Don't update the position every tick
if (!player.sprites.degrees && player.degree && tagpro.settings.ui.degrees) {
player.sprites.degrees = tr.veryPrettyText(player.degree + "°");
player.sprites.degrees.x = 25;
player.sprites.degrees.y = -10;
player.sprites.info.addChild(player.sprites.degrees);
}
};
tr.drawName = function(player, forceRedraw) { //Don't update the position every tick
if ((!player.sprites.name || forceRedraw) && tagpro.settings.ui.names) {
const color = player.auth ? "#BFFF00" : "#ffffff";
if (player.sprites.name) {
player.sprites.info.removeChild(player.sprites.name);
}
player.sprites.name = tr.veryPrettyText(player.name, color);
player.sprites.name.x = 20;
player.sprites.name.y = -21;
player.sprites.info.addChild(player.sprites.name);
}
};
tr.updatePlayer = function(player) {
if (!player.sprites) {
tr.setupPlayerSprites(player);
}
if (!player.sprite) {
tr.createPlayerSprite(player);
}
if (!tr.options.disableParticles) {
if (!player.sprites.emitter) {
tr.createPlayerEmitter(player);
}
tr.updatePlayerEmitter(player);
}
// if (!player.sprite.visible && (player.draw && !player.dead)) {
// player.toUpdate = true; //not needed????
// }
if (!player.sprites.info) {
let info = player.sprites.info = new PIXI.Container();
player.sprite.addChild(info);
}
player.sprite.visible = !!player.draw && !player.dead;
tr.drawDegree(player);
tr.drawName(player);
tr.drawFlair(player);
};
tr.updatePlayers = function() { //Only call drawPlayer() if in our viewport. Sets player.visible to true/false.
let meX, meY;
if (tagpro.state !== 3 && !tagpro.spectator) {
if (!tagpro.players.hasOwnProperty(tagpro.playerId)) {
console.log('TPE:: updatePlayers() !tagpro.playerId-000', tagpro.playerId); // there is maybe a bug here somewhere? (game is drawn, but players don't update)
//return;
} else {
meX = tagpro.players[tagpro.playerId].x;
meY = tagpro.players[tagpro.playerId].y;
}
// if (tagpro.players.hasOwnProperty(tagpro.playerId)) {
// meX = tagpro.players[tagpro.playerId].x;
// meY = tagpro.players[tagpro.playerId].y;
// } else {
// console.log('TPE:: updatePlayers() !tagpro.playerId-002', tagpro.playerId); // there is still a bug here somewhere (game is drawn, but players don't update)
// }
}
tr.resetFlagStatuses();
for (let id in tagpro.players) {
if (!tagpro.players.hasOwnProperty(id)) {
console.log('TPE:: updatePlayers() !tagpro.playerId-222', id, tagpro.playerId);
continue;
}
let player = tagpro.players[id];
tr.updatePlayer(player);
if (tagpro.spectator || player.id === tagpro.playerId || !meX || Math.abs(player.x - meX) < 680 || Math.abs(player.y - meY) < 440) { //updates from server are sent if x < 620 or y < 380 (then we add 20 + 40)
player.visible = true;
tr.drawPlayer(player, tr.layers.foreground);
} else {
player.visible = false;
tr.updateFlagsFromPlayer(player);
}
}
};
tr.destroyPlayer = function(player) {
if (!player.sprites) {
return;
}
if (!tr.options.disableParticles) {
let emitterIndex = tr.emitters.indexOf(player.sprites.emitter); //player emitter
if (emitterIndex !== -1) {
tr.emitters.splice(emitterIndex, 1);
}
if (player.sprites.emitter) {
player.sprites.emitter.destroy();
player.sprites.emitter = null;
}
let flairEmitterIndex = tr.emitters.indexOf(player.flairEmitter); //flair emitter (this properly fixes the Arc Reactor and Donor 7 poop bugs)
if (flairEmitterIndex !== -1) {
tr.emitters.splice(flairEmitterIndex, 1);
}
if (player.flairEmitter) {
player.flairEmitter.destroy();
player.flairEmitter = null;
}
}
if (player.sprite) {
//player.sprite.parent.removeChild(player.sprite);
player.sprite.destroy();
}
for (let key in player.sprites) {
if (player.sprites.hasOwnProperty(key)) {
let sprite = player.sprites[key];
if (sprite) {
//sprite.parent.removeChild(sprite);
sprite.destroy();
}
}
}
};
} // if (skipPlayerDraws)
/*****************************************************************************/
if (modify_UI) { // && !(isEaster || isEgg)
if (matchBallColors && tagpro.tiles.image) {
let createCanvas = function(width, height, forceDOM = false) {
if (typeof OffscreenCanvas !== 'undefined' && !forceDOM) {
return new OffscreenCanvas(width, height);
} else {
let canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
return canvas;
}
};
let canvas = createCanvas(80, 40);
let ctx = canvas.getContext("2d", { willReadFrequently: true });
ctx.drawImage(tagpro.tiles.image, 560,0,80,40, 0,0,80,40);
let getAverageColor = function(data, asHex=false, ignoreGrey=false, pixelInterval=1) {
let rgb = { r:null, g:null, b:null };
let length = data.length;
let count = 0;
let i = -4;
while ((i += pixelInterval * 4) < length) {
//let b = data[i] * 0.299 + data[i+1] * 0.587 + data[i+2] * 0.114;
let isGrey = ignoreGrey && (Math.abs(data[i] - data[i+1]) + Math.abs(data[i+1] - data[i+2]) < 30);
if (!isGrey && data[i+3]) {
rgb.r += data[i];
rgb.g += data[i+1];
rgb.b += data[i+2];
count++;
}
}
if (count === 0) { //transparent or all grey
return null;
}
rgb.r = Math.floor(rgb.r / count);
rgb.g = Math.floor(rgb.g / count);
rgb.b = Math.floor(rgb.b / count);
if (asHex) {
return rgbToHex(rgb.r, rgb.g, rgb.b).slice(0, 7);
} else {
return rgb;
}
};
let ballPixelData = ctx.getImageData(5, 5, 30, 30).data;
tagpro.averageColorRed = getAverageColor(ballPixelData, true, true) || '#ED4135';
ballPixelData = ctx.getImageData(45, 5, 30, 30).data;
tagpro.averageColorBlue = getAverageColor(ballPixelData, true, true) || '#0F49CB';
canvas = null;
}
const ui_styles = {
timer: {
font_size: 45,
color_state_1: 0xffffff, //normal time white: #ffffff
color_state_3: 0x3ff53f, //pre-game green: #3ff53f
color_state_5: 0xf4b942, //overtime orange: #f4b942
prefix_state_3: '-',
prefix_state_5: '+',
alpha: 0.65,
offset_y: 5, //-38
}, scores: {
font_size: 55,
alpha: 0.65,
offset_y: -63, //-55
red: {
color: tagpro.averageColorRed || '#ED4135', //#ff2222
offset_x: -90,
}, blue: {
color: tagpro.averageColorBlue || '#0F49CB', //#236fe0
offset_x: 90,
}
}, playerIndicators: {
indicator_size: 20,
alpha: 0.5,
offset_y: -53, //-45
red: {
offset_x: -110,
}, blue: {
offset_x: 110,
}
}, flags: {
alpha: 0.85,
offset_x_red: 75,
offset_x_blue: -78,
offset_y: -88, //93
flags_out_scale: 1.25, //default is 1.0
}, teamNames: {
font_size: 22,
alpha: 0.65,
offset_y: -16,
red: {
color: tagpro.averageColorRed || '#ff2222',
offset_x: -20,
}, blue: {
color: tagpro.averageColorBlue || '#236fe0',
offset_x: 20,
}, vs: {
color: '#ffffff',
offset_x: 0,
}
}, mapName: {
font_size: 11,
color: '#ffffff',
alpha: 0.65,
offset_y: -18, //-10
},
};
//only update the score when it changes (saves ~99% of text draws)...
let prevRedScore = null;
let prevBlueScore = null;
tagpro.ui.scores = function() {
if (tagpro.score.r === prevRedScore && tagpro.score.b === prevBlueScore) {
return; //don't redraw as the scores haven't changed
}
prevRedScore = tagpro.score.r;
prevBlueScore = tagpro.score.b;
const redScore = (tagpro.score.r ? tagpro.score.r.toString() : "0");
const blueScore = (tagpro.score.b ? tagpro.score.b.toString() : "0");
if (!tagpro.ui.sprites.blueScore) {
tagpro.ui.sprites.redScore = new PIXI.Text(redScore, { dropShadow:true, dropShadowAngle: 0.785, dropShadowAlpha:0.8, dropShadowDistance:2, dropShadowBlur:1, dropShadowColor:'#000000', fill:ui_styles.scores.red.color, fontFamily:'Arial', fontSize:ui_styles.scores.font_size, fontWeight:'bold', stroke:'#000000', strokeThickness:1 });
tagpro.ui.sprites.blueScore = new PIXI.Text(blueScore, { dropShadow:true, dropShadowAngle: 0.785, dropShadowAlpha:0.8, dropShadowDistance:2, dropShadowBlur:1, dropShadowColor:'#000000', fill:ui_styles.scores.blue.color, fontFamily:'Arial', fontSize:ui_styles.scores.font_size, fontWeight:'bold', stroke:'#000000', strokeThickness:1 });
tagpro.ui.sprites.redScore.alpha = ui_styles.scores.alpha;
tagpro.ui.sprites.blueScore.alpha = ui_styles.scores.alpha;
tagpro.ui.sprites.redScore.anchor.x = 1;
tagpro.ui.sprites.blueScore.anchor.x = 0;
tagpro.renderer.layers.ui.addChild(tagpro.ui.sprites.redScore);
tagpro.renderer.layers.ui.addChild(tagpro.ui.sprites.blueScore);
} else {
tagpro.ui.sprites.redScore.text = redScore;
tagpro.ui.sprites.blueScore.text = blueScore;
}
};
//This uses PIXI.BitmapFont which should be much faster...
//https://medium.com/@bigtimebuddy/lets-talk-about-text-pixijs-244b9f95f830
let timerTextStyle = new PIXI.TextStyle({
fontSize: 40,
fontWeight: 'bold',
fill: '#ffffff',
strokeThickness: 2,
dropShadow: true,
dropShadowAngle: 0.785,
dropShadowAlpha: 0.6,
dropShadowDistance: 2,
});
PIXI.BitmapFont.from('TimerFont', timerTextStyle, { chars: [ ['0', '9'], ':.' + ui_styles.timer.prefix_state_3 + ui_styles.timer.prefix_state_5] });
tagpro.ui.timer = function(xxx_context, xxx_center, xxx_size, display, test=false) {
// this only works without drawing 2x timers because we are also overriding `tagpro.ui.update` below (where `context` is missing, causing it to return early)
// the default setInterval() timer is probably still running though as we can't stop it
if (tagpro.state === 2) {
if (tagpro.overtimeStartedAt) {
display = ui_styles.timer.prefix_state_5 + display;
}
} else if (tagpro.state === 3) { //show pre-game as "-0:12"
display = ui_styles.timer.prefix_state_3 + display;
} else if (tagpro.state === 5) { //show overtime as "+1:23"
display = ui_styles.timer.prefix_state_5 + display;
}
tagpro.ui.sprites.timer.text = display;
};
//Updating Delta Accurate Timer: https://blog.bitsrc.io/how-to-get-an-accurate-setinterval-in-javascript-ca7623d1d26a
let startTime = Date.now();
let minutes, seconds;
let prevMinutes, prevSeconds, prevState;
let counter = 1;
let interval = 100;
let updateTimer = function() {
const nowTime = Date.now();
const nextTime = startTime + counter * interval;
let display = '';
counter++;
if (tagpro.gameEndsAt && !tagpro.overtimeStartedAt) {
let msLeft = tagpro.gameEndsAt - nowTime;
minutes = Math.max(0, Math.floor(msLeft / 60000)); //don't go negative
if (useTimerHundredths) {
seconds = Math.max(0, Math.floor(msLeft % 60000 / 100) / 10);
display = (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds.toFixed(1) : seconds.toFixed(1));
} else {
seconds = Math.max(0, Math.floor(msLeft % 60000 / 1000));
display = (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
}
} else if (tagpro.overtimeStartedAt) {
let msElapsed = nowTime - tagpro.overtimeStartedAt;
minutes = Math.floor(msElapsed / 60000);
if (useTimerHundredths) {
seconds = Math.floor(msElapsed % 60000 / 100) / 10;
display = (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds.toFixed(1) : seconds.toFixed(1));
} else {
seconds = Math.floor(msElapsed % 60000 / 1000);
display = (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
}
}
if (seconds !== prevSeconds || tagpro.state !== prevState) {
tagpro.ui.timer(null, null, null, display, true);
}
if (tagpro.state !== 2) {
setTimeout(updateTimer, interval - (nowTime - nextTime));
}
prevState = tagpro.state;
prevMinutes = minutes;
prevSeconds = seconds;
};
let updateTimerColor = function(color = '#ffffff') {
if (!tagpro.ui.sprites.usingBitmapTimer) {
tagpro.ui.sprites.timer = new PIXI.BitmapText('', {
fontName: 'TimerFont',
});
tagpro.ui.sprites.timer.alpha = ui_styles.timer.alpha;
tagpro.ui.sprites.timer.anchor.x = 0.5;
tagpro.ui.sprites.timer.anchor.y = 1;
tagpro.renderer.layers.ui.addChild(tagpro.ui.sprites.timer);
tagpro.ui.sprites.usingBitmapTimer = true;
}
timerTextStyle.fill = color;
PIXI.BitmapFont.from('TimerFont', timerTextStyle, { chars: [ ['0', '9'], ':.' + ui_styles.timer.prefix_state_3 + ui_styles.timer.prefix_state_5] });
tagpro.ui.sprites.timer.updateText();
};
updateTimerColor('#ffffff');
tagpro.socket.on('time', function(data) {
if (data.state === 3) { //pre-game green
updateTimerColor(ui_styles.timer.color_state_3);
} else if (data.state === 5) { //overtime orange
updateTimerColor(ui_styles.timer.color_state_5);
} else { //normal game time white
updateTimerColor(ui_styles.timer.color_state_1);
}
tagpro.ui.alignUI(true);
});
//only update the performance info (FPS/Ping/Loss) when it changes (saves ~90% of text draws)...
let original_tagpro_ui_performanceInfo = tagpro.ui.performanceInfo;
let prevFPS = 0;
let prevPing = 0;
let prevLoss = 0;
tagpro.ui.performanceInfo = function(e, t, n, r) {
let roundedFPS = Math.round(tagpro.fps);
let hasChanged = (tagpro.ping.avg !== prevPing || roundedFPS !== prevFPS || r !== prevLoss);
if (!hasChanged) {
return;
}
prevFPS = roundedFPS;
prevPing = tagpro.ping.avg;
prevLoss = r;
return original_tagpro_ui_performanceInfo(...arguments);
};
let size = { width: 0, height: 0 };
let center = { x: 0, y: 0 };
tagpro.ui.center = center;
tagpro.ui.size = size;
tagpro.mapName = '';
tagpro.mapAuthor = '';
function getMapData(data) {
tagpro.mapName = data.info.name;
tagpro.mapAuthor = data.info.author;
tagpro.rawSocket.removeListener('map', getMapData);
}
tagpro.rawSocket.on('map', getMapData);
tagpro.ui.resize = function(width, height) { //called in tr.centerView()
size.width = width;
size.height = height;
center.x = width / 2;
center.y = height / 2;
tagpro.ui.alignUI(true);
};
let prevViewportWidth = -1;
let prevViewportHeight = -1;
tagpro.ui.alignUI = function(forceUpdate = false) {
if (!tagpro.ui.sprites || !tagpro.ui.sprites.timer || !tagpro.map) {
setTimeout(() => {
tagpro.ui.alignUI(forceUpdate);
}, 50);
return false;
}
if (!forceUpdate && prevViewportWidth === size.width && prevViewportHeight === size.height) {
return;
}
prevViewportWidth = size.width;
prevViewportHeight = size.height;
let adjustY = -5;
if (tagpro.ui.sprites.blueTeamName) {
adjustY += -ui_styles.teamNames.font_size - 2;
}
if (tagpro.ui.sprites.mapName) {
adjustY += -ui_styles.mapName.font_size - 5;
}
if (!!tagpro.ui.sprites.redScore) {
tagpro.ui.sprites.redScore.position.x = center.x + ui_styles.scores.red.offset_x;
tagpro.ui.sprites.redScore.position.y = size.height + ui_styles.scores.offset_y + adjustY;
}
if (!!tagpro.ui.sprites.blueScore) {
tagpro.ui.sprites.blueScore.position.x = center.x + ui_styles.scores.blue.offset_x;
tagpro.ui.sprites.blueScore.position.y = size.height + ui_styles.scores.offset_y + adjustY;
}
if (!!tagpro.ui.sprites.timer) {
tagpro.ui.sprites.timer.position.x = center.x;
tagpro.ui.sprites.timer.position.y = size.height + ui_styles.timer.offset_y + adjustY;
}
if (!!tagpro.ui.sprites.playerIndicatorRed) {
setTimeout(function() {
tagpro.ui.sprites.playerIndicatorRed.x = center.x + ui_styles.playerIndicators.red.offset_x - tagpro.ui.sprites.redScore.width - tagpro.ui.sprites.playerIndicatorRed.width;
tagpro.ui.sprites.playerIndicatorRed.y = size.height + ui_styles.playerIndicators.offset_y + adjustY;
}, 100);
}
if (!!tagpro.ui.sprites.playerIndicatorBlue) {
setTimeout(function() {
tagpro.ui.sprites.playerIndicatorBlue.x = center.x + ui_styles.playerIndicators.blue.offset_x + tagpro.ui.sprites.blueScore.width;
tagpro.ui.sprites.playerIndicatorBlue.y = size.height + ui_styles.playerIndicators.offset_y + adjustY;
}, 100);
}
if (!!tagpro.ui.sprites.redTeamName && !!tagpro.ui.sprites.vsTeamName && !!tagpro.ui.sprites.blueTeamName) {
tagpro.ui.sprites.redTeamName.x = center.x + ui_styles.teamNames.red.offset_x;
tagpro.ui.sprites.redTeamName.y = size.height + ui_styles.teamNames.offset_y;
tagpro.ui.sprites.vsTeamName.x = center.x + ui_styles.teamNames.vs.offset_x;
tagpro.ui.sprites.vsTeamName.y = size.height + ui_styles.teamNames.offset_y;
tagpro.ui.sprites.blueTeamName.x = center.x + ui_styles.teamNames.blue.offset_x;
tagpro.ui.sprites.blueTeamName.y = size.height + ui_styles.teamNames.offset_y;
}
if (!!tagpro.ui.sprites.mapName) {
tagpro.ui.sprites.mapName.x = center.x;
tagpro.ui.sprites.mapName.y = size.height + ui_styles.mapName.offset_y;
}
if (!!tagpro.ui.sprites.performanceInfo) {
tagpro.ui.sprites.performanceInfo.x = 10;
tagpro.ui.sprites.performanceInfo.y = 10;
}
if (!!tagpro.ui.sprites.xhr) {
tagpro.ui.sprites.xhr.x = center.x - tagpro.ui.sprites.xhr.width / 2;
tagpro.ui.sprites.xhr.y = 10;
}
if (!!tagpro.ui.flags) {
for (let i = 0; i < tagpro.ui.flags.length; i++) {
const flag = tagpro.ui.flags[i];
flag.x = center.x + flag.xOffset;
flag.y = size.height + flag.yOffset + adjustY;
}
}
};
let createPlayerIndicators = function(ballTile, totalBalls) {
let parent = new PIXI.Container();
let size = 20;
let gap = 2;
let alpha = 0.5;
let drawNextBall = function(i) {
let x = 0;
let y = i % 2 === 0 ? (size + gap) : 0;
x += Math.floor((i-1) / 2) * (size + gap);
tagpro.tiles.draw(parent, ballTile, { x: x, y: y}, size, size, alpha);
};
let drawn = 1;
while (drawn <= totalBalls) {
drawNextBall(drawn++);
}
return parent;
};
let prevRedPlayers = -1;
let prevBluePlayers = -1;
tagpro.ui.updatePlayerIndicators = function() {
let redPlayers = 0;
let bluePlayers = 0;
for (let playerId in tagpro.players) {
if (tagpro.players.hasOwnProperty(playerId)) {
if (tagpro.players[playerId].team === 1) {
redPlayers++;
} else {
bluePlayers++;
}
}
}
if ((redPlayers == prevRedPlayers) && (bluePlayers == prevBluePlayers)) {
return;
}
prevRedPlayers = redPlayers;
prevBluePlayers = bluePlayers;
if (!tagpro.ui.sprites.playerIndicators) {
tagpro.ui.sprites.playerIndicators = new PIXI.Container();
tagpro.renderer.layers.ui.addChild(tagpro.ui.sprites.playerIndicators);
}
if (tagpro.ui.sprites.playerIndicatorRed) tagpro.ui.sprites.playerIndicatorRed.destroy();
if (tagpro.ui.sprites.playerIndicatorBlue) tagpro.ui.sprites.playerIndicatorBlue.destroy();
tagpro.ui.sprites.playerIndicatorRed = createPlayerIndicators("redball", redPlayers);
tagpro.ui.sprites.playerIndicatorBlue = createPlayerIndicators("blueball", bluePlayers);
tagpro.ui.sprites.playerIndicators.removeChildren();
tagpro.ui.sprites.playerIndicators.addChild(tagpro.ui.sprites.playerIndicatorRed);
tagpro.ui.sprites.playerIndicators.addChild(tagpro.ui.sprites.playerIndicatorBlue);
tagpro.ui.alignUI(true);
};
tagpro.ui.teamNames = function() {
const teamNameSetting = (tagpro.settings.ui.teamNames === "always") || (tagpro.settings.ui.teamNames === "spectating" && tagpro.spectator);
if (!teamNameSetting || teamNameSetting && tagpro.teamNames.redTeamName === "Red" && tagpro.teamNames.blueTeamName === "Blue") {
return;
}
tagpro.ui.sprites.redTeamName = new PIXI.Text(tagpro.teamNames.redTeamName, { dropShadow:true, dropShadowAlpha:0.65, dropShadowDistance:1, fill:ui_styles.teamNames.red.color, fontFamily:'Arial', fontSize:ui_styles.teamNames.font_size, strokeThickness:1 });
tagpro.ui.sprites.redTeamName.alpha = ui_styles.teamNames.alpha;
tagpro.ui.sprites.redTeamName.anchor.x = 1;
tagpro.ui.sprites.redTeamName.anchor.y = 1;
tagpro.ui.sprites.redTeamName.x = center.x + ui_styles.teamNames.red.offset_x;
tagpro.ui.sprites.redTeamName.y = size.height + ui_styles.teamNames.offset_y;
tagpro.renderer.layers.ui.addChild(tagpro.ui.sprites.redTeamName);
tagpro.ui.sprites.vsTeamName = new PIXI.Text('vs', { dropShadow:true, dropShadowAlpha:0.65, dropShadowDistance:1, fill:ui_styles.teamNames.vs.color, fontFamily:'Arial', fontSize:ui_styles.teamNames.font_size, strokeThickness:1 });
tagpro.ui.sprites.vsTeamName.alpha = ui_styles.teamNames.alpha;
tagpro.ui.sprites.vsTeamName.anchor.x = 0.5;
tagpro.ui.sprites.vsTeamName.anchor.y = 1;
tagpro.ui.sprites.vsTeamName.x = center.x + ui_styles.teamNames.vs.offset_x;
tagpro.ui.sprites.vsTeamName.y = size.height + ui_styles.teamNames.offset_y;
tagpro.renderer.layers.ui.addChild(tagpro.ui.sprites.vsTeamName);
tagpro.ui.sprites.blueTeamName = new PIXI.Text(tagpro.teamNames.blueTeamName, { dropShadow:true, dropShadowAlpha:0.65, dropShadowDistance:1, fill:ui_styles.teamNames.blue.color, fontFamily:'Arial', fontSize:ui_styles.teamNames.font_size, strokeThickness:1 });
tagpro.ui.sprites.blueTeamName.alpha = ui_styles.teamNames.alpha;
tagpro.ui.sprites.blueTeamName.anchor.x = 0;
tagpro.ui.sprites.blueTeamName.anchor.y = 1;
tagpro.ui.sprites.blueTeamName.x = center.x + ui_styles.teamNames.blue.offset_x;
tagpro.ui.sprites.blueTeamName.y = size.height + ui_styles.teamNames.offset_y;
tagpro.renderer.layers.ui.addChild(tagpro.ui.sprites.blueTeamName);
tagpro.ui.alignUI(true);
};
function createFlag(tile, name, drawPos) {
tagpro.ui.sprites[name] = tagpro.tiles.draw(tagpro.renderer.layers.ui, tile, drawPos, 35, 35);
tagpro.ui.sprites[name].scale = { x:ui_styles.flags.flags_out_scale, y:ui_styles.flags.flags_out_scale };
tagpro.ui.flags.push(tagpro.ui.sprites[name]);
return tagpro.ui.sprites[name];
}
function createFlags() {
let createFlagObject = function(tile, name, xOffset, yOffset) {
return {
tile: tile,
name: name,
drawPos: {x: center.x + xOffset, y: size.height + (yOffset || -40) },
xOffset: xOffset,
yOffset: yOffset || -40,
}
};
const flags = [
createFlagObject("redflag", "redFlag", ui_styles.flags.offset_x_red, ui_styles.flags.offset_y),
createFlagObject("blueflag", "blueFlag", ui_styles.flags.offset_x_blue, ui_styles.flags.offset_y),
createFlagObject("yellowflag", "yellowFlagTakenByRed", ui_styles.flags.offset_x_blue, ui_styles.flags.offset_y),
createFlagObject("yellowflag", "yellowFlagTakenByBlue", ui_styles.flags.offset_x_red, ui_styles.flags.offset_y),
createFlagObject("redpotato", "redPotatoTaken", ui_styles.flags.offset_x_red + 25, ui_styles.flags.offset_y), //the potato positions might be off - untested
createFlagObject("bluepotato", "bluePotatoTaken", ui_styles.flags.offset_x_blue - 25, ui_styles.flags.offset_y),
createFlagObject("yellowpotato", "yellowPotatoTakenByRed", ui_styles.flags.offset_x_blue + 25, ui_styles.flags.offset_y),
createFlagObject("yellowpotato", "yellowPotatoTakenByBlue", ui_styles.flags.offset_x_red - 25, ui_styles.flags.offset_y),
];
for (let i = 0; i < flags.length; i++) {
const flag = flags[i];
const flagSprite = createFlag(flag.tile, flag.name, flag.drawPos);
flagSprite.xOffset = flag.xOffset;
flagSprite.yOffset = flag.yOffset;
flagSprite.alpha = ui_styles.flags.alpha;
flagSprite.anchor.x = 0.5; // flags x position is the distance from the center of the viewport to the center of the flag
}
}
tagpro.ui.updateFlags = function() {
if (!tagpro.ui.sprites.redFlag) {
createFlags();
}
const flagNames = [
["redFlag", "redFlagTaken"],
["blueFlag", "blueFlagTaken"],
["yellowFlagTakenByRed", null],
["yellowFlagTakenByBlue", null],
["redPotatoTaken", null],
["bluePotatoTaken", null],
["yellowPotatoTakenByRed", null],
["yellowPotatoTakenByBlue", null]
];
for (let i = 0; i < flagNames.length; i++) {
const flagName = flagNames[i];
const sprite = tagpro.ui.sprites[flagName[0]];
let visible;
if (flagName[1]) {
visible = !!tagpro.ui[flagName[1]];
} else {
visible = !!tagpro.ui[flagName[0]];
}
sprite.visible = visible;
}
};
tagpro.ui.showMapName = function() {
if (!tagpro.mapName) {
return;
}
tagpro.ui.sprites.mapName = new PIXI.Text("Map: " + tagpro.mapName + " by " + tagpro.mapAuthor, {
dropShadow: false,
dropShadowAlpha: 0.3,
dropShadowAngle: 0,
dropShadowBlur: 1,
dropShadowDistance: 0,
letterSpacing: 1.1,
lineJoin: "round",
fill: ui_styles.mapName.color,
fontSize: ui_styles.mapName.font_size,
fontWeight: "bold",
stroke: "#000000",
strokeThickness: 2
});
tagpro.ui.sprites.mapName.anchor.x = 0.5;
tagpro.ui.sprites.mapName.x = Math.round(center.x);
tagpro.ui.sprites.mapName.y = size.height + ui_styles.mapName.offset_y;
tagpro.ui.sprites.mapName.alpha = ui_styles.mapName.alpha;
tagpro.renderer.layers.ui.addChild(tagpro.ui.sprites.mapName);
tagpro.ui.alignUI(true);
};
tagpro.renderer.largeText = function(text, color1='#cccccc', color2='#ffffff', fontSize=54, dropShadowBlur=true) {
return new PIXI.Text(text, {
dropShadow: dropShadowBlur,
dropShadowAlpha: 0.3,
dropShadowAngle: 0.785,
dropShadowBlur: 2,
dropShadowDistance: 3,
dropShadowColor: '#000000',
fill: [color2, color1],
fillGradientStops: [0.1, 0.8],
fontSize: fontSize,
fontWeight: "bold",
letterSpacing: 1,
padding: 10,
strokeThickness: 1
});
};
tagpro.ui.largeAlert = function(message, color1='#ffffff', color2='#ffffff', top=50, fontSize=54, dropShadowBlur=false) {
let sprite = tagpro.renderer.largeText(message, color1, color2, fontSize, dropShadowBlur);
sprite.x = Math.round(center.x);
sprite.y = top;
sprite.anchor.x = 0.5;
tagpro.renderer.layers.ui.addChild(sprite);
return sprite;
};
let winnerDeclared = false;
let runOnce = false;
tagpro.ui.update = function() {
if (tagpro.replayUpdateTimer && !tagpro.replayPaused) {
tagpro.replayUpdateTimer();
}
if (!tagpro.ui.enabled) {
return;
}
if (!runOnce) {
if (tagpro.settings.ui.matchState) {
updateTimer();
}
setTimeout(tagpro.ui.teamNames, 1100);
setTimeout(tagpro.ui.showMapName, 1200);
runOnce = true;
}
if (tagpro.settings.ui.spectatorInfo) {
//tagpro.ui.spectatorInfo();
}
if (tagpro.renderer.debug) {
//debugUI();
}
if (tagpro.settings.ui.matchState) {
const loss = Math.floor((tagpro.ping.loss || 0) * 100);
tagpro.ui.scores();
tagpro.ui.updateFlags();
tagpro.ui.updatePlayerIndicators();
tagpro.ui.performanceInfo(loss);
if (tagpro.events.afterUpdateUI) {
tagpro.events.afterUpdateUI.forEach(function(e) { e.afterUpdateUI(); });
}
if (tagpro.state === 2) {
if (!winnerDeclared) {
if (tagpro.winner === "red") {
tagpro.ui.largeAlert((tagpro.teamNames.redTeamName || "Red") + " Wins!", ui_styles.scores.red.color, "#ffffff", 50, 54, true);
} else if (tagpro.winner === "blue") {
tagpro.ui.largeAlert((tagpro.teamNames.blueTeamName || "Blue") + " Wins!", ui_styles.scores.blue.color, "#ffffff", 50, 54, true);
} else if (tagpro.winner === "tie") {
tagpro.ui.largeAlert("It's a tie!", "#ff7700", "#ffffff", 50);
} else {
tagpro.ui.largeAlert(tagpro.winner);
}
winnerDeclared = true;
}
}
}
};
window.addEventListener('focus', () => {
//tr.resizeAndCenterView();
tagpro.ui.resize($('#viewport').width(), $('#viewport').height());
tagpro.ui.alignUI(true);
tagpro.chat.resize();
});
setTimeout(() => {
tagpro.ui.resize($('#viewport').width(), $('#viewport').height());
tagpro.ui.alignUI(true);
tagpro.chat.resize();
}, 200);
} //if (modify_UI)
/*****************************************************************************/
if (disableInteractivity && !(isEaster || isEgg)) {
setTimeout(function() {
//removing the Interaction Manager from PIXI will disable it (and PIXI will no longer crawl through the object to check for interactivity)...
tr.renderer.plugins.interaction.destroy();
tr.renderer.plugins.interaction = null;
tr.stage.interactiveChildren = false;
tr.gameContainer.interactiveChildren = false;
//let's remove this too (not sure if it actually helps)...
tr.renderer.plugins.accessibility.destroy();
tr.renderer.plugins.accessibility = null;
}, 1000);
} // if (disableInteractivity)
/*****************************************************************************/
function rgbToHex(r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
/* eslint-disable */
// TinyColor v1.4.1
// https://github.com/bgrins/TinyColor
// 2016-07-07, Brian Grinstead, MIT License
!function(a){function b(a,d){if(a=a?a:"",d=d||{},a instanceof b)return a;if(!(this instanceof b))return new b(a,d);let e=c(a);this._originalInput=a,this._r=e.r,this._g=e.g,this._b=e.b,this._a=e.a,this._roundA=P(100*this._a)/100,this._format=d.format||e.format,this._gradientType=d.gradientType,this._r<1&&(this._r=P(this._r)),this._g<1&&(this._g=P(this._g)),this._b<1&&(this._b=P(this._b)),this._ok=e.ok,this._tc_id=O++}function c(a){let b={r:0,g:0,b:0},c=1,e=null,g=null,i=null,j=!1,k=!1;return"string"==typeof a&&(a=K(a)),"object"==typeof a&&(J(a.r)&&J(a.g)&&J(a.b)?(b=d(a.r,a.g,a.b),j=!0,k="%"===String(a.r).substr(-1)?"prgb":"rgb"):J(a.h)&&J(a.s)&&J(a.v)?(e=G(a.s),g=G(a.v),b=h(a.h,e,g),j=!0,k="hsv"):J(a.h)&&J(a.s)&&J(a.l)&&(e=G(a.s),i=G(a.l),b=f(a.h,e,i),j=!0,k="hsl"),a.hasOwnProperty("a")&&(c=a.a)),c=z(c),{ok:j,format:a.format||k,r:Q(255,R(b.r,0)),g:Q(255,R(b.g,0)),b:Q(255,R(b.b,0)),a:c}}function d(a,b,c){return{r:255*A(a,255),g:255*A(b,255),b:255*A(c,255)}}function e(a,b,c){a=A(a,255),b=A(b,255),c=A(c,255);let d,e,f=R(a,b,c),g=Q(a,b,c),h=(f+g)/2;if(f==g)d=e=0;else{let i=f-g;switch(e=h>.5?i/(2-f-g):i/(f+g),f){case a:d=(b-c)/i+(c>b?6:0);break;case b:d=(c-a)/i+2;break;case c:d=(a-b)/i+4}d/=6}return{h:d,s:e,l:h}}function f(a,b,c){function d(a,b,c){return 0>c&&(c+=1),c>1&&(c-=1),1/6>c?a+6*(b-a)*c:.5>c?b:2/3>c?a+6*(b-a)*(2/3-c):a}let e,f,g;if(a=A(a,360),b=A(b,100),c=A(c,100),0===b)e=f=g=c;else{let h=.5>c?c*(1+b):c+b-c*b,i=2*c-h;e=d(i,h,a+1/3),f=d(i,h,a),g=d(i,h,a-1/3)}return{r:255*e,g:255*f,b:255*g}}function g(a,b,c){a=A(a,255),b=A(b,255),c=A(c,255);let d,e,f=R(a,b,c),g=Q(a,b,c),h=f,i=f-g;if(e=0===f?0:i/f,f==g)d=0;else{switch(f){case a:d=(b-c)/i+(c>b?6:0);break;case b:d=(c-a)/i+2;break;case c:d=(a-b)/i+4}d/=6}return{h:d,s:e,v:h}}function h(b,c,d){b=6*A(b,360),c=A(c,100),d=A(d,100);let e=a.floor(b),f=b-e,g=d*(1-c),h=d*(1-f*c),i=d*(1-(1-f)*c),j=e%6,k=[d,h,g,g,i,d][j],l=[i,d,d,h,g,g][j],m=[g,g,i,d,d,h][j];return{r:255*k,g:255*l,b:255*m}}function i(a,b,c,d){let e=[F(P(a).toString(16)),F(P(b).toString(16)),F(P(c).toString(16))];return d&&e[0].charAt(0)==e[0].charAt(1)&&e[1].charAt(0)==e[1].charAt(1)&&e[2].charAt(0)==e[2].charAt(1)?e[0].charAt(0)+e[1].charAt(0)+e[2].charAt(0):e.join("")}function j(a,b,c,d,e){let f=[F(P(a).toString(16)),F(P(b).toString(16)),F(P(c).toString(16)),F(H(d))];return e&&f[0].charAt(0)==f[0].charAt(1)&&f[1].charAt(0)==f[1].charAt(1)&&f[2].charAt(0)==f[2].charAt(1)&&f[3].charAt(0)==f[3].charAt(1)?f[0].charAt(0)+f[1].charAt(0)+f[2].charAt(0)+f[3].charAt(0):f.join("")}function k(a,b,c,d){let e=[F(H(d)),F(P(a).toString(16)),F(P(b).toString(16)),F(P(c).toString(16))];return e.join("")}function l(a,c){c=0===c?0:c||10;let d=b(a).toHsl();return d.s-=c/100,d.s=B(d.s),b(d)}function m(a,c){c=0===c?0:c||10;let d=b(a).toHsl();return d.s+=c/100,d.s=B(d.s),b(d)}function n(a){return b(a).desaturate(100)}function o(a,c){c=0===c?0:c||10;let d=b(a).toHsl();return d.l+=c/100,d.l=B(d.l),b(d)}function p(a,c){c=0===c?0:c||10;let d=b(a).toRgb();return d.r=R(0,Q(255,d.r-P(255*-(c/100)))),d.g=R(0,Q(255,d.g-P(255*-(c/100)))),d.b=R(0,Q(255,d.b-P(255*-(c/100)))),b(d)}function q(a,c){c=0===c?0:c||10;let d=b(a).toHsl();return d.l-=c/100,d.l=B(d.l),b(d)}function r(a,c){let d=b(a).toHsl(),e=(d.h+c)%360;return d.h=0>e?360+e:e,b(d)}function s(a){let c=b(a).toHsl();return c.h=(c.h+180)%360,b(c)}function t(a){let c=b(a).toHsl(),d=c.h;return[b(a),b({h:(d+120)%360,s:c.s,l:c.l}),b({h:(d+240)%360,s:c.s,l:c.l})]}function u(a){let c=b(a).toHsl(),d=c.h;return[b(a),b({h:(d+90)%360,s:c.s,l:c.l}),b({h:(d+180)%360,s:c.s,l:c.l}),b({h:(d+270)%360,s:c.s,l:c.l})]}function v(a){let c=b(a).toHsl(),d=c.h;return[b(a),b({h:(d+72)%360,s:c.s,l:c.l}),b({h:(d+216)%360,s:c.s,l:c.l})]}function w(a,c,d){c=c||6,d=d||30;let e=b(a).toHsl(),f=360/d,g=[b(a)];for(e.h=(e.h-(f*c>>1)+720)%360;--c;)e.h=(e.h+f)%360,g.push(b(e));return g}function x(a,c){c=c||6;for(let d=b(a).toHsv(),e=d.h,f=d.s,g=d.v,h=[],i=1/c;c--;)h.push(b({h:e,s:f,v:g})),g=(g+i)%1;return h}function y(a){let b={};for(let c in a)a.hasOwnProperty(c)&&(b[a[c]]=c);return b}function z(a){return a=parseFloat(a),(isNaN(a)||0>a||a>1)&&(a=1),a}function A(b,c){D(b)&&(b="100%");let d=E(b);return b=Q(c,R(0,parseFloat(b))),d&&(b=parseInt(b*c,10)/100),a.abs(b-c)<1e-6?1:b%c/parseFloat(c)}function B(a){return Q(1,R(0,a))}function C(a){return parseInt(a,16)}function D(a){return"string"==typeof a&&-1!=a.indexOf(".")&&1===parseFloat(a)}function E(a){return"string"==typeof a&&-1!=a.indexOf("%")}function F(a){return 1==a.length?"0"+a:""+a}function G(a){return 1>=a&&(a=100*a+"%"),a}function H(b){return a.round(255*parseFloat(b)).toString(16)}function I(a){return C(a)/255}function J(a){return!!V.CSS_UNIT.exec(a)}function K(a){a=a.replace(M,"").replace(N,"").toLowerCase();let b=!1;if(T[a])a=T[a],b=!0;else if("transparent"==a)return{r:0,g:0,b:0,a:0,format:"name"};let c;return(c=V.rgb.exec(a))?{r:c[1],g:c[2],b:c[3]}:(c=V.rgba.exec(a))?{r:c[1],g:c[2],b:c[3],a:c[4]}:(c=V.hsl.exec(a))?{h:c[1],s:c[2],l:c[3]}:(c=V.hsla.exec(a))?{h:c[1],s:c[2],l:c[3],a:c[4]}:(c=V.hsv.exec(a))?{h:c[1],s:c[2],v:c[3]}:(c=V.hsva.exec(a))?{h:c[1],s:c[2],v:c[3],a:c[4]}:(c=V.hex8.exec(a))?{r:C(c[1]),g:C(c[2]),b:C(c[3]),a:I(c[4]),format:b?"name":"hex8"}:(c=V.hex6.exec(a))?{r:C(c[1]),g:C(c[2]),b:C(c[3]),format:b?"name":"hex"}:(c=V.hex4.exec(a))?{r:C(c[1]+""+c[1]),g:C(c[2]+""+c[2]),b:C(c[3]+""+c[3]),a:I(c[4]+""+c[4]),format:b?"name":"hex8"}:(c=V.hex3.exec(a))?{r:C(c[1]+""+c[1]),g:C(c[2]+""+c[2]),b:C(c[3]+""+c[3]),format:b?"name":"hex"}:!1}function L(a){let b,c;return a=a||{level:"AA",size:"small"},b=(a.level||"AA").toUpperCase(),c=(a.size||"small").toLowerCase(),"AA"!==b&&"AAA"!==b&&(b="AA"),"small"!==c&&"large"!==c&&(c="small"),{level:b,size:c}}let M=/^\s+/,N=/\s+$/,O=0,P=a.round,Q=a.min,R=a.max,S=a.random;b.prototype={isDark:function(){return this.getBrightness()<128},isLight:function(){return!this.isDark()},isValid:function(){return this._ok},getOriginalInput:function(){return this._originalInput},getFormat:function(){return this._format},getAlpha:function(){return this._a},getBrightness:function(){let a=this.toRgb();return(299*a.r+587*a.g+114*a.b)/1e3},getLuminance:function(){let b,c,d,e,f,g,h=this.toRgb();return b=h.r/255,c=h.g/255,d=h.b/255,e=.03928>=b?b/12.92:a.pow((b+.055)/1.055,2.4),f=.03928>=c?c/12.92:a.pow((c+.055)/1.055,2.4),g=.03928>=d?d/12.92:a.pow((d+.055)/1.055,2.4),.2126*e+.7152*f+.0722*g},setAlpha:function(a){return this._a=z(a),this._roundA=P(100*this._a)/100,this},toHsv:function(){let a=g(this._r,this._g,this._b);return{h:360*a.h,s:a.s,v:a.v,a:this._a}},toHsvString:function(){let a=g(this._r,this._g,this._b),b=P(360*a.h),c=P(100*a.s),d=P(100*a.v);return 1==this._a?"hsv("+b+", "+c+"%, "+d+"%)":"hsva("+b+", "+c+"%, "+d+"%, "+this._roundA+")"},toHsl:function(){let a=e(this._r,this._g,this._b);return{h:360*a.h,s:a.s,l:a.l,a:this._a}},toHslString:function(){let a=e(this._r,this._g,this._b),b=P(360*a.h),c=P(100*a.s),d=P(100*a.l);return 1==this._a?"hsl("+b+", "+c+"%, "+d+"%)":"hsla("+b+", "+c+"%, "+d+"%, "+this._roundA+")"},toHex:function(a){return i(this._r,this._g,this._b,a)},toHexString:function(a){return"#"+this.toHex(a)},toHex8:function(a){return j(this._r,this._g,this._b,this._a,a)},toHex8String:function(a){return"#"+this.toHex8(a)},toRgb:function(){return{r:P(this._r),g:P(this._g),b:P(this._b),a:this._a}},toRgbString:function(){return 1==this._a?"rgb("+P(this._r)+", "+P(this._g)+", "+P(this._b)+")":"rgba("+P(this._r)+", "+P(this._g)+", "+P(this._b)+", "+this._roundA+")"},toPercentageRgb:function(){return{r:P(100*A(this._r,255))+"%",g:P(100*A(this._g,255))+"%",b:P(100*A(this._b,255))+"%",a:this._a}},toPercentageRgbString:function(){return 1==this._a?"rgb("+P(100*A(this._r,255))+"%, "+P(100*A(this._g,255))+"%, "+P(100*A(this._b,255))+"%)":"rgba("+P(100*A(this._r,255))+"%, "+P(100*A(this._g,255))+"%, "+P(100*A(this._b,255))+"%, "+this._roundA+")"},toName:function(){return 0===this._a?"transparent":this._a<1?!1:U[i(this._r,this._g,this._b,!0)]||!1},toFilter:function(a){let c="#"+k(this._r,this._g,this._b,this._a),d=c,e=this._gradientType?"GradientType = 1, ":"";if(a){let f=b(a);d="#"+k(f._r,f._g,f._b,f._a)}return"progid:DXImageTransform.Microsoft.gradient("+e+"startColorstr="+c+",endColorstr="+d+")"},toString:function(a){let b=!!a;a=a||this._format;let c=!1,d=this._a<1&&this._a>=0,e=!b&&d&&("hex"===a||"hex6"===a||"hex3"===a||"hex4"===a||"hex8"===a||"name"===a);return e?"name"===a&&0===this._a?this.toName():this.toRgbString():("rgb"===a&&(c=this.toRgbString()),"prgb"===a&&(c=this.toPercentageRgbString()),("hex"===a||"hex6"===a)&&(c=this.toHexString()),"hex3"===a&&(c=this.toHexString(!0)),"hex4"===a&&(c=this.toHex8String(!0)),"hex8"===a&&(c=this.toHex8String()),"name"===a&&(c=this.toName()),"hsl"===a&&(c=this.toHslString()),"hsv"===a&&(c=this.toHsvString()),c||this.toHexString())},clone:function(){return b(this.toString())},_applyModification:function(a,b){let c=a.apply(null,[this].concat([].slice.call(b)));return this._r=c._r,this._g=c._g,this._b=c._b,this.setAlpha(c._a),this},lighten:function(){return this._applyModification(o,arguments)},brighten:function(){return this._applyModification(p,arguments)},darken:function(){return this._applyModification(q,arguments)},desaturate:function(){return this._applyModification(l,arguments)},saturate:function(){return this._applyModification(m,arguments)},greyscale:function(){return this._applyModification(n,arguments)},spin:function(){return this._applyModification(r,arguments)},_applyCombination:function(a,b){return a.apply(null,[this].concat([].slice.call(b)))},analogous:function(){return this._applyCombination(w,arguments)},complement:function(){return this._applyCombination(s,arguments)},monochromatic:function(){return this._applyCombination(x,arguments)},splitcomplement:function(){return this._applyCombination(v,arguments)},triad:function(){return this._applyCombination(t,arguments)},tetrad:function(){return this._applyCombination(u,arguments)}},b.fromRatio=function(a,c){if("object"==typeof a){let d={};for(let e in a)a.hasOwnProperty(e)&&(d[e]="a"===e?a[e]:G(a[e]));a=d}return b(a,c)},b.equals=function(a,c){return a&&c?b(a).toRgbString()==b(c).toRgbString():!1},b.random=function(){return b.fromRatio({r:S(),g:S(),b:S()})},b.mix=function(a,c,d){d=0===d?0:d||50;let e=b(a).toRgb(),f=b(c).toRgb(),g=d/100,h={r:(f.r-e.r)*g+e.r,g:(f.g-e.g)*g+e.g,b:(f.b-e.b)*g+e.b,a:(f.a-e.a)*g+e.a};return b(h)},b.readability=function(c,d){let e=b(c),f=b(d);return(a.max(e.getLuminance(),f.getLuminance())+.05)/(a.min(e.getLuminance(),f.getLuminance())+.05)},b.isReadable=function(a,c,d){let e,f,g=b.readability(a,c);switch(f=!1,e=L(d),e.level+e.size){case"AAsmall":case"AAAlarge":f=g>=4.5;break;case"AAlarge":f=g>=3;break;case"AAAsmall":f=g>=7}return f},b.mostReadable=function(a,c,d){let e,f,g,h,i=null,j=0;d=d||{},f=d.includeFallbackColors,g=d.level,h=d.size;for(let k=0;k<c.length;k++)e=b.readability(a,c[k]),e>j&&(j=e,i=b(c[k]));return b.isReadable(a,i,{level:g,size:h})||!f?i:(d.includeFallbackColors=!1,b.mostReadable(a,["#fff","#000"],d))};let T=b.names={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"0ff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000",blanchedalmond:"ffebcd",blue:"00f",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",burntsienna:"ea7e5d",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"0ff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkgrey:"a9a9a9",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkslategrey:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dimgrey:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"f0f",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",grey:"808080",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgray:"d3d3d3",lightgreen:"90ee90",lightgrey:"d3d3d3",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"789",lightslategrey:"789",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"0f0",limegreen:"32cd32",linen:"faf0e6",magenta:"f0f",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370db",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"db7093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",rebeccapurple:"663399",red:"f00",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",slategrey:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"fff",whitesmoke:"f5f5f5",yellow:"ff0",yellowgreen:"9acd32"},U=b.hexNames=y(T),V=function(){let a="[-\\+]?\\d+%?",b="[-\\+]?\\d*\\.\\d+%?",c="(?:"+b+")|(?:"+a+")",d="[\\s|\\(]+("+c+")[,|\\s]+("+c+")[,|\\s]+("+c+")\\s*\\)?",e="[\\s|\\(]+("+c+")[,|\\s]+("+c+")[,|\\s]+("+c+")[,|\\s]+("+c+")\\s*\\)?";return{CSS_UNIT:new RegExp(c),rgb:new RegExp("rgb"+d),rgba:new RegExp("rgba"+e),hsl:new RegExp("hsl"+d),hsla:new RegExp("hsla"+e),hsv:new RegExp("hsv"+d),hsva:new RegExp("hsva"+e),hex3:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex4:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex8:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/}}();"undefined"!=typeof module&&module.exports?module.exports=b:"function"==typeof define&&define.amd?define(function(){return b}):window.tinycolor=b}(Math);
}); //tagpro.ready()
} // if !event
@wilcooo
Copy link

wilcooo commented Jun 28, 2018

Hey nabby,

I got the "draw new dyntile (shouldn't happen)" alert, while loading the game.

I think it is connected to this bug in TagPro; Whenever you join right at the same moment a dynamic element updates, this element will keep that state for the full game. My guess is that the server sends the "mapupdate" before sending the "map" event, but I'm not sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment