// ==UserScript==
// @name TagPro Player Monitor
// @version 2.7
// @author bash# ; Ko
// @namespace http://www.reddit.com/user/bash_tp/
// @description Shows an on-screen list of players in the game and their current status
// @include http://tagpro-*.koalabeast.com:*
// @include http://tangent.jukejuice.com:*
// @include http://*.newcompte.fr:*
// @supportURL https://www.reddit.com/message/compose/?to=Wilcooo
// @website https://redd.it/6pe5e9
// @license MIT
// ==/UserScript==
////////////////////////////////////////////////////////////////////////////////////////////
// ### --- OPTIONS --- ### //
//////////////////////////////////////////////////////////////////////////////////////// //
// //
// When set to true, the regular 'taken-flag-indicators' are hidden. // //
// This script shows who has the flag, so with this option double info is hidden. // //
var hide_flagTaken = true; // //
// //
// The devs of TagPro added ball indicators to TagPro, which is awesome!! // //
// However, this script still has the advantage of showing // //
// names, pups, deaths and flags. So you can now hide the native TagPro // //
// indicators with this option. // //
var hide_playerIndicators = true; // //
// //
// Position; where to put the Player Monitor. You have these 11 choices: // //
// 'top-left'; 'top-mid'; 'top-right'; 'top-split'; // //
// 'mid-left'; 'mid-right'; 'mid-split'; // //
// 'bot-left'; 'bot-mid'; 'bot-right'; 'bot-split'; // //
// Make sure to not make any typos! // //
var position = 'bot-mid'; // //
// //
// Order; in what order to put the teammembers? Choose between: // //
// 'constant'; When you don't want the balls to change position // //
// 'score'; The same order as on the scoreboard // //
// 'alphabetic'; Alphabetic order // //
// Don't worry, the list will only update every 3 seconds (by default) // //
var order = 'constant'; // //
// //
// Whether to show if someone is honking. // //
// This function requires the Honk script. // //
var show_honk = true; // //
// //
// If you dare, you can edit the constants below to manipulate this script even more. // //
// //
//////////////////////////////////////////////////////////////////////////////////////// //
// ### --- END OF OPTIONS --- ### //
////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////
// SCROLL FURTHER AT YOUR OWN RISK! //
//////////////////////////////////////
var short_name = 'monitor'; // An alphabetic (no spaces/numbers) distinctive name for the script.
var version = GM_info.script.version; // The version number is automatically fetched from the metadata.
tagpro.ready(function(){ if (!tagpro.scripts) tagpro.scripts = {}; tagpro.scripts[short_name]={version:version};});
console.log('START: ' + GM_info.script.name + ' (v' + version + ' by ' + GM_info.script.author + ')');
// CONSTANTS
const size = 16; // Size of a ball icon
const space = 18; // Vertical space per name+icon
const textVshift = 0; // relative vertical shift of text
const textHLshift = -2; // relative horizontal shift of text on the left of a ball
const textHRshift = 25; // relative horizontal shift of text on the right of a ball
const show_ball = true; // Whether to show the ball icon
// (if you disable this, you might want to disable show_grip, show_speed, show_tagpro, show_bomb)
const show_dead = true; // Whether to fade the ball when its dead/spawning
const show_name = true; // Shows the playernames next to the corresponding balls (recommended)
const show_flag = true; // Show a flag next to players with a flag
const flag_size = size; // size of the flag icon next to an FC
const flag_x = 10; // Position of the flag, relative to the ball
const flag_y = 0;
const show_grip = true; // Show a JJ pup on balls with Juke Juice
const grip_size = 10; // size of the Juke Juice icon
const grip_x = -1; // relative position
const grip_y = 8;
const show_speed = false; // This won't work unless they put the topspeed pup back in the game (and even then probably not)
const speed_size = 10; // size of the Top Speed icon (a deprecated TagPro powerup)
const speed_x = -1; // relative position
const speed_y = -3;
const show_tagpro = true; // Show a green circle on balls with a TP
const tagpro_color = 0x00FF00; // Color of the (usually green) TagPro powerup circle
const tagpro_thick = 1.5; // Thickness of that circle
// Tip, use : https://www.google.com/search?q=pick+color
const show_bomb = true; // Flash balls with a Rolling Bomb
const bomb_color = 0xFFFF00; // Color of the flashing RollingBomb
const style = // The style of the text (1: red, 2: blue)
{
1: {
fontSize: "8pt",
fontWeight: "bold",
strokeThickness: 3,
fill: 0xFFB5BD, // text-color (Tip: https://www.google.com/search?q=pick+color)
},
2: {
fontSize: "8pt",
fontWeight: "bold",
strokeThickness: 3,
fill: 0xCFCFFF,
},
};
const presets = { // 1: red team , 2: blue team
'top-left' : {
1 : { x:10, y:10, },
2 : { x:10, y:10 + 4.5*space, },
},
'mid-left' : {
1 : { x:10, y:-0.25*space, anchor : {x:0, y:0.5}, bottomToTop: true, },
2 : { x:10, y: 0.25*space, anchor : {x:0, y:0.5}, },
},
'bot-left' : {
1 : { x:10, y:-10 - 5.5*space, anchor : {x:0, y:1}, bottomToTop: true, },
2 : { x:10, y:-10 - space, anchor : {x:0, y:1}, bottomToTop: true, },
},
'top-mid' : {
1 : { x:-25, y:10, anchor : {x:0.5, y:0}, leftText: true, },
2 : { x:5, y:10, anchor : {x:0.5, y:0}, },
},
'bot-mid' : {
1 : { x:-135, y:-10 - space, anchor : {x:0.5, y:1}, bottomToTop: true, leftText: true, },
2 : { x: 115, y:-10 - space, anchor : {x:0.5, y:1}, bottomToTop: true, },
},
'top-right' : {
1 : { x:-30, y:10, anchor : {x:1, y:0}, leftText: true, },
2 : { x:-30, y:10 + 4.5*space, anchor : {x:1, y:0}, leftText: true, },
},
'mid-rigth' : {
1 : { x:-30, y:-0.25*space, anchor : {x:1, y:0.5}, bottomToTop: true, leftText: true, },
2 : { x:-30, y: 0.25*space, anchor : {x:1, y:0.5}, leftText: true, },
},
'bot-right' : {
1 : { x:-30, y:-10 - 5.5*space, anchor : {x:1, y:1}, bottomToTop: true, leftText: true, },
2 : { x:-30, y:-10 - space, anchor : {x:1, y:1}, bottomToTop: true, leftText: true, },
},
'top-split' : {
1 : { x:10, y:10, },
2 : { x:-30, y:10, anchor : {x:1, y:0}, leftText: true, },
},
'mid-split' : {
1 : { x:10, y:-2*space, anchor : {x:0, y:0.5}, },
2 : { x:-30, y:-2*space, anchor : {x:1, y:0.5}, leftText: true, },
},
'bot-split' : {
1 : { x:10, y:-10 - space, anchor : {x:0, y:1}, bottomToTop: true, },
2 : { x:-30, y:-10 - space, anchor : {x:1, y:1}, bottomToTop: true, leftText: true, },
},
'your-preset' : { // A preset to experiment with!
1 : { x:0, y:0, },
2 : { x:0, y:0, },
},
};
// EXPLANATION OF THE PRESETS:
// 'x' and 'y' form the position of the player monitor
// 'anchor' is the reference point from which the 'x' and 'y' above are calculated.
// anchors 'x' and 'y' are position of the reference point, relative to the viewport size. (so 0.5 is in the middle of the screen)
//
// The position described above is the position of the first ball in the player monitor.
// The next balls are usually drawn below it, but when 'bottomToTop' is true, they are drawn above the first one.
//
// 'leftText' makes the name of the ball appear on the left side of the ball.
//
// You may add a preset to the list, and select it in the options in the top of this script.
const preset = presets[position] || presets["bot-mid"]; // If no valid preset is chosen, fallback to 'bot-mid'
const flagsprite =
{
1: "red", // Note: 'flag' or 'potato' gets added to this later in this script
2: "blue",
3: "yellow",
};
const ballsprite =
{
1: "redball",
2: "blueball",
};
const honksprite = PIXI.Texture.fromImage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTZEaa/1AAACiUlEQVR4Xu3bMa7bQAwG4RwkZe5/s5zBiYoAgjFFJJPL1fgvPjxgDAjksn4/Xq/XV/n989ffP/ybEUar47j/vP9mhdEqB5bLgeVyYLkcWC4HlsuB5XJguRxYLgeWy4HlcmC5HFguB5bLgW940qM9ZdbKOTFecR6mYqBOT5jzPGPFnBiveB+oYqguu894nq9qToxXdQzWYef5zrNVzojxjq4Bq+041/u7Vc6I8a7OQa263wzjJ7oHNlnxVhg/tWLwp1v1RhgrrFrgiVa+DcYqKxd5itVvgrHS6oV2NvEWGKtNLLabqTfA2GFqwR1M7o6xy+SiU6Z3xthpctkJ0/ti7Da58GrTu2IMD4zhgTE8MIYHxvDAGB4YwwNjeGAMD4zhgTE8MIYHxvDAGB4YwwNjeGAMD4zhgTE8MIYHxvDAGB4YwwNjeGAMD4zhgTHmnP/V5VPH91o+GvfQe36q5aPvg8f/o/e86/hey0djHxjDA2N4YAwPjOGBMTwwhgfG8MAYHhjDA2N4YAwPjOGBMTwwhgfG8MAYHhjDA2N4YAwPjOGBMTwwhgfG8MAYHhjDA2N4YOz2Tf/qMr0rxk7nhaeWXml6X4xd3pedWHi16Z0xdphedNLk7hirTS64i6k3wFhparEdTbwFxioTC+1u9ZtgrLB6kSdZ+TYYP7Vygada9UYYP7FqcIMVb4XxrhUD23S/GcY7ugetsONMh/d3q5wT41WdA1bZebbDeb7KOTFe0TVYtd3nO5xnrJoV4xXVA3V5woyH85wVs2K8onKYTk+Z81A5K0ajykd7EoxGObBcDiyXA8vlwHI5sFwOLJcDy+XAcjmwXA4slwPL5cByObBcDvwFvu24r9frxx9ThG6n1YCdggAAAABJRU5ErkJggg==");
const refresh_rate = 3e3; // An interval (milliseconds), after which the order of the playerlists gets updated.
tagpro.ready(function () {
// Hide the flagTaken indicators
if (hide_flagTaken)
tagpro.renderer.updateFlagsFromPlayer = function() { console.warn('The flag indicators are blocked by the TagPro Player Monitor script'); };
// Hide TagPro's new Player Indicators
if (hide_playerIndicators) {
tagpro.ui.updatePlayerIndicators = function() { console.warn('The player indicators are blocked by the TagPro Player Monitor script'); };
}
function getPlayer(player) {
var state = {
team: player.team,
id: player.id,
};
if (show_name) state.name = player.name;
if (show_dead) state.dead = player.dead;
if (show_flag) state.flag = player.flag;
if (show_bomb) state.bomb = player.bomb;
if (show_tagpro) state.tagpro = player.tagpro;
if (show_grip) state.grip = player.grip;
if (show_speed) state.speed = player.speed;
if (show_honk) state.isHonking = player.isHonking;
return state;
}
// Create PIXI containers for both player lists
var redList = new PIXI.Container();
tagpro.renderer.layers.ui.addChild(redList);
var blueList = new PIXI.Container();
tagpro.renderer.layers.ui.addChild(blueList);
var teamLists =
{
1: redList,
2: blueList,
};
tagpro.ui.sprites.redPlayerMonitor = redList;
tagpro.ui.sprites.bluePlayerMonitor = blueList;
// This function gets called when the browser window resizes
// It moves the playerlists to the right location
var org_alignUI = tagpro.ui.alignUI;
tagpro.ui.alignUI = function() {
redList.x = ( preset[1].anchor ? (tagpro.renderer.vpWidth * preset[1].anchor.x) : 0 ) + preset[1].x;
redList.y = ( preset[1].anchor ? (tagpro.renderer.vpHeight * preset[1].anchor.y) : 0 ) + preset[1].y;
blueList.x = ( preset[2].anchor ? (tagpro.renderer.vpWidth * preset[2].anchor.x) : 0 ) + preset[2].x;
blueList.y = ( preset[2].anchor ? (tagpro.renderer.vpHeight * preset[2].anchor.y) : 0 ) + preset[2].y;
org_alignUI();
};
tagpro.ui.alignUI();
// The rolling_bomb graphics are stored here, so that they can be updated (animated)
if (show_bomb) {
var rolling_bombs = {};
// Rewriting the TagPro's ui.update function to include the rendering of the RBs
var org_UIupdate = tagpro.ui.update;
tagpro.ui.update = function () {
org_UIupdate();
for (var b in rolling_bombs) {
rolling_bombs[b].alpha = (tagpro.players[b] && tagpro.players[b].dead ? 0.375 : 0.75)*Math.abs(Math.sin(performance.now() / 150));
}
};
}
// Update a single player
function drawPlayer(player) {
if (typeof player.monitor === 'undefined') {
player.monitor = new PIXI.Container();
}
player.monitor.removeChildren();
// Draw ball
if (show_ball) tagpro.tiles.draw(player.monitor, ballsprite[player.team], { x: 0, y: 0 }, size, size, player.dead ? 0.5 : 1);
// Draw bomb (rolling bomb)
if (show_bomb && player.bomb) {
var bomb = new PIXI.Graphics();
bomb.beginFill(bomb_color, (player.dead ? 0.375 : 0.75)*Math.abs(Math.sin(performance.now() / 150)) );
bomb.drawCircle(size/2, size/2, size/2);
player.monitor.addChild(bomb);
rolling_bombs[player.id] = bomb;
} else if (show_bomb) delete rolling_bombs[player.id];
// Draw tagpro
if (show_tagpro && player.tagpro) {
var tp = new PIXI.Graphics();
tp.lineStyle(tagpro_thick, tagpro_color, player.dead ? 0.5 : 1 );
tp.drawCircle(size/2, size/2, size/2);
player.monitor.addChild(tp);
}
// Draw honk
if (show_honk && player.isHonking) {
var honk = new PIXI.Sprite(honksprite);
honk.width = honksprite.width * (size/40);
honk.height = honksprite.height * (size/40);
honk.x = ( -honk.width + size ) / 2;
honk.y = ( -honk.height + size ) / 2;
player.monitor.addChild(honk);
}
// Draw grip (juke juice)
if (show_grip && player.grip) {
tagpro.tiles.draw(player.monitor, 'grip' , { x: grip_x, y: grip_y }, grip_size, grip_size, player.dead ? 0.5 : 1);
}
// Draw speed (a deprecated powerup)
if (show_speed && player.speed) {
tagpro.tiles.draw(player.monitor, 'speed' , { x: speed_x, y: speed_y }, speed_size, speed_size, player.dead ? 0.5 : 1);
}
// Draw flag/potato
if (show_flag && player.flag && !player.dead) {
tagpro.tiles.draw(player.monitor, flagsprite[player.flag]+(player.potatoFlag ? 'potato':'flag') , { x: flag_x, y: flag_y }, flag_size, flag_size);
}
// Draw name
if (show_name) {
var name = new PIXI.Text(player.name, style[player.team]);
if (preset[player.team].leftText) name.x = textHLshift - name.width;
else name.x = textHRshift;
name.y = textVshift;
name.alpha = (show_dead && player.dead) ? 0.5 : 1;
player.monitor.addChild(name);
}
}
// Update either the red or blue list
function orderTeamList(team) {
var teamList = teamLists[team];
teamList.removeChildren();
var teamPlayers = [];
for (let p in tagpro.players) {
var player = tagpro.players[p];
if (player.team != team) continue;
if (!player.monitor) {
drawPlayer(player);
}
teamPlayers.push(player);
}
var sign = -2 * Boolean(preset[team].bottomToTop) + 1;
// sign == 1 if the list renders downward
// sign == -1 if the list renders upward (bottomToTop = true)
switch (order) {
case 'score':
teamPlayers.sort( (p1,p2) => sign * ( p2.score - p1.score ) );
// This sorts the teamPlayers list based on the .score of every player (desc.)
break;
case 'alphabetic':
teamPlayers.sort( (p1,p2) => sign * ( p1.name.toLowerCase() > p2.name.toLowerCase() || -(p1.name.toLowerCase() < p2.name.toLowerCase()) ) );
// This sorts the teamPlayers list based on the .score of every player (asc.)
break;
default:
// When something else, or 'constant' is chosen, the order of player id's is conserved
// which is the order that they joined the game.
}
var count = 0;
for (let player of teamPlayers) {
teamList.addChild(player.monitor);
player.monitor.y = sign * space * (count++);
}
}
tagpro.socket.on("p", function(data) {
if (data instanceof Array) {
let player = tagpro.players[data[0].id];
drawPlayer(player);
orderTeamList(player.team);
return;
}
for (var p in data.u) {
let player = tagpro.players[data.u[p].id];
var old_json = player.json;
player.json = JSON.stringify(getPlayer(player));
if (player.json != old_json) {
drawPlayer(player);
}
}
});
tagpro.socket.on("playerLeft", function(data) {
var player = tagpro.players[data];
delete player.monitor;
orderTeamList(player.team);
});
org_drawHonk = tagpro.drawHonk || (() => 0);
tagpro.drawHonk = function(player, remove) {
org_drawHonk(player, remove);
drawPlayer(player);
};
setInterval(function() {orderTeamList(1); orderTeamList(2);}, refresh_rate);
});