· 6 years ago · Mar 27, 2020, 11:38 AM
1// ==UserScript==
2// @name Triangle Flank Hybrid (With AC Bot)
3// @namespace http://tampermonkey.net/
4// @version 2
5// @description made by icefire pro
6// @author diep.io
7// @match https://diep.io/
8// @grant GM_xmlhttpRequest
9// @grant GM_registerMenuCommand
10// @grant GM_getValue
11// @grant GM_setValue
12// @connect pubproxy.com
13// ==/UserScript==
14
15(function() {
16 'use strict';
17
18 var realSend = WebSocket.prototype.send;
19 var mainWS;
20 var serverWS;
21 var websockets = new Set();
22 var ACBots = new Set();
23
24 // THIS DOES NOT LOG YOUR IP
25 // You can verify this yourself, have a look at the server's source code: https://repl.it/@diepio/JavaScript-Websocket-Proxy-Enabler
26 // If you are still concerned, set the below to false;
27 const useServerFeatures = false;
28
29 const tanks = {FlankGuard: 72, Triangle: 74, Sniper: 84, Overseer: 78, Necromancer: 122, Stalker: 114, Overlord: 64, GTrapper: 24, Trapper: 102};
30 const binaryFlags = ["leftclick", "up", "left", "down", "right", "godmode", "suicide", "rightclick", "levelup", "gamepad", "slash"];
31 const URLRegex = /^wss?:\/\/[a-z0-9]{4}\.s\.m28n\.net\/$/g;
32
33 if (useServerFeatures)
34 {
35 serverWS = new WebSocket("wss://JavaScript-Websocket-Proxy-Enabler--diepio.repl.co");
36 serverWS.binaryType = "arraybuffer";
37 serverWS.dontRegister = true;
38 }
39
40 WebSocket.prototype.send = function(data)
41 {
42 if (!(data instanceof Int8Array && this.url.match(URLRegex)) || this.dontRegister) // The other websockets (for checking latency) uses ArrayBuffer, we want only the diep.io game's websocket
43 {
44 return realSend.call(this, data);
45 }
46
47 if (this !== mainWS)
48 {
49 mainWS = this;
50
51 this.serverID = this.url.split("://")[1].split(".")[0].toLowerCase();
52 this.queuedSlashes = 0;
53 this.blockSlash = false;
54 this.firstSpawn = true;
55
56 this.doHybrid = false;
57 this.doLordSeer = false;
58 this.doTrapHybrid = false;
59 this.noTrap = false;
60 this.doStalker = false;
61 this.doNecro = false;
62 this.noFlank = false; // If you want the Triangle-Flank hybrid to not give a delay for Flank Guard before switching back to Triangle
63 this.ACShooting = false;
64
65 this.realClose = this.onclose;
66 this.onclose = function(event)
67 {
68 //ACBots.forEach(ws => ws.close());
69 return this.realClose.call(this, event);
70 }
71
72 this.realRecv = this.onmessage;
73 this.onmessage = function(event)
74 {
75 var data = new Uint8Array(event.data);
76 switch (data[0]) // Trying to figure this out by logging
77 {
78 case 3: // Baisically prevents the game from sending the Drone/Predator notification which is super annoying.
79 {
80 var chars = data.slice(1, data.indexOf(0));
81 var text = "";
82 for (let char of chars) text += String.fromCharCode(char);
83 if (text === "Use your left mouse button to control the drones" || text === "Use your right mouse button to look further in the direction you're facing") return; // If you know how to install this script, you know how to use drones/Predator.
84 break;
85 }
86 case 4: //console.log(data[1])
87 {
88 if (!this.partyCode) this.onmessage({data: [6]}); // Allow copying links for other gamemodes that you cannot normally generate links for
89 break;
90 }
91 case 5:
92 {
93 if (this.pingSpoof !== undefined) return;
94 break;
95 }
96 case 6:
97 {
98 if (window.location.hash && window.location.hash.indexOf("00") && window.location.hash.indexOf("00") + 2 !== window.location.hash.length)
99 {
100 this.partyCode = window.location.hash.slice(window.location.hash.indexOf("00") + 2).toUpperCase();
101 }
102 else
103 {
104 this.partyCode = Array.from(data).slice(1).map(r => r.toString(16).padStart(2, "0").split('').reverse().join("")).join("").toUpperCase();
105 }
106 if (useServerFeatures) serverWS.send("\x00" + this.serverID + "\x00" + this.partyCode);
107 break;
108 }
109 case 10:
110 {
111 var playerCount = Array.from(data).slice(1);
112 this.playerCount = 0;
113 for (let i = 0; i < playerCount.length; i++)
114 {
115 this.playerCount += playerCount[i] % 128 * Math.pow(128, i);
116 if (playerCount[i] < 128) break;
117 }
118 if (this.playerSpoof !== undefined) return;
119 break;
120 }
121 }
122 return this.realRecv.call(this, event);
123 }
124 } // These are game utilities that are quite useful to have + some other interesting stuff :3
125
126 var switchTank = null;
127
128 //console.log(data[0])
129 switch (data[0]) // Trying to figure this out.
130 {
131 case 0:
132 {
133 console.log(Array.from(data).splice(1));
134 var view = new DataView(new ArrayBuffer(data.byteLength));
135 for (let i in data) view.setInt8(i, data[i]); // For some reason I must create a new ArrayBuffer object and copy over all the contents, reusing the sent one doesn't work
136 this.joinPacket = view.buffer;
137 break;
138 }
139 case 1:
140 {
141 var ACPacket = new Int8Array(data);
142 ACPacket[1] &= 0b10000001;
143 ACPacket[2] &= 0b11110010;
144 ACPacket[1] |= 0b00000001;
145 ACPacket[2] |= 0b00000010;
146
147 ACPacket[1] |= !this.ACShooting << binaryFlags.indexOf("suicide");
148
149 ACBots.forEach(ws => {
150 if (ws.packetSaving) return;
151 if (ws.finishedLevelingUp)
152 {
153 ws.send(new Int8Array([4, tanks.Sniper]));
154 ws.send(new Int8Array([4, tanks.Overseer]));
155 ws.send(new Int8Array([4, tanks.Necromancer]));
156 ACPacket[2] |= 0b00001000; // Slash to get to AC
157
158 for (let i = 0; i < 7; i++)
159 {
160 ws.send(new Int8Array([3, 8, 1])); // Bullet Damage
161 ws.send(new Int8Array([3, 10, 1])); // Bullet Pen.
162 ws.send(new Int8Array([3, 12, 1])); // Bullet Speed
163 ws.send(new Int8Array([3, 6, 1])); // Reload
164 }
165 }
166 realSend.call(ws, ACPacket);
167 if (ws.finishedLevelingUp)
168 {
169 ws.finishedLevelingUp = false;
170 ACPacket[2] &= 0b11110111;
171 }
172 if (!this.ACShooting) ws.packetSaving = true;
173 });
174
175 var flags = (data[1] & 0b01111111) + ((data[2] & 0b00001111) << 7);
176 if (this.queuedSlashes && (!this.blockSlash || (this.doHybrid && this.noFlank)))
177 {
178 data[2] |= 1 << (binaryFlags.indexOf("slash") - 7); // Modify the outgoing packet to include holding down \
179 this.queuedSlashes--;
180 this.blockSlash = true;
181 setTimeout((mainWS) => {mainWS.blockSlash = false}, this.doHybrid ? 0 : 250, this);
182 if (this.doHybrid)
183 {
184 switchTank = tanks.Triangle; // Switch to Triangle for the Triangle-Flank hybrid cycle
185 }
186 }
187 else if (this.queuedSlashes && (!this.blockSlash || (this.doLordSeer)))
188 {
189 data[2] |= 1 << (binaryFlags.indexOf("slash") - 7); // Modify the outgoing packet to include holding down \
190 this.queuedSlashes--;
191 this.blockSlash = true;
192 setTimeout((mainWS) => {mainWS.blockSlash = false}, this.doLordSeer ? 0 : 250, this);
193 if (this.doLordSeer)
194 {
195 switchTank = tanks.Overlord; // Switch to Triangle for the Triangle-Flank hybrid cycle
196 }
197 }
198 else if (this.queuedSlashes && (!this.blockSlash || (this.doTrapHybrid && this.noTrap)))
199 {
200 data[2] |= 1 << (binaryFlags.indexOf("slash") - 7); // Modify the outgoing packet to include holding down \
201 this.queuedSlashes--;
202 this.blockSlash = true;
203 setTimeout((mainWS) => {mainWS.blockSlash = false}, this.doTrapHybrid ? 0 : 250, this);
204 if (this.doTrapHybrid)
205 {
206 switchTank = tanks.GTrapper; // Switch to Triangle for the Triangle-Flank hybrid cycle
207 }
208 }
209 else if (this.doStalker)
210 {
211 switchTank = tanks.Stalker; // Switch to Stalker after pressing \ once from AC
212 this.doStalker = false;
213 }
214 else if (this.doNecro)
215 {
216 switchTank = tanks.Stalker;
217 this.doStalker = false;
218 }
219 else if (this.doLordSeer)
220 {
221 switchTank = tanks.Overlord;
222 }
223 else if (this.doTrapHybrid)
224 {
225 switchTank = tanks.GTrapper;
226 }
227 break;
228 }
229 case 2:
230 {
231 if (this.firstSpawn)
232 {
233 this.firstSpawn = false;
234
235 notification("Anti-Shadam Zone: ACTIVATED", 0, 255, 0, 5000);
236 notification(["Menu",
237 "/ - Triangle-Flank Hybrid",
238 "Shift + / - Triangle Only for Triangle-Flank Hybrid",
239 "` - Instant AC (From Basic/Sniper/Overseer/Necromancer)",
240 "Z - Instant Overlord (From Basic/Sniper/Overseer)",
241 "G - Overlord-Overseer Hybrid",
242 "Shift + f - Connect an AC bot",
243 "F - spawns an AC bot (after connecting of course) which shoots at your cursor/mouse",
244 "V - Instant AC (From Overlord)",
245 ", - AC -> Stalker",
246 "B - GTrapper-Trapper Hybrid",
247 "Shift + B - GTrapper-Trapper Hybrid",
248 ". - Stalker -> AC"], 255, 0, 0, 7000);
249 setTimeout(notification, 0, "", 0, 0, 0, 1, "adblock"); // Nobody cares. You make enough ad revenue anyway
250 }
251 break;
252 }
253 case 3:
254 {
255 break;
256 }
257 case 4: console.log(data[1])
258 {
259 if (this.doHybrid && data[1] === tanks.Triangle) // If just switched to Triangle from the Triangle-Flank hybrid cycle
260 {
261 this.queuedSlashes++;
262 }
263 else if (this.doLordSeer && data[1] === tanks.Overlord) // If we just switched to Overlord
264 {
265 this.queuedSlashes++;
266 }
267 else if (this.doTrapHybrid && data[1] === tanks.GTrapper) // If we just switched to Gunner Trapper
268 {
269 this.queuedSlashes++;
270 }
271 break;
272 }
273 case 5:
274 {
275 if (this.pingSpoof !== undefined) return;
276 break;
277 }
278 case 7:
279 {
280 return; // Some extensions get detected and it gets sent to the diep servers. Currently it appears they do nothing with it, but incase they ever start banning in the future, this script will protect you by preventing them from knowing if you used some shit extension that got detected.
281 // You may still get the "You're using a modified game client" popup, but the data will never get sent.
282 }
283 case 8: // If you died.
284 {
285 this.queuedSlashes = 0;
286 this.doHybrid = false;
287 this.doLordSeer = false;
288 this.doTrapHybrid = false;
289 break;
290 }
291 }
292
293 if (switchTank)
294 {
295 setTimeout((ws) => {ws.send(new Int8Array([4, switchTank]))}, this.noFlank ? 0 : 65, this);
296 setTimeout((ws) => {ws.send(new Int8Array([4, switchTank]))}, this.noTrap ? 0 : 0, this);
297 }
298 return realSend.call(this, data);
299 }
300
301 function notification(textOrArray, R, G, B, time, unique)
302 {
303 if (typeof R !== "number") R = 0; while (R < 0) R += 256; while (R > 255) R -= 256;
304 if (typeof G !== "number") G = 0; while (G < 0) G += 256; while (G > 255) G -= 256;
305 if (typeof B !== "number") B = 0; while (B < 0) B += 256; while (B > 255) B -= 256;
306 if (typeof time !== "number" || time < 0) time = 5000; // 0 for permanent, null for invalid/cancel
307 if (unique && typeof unique !== "string") unique = "";
308
309 if (typeof textOrArray !== "string") // If we passed an array of strings, we want to send each string as a seperate notification row
310 {
311 for (let n of textOrArray)
312 {
313 notification(n, R, G, B, time, unique);
314 }
315 return;
316 }
317
318 var packet = [3];
319
320 for (let i in textOrArray)
321 {
322 packet.push(textOrArray.charCodeAt(i));
323 }
324
325 packet.push(0);
326
327 /* Known colours:
328 Normal, e.g. adblock/predator/drones/godmode/kill/bossspawn/bosskill/domtaken/domsurrender: 0,0,0
329 Announcement, e.g. arena closed/team full: 255,0,0
330 Toggle, e.g. autofire/autospin: 0,0,255
331
332 Team related, e.g. win,domcontesting,domcontrolled,takedom
333 Blue Team: 0,178,225
334 Red Team: 241,78,84
335 Purple Team: 191,127,245
336 Green Team: 0,225,110
337 Yellow Team: 255,232,105
338 Grey Team: // Apparently fallen bosses can also capture a dominator, obviously this is extremely rare, but if I ever find the colour of their notification I will put it here
339 */
340
341 packet.push(B); // Little-endian format, RGB in reverse order
342 packet.push(G);
343 packet.push(R);
344
345 packet.push(0);
346
347 var view = new DataView(new ArrayBuffer(4));
348 view.setFloat32(0, time) // Time is in milliseconds
349 for (let i = 3; i >= 0; i--) // Little-endian format
350 {
351 packet.push(view.getInt8(i));
352 }
353
354 /* Known uniques:
355 Client sided:
356 autofire: Auto Fire: ON/OFF
357 autospin: Auto Spin: ON/OFF
358 gamepad_enabled: Gamepad enabled/disabled // untested, obtained from the client's .wasm.wasm file
359 adblock: You're using an adblocker, please consider disabling it to support the game
360 Server sided:
361 godmode_toggle: God mode: ON/OFF
362 cant_claim_info: Someone has already taken that tank
363 */
364
365 if (unique)
366 {
367 for (let i in unique)
368 {
369 packet.push(unique.charCodeAt(i));
370 }
371 }
372
373 packet.push(0);
374
375 mainWS.realRecv.call(mainWS, {data: packet});
376 }
377
378 function playerCount(players)
379 {
380 if (typeof players !== "number" || players < 0 || players > ~(1 << 31)) players = 0; // 0 for hide player count
381 mainWS.playerSpoof = players;
382
383 var packet = [];
384
385 var power = 0;
386 while (players > 0)
387 {
388 if (power > 0) packet[power - 1] += 128;
389 packet[power] = players / Math.pow(128, power) % 128;
390 players -= packet[power] % 128 * Math.pow(128, power);
391 power++;
392 }
393
394 packet.unshift(10);
395
396 mainWS.realRecv.call(mainWS, {data: packet});
397 }
398
399 document.addEventListener('keydown', function(event)
400 {
401 if (!document.getElementById("textInput").disabled) return; // Disable keybinds while we are typing into the textbox where you enter the name to spawn in with
402 var keyCode = event.keyCode || event.which;
403 switch (keyCode)
404 {
405 case 220: // Back slash
406 {
407 if (event.shiftKey)
408 {
409 mainWS.queuedSlashes = 0; // Just incase someone spammed something that queued slashes and they want to quickly cancel it
410 event.cancelBubble = true; // Prevent the \ ingamed
411 notification("Canceled switch tank", 0, 0, 255, 5000, "switchtank_info");
412 }
413 break;
414 }
415 case 191: // Forward slash
416 {
417 if (!event.shiftKey)
418 {
419 mainWS.queuedSlashes = 0;
420 mainWS.doHybrid = !mainWS.doHybrid;
421 notification("Triangle-Flank Hybrid: " + (mainWS.doHybrid ? "ON" : "OFF"), 0, 0, 255, 5000, "triangleflank_toggle");
422 if (mainWS.doHybrid)
423 {
424 mainWS.send(new Int8Array([4, tanks.FlankGuard]));
425 mainWS.send(new Int8Array([4, tanks.Triangle]));
426 }
427 }
428 else
429 {
430 mainWS.noFlank = !mainWS.noFlank;
431 notification("Triangle Only: " + (mainWS.noFlank ? "ON" : "OFF"), 0, 0, 255, 5000, "triangleflank_triangleonly");
432 }
433 break;
434 }
435 case 66: // B
436 {
437 if (!event.shiftKey)
438 {
439 mainWS.queuedSlashes = 0;
440 mainWS.doTrapHybrid = !mainWS.doTrapHybrid;
441 notification("GTrapper-Trapper Hybrid: " + (mainWS.doTrapHybrid ? "ON" : "OFF"), 0, 0, 255, 5000, "triangleflank_toggle");
442 if (mainWS.TrapHybrid)
443 {
444 mainWS.send(new Int8Array([4, tanks.Trapper]));
445 mainWS.send(new Int8Array([4, tanks.GTrapper]));
446 }
447 }
448 else
449 {
450 mainWS.noTrap = !mainWS.noTrap;
451 notification("GTrapper Only: " + (mainWS.noTrap ? "ON" : "OFF"), 0, 0, 255, 5000, "triangleflank_triangleonly")
452 }
453 break;
454 }
455 case 190: // Full stop
456 {
457 mainWS.queuedSlashes += 5; // Stalker -> AC requires five slashes
458 notification("Stalker -> AC", 0, 0, 255, 5000, "switchtank_info");
459 break;
460 }
461 case 188: // Comma
462 {
463 mainWS.queuedSlashes++;
464 mainWS.doStalker = true;
465 notification("AC -> Stalker", 0, 0, 255, 5000, "switchtank_info");
466 break;
467 }
468 case 71: // G
469 {
470 mainWS.queuedSlashes = 0
471 mainWS.doLordSeer = !mainWS.doLordSeer;
472 notification("Overlord-seer Hybrid: " + (mainWS.doLordSeer ? "ON" : "OFF"), 0, 0, 255, 5000, "triangleflank_toggle");
473 if (mainWS.doLordSeer)
474 {
475 mainWS.send(new Int8Array([4, tanks.Overseer]));
476 mainWS.send(new Int8Array([4, tanks.Overlord]));
477 }
478 break;
479 }
480 case 192: // Grave
481 { // This function works wether you're at basic, Sniper, Overseer or Necromancer, just make sure you're at level 45
482 mainWS.send(new Int8Array([4, tanks.Sniper]));
483 mainWS.send(new Int8Array([4, tanks.Overseer]));
484 mainWS.send(new Int8Array([4, tanks.Necromancer]));
485 mainWS.queuedSlashes++;
486 //ws.realRecv.call(ws, {data: [3, 65, 114, 101, 110, 97, 32, 99, 108, 111, 115, 101, 100, 58, 32, 78, 111, 32, 112, 108, 97, 121, 101, 114, 115, 32, 99, 97, 110, 32, 106, 111, 105, 110, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0]}); // Arena closed: No players can join
487 //ws.realRecv.call(ws, {data: [3, 84, 104, 101, 32, 116, 101, 97, 109, 32, 121, 111, 117, 32, 116, 114, 105, 101, 100, 32, 116, 111, 32, 106, 111, 105, 110, 32, 105, 115, 32, 102, 117, 108, 108, 0, 0, 0, -1, 0, 0, 64, 28, 70, 0]}); // The team you tried to join is full
488 //ws.realRecv.call(ws, {data: [3, 89, 111, 117, 39, 118, 101, 32, 107, 105, 108, 108, 101, 100, 32, 92, 0, 0, 0, 0, 0, 0, 64, 100, 69, 0]}); // You've killed /
489 //ws.realRecv.call(ws, {data: [3, 85, 115, 101, 32, 121, 111, 117, 114, 32, 114, 105, 103, 104, 116, 32, 109, 111, 117, 115, 101, 32, 98, 117, 116, 116, 111, 110, 32, 116, 111, 32, 108, 111, 111, 107, 32, 102, 117, 114, 116, 104, 101, 114, 32, 105, 110, 32, 116, 104, 101, 32, 100, 105, 114, 101, 99, 116, 105, 111, 110, 32, 121, 111, 117, 39, 114, 101, 32, 102, 97, 99, 105, 110, 103, 0, 0, 0, 0, 0, 0, 64, 28, 70, 0]}); // Use your right mouse button to look further in the direction you're facing
490 //ws.realRecv.call(ws, {data: [3, 85, 115, 101, 32, 121, 111, 117, 114, 32, 108, 101, 102, 116, 32, 109, 111, 117, 115, 101, 32, 98, 117, 116, 116, 111, 110, 32, 116, 111, 32, 99, 111, 110, 116, 114, 111, 108, 32, 116, 104, 101, 32, 100, 114, 111, 110, 101, 115, 0, 0, 0, 0, 0, 0, 64, 28, 70, 0]}); // Use your left mouse button to control the drones
491 //ws.realRecv.call(ws, {data: [3, 84, 104, 101, 32, 78, 87, 32, 68, 111, 109, 105, 110, 97, 116, 111, 114, 32, 105, 115, 32, 98, 101, 105, 110, 103, 32, 99, 111, 110, 116, 101, 115, 116, 101, 100, 0, 105, -24, -1, 0, 0, 64, 28, 70, 0]}); // The NW Dominator is being contested
492 //ws.realRecv.call(ws, {data: [3, 84, 104, 101, 32, 78, 87, 32, 68, 111, 109, 105, 110, 97, 116, 111, 114, 32, 105, 115, 32, 110, 111, 119, 32, 99, 111, 110, 116, 114, 111, 108, 108, 101, 100, 32, 98, 121, 32, 82, 69, 68, 0, 84, 78, -15, 0, 0, 64, 28, 70, 0]}); // The NW Dominator is now controlled by RED
493 //ws.realRecv.call(ws, {data: [3, 80, 114, 101, 115, 115, 32, 72, 32, 116, 111, 32, 116, 97, 107, 101, 32, 99, 111, 110, 116, 114, 111, 108, 32, 111, 102, 32, 116, 104, 101, 32, 100, 111, 109, 105, 110, 97, 116, 111, 114, 0, -31, -78, 0, 0, 0, 64, -100, 69, 0]}); // Press H to take control of the dominator
494 //ws.realRecv.call(ws, {data: [3, 83, 111, 109, 101, 111, 110, 101, 32, 104, 97, 115, 32, 97, 108, 114, 101, 97, 100, 121, 32, 116, 97, 107, 101, 110, 32, 116, 104, 97, 116, 32, 116, 97, 110, 107, 0, 0, 0, 0, 0, 0, 64, -100, 69, 99, 97, 110, 116, 95, 99, 108, 97, 105, 109, 95, 105, 110, 102, 112, 0]}); // Someone has already taken that tank // cant_claim_info
495 //ws.realRecv.call(ws, {data: [3, 80, 114, 101, 115, 115, 32, 72, 32, 116, 111, 32, 115, 117, 114, 114, 101, 110, 100, 101, 114, 32, 99, 111, 110, 116, 114, 111, 108, 32, 111, 102, 32, 116, 104, 101, 32, 116, 97, 110, 107, 0, 0, 0, 0, 0, 0, 96, 106, 70, 0]}); // Press H to surrender control of the tank
496 //ws.realRecv.call(ws, {data: [3, 82, 69, 68, 32, 72, 65, 83, 32, 87, 79, 78, 32, 84, 72, 69, 32, 71, 65, 77, 69, 33, 0, 84, 78, -15, 0, 0, 0, 0, 0, 0]}); // RED HAS WON THE GAME!
497 //ws.realRecv.call(ws, {data: [3, 66, 76, 85, 69, 32, 72, 65, 83, 32, 87, 79, 78, 32, 84, 72, 69, 32, 71, 65, 77, 69, 33, 0, -31, -78, 0, 0, 0, 0, 0, 0, 0]}); // BLUE HAS WON THE GAME!
498 //ws.realRecv.call(ws, {data: [3, 80, 85, 82, 80, 76, 69, 32, 72, 65, 83, 32, 87, 79, 78, 32, 84, 72, 69, 32, 71, 65, 77, 69, 33, 0, 245, 127, 191, 0, 0, 0, 0, 0, 0]}); // PURPLE HAS WON THE GAME!
499 //ws.realRecv.call(ws, {data: [3, 71, 82, 69, 69, 78, 32, 72, 65, 83, 32, 87, 79, 78, 32, 84, 72, 69, 32, 71, 65, 77, 69, 33, 0, 110, -31, 0, 0, 0, 0, 0, 0, 0]}); // GREEN HAS WON THE GAME!
500 //ws.realRecv.call(ws, {data: [3, 71, 111, 100, 32, 109, 111, 100, 101, 58, 32, 79, 78, 0, 0, 0, 0, 0, 0, 64, -100, 69, 103, 111, 100, 109, 111, 100, 101, 95, 116, 111, 103, 103, 108, 101, 0]}); // God mode: ON // godmode_toggle
501 //ws.realRecv.call(ws, {data: [3, 84, 104, 101, 32, 70, 97, 108, 108, 101, 110, 32, 79, 118, 101, 114, 108, 111, 114, 100, 32, 104, 97, 115, 32, 115, 112, 97, 119, 110, 101, 100, 33, 0, 0, 0, 0, 0, 0, 64, 28, 70, 0]}); // The Fallen Overlord has spawned!
502 //ws.realRecv.call(ws, {data: [3, 84, 104, 101, 32, 70, 97, 108, 108, 101, 110, 32, 79, 118, 101, 114, 108, 111, 114, 100, 32, 104, 97, 115, 32, 98, 101, 101, 110, 32, 100, 101, 102, 101, 97, 116, 101, 100, 32, 98, 121, 32, -58, -99, -61, -104, -58, -84, 32, -58, -92, -58, -90, -30, -104, -81, 33, 0, 0, 0, 0, 0, 0, 64, 28, 70, 0]}); // The Fallen Overlord has been defeated by <name>!
503 notification("Switching to Arena Closer", 255, 0, 0, 5000, "switchtank_info");
504 break;
505 }
506 case 90: // Z
507 {
508 mainWS.send(new Int8Array([4, tanks.Sniper]));
509 mainWS.send(new Int8Array([4, tanks.Overseer]));
510 mainWS.send(new Int8Array([4, tanks.Overlord]));
511 break;
512 }
513 case 86: // V
514 {
515 mainWS.queuedSlashes++;
516 setTimeout(function(){
517 mainWS.send(new Int8Array([4, tanks.Necromancer]));
518 }, 40)
519 //mainWS.send(new Int8Array([4, tanks.Necromancer]));
520 mainWS.queuedSlashes++;
521 notification("Switching to Arena Closer", 255, 0, 0, 5000, "switchtank_info")
522 break;
523 }
524 case 70: // F
525 {
526 if (event.shiftKey) // WARNING: DO NOT SPAM OR THE PROXY LIST API WILL TEMPORARILY BLOCK THE IP
527 {
528 if (ACBots.size === 0)
529 {
530 notification("Fetching proxy list", 255, 0, 0, 5000, "acbots_status");
531 GM_xmlhttpRequest({
532 method: "GET",
533 url: "google.com",
534 onload: (results) => {
535 try
536 {
537 var response = {data:[1]};
538 }
539 catch(e)
540 {
541 return notification("IP Temporarily Blocked", 255, 0, 0, 5000, "acbots_status");
542 }
543 notification("Arena Closer Bots: ACTIVATED", 255, 0, 0, 5000, "acbots_status"); // For now just 1 bot
544 for (let repeat = 0; repeat < 1; repeat++) // Set to 2 when proxying done
545 {
546 for (let proxy of [response.data.shift()]) // Set to response.data when proxying done
547 {
548 var ws = new WebSocket(mainWS.url);
549 ws.binaryType = "arraybuffer";
550 ws.dontRegister = true;
551 ws.onclose = function(event)
552 {
553 ACBots.delete(this);
554 }
555 ws.onopen = function(event)
556 {
557 ACBots.add(this);
558 this.send(mainWS.joinPacket);
559 }
560 }
561 }
562 }
563 });
564 }
565 else
566 {
567 notification("Arena Closer Bots: DEACTIVATED", 255, 0, 0, 5000, "acbots_status");
568 mainWS.ACShooting = false;
569 ACBots.forEach(ws => ws.close());
570 }
571 }
572 else if (ACBots.size > 0)
573 {
574 mainWS.ACShooting = !mainWS.ACShooting;
575 if (mainWS.ACShooting)
576 {
577 ACBots.forEach(bot => {
578 bot.packetSaving = false;
579 var spawnPacket = [2];
580 var spawnName = GM_getValue("ACName", "");
581 for (var i in spawnName) spawnPacket.push(spawnName.charCodeAt(i));
582 spawnPacket.push(0);
583 bot.send(new Int8Array(spawnPacket));
584
585 bot.leveledUpTimeout = setTimeout(() => {bot.finishedLevelingUp = true; bot.leveledUpTimeout = null;}, 2500);
586 });
587 }
588 else
589 {
590 ACBots.forEach(ws => {
591 clearTimeout(ws.leveledUpTimeout);
592 ws.leveledUpTimeout = null;
593 });
594 }
595 }
596 break;
597 }
598 default:
599 {
600 if (keyCode >= 48 && keyCode <= 57) // 0-9
601 {
602 var number = keyCode - 48; // Cannot use event.key and parseInt because shift etc modifiers will change the key, e.g. shift + 1 -> ! even though the keyCode is the same
603 var builds = GM_getValue("builds", new Array(10));
604
605 if (event.shiftKey)
606 {
607 event.cancelBubble = true;
608 if (!builds[number] || !builds[number].stats)
609 {
610 notification("There is no build in that slot", 0, 0, 255, 5000, "autobuild_loaded");
611 }
612 else
613 {
614 input.set_convar("game_stats_build", builds[number].stats);
615 notification("Loaded build" + (builds[number].name ? ": " + builds[number].name : " #" + number.toString()), 0, 0, 255, 5000, "autobuild_loaded");
616 }
617 }
618 else if (event.ctrlKey)
619 {
620 event.cancelBubble = true;
621 if (!builds[number]) builds[number] = {stats: "", name: ""};
622 var build = prompt("Enter the new stats for this autobuild slot", builds[number].stats);
623 if (build !== null)
624 {
625 var stats = "";
626 switch (build.length)
627 {
628 case 0:
629 build = "00000000";
630 case 8:
631 {
632 for (let stat in build)
633 {
634 stat = parseInt(stat);
635 let level = parseInt(build[stat], 16); // Smasher classes can have up to 10, use A for 10
636 if (isNaN(level)) return setTimeout(notification, 0, "Invalid build", 0, 0, 255, 5000, "autobuild_invalid"); // prompt() freezes the game and causes timing issues
637 stats += (stat + 1).toString().repeat(level); // It's 1-8, not 0-7
638 }
639 break;
640 }
641 default:
642 return setTimeout(notification, 0, "Invalid build", 0, 0, 255, 5000, "autobuild_invalid");
643 }
644 builds[number].stats = stats;
645 GM_setValue("builds", builds);
646 setTimeout(notification, 0, (stats ? "Set" : "Cleared") + " the build" + (builds[number].name ? ": " + builds[number].name : " at slot #" + number.toString()), 0, 0, 255, 5000, "autobuild_set_" + number.toString());
647 }
648 }
649 else if (event.altKey)
650 {
651 event.cancelBubble = true;
652 if (!builds[number]) builds[number] = {stats: "", name: ""};
653 var name = prompt("Enter the new name for this autobuild slot", builds[number].name);
654 if (name !== null)
655 {
656 builds[number].name = name;
657 GM_setValue("builds", builds);
658 setTimeout(notification, 0, (name ? "Set" : "Cleared") + " the name for slot #" + number.toString() + (builds[number].name ? ": " + builds[number].name : ""), 0, 0, 255, 5000, "autobuild_setname_" + number.toString());
659 }
660 }
661 }
662 break;
663 }
664 }
665 });
666
667 GM_registerMenuCommand('Spoof ping', function() { // Extremely shit
668 var ping = prompt("Enter the ping", mainWS.pingSpoof);
669 if (typeof ping === "string")
670 {
671 if (ping.length === 0)
672 {
673 clearInterval(mainWS.pingSpoofInterval);
674 if (mainWS.pingSpoof !== undefined) mainWS.send("\x05"); // Restart the loop
675 delete mainWS.pingSpoof;
676 delete mainWS.pingSpoofInterval;
677 setTimeout(notification, 0, "Ping Spoof: OFF", 0, 0, 255, 5000, "pingspoof_ping");
678 }
679 else
680 {
681 ping = parseFloat(ping);
682 if (typeof ping === "number")
683 {
684 clearInterval(mainWS.pingSpoofInterval);
685 mainWS.pingSpoof = ping;
686 mainWS.pingSpoofInterval = setInterval((mainWS) => {mainWS.realRecv.call(mainWS, {data: [5]}); realSend.call(mainWS, "\x05")}, mainWS.pingSpoof, mainWS);
687 setTimeout(notification, 0, "Ping Spoof: " + mainWS.pingSpoof.toString(), 0, 0, 255, 5000, "pingspoof_ping");
688 }
689 else
690 {
691 setTimeout(notification, 0, "Invalid number", 0, 0, 255, 5000, "pingspoof_invalid");
692 }
693 }
694 }
695 });
696
697 GM_registerMenuCommand('Spoof player count', function() {
698 var players = prompt("Enter the player count", mainWS.playerSpoof || mainWS.playerCount);
699 if (typeof players === "string")
700 {
701 if (players.length === 0)
702 {
703 playerCount(mainWS.playerCount);
704 delete mainWS.playerSpoof;
705 setTimeout(notification, 0, "Player Count Spoof: OFF", 0, 0, 255, 5000, "playercountspoof_players");
706 }
707 else
708 {
709 players = parseInt(players, 10);
710 if (typeof players === "number")
711 {
712 playerCount(players);
713 setTimeout(notification, 0, "Player Count Spoof: " + mainWS.playerSpoof.toString(), 0, 0, 255, 5000, "playercountspoof_players");
714 }
715 else
716 {
717 setTimeout(notification, 0, "Invalid number", 0, 0, 255, 5000, "playercountspoof_invalid");
718 }
719 }
720 }
721 });
722
723 GM_registerMenuCommand('Set AC bots name', function() {
724 var name = prompt("Enter the name for the AC bots to spawn in with", GM_getValue("ACName", ""));
725 if (typeof name === "string")
726 {
727 GM_setValue("ACName", name);
728 setTimeout(notification, 0, name === "" ? "Emptied name" : "Set name to: " + name, 0, 0, 255, 5000, "acbots_name"); // prompt() freezes the game and causes timing issues
729 if ((new TextEncoder('utf-8').encode(name)).length > 15) setTimeout(notification, 0, "Warning: Name is too long", 255, 128, 0, 5000, "acbots_name_warning"); // The script won't stop you from trying, but the server automatically truncates the name to 15 bytes
730 }
731 });
732})();