TagPro Player Monitor

Shows an on-screen list of players in the game and their current status

  1. // ==UserScript==
  2. // @name TagPro Player Monitor
  3. // @version 4.4
  4. // @author bash# ; Ko
  5. // @description Shows an on-screen list of players in the game and their current status
  6. // @match *://*.koalabeast.com/*
  7. // @match *://*.jukejuice.com/*
  8. // @match *://*.newcompte.fr/*
  9. // @namespace https://greasyfork.org/users/152992
  10. // @icon https://github.com/wilcooo/TagPro-ScriptResources/raw/master/playermonitor.png
  11. // @supportURL https://www.reddit.com/message/compose/?to=Wilcooo
  12. // @website https://redd.it/6pe5e9
  13. // @require https://greasyfork.org/scripts/371240/code/TagPro%20Userscript%20Library.js
  14. // @grant GM_getValue
  15. // @grant GM_setValue
  16. // @license MIT
  17. // ==/UserScript==
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25. var short_name = 'monitor'; // An alphabetic (no spaces/numbers) distinctive name for the script.
  26. var version = GM_info.script.version; // The version number is automatically fetched from the metadata.
  27. tagpro.ready(function(){ if (!tagpro.scripts) tagpro.scripts = {}; tagpro.scripts[short_name]={version:version};});
  28. console.log('START: ' + GM_info.script.name + ' (v' + version + ' by ' + GM_info.script.author + ')');
  29.  
  30.  
  31.  
  32.  
  33. // SETTINGS (edit them on the TagPro homepage or on the scoreboard
  34.  
  35. var settings = tpul.settings.addSettings({
  36. id: 'monitor',
  37. title: "Configure the Player Monitor",
  38. tooltipText: "Player Monitor",
  39. icon: "https://github.com/wilcooo/TagPro-ScriptResources/raw/master/playermonitor.png",
  40.  
  41. fields: {
  42. hide_flagTaken: {
  43. label: 'Hide the default taken flag indicators',
  44. section: ['','Note: You might need to refresh for some settings to take effect.\n\n<br><br>By default, TagPro will show you what flags are taken, and the number of players on each team. This script does the same (but more), so you probably want to hide the default indicators.'],
  45. type: 'checkbox',
  46. default: true
  47. },
  48. hide_playerIndicators: {
  49. label: 'Hide the default team count indicators',
  50. type: 'checkbox',
  51. default: true
  52. },
  53. position: {
  54. label: 'The position preset',
  55. section: ['','Choose one of these eleven position presets; so Top/Middle/Bottom, Left/Middle/Right or Split. "Split" means that the blue red team is shown on the left side, and the blue team on the right side of the screen.'],
  56. type: 'select',
  57. // add your-preset to the following list if you want to use it LEOPARD
  58. options: ['top-left', 'top-mid', 'top-right', 'mid-left', 'mid-right', 'bot-left', 'bot-mid', 'bot-right','top-split', 'mid-split', 'bot-split'],
  59. default: 'bot-mid'
  60. },
  61. order: {
  62. label: 'Sorting order of the players',
  63. type: 'select',
  64. options: ['constant','alphabetic','score'],
  65. default: 'constant'
  66. },
  67. show_hold: {
  68. label: 'Show a timer that counts how long the FC has been holding',
  69. type: 'checkbox',
  70. default: false
  71. },
  72. show_honk: {
  73. label: 'Show when a (nearby) player is honking. Only works when the Honk script is installed.',
  74. type: 'checkbox',
  75. default: true
  76. }
  77. },
  78.  
  79. events: {
  80. save: function(){
  81. hide_flagTaken = settings.get("hide_flagTaken")
  82. hide_playerIndicators = settings.get("hide_playerIndicators")
  83. position = settings.get("position")
  84. order = settings.get("order")
  85. show_honk = settings.get("show_honk")
  86. show_hold = settings.get("show_hold")
  87.  
  88. if (tpul.playerLocation == 'game'){
  89. preset = presets[position] || presets["bot-mid"];
  90. tagpro.ui.alignUI();
  91. }
  92. }
  93. }
  94. });
  95.  
  96. var hide_flagTaken = settings.get("hide_flagTaken"),
  97. hide_playerIndicators = settings.get("hide_playerIndicators"),
  98. position = settings.get("position"),
  99. order = settings.get("order"),
  100. show_honk = settings.get("show_honk"),
  101. show_hold = settings.get("show_hold");
  102.  
  103.  
  104.  
  105.  
  106. // CONSTANTS
  107.  
  108. const size = 16; // Size of a ball icon
  109. const space = 18; // Vertical space per name+icon
  110. const textVshift = 0; // relative vertical shift of text
  111. const textHLshift = -2; // relative horizontal shift of text on the left of a ball
  112. const textHRshift = 25; // relative horizontal shift of text on the right of a ball
  113.  
  114. const show_ball = true; // Whether to show the ball icon
  115. // (if you disable this, you might want to disable show_grip, show_speed, show_tagpro, show_bomb)
  116. const show_dead = true; // Whether to fade the ball when its dead/spawning
  117. const show_name = true; // Shows the playernames next to the corresponding balls (recommended)
  118.  
  119. const show_flag = true; // Show a flag next to players with a flag
  120. const flag_size = size; // size of the flag icon next to an FC
  121. const flag_x = 10; // Position of the flag, relative to the ball
  122. const flag_y = 0;
  123.  
  124. const show_grip = true; // Show a JJ pup on balls with Juke Juice
  125. const grip_size = 10; // size of the Juke Juice icon
  126. const grip_x = -1; // relative position
  127. const grip_y = 8;
  128.  
  129. const show_speed = false; // This won't work unless they put the topspeed pup back in the game (and even then probably not)
  130. const speed_size = 10; // size of the Top Speed icon (a deprecated TagPro powerup)
  131. const speed_x = -1; // relative position
  132. const speed_y = -3;
  133.  
  134. const show_tagpro = true; // Show a green circle on balls with a TP
  135. const tagpro_color = 0x00FF00; // Color of the (usually green) TagPro powerup circle
  136. const tagpro_thick = 1.5; // Thickness of that circle
  137.  
  138. // Tip, use : https://www.google.com/search?q=pick+color
  139.  
  140. const show_bomb = true; // Flash balls with a Rolling Bomb
  141. const bomb_color = 0xFFFF00; // Color of the flashing RollingBomb
  142.  
  143. const style = // The style of the text (1: red, 2: blue)
  144. {
  145. 1: {
  146. fontSize: "8pt",
  147. fontWeight: "bold",
  148. strokeThickness: 3,
  149. fill: 0xFFB5BD, // text-color (Tip: https://www.google.com/search?q=pick+color)
  150. },
  151. 2: {
  152. fontSize: "8pt",
  153. fontWeight: "bold",
  154. strokeThickness: 3,
  155. fill: 0xCFCFFF,
  156. },
  157. };
  158.  
  159. const presets = { // 1: red team , 2: blue team
  160. 'top-left' : {
  161. 1 : { x:10, y:10, },
  162. 2 : { x:10, y:10 + 4.5*space, },
  163. },
  164. 'mid-left' : {
  165. 1 : { x:10, y:-0.75*space, anchor : {x:0, y:0.5}, bottomToTop: true, },
  166. 2 : { x:10, y: 0.75*space, anchor : {x:0, y:0.5}, },
  167. },
  168. 'bot-left' : {
  169. 1 : { x:10, y:-10 - 5.5*space, anchor : {x:0, y:1}, bottomToTop: true, },
  170. 2 : { x:10, y:-10 - space, anchor : {x:0, y:1}, bottomToTop: true, },
  171. },
  172. 'top-mid' : {
  173. 1 : { x:-25, y:10, anchor : {x:0.5, y:0}, leftText: true, },
  174. 2 : { x:5, y:10, anchor : {x:0.5, y:0}, },
  175. },
  176. 'bot-mid' : {
  177. 1 : { x:-135, y:-10 - space, anchor : {x:0.5, y:1}, bottomToTop: true, leftText: true, },
  178. 2 : { x: 115, y:-10 - space, anchor : {x:0.5, y:1}, bottomToTop: true, },
  179. },
  180. 'top-right' : {
  181. 1 : { x:-30, y:10, anchor : {x:1, y:0}, leftText: true, },
  182. 2 : { x:-30, y:10 + 4.5*space, anchor : {x:1, y:0}, leftText: true, },
  183. },
  184. 'mid-right' : {
  185. 1 : { x:-30, y:-0.75*space, anchor : {x:1, y:0.5}, bottomToTop: true, leftText: true, },
  186. 2 : { x:-30, y: 0.75*space, anchor : {x:1, y:0.5}, leftText: true, },
  187. },
  188. 'bot-right' : {
  189. 1 : { x:-30, y:-10 - 5.5*space, anchor : {x:1, y:1}, bottomToTop: true, leftText: true, },
  190. 2 : { x:-30, y:-10 - space, anchor : {x:1, y:1}, bottomToTop: true, leftText: true, },
  191. },
  192. 'top-split' : {
  193. 1 : { x:10, y:10, },
  194. 2 : { x:-30, y:10, anchor : {x:1, y:0}, leftText: true, },
  195. },
  196. 'mid-split' : {
  197. 1 : { x:10, y:-2*space, anchor : {x:0, y:0.5}, },
  198. 2 : { x:-30, y:-2*space, anchor : {x:1, y:0.5}, leftText: true, },
  199. },
  200. 'bot-split' : {
  201. 1 : { x:10, y:-10 - space, anchor : {x:0, y:1}, bottomToTop: true, },
  202. 2 : { x:-30, y:-10 - space, anchor : {x:1, y:1}, bottomToTop: true, leftText: true, },
  203. },
  204. 'your-preset' : { // A preset to experiment with!
  205. // Don't forget to add your-preset to the options list, search this script (ctrl+F) for LEOPARD
  206. 1 : { x:0, y:0, },
  207. 2 : { x:0, y:0, },
  208. },
  209. };
  210.  
  211. // EXPLANATION OF THE PRESETS:
  212. // 'x' and 'y' form the position of the player monitor
  213. // 'anchor' is the reference point from which the 'x' and 'y' above are calculated.
  214. // 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)
  215. //
  216. // The position described above is the position of the first ball in the player monitor.
  217. // The next balls are usually drawn below it, but when 'bottomToTop' is true, they are drawn above the first one.
  218. //
  219. // 'leftText' makes the name of the ball appear on the left side of the ball.
  220. //
  221. // You may add a preset to the list, and select it in the options on the homepage
  222.  
  223.  
  224.  
  225.  
  226.  
  227. var preset = presets[position] || presets["bot-mid"]; // If no valid preset is chosen, fallback to 'bot-mid'
  228.  
  229. if (tpul.playerLocation == 'game') {
  230.  
  231. const flagsprite =
  232. {
  233. 1: "red", // Note: 'flag' or 'potato' gets added to this later in this script
  234. 2: "blue",
  235. 3: "yellow",
  236. };
  237. const ballsprite =
  238. {
  239. 1: "redball",
  240. 2: "blueball",
  241. };
  242.  
  243. 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==");
  244.  
  245. const refresh_rate = 3e3; // An interval (milliseconds), after which the order of the playerlists gets updated.
  246.  
  247. tagpro.ready(function () {
  248.  
  249.  
  250.  
  251.  
  252. // Hide the flagTaken indicators
  253.  
  254. if (hide_flagTaken)
  255. tagpro.renderer.updateFlagsFromPlayer = function() {
  256. console.warn('The flag indicators are blocked by the TagPro Player Monitor script');
  257. tagpro.renderer.updateFlagsFromPlayer = ()=>0;
  258. };
  259.  
  260. // Hide TagPro's new Player Indicators
  261.  
  262. if (hide_playerIndicators) {
  263. tagpro.ui.updatePlayerIndicators = function() {
  264. console.warn('The player indicators are blocked by the TagPro Player Monitor script');
  265. tagpro.ui.updatePlayerIndicators = ()=>0;
  266. };
  267. }
  268.  
  269.  
  270. // This function returns a summary of a player.
  271. // To be used to check whether something has changed.
  272.  
  273. function getPlayer(player) {
  274.  
  275. var state = {
  276. team: player.team,
  277. id: player.id,
  278. };
  279.  
  280. if (show_name) state.name = player.name;
  281. if (show_dead) state.dead = player.dead;
  282. if (show_flag) state.flag = player.flag;
  283. if (show_bomb) state.bomb = player.bomb;
  284. if (show_tagpro) state.tagpro = player.tagpro;
  285. if (show_grip) state.grip = player.grip;
  286. if (show_speed) state.speed = player.speed;
  287. if (show_honk) state.isHonking = player.isHonking;
  288.  
  289. return state;
  290. }
  291.  
  292.  
  293.  
  294.  
  295.  
  296. // Create PIXI containers for both player lists
  297.  
  298. var redList = new PIXI.Container();
  299. tagpro.renderer.layers.ui.addChild(redList);
  300.  
  301. var blueList = new PIXI.Container();
  302. tagpro.renderer.layers.ui.addChild(blueList);
  303.  
  304. var teamLists =
  305. {
  306. 1: redList,
  307. 2: blueList,
  308. };
  309.  
  310. tagpro.ui.sprites.redPlayerMonitor = redList;
  311. tagpro.ui.sprites.bluePlayerMonitor = blueList;
  312.  
  313.  
  314. // This function gets called when the browser window resizes
  315. // It moves the playerlists to the right location
  316.  
  317. var org_alignUI = tagpro.ui.alignUI;
  318. tagpro.ui.alignUI = function() {
  319. redList.x = ( preset[1].anchor ? (tagpro.renderer.vpWidth * preset[1].anchor.x) : 0 ) + preset[1].x;
  320. redList.y = ( preset[1].anchor ? (tagpro.renderer.vpHeight * preset[1].anchor.y) : 0 ) + preset[1].y;
  321. blueList.x = ( preset[2].anchor ? (tagpro.renderer.vpWidth * preset[2].anchor.x) : 0 ) + preset[2].x;
  322. blueList.y = ( preset[2].anchor ? (tagpro.renderer.vpHeight * preset[2].anchor.y) : 0 ) + preset[2].y;
  323.  
  324. // Move a few pixels down when overlapping with the FPS/ping
  325. if (tagpro.settings.ui.performanceInfo && ['top-left','top-split'].includes(position)) {
  326. redList.y += 20
  327. blueList.y += 20
  328. }
  329. org_alignUI();
  330. };
  331.  
  332. tagpro.ui.alignUI();
  333.  
  334.  
  335.  
  336. function format_time(seconds=0) { // example: 179 seconds will return (2:59)
  337. var minutes = Math.floor(seconds / 60),
  338. seconds = ("0" + seconds % 60).slice(-2)
  339. return " (" + minutes + ":" + seconds + ") "
  340. }
  341.  
  342.  
  343.  
  344.  
  345.  
  346. // The rolling_bomb graphics are stored here, so that they can be updated (animated) seperately
  347. if (show_bomb) {
  348. var rolling_bombs = {};
  349.  
  350. // Rewriting the TagPro's ui.update function to include the rendering of the RBs
  351. tagpro.ui.org_update = tagpro.ui.update;
  352. tagpro.ui.update = function () {
  353. for (var b in rolling_bombs) {
  354. rolling_bombs[b].alpha = (show_dead && tagpro.players[b] && tagpro.players[b].dead ? 0.375 : 0.75)*Math.abs(Math.sin(performance.now() / 150));
  355. }
  356. return tagpro.ui.org_update(...arguments)
  357. };
  358. }
  359.  
  360.  
  361. var hold_update_functions = {};
  362.  
  363.  
  364. // Update a single player
  365.  
  366. function drawPlayer(player) {
  367.  
  368. if (typeof player.monitor === 'undefined') {
  369. player.monitor = new PIXI.Container();
  370. player.monitor.hold_time = null;
  371. }
  372.  
  373. player.monitor.removeChildren();
  374.  
  375. // Draw ball
  376. if (show_ball) tagpro.tiles.draw(player.monitor, ballsprite[player.team], { x: 0, y: 0 }, size, size, player.dead ? 0.5 : 1);
  377.  
  378. // Draw bomb (rolling bomb)
  379. if (show_bomb && player.bomb) {
  380. var bomb = new PIXI.Graphics();
  381. bomb.beginFill(bomb_color, (player.dead ? 0.375 : 0.75)*Math.abs(Math.sin(performance.now() / 150)) );
  382. bomb.drawCircle(size/2, size/2, size/2);
  383.  
  384. player.monitor.addChild(bomb);
  385.  
  386. rolling_bombs[player.id] = bomb;
  387. } else if (show_bomb) delete rolling_bombs[player.id];
  388.  
  389. // Draw tagpro
  390. if (show_tagpro && player.tagpro) {
  391. var tp = new PIXI.Graphics();
  392. tp.lineStyle(tagpro_thick, tagpro_color, player.dead ? 0.5 : 1 );
  393. tp.drawCircle(size/2, size/2, size/2);
  394.  
  395. player.monitor.addChild(tp);
  396. }
  397.  
  398. // Draw honk
  399. if (show_honk && player.isHonking) {
  400. var honk = new PIXI.Sprite(honksprite);
  401. honk.width = honksprite.width * (size/40);
  402. honk.height = honksprite.height * (size/40);
  403. honk.x = ( -honk.width + size ) / 2;
  404. honk.y = ( -honk.height + size ) / 2;
  405.  
  406. player.monitor.addChild(honk);
  407. }
  408.  
  409. // Draw grip (juke juice)
  410. if (show_grip && player.grip) {
  411. tagpro.tiles.draw(player.monitor, 'grip' , { x: grip_x, y: grip_y }, grip_size, grip_size, show_dead && player.dead ? 0.5 : 1);
  412. }
  413.  
  414. // Draw speed (a deprecated powerup)
  415. if (show_speed && player.speed) {
  416. tagpro.tiles.draw(player.monitor, 'speed' , { x: speed_x, y: speed_y }, speed_size, speed_size, show_dead && player.dead ? 0.5 : 1);
  417. }
  418.  
  419. // Draw name
  420. if (show_name) {
  421. var name = new PIXI.Text(player.name, style[player.team]);
  422.  
  423. if (preset[player.team].leftText) name.x = textHLshift - name.width;
  424. else name.x = textHRshift;
  425.  
  426. name.y = textVshift;
  427. name.alpha = (show_dead && player.dead) ? 0.5 : 1;
  428.  
  429. player.monitor.addChild(name);
  430. }
  431.  
  432. // Draw flag/potato
  433. if (show_flag && player.flag && !player.dead) {
  434. tagpro.tiles.draw(player.monitor, flagsprite[player.flag]+(player.potatoFlag ? 'potato':'flag') , { x: flag_x, y: flag_y }, flag_size, flag_size);
  435.  
  436. // Draw hold time
  437. if (show_hold) {
  438.  
  439. var hold = new PIXI.Text(format_time(player.monitor.hold_time), style[player.team])
  440.  
  441. if (preset[player.team].leftText) hold.x = textHLshift - hold.width - (name ? name.width:0);
  442. else hold.x = textHRshift + (name ? name.width:0);
  443.  
  444. hold.y = textVshift;
  445. hold.alpha = (show_dead && player.dead) ? 0.5 : 1;
  446.  
  447. player.monitor.addChild(hold)
  448.  
  449. // Update the hold time every second
  450.  
  451. player.monitor.update_hold = function(t){
  452. if (!(player.id in tagpro.players)) return
  453. if (tagpro.state == 2) return
  454.  
  455. hold.text = format_time(++player.monitor.hold_time)
  456. if (preset[player.team].leftText) hold.x = textHLshift - hold.width - (name ? name.width:0)
  457.  
  458. setTimeout( player.monitor.update_hold, 1000 - (Date.now()-t)%1000, t )
  459. }
  460.  
  461. if (player.monitor.hold_time == null) {
  462. setTimeout( player.monitor.update_hold, 1000, Date.now()%1000 )
  463. }
  464. }
  465. } else {
  466. player.monitor.hold_time = null
  467. player.monitor.update_hold = ()=>{}
  468. }
  469.  
  470. }
  471.  
  472.  
  473.  
  474.  
  475.  
  476.  
  477. // Update either the red or blue list
  478.  
  479. function orderTeamList(team) {
  480.  
  481. var teamList = teamLists[team];
  482.  
  483. teamList.removeChildren();
  484.  
  485. var teamPlayers = [];
  486.  
  487. for (let p in tagpro.players) {
  488.  
  489. let player = tagpro.players[p];
  490.  
  491. if (player.team != team) continue;
  492.  
  493. if (!player.monitor) {
  494. drawPlayer(player);
  495. }
  496.  
  497. teamPlayers.push(player);
  498. }
  499.  
  500.  
  501. if (preset[team].bottomToTop) var sign = -1
  502. else var sign = 1
  503.  
  504. switch (order) {
  505. case 'score':
  506. teamPlayers.sort( (p1,p2) => sign * ( p2.score - p1.score ) );
  507. // This sorts the teamPlayers list based on the .score of every player (desc.)
  508. break;
  509. case 'alphabetic':
  510. teamPlayers.sort( (p1,p2) => sign * ( p1.name.toLowerCase() > p2.name.toLowerCase() || -(p1.name.toLowerCase() < p2.name.toLowerCase()) ) );
  511. // This sorts the teamPlayers list based on the .score of every player (asc.)
  512. break;
  513. default:
  514. // When something else, or 'constant' is chosen, the order of player id's is conserved
  515. // which is the order that they joined the game.
  516. }
  517.  
  518. var count = 0;
  519.  
  520. for (let player of teamPlayers) {
  521. teamList.addChild(player.monitor);
  522. player.monitor.y = sign * space * (count++);
  523. }
  524.  
  525. }
  526.  
  527.  
  528.  
  529.  
  530.  
  531. tagpro.socket.on("p", function(data) {
  532.  
  533. if (data instanceof Array) {
  534. let player = tagpro.players[data[0].id];
  535. drawPlayer(player);
  536. orderTeamList(player.team);
  537. return;
  538. }
  539.  
  540. for (var p in data.u) {
  541. let player = tagpro.players[data.u[p].id];
  542. var old_json = player.json;
  543. player.json = JSON.stringify(getPlayer(player));
  544. if (player.json != old_json) {
  545. drawPlayer(player);
  546. }
  547. }
  548.  
  549. });
  550.  
  551. // When tagpro receives a playerLeft event, it deletes the player.
  552. // We want to know what team the player was on so we can update only the necessary teamlist.
  553. // So instead of using tagpro.socket.on, we use a trick to insert our listener
  554. // BEFORE all other listeners for the playerLeft event.
  555. tagpro.rawSocket.listeners('playerLeft').unshift( function(data) {
  556. let player = tagpro.players[data]; // < now we know for sure that the player still exists!
  557. //delete player.monitor;
  558. setTimeout(orderTeamList(player.team)) // update the teamlist AFTER TagPro has removed the player
  559. } )
  560.  
  561. /*tagpro.socket.on('playerLeft', function(p) {
  562. orderTeamList(1); orderTeamList(2);
  563. }*/
  564.  
  565. org_drawHonk = tagpro.drawHonk || (() => 0);
  566. tagpro.drawHonk = function(player, remove) {
  567. org_drawHonk(player, remove);
  568. drawPlayer(player);
  569. };
  570.  
  571. // Just in case "something" happens, we update the teamlists every once in a while.
  572. setInterval(function() {orderTeamList(1); orderTeamList(2);}, refresh_rate);
  573.  
  574. });
  575.  
  576. }