Splo Mod

Autobreak-trap, Autoheal, Hat macros, Anti-trap, AutoPush & more!

  1. // ==UserScript==
  2. // @name Splo Mod
  3. // @description Autobreak-trap, Autoheal, Hat macros, Anti-trap, AutoPush & more!
  4. // @version 2.8
  5. // @author Wealthy#8266 & Nuro#9999
  6. // @match *://sploop.io/*
  7. // @run-at document-start
  8. // @require http://code.jquery.com/jquery-3.3.1.min.js
  9. // @require https://code.jquery.com/ui/1.12.0/jquery-ui.min.js
  10. // @grant none
  11. // @namespace https://greasyfork.org/users/761829
  12. // ==/UserScript==
  13.  
  14. let version = "2.8"
  15.  
  16. let addHealMS = 0
  17. let Game;
  18. let Entity = new Array();
  19. let Canvas;
  20. let ctx;
  21. let keyDown = [];
  22. let user = {};
  23. let tribe = [];
  24. let enemy;
  25. let encoder = new TextEncoder();
  26. let decoder = new TextDecoder();
  27. let server;
  28.  
  29. let Config = {
  30. update: (type) => {
  31. Config[type] += 1;
  32. setTimeout(() => (Config[type] -= 1), 1e3);
  33. },
  34. serverUpdate: 1e3 / 9,
  35. breaking: false,
  36. pushing: false,
  37. rate: 1e3,
  38. pps: 0,
  39. weapon: 0,
  40. cps: 0,
  41. tps: 0,
  42. fps: 0,
  43. ping: 0,
  44. freeze: {
  45. send: true,
  46. pps: true,
  47. message: false,
  48. setup: false
  49. },
  50. angle: 0,
  51. move: 0,
  52. messages: new Array([], []),
  53. counter: 0,
  54. resolver: function(){},
  55. last: Date.now(),
  56. isJson: (data) => {
  57. try {
  58. JSON.parse(data);
  59. } catch (e) {
  60. return false;
  61. }
  62. return true;
  63.  
  64. },
  65. WS: null
  66. };
  67.  
  68. Config.tick = () => {
  69. return new Promise((e) => (Config.resolver = e))
  70. };
  71.  
  72. let Toggle = {
  73. UI: false,
  74. autoBreak: true,
  75. autoPush: true,
  76. autoPlace: true,
  77. autoSync: true
  78. }
  79.  
  80. class Macro {
  81. constructor(advanced, spike, trap, mill, food){
  82. this.advanced = advanced;
  83. this.spike = spike;
  84. this.trap = trap;
  85. this.mill = mill;
  86. this.food = food;
  87. };
  88.  
  89. update(){
  90. if(keyDown[this.spike]) Sploop.newPlace(4);
  91. if(keyDown[this.trap]) Sploop.newPlace(7);
  92. if(keyDown[this.mill]) Sploop.newPlace(5);
  93. if(keyDown[this.food]) Sploop.newPlace(2);
  94. };
  95. };
  96.  
  97. let Placer = new Macro(true, 86, 70, 78, 81);
  98.  
  99. class Sploop {
  100. static place(id, angle = Config.angle){
  101. Config.update('cps');
  102. Sploop.take(id);
  103. Sploop.hit(angle);
  104. Sploop.take(Config.weapon);
  105. }
  106.  
  107. static newPlace(id, angle = Config.angle){
  108. let increasor = Math.PI / 8; // 22.25 radians
  109. let offset = Math.PI / 4; // 45 radians
  110.  
  111. this.place(id, angle, true)
  112.  
  113. for(let newAngle = 0; newAngle < offset; newAngle += increasor){
  114. Sploop.place(id, angle - newAngle);
  115. Sploop.place(id, angle + newAngle);
  116. }
  117.  
  118. Sploop.take(Config.weapon);
  119. }
  120.  
  121. static quad(id, angle = 0){
  122. for(let newAngle = 0; newAngle < Math.PI * 2; newAngle += Math.PI / 8){
  123. let time = (Config.serverUpdate / 4) * (newAngle / (Math.PI / 8))
  124.  
  125. setTimeout(() => Sploop.place(7, angle + newAngle), time);
  126. }
  127. }
  128.  
  129. static heal(amount) {
  130. for(let count = 0; count <= amount; count++) Sploop.place(2);
  131. }
  132.  
  133. static equip(id) {
  134. if(user.skin != id) Game.send(new Uint8Array([9, id]));
  135. }
  136.  
  137. static walk(angle = Config.move) {
  138. if(typeof angle !== 'number') return Game.send(new Uint8Array([1, 0]));
  139.  
  140. angle = (65535 * (angle + Math.PI)) / (2 * Math.PI);
  141.  
  142. Game.send(new Uint8Array([15, 255 & angle, (angle >> 8) & 255]));
  143. }
  144.  
  145. static take(id) {
  146. Game.send(new Uint8Array([8, id]));
  147. }
  148.  
  149. static chat(text){
  150. text = encoder.encode(text);
  151.  
  152. Game.send(new Uint8Array([10, ...text]))
  153. }
  154.  
  155. static hit(angle) {
  156. angle = (65535 * (angle + Math.PI)) / (2 * Math.PI);
  157.  
  158. Game.send(new Uint8Array([4, 255 & angle, (angle >> 8) & 255]));
  159. Game.send(new Uint8Array([5]));
  160. }
  161.  
  162. static watch(angle){
  163. angle = (65535 * (angle + Math.PI)) / (2 * Math.PI);
  164.  
  165. Game.send(new Uint8Array([2, 255 & angle, (angle >> 8) & 255]));
  166. }
  167.  
  168. static offensive(){
  169. let offensive = () => {
  170. let distance = enemy ? Math.dist(enemy, user) : 0;
  171.  
  172. if(user.y <= 9e3 && user.y >= 8e3) return 9;
  173. if(enemy && distance <= 300) return false && distance <= 150 ? 6 : 5;
  174.  
  175. return 7;
  176. }
  177.  
  178. Sploop.equip(offensive());
  179. }
  180.  
  181. static healthChange(health, oldHealth){
  182. if(oldHealth > health){
  183. user.hitDate = Config.counter;
  184. };
  185.  
  186. user.health = health;
  187. }
  188.  
  189. static mine(build){
  190. if(user.id2 == build.id2) return true;
  191. if(user.team){
  192. let length = tribe.length;
  193. for(let index = 0; index < length; index++) {
  194. let teammate = tribe[index];
  195. if(build.id2 == teammate.id2) return true;
  196. }
  197. }
  198. return false;
  199. }
  200.  
  201. static update(){
  202. Config.counter += 1;
  203. Config.resolver();
  204. Config.last = Date.now();
  205.  
  206. if(user.alive){
  207. if(user.health < 100){
  208. setTimeout(() => {
  209. let amount = 3;
  210. Sploop.heal(amount);
  211. }, (Config.serverUpdate - 20 - Config.ping) + addHealMS);
  212. };
  213.  
  214. let trap = Entity.find(c => c && Math.dist(c, user) <= 50 && c.type == 6 && !Sploop.mine(c));
  215. let wasBreaking = Config.breaking;
  216. Config.breaking = false;
  217.  
  218. if(trap && Toggle.autoBreak){
  219. console.log(trap, user)
  220. let angle = Math.angle(trap, user);
  221. Config.breaking = true;
  222.  
  223. Sploop.hit(angle);
  224. Sploop.equip(6);
  225.  
  226. if(!wasBreaking) Sploop.quad(7, angle);
  227. } else if(wasBreaking){
  228. Sploop.offensive();
  229. }
  230.  
  231. let wasPushing = Config.pushing;
  232. Config.pushing = false;
  233.  
  234. if(enemy && !trap && user.alive){
  235. let distance = Math.dist(enemy, user);
  236.  
  237. if(Toggle.autoPush && distance <= 250){
  238. let trap = Entity.find(c => c && Math.dist(c, enemy) <= 50 && c.type == 6 && Sploop.mine(c));
  239.  
  240. if(trap){
  241. let spikes = Entity.filter(c => c && [2, 7, 17].includes(c.type) && Sploop.mine(c) && Math.dist(c, trap) <= 130);
  242.  
  243. if(spikes.length){
  244. let spike = spikes.sort((a, b) => Math.dist(a, trap) - Math.dist(b, trap))[0];
  245. let angle = Math.angle(enemy, spike);
  246. distance = Math.dist(enemy, spike) + 70;
  247. let position = {
  248. x: spike.x + (distance * Math.cos(angle)),
  249. y: spike.y + (distance * Math.sin(angle))
  250. };
  251.  
  252. distance = Math.dist(position, user);
  253. angle = () => {
  254. if(distance > 40){
  255. return Math.angle(position, user)
  256. } else {
  257. let angleDifference = Math.abs(Math.angle(spike, position) - Math.angle(spike, user))
  258. let message = `diffence [${angleDifference / (Math.PI / 180)}]`
  259.  
  260. // Sploop.chat(message);
  261. return Math.angle(enemy, user)
  262. }
  263. }
  264.  
  265. Config.pushing = true;
  266. Sploop.walk(angle())
  267. }
  268. }
  269. }
  270.  
  271. distance = Math.dist(enemy, user)
  272. if(Toggle.autoPlace && distance <= 200){
  273. let trap = Entity.find(c => c && c.type == 6 && Sploop.mine(c) && Math.dist(c, enemy) <= 50);
  274. let enemyPos = {
  275. x: enemy.x + enemy.xVel,
  276. y: enemy.y + enemy.yVel
  277. }
  278. let userPos = {
  279. x: user.x + user.xVel,
  280. y: user.y + user.yVel
  281. }
  282. distance = Math.dist(enemyPos, userPos);
  283. let angle = Math.angle(enemyPos, userPos)
  284. let range = 28 * 2 + 50;
  285.  
  286. if(trap){
  287. angle = Math.angle(trap, userPos);
  288.  
  289. for(let newAngle = 0; newAngle < Math.PI / 2; newAngle += Math.PI / 9){
  290. Sploop.place(4, angle + newAngle);
  291. Sploop.place(4, angle - newAngle);
  292. }
  293. } else {
  294. if(Toggle.autoSync && distance < 250){
  295. let spike = Entity.find(c => c && [2, 7, 17].includes(c.type) && Sploop.mine(c) && Math.dist(c, enemyPos) <= 60);
  296. if(spike){
  297. Sploop.equip(2);
  298. Sploop.take(0);
  299. Sploop.hit(angle);
  300. setTimeout(() => Sploop.offensive(), 2e3);
  301. }
  302. if(enemy.health <= (enemy.skin == 2 ? 78 : 85) && user.skin == 5 && user.health <= 70){
  303. Sploop.equip(2);
  304. Sploop.take(0);
  305. Sploop.hit(angle);
  306. setTimeout(() => Sploop.offensive(), 2e3);
  307. }
  308. }
  309.  
  310. if(range >= distance){
  311. Sploop.place(7, angle);
  312. }
  313. }
  314. }
  315. }
  316.  
  317. if(wasPushing && !Config.pushing) Sploop.walk('Stop');
  318. }
  319.  
  320. Placer.update();
  321. }
  322. }
  323.  
  324.  
  325. class Script {
  326. setup(){
  327. this.run();
  328. };
  329. override(ws, data){
  330. !Config.freeze.send && this.log(`WebSocket`, `⬈`, data[0], '#8ecc51');
  331. ws.classic(data, true);
  332. let string = Config.isJson(data);
  333. data = string ? JSON.parse(data) : new Uint8Array(data);
  334. let item = data[0];
  335. switch(item) {
  336. case 6:
  337. user.name = data[1];
  338. break;
  339. }
  340.  
  341.  
  342. Config.update('pps');
  343. }
  344.  
  345. send(data){
  346. this.ws && 1 === this.ws.readyState && (typeof data !== "string" && window.encoder.encode(data), this.ws.classic(data, true))
  347. Config.update('pps');
  348. }
  349.  
  350. message(event){
  351. let data = event.data;
  352. let string = typeof data === 'string';
  353. let decoded = string ? JSON.parse(data) : new Uint8Array(data);
  354. let length = decoded.length;
  355. let id = Number(decoded[0]);
  356. let found = Config.messages[Number(string)].find(item => item && item.id == id);
  357.  
  358. if(!found) return !Config.freeze.message && Game.log(`WebSocket`, `⬉`, `${decoded} | ${string}`, '#c7cc51');
  359.  
  360. switch(found.name){
  361. case 'Player update':
  362. enemy = null;
  363. tribe = [];
  364.  
  365. for(let index = 0; index < Entity.length; index++){
  366. let player = Entity[index];
  367. if(player) player.visible = false;
  368. }
  369.  
  370. for (let int = 1; int < length; int += 18) {
  371. let type = decoded[int],
  372. owner = decoded[int + 1],
  373. index = decoded[int + 2] | decoded[int + 3] << 8,
  374. x = decoded[int + 4] | decoded[int + 5] << 8,
  375. y = decoded[int + 6] | decoded[int + 7] << 8,
  376. broken = decoded[int + 8],
  377. skin = decoded[int + 11],
  378. team = decoded[int + 12],
  379. health = decoded[int + 13] / 255 * 100,
  380. clown = decoded[int + 8];
  381.  
  382. let Default = {fd: 2, active: true, health: 100, x: 0, y: 0, xVel: 0, yVel: 0};
  383. let temp = Entity[index] || Default;
  384. temp.visible = true;
  385.  
  386.  
  387. if (broken & 2) {
  388. Entity[index] = null;
  389. } else {
  390. if (temp.fd & 2) {
  391. temp.type = type;
  392. temp.id = index;
  393. temp.health = health;
  394. temp.id3 = broken;
  395. temp.xVel = temp.x - x;
  396. temp.yVel = temp.y - y;
  397. temp.speed = Math.hypot(y - temp.y, x - temp.x);
  398. temp.move = Math.atan2(y - temp.y, x - temp.x);
  399. temp.x = x;
  400. temp.y = y;
  401. temp.id2 = owner;
  402. temp.skin = skin;
  403. temp.team = team;
  404. temp.clown = Boolean(clown);
  405. }
  406.  
  407. Entity[index] = temp;
  408.  
  409. if(temp.id === user.id) {
  410. Sploop.healthChange(temp.health, user.health);
  411. Object.assign(user, temp)
  412. } else if(!temp.type && (user.team && user.team == temp.team)){
  413. tribe.push(temp);
  414. } else if(!temp.type && (!user.team || temp.team != user.team)){
  415. let distance = Math.hypot(user.y - temp.y, user.x - temp.x);
  416. let distance2 = enemy ? Math.hypot(user.y - enemy.y, user.x - enemy.x) : null;
  417. if(enemy){
  418. if(distance < distance2) enemy = temp;
  419. } else {
  420. enemy = temp;
  421. }
  422. }
  423.  
  424. }
  425. }
  426.  
  427. Config.update('tps');
  428. Sploop.update();
  429. break;
  430. case 'Spawn':
  431. user.id = decoded[1];
  432. user.alive = true;
  433. user.spawnDate = Date.now();
  434. user.health = 100;
  435. Config.weapon = 0;
  436. break;
  437. case 'Death':
  438. user.health = 0;
  439. user.speed = 0;
  440. user.alive = false;
  441. break;
  442. case 'Ping update':
  443. Config.ping = decoded[1] | (decoded[2] << 8);
  444. break;
  445. }
  446.  
  447. Placer.update();
  448. }
  449.  
  450. log(group, symbol, result, color){
  451. return console.log(`%c[${group}] %c${symbol}`, `color:${color};font-weight:bold`, `color:${color}`, result);
  452. }
  453.  
  454. run(ws){
  455. !Config.freeze.setup && Game.log(`Hijacked Iframe`, `✔`, ws.url, '#0f0');
  456. let notifications = `<div class="notifications-holder"></div><style>.box span{font-size: 20px; white-space: nowrap;}.box{width: max-content; height: 40px; display: flex; align-items: center; padding-top: 3.5px; padding-left: 7px; padding-right: 7px; border-radius: 7px; background-color: rgb(40 45 34 / 60%); border: 4px solid #141414; margin-bottom: 5px; color: white; letter-spacing: 1px; font-weight: bold; box-shadow: inset 0 -3px 0 #333; text-shadow: rgb(20 20 20) 3px 0px 0px, rgb(20 20 20) 2.83487px 0.981584px 0px, rgb(20 20 20) 2.35766px 1.85511px 0px, rgb(20 20 20) 1.62091px 2.52441px 0px, rgb(20 20 20) 0.705713px 2.91581px 0px, rgb(20 20 20) -0.287171px 2.98622px 0px, rgb(20 20 20) -1.24844px 2.72789px 0px, rgb(20 20 20) -2.07227px 2.16926px 0px, rgb(20 20 20) -2.66798px 1.37182px 0px, rgb(20 20 20) -2.96998px 0.42336px 0px, rgb(20 20 20) -2.94502px -0.571704px 0px, rgb(20 20 20) -2.59586px -1.50383px 0px, rgb(20 20 20) -1.96093px -2.27041px 0px, rgb(20 20 20) -1.11013px -2.78704px 0px, rgb(20 20 20) -0.137119px -2.99686px 0px, rgb(20 20 20) 0.850987px -2.87677px 0px, rgb(20 20 20) 1.74541px -2.43999px 0px, rgb(20 20 20) 2.44769px -1.73459px 0px, rgb(20 20 20) 2.88051px -0.838247px 0px;}.notifications-holder{position: absolute; left: 20px; top: 20px; display: flex; flex-direction: column; z-index: 5;}</style>`
  457. $("body").append(notifications)
  458. this.ws = ws;
  459.  
  460. let infoPanel = '\n<div class="info-panel-holder">\n <div id="info-content">\n <p id="health"></div>\n</div>\n<style>\n#info-content {\n color: #fff;\n font-size: 22px;\n text-shadow: 0px 0px 5px black, 0px 0px 7px black;\n}\n.info-panel-holder {\n position: absolute;\n top: 20px;\n left: 20px;\n}\n</style>\n';
  461. $("body").append(infoPanel)
  462.  
  463. setInterval(() => {
  464. !Config.freeze.pps && this.log(`PPS`, `⬍`, Config.pps, '#516ecc');
  465. }, Config.rate);
  466.  
  467. Config.width = Canvas.clientWidth;
  468. Config.height = Canvas.clientHeight;
  469.  
  470. $(window).resize(() => {
  471. Config.width = Canvas.clientWidth;
  472. Config.height = Canvas.clientHeight;
  473. });
  474.  
  475. Canvas.addEventListener('mousemove', (event) => {
  476. Config.mouseX = event.clientX;
  477. Config.mouseY = event.clientY;
  478. Config.angle = Math.atan2(Config.mouseY - Config.height / 2, Config.mouseX - Config.width / 2);
  479. });
  480.  
  481. }
  482.  
  483. constructor(){
  484. this.ws = null;
  485. }
  486. };
  487.  
  488. const Setup = () => {
  489. Game = new Script();
  490. Game.log(`Setup`, `⦿`, '', '#000000');
  491. let data = Config.messages;
  492.  
  493. data[0][1] = {name: 'Player update', string: false};
  494. data[0][2] = {name: 'Verify', string: false};
  495. data[0][5] = {name: 'Choose', string: false};
  496. data[0][7] = {name: 'Hit', string: false};
  497. data[0][14] = {name: 'Resource update', string: false};
  498. data[0][16] = {name: 'Projectile Hit', string: false};
  499. data[0][18] = {name: 'Chat', string: false};
  500. data[0][19] = {name: 'Choose x3', string: true};
  501. data[0][20] = {name: 'Choose x2', string: false};
  502. data[0][22] = {name: 'Ping update', string: false};
  503. data[0][23] = {name: 'Ping update', string: false};
  504. data[0][24] = {name: 'Create clan', string: false};
  505. data[0][25] = {name: 'Leave clan', string: false};
  506. data[0][26] = {name: 'Create clan', string: false};
  507. data[0][27] = {name: 'Leave clan', string: false};
  508. data[0][30] = {name: 'Place', string: false};
  509.  
  510. data[1][2] = {name: 'Spawn', string: true};
  511. data[1][8] = {name: 'Player setup', string: true};
  512. data[1][9] = {name: 'Leaderboard update', string: true};
  513. data[1][11] = {name: 'Text', string: true};
  514. data[1][13] = {name: 'Death', string: true};
  515. data[1][19] = {name: 'Choose', string: true};
  516. data[1][35] = {name: 'new Verify', string: true};
  517.  
  518. for(let index = 0; index <= 1; index++) {
  519. let length = data[index].length;
  520. for(let id = 0; id < length; id++) {
  521. if(data[index][id]) data[index][id].id = id;
  522. };
  523. };
  524. };
  525. Setup();
  526.  
  527. class Nuro extends WebSocket {
  528. constructor(url, protocols) {
  529. Config.WS = super(url, protocols);
  530. this.addEventListener('message', event => Game.message(event));
  531. this.classic = this.send;
  532. this.send = data => Game.override(this, data);
  533. window.ws = this;
  534. Game.run(this);
  535. }
  536. set onmessage(f) {
  537. !Config.freeze.setup && console.log('onmessage', f);
  538. super.onmessage = f;
  539. }
  540. }
  541.  
  542. const hijacked = Symbol();
  543.  
  544. function hijack(window) {
  545. const getter = window.HTMLIFrameElement.prototype.__lookupGetter__('contentWindow');
  546. window.HTMLIFrameElement.prototype.__defineGetter__('contentWindow', function() {
  547. const hiddenWindow = getter.call(this);
  548. if (!hiddenWindow[hijacked]) {
  549. hijack(hiddenWindow);
  550. hiddenWindow.WebSocket = Nuro;
  551. hiddenWindow[hijacked] = true;
  552. }
  553. return hiddenWindow;
  554. });
  555. }
  556.  
  557. hijack(window);
  558.  
  559. let blockReact = ['clan-menu-clan-name-input', 'nickname', 'chat'];
  560.  
  561. const keyChange = (event, down) => {
  562. if(blockReact.includes(document.activeElement.id.toLowerCase())) return `Blocked key change.`
  563. keyDown[event.keyCode] = down;
  564.  
  565. let isPrimary = [49, 97].includes(event.keyCode);
  566. let isSecondary = [50, 98].includes(event.keyCode);
  567.  
  568. if(down && (isPrimary || isSecondary)) Config.weapon = Number(isSecondary);
  569.  
  570. switch(event.key.toUpperCase()) {
  571. case "T":
  572. Sploop.equip(4)
  573. break
  574. case "B":
  575. Sploop.equip(7)
  576. break;
  577. case "C":
  578. Sploop.equip(2)
  579. break
  580. case "G":
  581. Sploop.equip(5)
  582. break;
  583. }
  584. Placer.update();
  585. };
  586.  
  587. setInterval(Placer.update, 50);
  588.  
  589. document.addEventListener("keydown", (event) => keyChange(event, true));
  590. document.addEventListener("keyup", (event) => keyChange(event, false));
  591.  
  592. Math.dist = (player, player2) => {
  593. return Math.sqrt(Math.pow((player.x - player2.x), 2) + Math.pow((player.y - player2.y), 2));
  594. }
  595.  
  596. Math.angle = (player, player2) => {
  597. return Math.atan2(player.y - player2.y, player.x - player2.x)
  598. }
  599.  
  600. const encodeSym = Symbol();
  601. Object.defineProperty(Object.prototype, 'encode', {
  602. get() {
  603. return this[encodeSym];
  604. },
  605. set(encode) {
  606. if(this.init) {
  607. window.encoder = this
  608. }
  609. this[encodeSym] = function() {
  610. return encode.apply(this, arguments)
  611. }
  612. }
  613. });
  614.  
  615. const ReqFrame = requestAnimationFrame;
  616. window.requestAnimationFrame = function() {
  617. Config.update("fps");
  618. ReqFrame.apply(this, arguments);
  619. }
  620.  
  621.  
  622. let updateInfo = () => {
  623. if(user && user.alive){
  624. let Display = ``;
  625. let addText = (text = '') => {
  626. Display += (text + '<br/>')
  627. }
  628.  
  629. addText(`health: ${Math.round(user.health)}/100`);
  630. addText(`push: o${Config.pushing ? 'n' : 'ff'}line`);
  631. addText(`stuck: ${Config.breaking ? 'yes' : 'no'}`);
  632. addText(`speed: ${Math.round(user.speed)}`);
  633. addText();
  634. addText(`cps: ${Config.cps}`);
  635. addText(`pps: ${Config.pps}`);
  636. addText(`tps: ${Config.tps}`);
  637. addText(`fps: ${Config.fps}`);
  638.  
  639. $("#info-content").html(Display)
  640. };
  641. }
  642.  
  643. const gctx = CanvasRenderingContext2D.prototype.clearRect;
  644. CanvasRenderingContext2D.prototype.clearRect = function() {
  645. if (this.canvas.id === "game-canvas") {
  646. Canvas = this.canvas
  647. }
  648. return gctx.apply(this, arguments);
  649. }
  650. const { fillText } = CanvasRenderingContext2D.prototype;
  651. CanvasRenderingContext2D.prototype.fillText = function(text, x, y) {
  652. if(text == user.name && text.length > 1 || typeof text == "string" && text.startsWith(String.fromCharCode(0))) {
  653. let hue = 0;
  654. let step = 360 / user.name.length;
  655. for (let letter of text) {
  656. this.fillStyle = `hsl(${hue}, 100%, 50%)`;
  657. fillText.call(this, letter, x, y);
  658. x += this.measureText(letter).width;
  659. hue = (hue + step) % 360;
  660. }
  661. return;
  662. }
  663. return fillText.apply(this, arguments);
  664. }
  665.