Slither.io Trainer Hack

A Trainer to help you play on slither.io.Reveal the positions of worms close to you.Shows where's the closest and biggest food near you, and can also create 2 danger zone circles around you.

当前为 2021-11-21 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Slither.io Trainer Hack
  3. // @namespace slithertrainer
  4. // @version 1.2
  5. // @description A Trainer to help you play on slither.io.Reveal the positions of worms close to you.Shows where's the closest and biggest food near you, and can also create 2 danger zone circles around you.
  6. // @author hacker09
  7. // @match http://slither.io/
  8. // @license Mozilla Public License Version 2.0
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. const TARGET_FPS = 30;
  13.  
  14. window.log = function() {};
  15.  
  16. window.getSnakeLength = function() {
  17. return (Math.floor(
  18. 150 *
  19. (window.fpsls[window.snake.sct] + window.snake.fam / window.fmlts[window.snake.sct] - 1) -
  20. 50) / 10);
  21. };
  22. window.getSnakeWidth = function(sc) {
  23. if (sc === undefined) sc = window.snake.sc;
  24. return sc * 29.0;
  25. };
  26.  
  27. var canvas = window.canvas = (function() {
  28. return {
  29. // Ratio of screen size divided by canvas size.
  30. canvasRatio: {
  31. x: window.mc.width / window.ww,
  32. y: window.mc.height / window.hh
  33. },
  34.  
  35. // Convert snake-relative coordinates to absolute screen coordinates.
  36. mouseToScreen: function(point) {
  37. var screenX = point.x + (window.ww / 2);
  38. var screenY = point.y + (window.hh / 2);
  39. return { x: screenX, y: screenY };
  40. },
  41.  
  42. // Convert screen coordinates to canvas coordinates.
  43. screenToCanvas: function(point) {
  44. var canvasX = window.csc *
  45. (point.x * canvas.canvasRatio.x) - parseInt(window.mc.style.left);
  46. var canvasY = window.csc *
  47. (point.y * canvas.canvasRatio.y) - parseInt(window.mc.style.top);
  48. return { x: canvasX, y: canvasY };
  49. },
  50.  
  51. // Convert map coordinates to mouse coordinates.
  52. mapToMouse: function(point) {
  53. var mouseX = (point.x - window.snake.xx) * window.gsc;
  54. var mouseY = (point.y - window.snake.yy) * window.gsc;
  55. return { x: mouseX, y: mouseY };
  56. },
  57.  
  58. // Map cordinates to Canvas cordinate shortcut
  59. mapToCanvas: function(point) {
  60. var c = canvas.mapToMouse(point);
  61. c = canvas.mouseToScreen(c);
  62. c = canvas.screenToCanvas(c);
  63. return c;
  64. },
  65.  
  66. // Map to Canvas coordinate conversion for drawing circles.
  67. // Radius also needs to scale by .gsc
  68. circleMapToCanvas: function(circle) {
  69. var newCircle = canvas.mapToCanvas(circle);
  70. return canvas.circle(
  71. newCircle.x,
  72. newCircle.y,
  73. circle.radius * window.gsc
  74. );
  75. },
  76.  
  77. // Constructor for point type
  78. point: function(x, y) {
  79. var p = {
  80. x: Math.round(x),
  81. y: Math.round(y)
  82. };
  83.  
  84. return p;
  85. },
  86.  
  87. // Constructor for rect type
  88. rect: function(x, y, w, h) {
  89. var r = {
  90. x: Math.round(x),
  91. y: Math.round(y),
  92. width: Math.round(w),
  93. height: Math.round(h)
  94. };
  95.  
  96. return r;
  97. },
  98.  
  99. // Constructor for circle type
  100. circle: function(x, y, r) {
  101. var c = {
  102. x: Math.round(x),
  103. y: Math.round(y),
  104. radius: Math.round(r)
  105. };
  106.  
  107. return c;
  108. },
  109.  
  110. // Fast atan2
  111. fastAtan2: function(y, x) {
  112. const QPI = Math.PI / 4;
  113. const TQPI = 3 * Math.PI / 4;
  114. var r = 0.0;
  115. var angle = 0.0;
  116. var abs_y = Math.abs(y) + 1e-10;
  117. if (x < 0) {
  118. r = (x + abs_y) / (abs_y - x);
  119. angle = TQPI;
  120. } else {
  121. r = (x - abs_y) / (x + abs_y);
  122. angle = QPI;
  123. }
  124. angle += (0.1963 * r * r - 0.9817) * r;
  125. if (y < 0) {
  126. return -angle;
  127. }
  128.  
  129. return angle;
  130. },
  131.  
  132. // Adjusts zoom in response to the mouse wheel.
  133. setZoom: function(e) {
  134. // Scaling ratio
  135. if (window.gsc) {
  136. window.gsc *= Math.pow(0.9, e.wheelDelta / -120 || e.detail / 2 || 0);
  137. window.desired_gsc = window.gsc;
  138. }
  139. },
  140.  
  141. // Restores zoom to the default value.
  142. resetZoom: function() {
  143. window.gsc = 0.9;
  144. window.desired_gsc = 0.9;
  145. },
  146.  
  147. // Maintains Zoom
  148. maintainZoom: function() {
  149. if (window.desired_gsc !== undefined) {
  150. window.gsc = window.desired_gsc;
  151. }
  152. },
  153.  
  154. // Sets background to the given image URL.
  155. // Defaults to slither.io's own background.
  156. setBackground: function(url) {
  157. url = typeof url !== 'undefined' ? url : '/s/bg45.jpg';
  158. window.ii.src = url;
  159. },
  160.  
  161. // Draw a rectangle on the canvas.
  162. drawRect: function(rect, color, fill, alpha) {
  163. if (alpha === undefined) alpha = 1;
  164.  
  165. var context = window.mc.getContext('2d');
  166. var lc = canvas.mapToCanvas({x: rect.x, y: rect.y});
  167.  
  168. context.save();
  169. context.globalAlpha = alpha;
  170. context.strokeStyle = color;
  171. context.rect(lc.x, lc.y, rect.width * window.gsc, rect.height * window.gsc);
  172. context.stroke();
  173. if (fill) {
  174. context.fillStyle = color;
  175. context.fill();
  176. }
  177. context.restore();
  178. },
  179.  
  180. // Draw a circle on the canvas.
  181. drawCircle: function(circle, color, fill, alpha) {
  182. if (alpha === undefined) alpha = 1;
  183. if (circle.radius === undefined) circle.radius = 5;
  184.  
  185. var context = window.mc.getContext('2d');
  186. var drawCircle = canvas.circleMapToCanvas(circle);
  187.  
  188. context.save();
  189. context.globalAlpha = alpha;
  190. context.beginPath();
  191. context.strokeStyle = color;
  192. context.arc(drawCircle.x, drawCircle.y, drawCircle.radius, 0, Math.PI * 2);
  193. context.stroke();
  194. if (fill) {
  195. context.fillStyle = color;
  196. context.fill();
  197. }
  198. context.restore();
  199. },
  200.  
  201. // Draw an angle.
  202. // @param {number} start -- where to start the angle
  203. // @param {number} angle -- width of the angle
  204. // @param {bool} danger -- green if false, red if true
  205. drawAngle: function(start, angle, color, fill, alpha) {
  206. if (alpha === undefined) alpha = 0.6;
  207.  
  208. var context = window.mc.getContext('2d');
  209.  
  210. context.save();
  211. context.globalAlpha = alpha;
  212. context.beginPath();
  213. context.moveTo(window.mc.width / 2, window.mc.height / 2);
  214. context.arc(window.mc.width / 2, window.mc.height / 2, window.gsc * 100, start, angle);
  215. context.lineTo(window.mc.width / 2, window.mc.height / 2);
  216. context.closePath();
  217. context.stroke();
  218. if (fill) {
  219. context.fillStyle = color;
  220. context.fill();
  221. }
  222. context.restore();
  223. },
  224.  
  225. // Draw a line on the canvas.
  226. drawLine: function(p1, p2, color, width) {
  227. if (width === undefined) width = 5;
  228.  
  229. var context = window.mc.getContext('2d');
  230. var dp1 = canvas.mapToCanvas(p1);
  231. var dp2 = canvas.mapToCanvas(p2);
  232.  
  233. context.save();
  234. context.beginPath();
  235. context.lineWidth = width * window.gsc;
  236. context.strokeStyle = color;
  237. context.moveTo(dp1.x, dp1.y);
  238. context.lineTo(dp2.x, dp2.y);
  239. context.stroke();
  240. context.restore();
  241. },
  242.  
  243. // Given the start and end of a line, is point left.
  244. isLeft: function(start, end, point) {
  245. return ((end.x - start.x) * (point.y - start.y) -
  246. (end.y - start.y) * (point.x - start.x)) > 0;
  247.  
  248. },
  249.  
  250. // Get distance squared
  251. getDistance2: function(x1, y1, x2, y2) {
  252. var distance2 = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
  253. return distance2;
  254. },
  255.  
  256. getDistance2FromSnake: function(point) {
  257. point.distance = canvas.getDistance2(window.snake.xx, window.snake.yy,
  258. point.xx, point.yy);
  259. return point;
  260. },
  261.  
  262. // Check if point in Rect
  263. pointInRect: function(point, rect) {
  264. if (rect.x <= point.x && rect.y <= point.y &&
  265. rect.x + rect.width >= point.x && rect.y + rect.height >= point.y) {
  266. return true;
  267. }
  268. return false;
  269. },
  270.  
  271. // Check if circles intersect
  272. circleIntersect: function(circle1, circle2) {
  273. var bothRadii = circle1.radius + circle2.radius;
  274.  
  275. // Pretends the circles are squares for a quick collision check.
  276. // If it collides, do the more expensive circle check.
  277. if (circle1.x + bothRadii > circle2.x &&
  278. circle1.y + bothRadii > circle2.y &&
  279. circle1.x < circle2.x + bothRadii &&
  280. circle1.y < circle2.y + bothRadii) {
  281.  
  282. var distance2 = canvas.getDistance2(circle1.x, circle1.y, circle2.x, circle2.y);
  283.  
  284. if (distance2 < bothRadii * bothRadii) {
  285. if (window.visualDebugging) {
  286. var collisionPointCircle = canvas.circle(
  287. ((circle1.x * circle2.radius) + (circle2.x * circle1.radius)) /
  288. bothRadii,
  289. ((circle1.y * circle2.radius) + (circle2.y * circle1.radius)) /
  290. bothRadii,
  291. 5
  292. );
  293. canvas.drawCircle(circle2, 'red', true);
  294. canvas.drawCircle(collisionPointCircle, 'cyan', true);
  295. }
  296. return true;
  297. }
  298. }
  299. return false;
  300. }
  301. };
  302. })();
  303.  
  304. var bot = window.bot = (function() {
  305. return {
  306. isBotRunning: false,
  307. isBotEnabled: true,
  308. lookForFood: false,
  309. collisionPoints: [],
  310. collisionAngles: [],
  311. scores: [],
  312. foodTimeout: undefined,
  313. sectorBoxSide: 0,
  314. defaultAccel: 0,
  315. sectorBox: {},
  316. currentFood: {},
  317. MID_X: 0,
  318. MID_Y: 0,
  319. MAP_R: 0,
  320.  
  321. quickRespawn: function() {
  322. window.dead_mtm = 0;
  323. window.login_fr = 0;
  324.  
  325. bot.isBotRunning = false;
  326. window.forcing = true;
  327. window.connect();
  328. window.forcing = false;
  329. },
  330.  
  331. // Avoid headPoint
  332. avoidHeadPoint: function(collisionPoint) {},
  333.  
  334. // Avoid collison point by ang
  335. // ang radians <= Math.PI (180deg)
  336. avoidCollisionPoint: function(collisionPoint, ang) {
  337. },
  338.  
  339. // Sorting by property 'distance'
  340. sortDistance: function(a, b) {
  341. return a.distance - b.distance;
  342. },
  343.  
  344. // get collision angle index, expects angle +/i 0 to Math.PI
  345. getAngleIndex: function(angle) {
  346. const ARCSIZE = Math.PI / 4;
  347. var index;
  348.  
  349. if (angle < 0) {
  350. angle += 2 * Math.PI;
  351. }
  352.  
  353. index = Math.round(angle * (1 / ARCSIZE));
  354.  
  355. if (index === (2 * Math.PI) / ARCSIZE) {
  356. return 0;
  357. }
  358. return index;
  359. },
  360.  
  361. // Add to collisionAngles if distance is closer
  362. addCollisionAngle: function(sp) {
  363. var ang = canvas.fastAtan2(
  364. Math.round(sp.yy - window.snake.yy),
  365. Math.round(sp.xx - window.snake.xx));
  366. var aIndex = bot.getAngleIndex(ang);
  367.  
  368. var actualDistance = Math.round(
  369. sp.distance - (Math.pow(window.getSnakeWidth(window.snakes[sp.snake].sc), 2) / 2));
  370.  
  371. if (bot.collisionAngles[aIndex] === undefined) {
  372. bot.collisionAngles[aIndex] = {
  373. x: Math.round(sp.xx),
  374. y: Math.round(sp.yy),
  375. ang: ang,
  376. snake: sp.snake,
  377. distance: actualDistance
  378. };
  379. } else if (bot.collisionAngles[aIndex].distance > sp.distance) {
  380. bot.collisionAngles[aIndex].x = Math.round(sp.xx);
  381. bot.collisionAngles[aIndex].y = Math.round(sp.yy);
  382. bot.collisionAngles[aIndex].ang = ang;
  383. bot.collisionAngles[aIndex].snake = sp.snake;
  384. bot.collisionAngles[aIndex].distance = actualDistance;
  385. }
  386. },
  387.  
  388. // Get closest collision point per snake.
  389. getCollisionPoints: function() {
  390. var scPoint;
  391.  
  392. bot.collisionPoints = [];
  393. bot.collisionAngles = [];
  394.  
  395.  
  396. for (var snake = 0, ls = window.snakes.length; snake < ls; snake++) {
  397. scPoint = undefined;
  398.  
  399. if (window.snakes[snake].id !== window.snake.id &&
  400. window.snakes[snake].alive_amt === 1) {
  401. if (window.visualDebugging) {
  402. canvas.drawCircle(canvas.circle(
  403. window.snakes[snake].xx,
  404. window.snakes[snake].yy,
  405. window.getSnakeWidth(window.snakes[snake].sc) / 2),
  406. 'red', false);
  407. }
  408. scPoint = {
  409. xx: window.snakes[snake].xx,
  410. yy: window.snakes[snake].yy,
  411. snake: snake
  412. };
  413. canvas.getDistance2FromSnake(scPoint);
  414. bot.addCollisionAngle(scPoint);
  415.  
  416. for (var pts = 0, lp = window.snakes[snake].pts.length; pts < lp; pts++) {
  417. if (!window.snakes[snake].pts[pts].dying &&
  418. canvas.pointInRect(
  419. {x: window.snakes[snake].pts[pts].xx,
  420. y: window.snakes[snake].pts[pts].yy}, bot.sectorBox)
  421. ) {
  422. var collisionPoint = {
  423. xx: window.snakes[snake].pts[pts].xx,
  424. yy: window.snakes[snake].pts[pts].yy,
  425. snake: snake
  426. };
  427.  
  428. if (window.visualDebugging && true === false) {
  429. canvas.drawCircle(canvas.circle(
  430. collisionPoint.xx,
  431. collisionPoint.yy,
  432. window.getSnakeWidth(window.snakes[snake].sc) / 2),
  433. '#00FF00', false);
  434. }
  435.  
  436. canvas.getDistance2FromSnake(collisionPoint);
  437. bot.addCollisionAngle(collisionPoint);
  438.  
  439. if (scPoint === undefined ||
  440. scPoint.distance > collisionPoint.distance) {
  441. scPoint = collisionPoint;
  442. }
  443. }
  444. }
  445. }
  446. if (scPoint !== undefined) {
  447. bot.collisionPoints.push(scPoint);
  448. if (window.visualDebugging) {
  449. canvas.drawCircle(canvas.circle(
  450. scPoint.xx,
  451. scPoint.yy,
  452. window.getSnakeWidth(window.snakes[scPoint.snake].sc) / 2
  453. ), 'red', false);
  454. }
  455. }
  456. }
  457.  
  458. if (canvas.getDistance2(bot.MID_X, bot.MID_Y, window.snake.xx, window.snake.yy) >
  459. Math.pow(bot.MAP_R - 1000, 2)) {
  460. var midAng = canvas.fastAtan2(
  461. window.snake.yy - bot.MID_X, window.snake.xx - bot.MID_Y);
  462. scPoint = {
  463. xx: bot.MID_X + bot.MAP_R * Math.cos(midAng),
  464. yy: bot.MID_Y + bot.MAP_R * Math.sin(midAng),
  465. snake: -1
  466. };
  467. bot.collisionPoints.push(scPoint);
  468. if (window.visualDebugging) {
  469. canvas.drawCircle(canvas.circle(
  470. scPoint.xx,
  471. scPoint.yy,
  472. window.getSnakeWidth(1) * 5
  473. ), 'yellow', false);
  474. }
  475. }
  476.  
  477.  
  478. bot.collisionPoints.sort(bot.sortDistance);
  479. if (window.visualDebugging) {
  480. for (var i = 0; i < bot.collisionAngles.length; i++) {
  481. if (bot.collisionAngles[i] !== undefined) {
  482. canvas.drawLine(
  483. {x: window.snake.xx, y: window.snake.yy},
  484. {x: bot.collisionAngles[i].x, y: bot.collisionAngles[i].y},
  485. '#99ffcc', 2);
  486. }
  487. }
  488. }
  489. },
  490.  
  491. // Checks to see if you are going to collide with anything in the collision detection radius
  492. checkCollision: function(r) {
  493. if (!window.collisionDetection) return false;
  494.  
  495. r = Number(r);
  496. var xx = Number(window.snake.xx.toFixed(3));
  497. var yy = Number(window.snake.yy.toFixed(3));
  498.  
  499. window.snake.cos = Math.cos(window.snake.ang).toFixed(3);
  500. window.snake.sin = Math.sin(window.snake.ang).toFixed(3);
  501.  
  502. const speedMult = window.snake.sp / 5.78;
  503. const widthMult = window.getSnakeWidth();
  504.  
  505. var headCircle = canvas.circle(
  506. xx, yy,
  507. speedMult * r / 2 * widthMult / 2
  508. );
  509.  
  510. var fullHeadCircle = canvas.circle(
  511. xx, yy,
  512. r * widthMult / 2
  513. );
  514.  
  515. var sidecircle_r = canvas.circle(
  516. window.snake.lnp.xx -
  517. ((window.snake.lnp.yy + window.snake.sin * window.getSnakeWidth()) -
  518. window.snake.lnp.yy),
  519. window.snake.lnp.yy +
  520. ((window.snake.lnp.xx + window.snake.cos * window.getSnakeWidth()) -
  521. window.snake.lnp.xx),
  522. window.getSnakeWidth() * speedMult
  523. );
  524.  
  525. var sidecircle_l = canvas.circle(
  526. window.snake.lnp.xx +
  527. ((window.snake.lnp.yy + window.snake.sin * window.getSnakeWidth()) -
  528. window.snake.lnp.yy),
  529. window.snake.lnp.yy -
  530. ((window.snake.lnp.xx + window.snake.cos * window.getSnakeWidth()) -
  531. window.snake.lnp.xx),
  532. window.getSnakeWidth() * speedMult
  533. );
  534.  
  535. window.snake.sidecircle_r = sidecircle_r;
  536. window.snake.sidecircle_l = sidecircle_l;
  537.  
  538. if (window.visualDebugging) {
  539. canvas.drawCircle(fullHeadCircle, 'red');
  540. canvas.drawCircle(headCircle, 'blue', false);
  541. // canvas.drawCircle(sidecircle_r, 'orange', true, 0.3);
  542. // canvas.drawCircle(sidecircle_l, 'orange', true, 0.3);
  543. }
  544.  
  545. bot.getCollisionPoints();
  546. if (bot.collisionPoints.length === 0) return false;
  547.  
  548. for (var i = 0; i < bot.collisionPoints.length; i++) {
  549. // -1 snake is special case for non snake object.
  550. var colR = bot.collisionPoints[i].snake === -1 ? window.getSnakeWidth(1) * 5 :
  551. window.getSnakeWidth(window.snakes[bot.collisionPoints[i].snake].sc) / 2;
  552.  
  553. var collisionCircle = canvas.circle(
  554. bot.collisionPoints[i].xx,
  555. bot.collisionPoints[i].yy,
  556. colR
  557. );
  558.  
  559. if (canvas.circleIntersect(headCircle, collisionCircle)) {
  560. window.setAcceleration(bot.defaultAccel);
  561. bot.avoidCollisionPoint(bot.collisionPoints[i]);
  562. return true;
  563. }
  564.  
  565. if (bot.collisionPoints[i].snake !== -1) {
  566. var eHeadCircle = canvas.circle(
  567. window.snakes[bot.collisionPoints[i].snake].xx,
  568. window.snakes[bot.collisionPoints[i].snake].yy,
  569. colR
  570. );
  571. }
  572. }
  573. window.setAcceleration(bot.defaultAccel);
  574. return false;
  575. },
  576.  
  577. sortScore: function(a, b) {
  578. return b.score - a.score;
  579. },
  580.  
  581. // 2.546 ~ 1 / (Math.PI / 8) - round angle difference up to nearest 22.5 degrees.
  582. // Round food up to nearest 5, square for distance^2
  583. scoreFood: function(f) {
  584. f.score = Math.pow(Math.ceil(f.sz / 5) * 5, 2) /
  585. f.distance / (Math.ceil(f.da * 2.546) / 2.546);
  586. },
  587.  
  588. computeFoodGoal: function() {
  589. var foodClusters = [];
  590. var foodGetIndex = [];
  591. var fi = 0;
  592. var sw = window.getSnakeWidth();
  593.  
  594. for (var i = 0; i < window.foods.length && window.foods[i] !== null; i++) {
  595. var a;
  596. var da;
  597. var distance;
  598. var sang = window.snake.ehang;
  599. var f = window.foods[i];
  600.  
  601. if (!f.eaten &&
  602. !(
  603. canvas.circleIntersect(
  604. canvas.circle(f.xx, f.yy, 2),
  605. window.snake.sidecircle_l) ||
  606. canvas.circleIntersect(
  607. canvas.circle(f.xx, f.yy, 2),
  608. window.snake.sidecircle_r))) {
  609.  
  610. var cx = Math.round(Math.round(f.xx / sw) * sw);
  611. var cy = Math.round(Math.round(f.yy / sw) * sw);
  612. var csz = Math.round(f.sz);
  613.  
  614. if (foodGetIndex[cx + '|' + cy] === undefined) {
  615. foodGetIndex[cx + '|' + cy] = fi;
  616. a = canvas.fastAtan2(cy - window.snake.yy, cx - window.snake.xx);
  617. da = Math.min(
  618. (2 * Math.PI) - Math.abs(a - sang), Math.abs(a - sang));
  619. distance = Math.round(
  620. canvas.getDistance2(cx, cy, window.snake.xx, window.snake.yy));
  621. foodClusters[fi] = {
  622. x: cx, y: cy, a: a, da: da, sz: csz, distance: distance, score: 0.0 };
  623. fi++;
  624. } else {
  625. foodClusters[foodGetIndex[cx + '|' + cy]].sz += csz;
  626. }
  627. }
  628. }
  629.  
  630. foodClusters.forEach(bot.scoreFood);
  631. foodClusters.sort(bot.sortScore);
  632.  
  633. for (i = 0; i < foodClusters.length; i++) {
  634. var aIndex = bot.getAngleIndex(foodClusters[i].a);
  635. if (bot.collisionAngles[aIndex] === undefined ||
  636. (bot.collisionAngles[aIndex].distance - Math.pow(window.getSnakeWidth(), 2) >
  637. foodClusters[i].distance && foodClusters[i].sz > 10)
  638. ) {
  639. bot.currentFood = foodClusters[i];
  640. return;
  641. }
  642. }
  643. bot.currentFood = {x: bot.MID_X, y: bot.MID_Y};
  644. },
  645.  
  646. foodAccel: function() {
  647. var aIndex = 0;
  648.  
  649. if (bot.currentFood && bot.currentFood.sz > 60) {
  650. aIndex = bot.getAngleIndex(bot.currentFood.a);
  651.  
  652. if (
  653. bot.collisionAngles[aIndex] && bot.collisionAngles[aIndex].distance >
  654. bot.currentFood.distance * 2) {
  655. return 1;
  656. }
  657.  
  658. if (bot.collisionAngles[aIndex] === undefined) {
  659. return 1;
  660. }
  661. }
  662.  
  663. return bot.defaultAccel;
  664. },
  665.  
  666. // Loop version of collision check
  667. collisionLoop: function() {
  668. if (bot.checkCollision(window.collisionRadiusMultiplier)) {
  669. bot.lookForFood = false;
  670. if (bot.foodTimeout) {
  671. window.clearTimeout(bot.foodTimeout);
  672. bot.foodTimeout = window.setTimeout(bot.foodTimer, 1000 / TARGET_FPS * 4);
  673. }
  674. } else {
  675. bot.lookForFood = true;
  676. if (bot.foodTimeout === undefined) {
  677. bot.foodTimeout = window.setTimeout(bot.foodTimer, 1000 / TARGET_FPS * 4);
  678. }
  679. window.setAcceleration(bot.foodAccel());
  680. }
  681. },
  682.  
  683. // Timer version of food check
  684. foodTimer: function() {
  685. if (window.playing && bot.lookForFood &&
  686. window.snake !== null && window.snake.alive_amt === 1) {
  687. bot.computeFoodGoal();
  688. window.goalCoordinates = bot.currentFood;
  689. }
  690. bot.foodTimeout = undefined;
  691. }
  692. };
  693. })();
  694.  
  695. var userInterface = window.userInterface = (function() {
  696. // Save the original slither.io functions so we can modify them, or reenable them later.
  697. var original_keydown = document.onkeydown;
  698. var original_onmouseDown = window.onmousedown;
  699. var original_oef = window.oef;
  700. var original_redraw = window.redraw;
  701. var original_onmousemove = window.onmousemove;
  702.  
  703. window.oef = function() {};
  704. window.redraw = function() {};
  705.  
  706. return {
  707. overlays: {},
  708.  
  709. initOverlays: function() {
  710. var botOverlay = document.createElement('div');
  711. botOverlay.style.position = 'fixed';
  712. botOverlay.style.right = '5px';
  713. botOverlay.style.bottom = '112px';
  714. botOverlay.style.width = '150px';
  715. botOverlay.style.height = '85px';
  716. // botOverlay.style.background = 'rgba(0, 0, 0, 0.5)';
  717. botOverlay.style.color = '#C0C0C0';
  718. botOverlay.style.fontFamily = 'Consolas, Verdana';
  719. botOverlay.style.zIndex = 999;
  720. botOverlay.style.fontSize = '14px';
  721. botOverlay.style.padding = '5px';
  722. botOverlay.style.borderRadius = '5px';
  723. botOverlay.className = 'nsi';
  724. document.body.appendChild(botOverlay);
  725.  
  726. var serverOverlay = document.createElement('div');
  727. serverOverlay.style.position = 'fixed';
  728. serverOverlay.style.right = '5px';
  729. serverOverlay.style.bottom = '5px';
  730. serverOverlay.style.width = '160px';
  731. serverOverlay.style.height = '14px';
  732. serverOverlay.style.color = '#C0C0C0';
  733. serverOverlay.style.fontFamily = 'Consolas, Verdana';
  734. serverOverlay.style.zIndex = 999;
  735. serverOverlay.style.fontSize = '14px';
  736. serverOverlay.className = 'nsi';
  737. document.body.appendChild(serverOverlay);
  738. var prefOverlay = document.createElement('div');
  739. prefOverlay.style.position = 'fixed';
  740. prefOverlay.style.left = '10px';
  741. prefOverlay.style.top = '75px';
  742. prefOverlay.style.width = '260px';
  743. prefOverlay.style.height = '210px';
  744. // prefOverlay.style.background = 'rgba(0, 0, 0, 0.5)';
  745. prefOverlay.style.color = '#C0C0C0';
  746. prefOverlay.style.fontFamily = 'Consolas, Verdana';
  747. prefOverlay.style.zIndex = 999;
  748. prefOverlay.style.fontSize = '14px';
  749. prefOverlay.style.padding = '5px';
  750. prefOverlay.style.borderRadius = '5px';
  751. prefOverlay.className = 'nsi';
  752. document.body.appendChild(prefOverlay);
  753.  
  754. var statsOverlay = document.createElement('div');
  755. statsOverlay.style.position = 'fixed';
  756. statsOverlay.style.left = '10px';
  757. statsOverlay.style.top = '295px';
  758. statsOverlay.style.width = '140px';
  759. statsOverlay.style.height = '210px';
  760. // statsOverlay.style.background = 'rgba(0, 0, 0, 0.5)';
  761. statsOverlay.style.color = '#C0C0C0';
  762. statsOverlay.style.fontFamily = 'Consolas, Verdana';
  763. statsOverlay.style.zIndex = 998;
  764. statsOverlay.style.fontSize = '14px';
  765. statsOverlay.style.padding = '5px';
  766. statsOverlay.style.borderRadius = '5px';
  767. statsOverlay.className = 'nsi';
  768. document.body.appendChild(statsOverlay);
  769.  
  770. userInterface.overlays.botOverlay = botOverlay;
  771. userInterface.overlays.serverOverlay = serverOverlay;
  772. userInterface.overlays.prefOverlay = prefOverlay;
  773. userInterface.overlays.statsOverlay = statsOverlay;
  774. },
  775.  
  776. toggleOverlays: function() {
  777. Object.keys(userInterface.overlays).forEach(function(okey) {
  778. var oVis = userInterface.overlays[okey].style.visibility !== 'hidden' ?
  779. 'hidden' : 'visible';
  780. userInterface.overlays[okey].style.visibility = oVis;
  781. window.visualDebugging = oVis === 'visible';
  782. });
  783. },
  784.  
  785. // Save variable to local storage
  786. savePreference: function(item, value) {
  787. window.localStorage.setItem(item, value);
  788. userInterface.onPrefChange();
  789. },
  790.  
  791. // Load a variable from local storage
  792. loadPreference: function(preference, defaultVar) {
  793. var savedItem = window.localStorage.getItem(preference);
  794. if (savedItem !== null) {
  795. if (savedItem === 'true') {
  796. window[preference] = true;
  797. } else if (savedItem === 'false') {
  798. window[preference] = false;
  799. } else {
  800. window[preference] = savedItem;
  801. }
  802. window.log('Setting found for ' + preference + ': ' + window[preference]);
  803. } else {
  804. window[preference] = defaultVar;
  805. window.log('No setting found for ' + preference +
  806. '. Used default: ' + window[preference]);
  807. }
  808. userInterface.onPrefChange();
  809. return window[preference];
  810. },
  811.  
  812. // Saves username when you click on "Play" button
  813. playButtonClickListener: function() {
  814. userInterface.saveNick();
  815. userInterface.loadPreference('autoRespawn', false);
  816. userInterface.onPrefChange();
  817. },
  818.  
  819. // Preserve nickname
  820. saveNick: function() {
  821. var nick = document.getElementById('nick').value;
  822. userInterface.savePreference('savedNick', nick);
  823. },
  824.  
  825. // Hide top score
  826. hideTop: function() {
  827. var nsidivs = document.querySelectorAll('div.nsi');
  828. for (var i = 0; i < nsidivs.length; i++) {
  829. if (nsidivs[i].style.top === '4px' && nsidivs[i].style.width === '300px') {
  830. nsidivs[i].style.visibility = 'hidden';
  831. bot.isTopHidden = true;
  832. window.topscore = nsidivs[i];
  833. }
  834. }
  835. },
  836.  
  837. // Store FPS data
  838. framesPerSecond: {
  839. fps: 0,
  840. fpsTimer: function() {
  841. if (window.playing && window.fps && window.lrd_mtm) {
  842. if (Date.now() - window.lrd_mtm > 970) {
  843. userInterface.framesPerSecond.fps = window.fps;
  844. }
  845. }
  846. }
  847. },
  848.  
  849. onkeydown: function(e) {
  850. // Original slither.io onkeydown function + whatever is under it
  851. original_keydown(e);
  852. if (window.playing) {
  853. // Letter `T` to toggle bot
  854. if (e.keyCode === 84) {
  855. bot.isBotEnabled = !bot.isBotEnabled;
  856. }
  857. // Letter 'U' to toggle debugging (console)
  858. if (e.keyCode === 85) {
  859. window.logDebugging = !window.logDebugging;
  860. console.log('Log debugging set to: ' + window.logDebugging);
  861. userInterface.savePreference('logDebugging', window.logDebugging);
  862. }
  863. // Letter 'Y' to toggle debugging (visual)
  864. if (e.keyCode === 89) {
  865. window.visualDebugging = !window.visualDebugging;
  866. console.log('Visual debugging set to: ' + window.visualDebugging);
  867. userInterface.savePreference('visualDebugging', window.visualDebugging);
  868. }
  869. // Letter 'I' to toggle autorespawn
  870. if (e.keyCode === 73) {
  871. window.autoRespawn = !window.autoRespawn;
  872. console.log('Automatic Respawning set to: ' + window.autoRespawn);
  873. userInterface.savePreference('autoRespawn', window.autoRespawn);
  874. }
  875. // Letter 'H' to toggle hidden mode
  876. if (e.keyCode === 72) {
  877. userInterface.toggleOverlays();
  878. }
  879. // Letter 'O' to change rendermode (visual)
  880. if (e.keyCode === 79) {
  881. userInterface.toggleMobileRendering(!window.mobileRender);
  882. }
  883. // Letter 'C' to toggle Collision detection / enemy avoidance
  884. if (e.keyCode === 67) {
  885. window.collisionDetection = !window.collisionDetection;
  886. console.log('collisionDetection set to: ' + window.collisionDetection);
  887. userInterface.savePreference('collisionDetection', window.collisionDetection);
  888. }
  889. // Letter 'A' to increase collision detection radius
  890. if (e.keyCode === 65) {
  891. window.collisionRadiusMultiplier++;
  892. console.log(
  893. 'collisionRadiusMultiplier set to: ' + window.collisionRadiusMultiplier);
  894. userInterface.savePreference(
  895. 'collisionRadiusMultiplier', window.collisionRadiusMultiplier);
  896. }
  897. // Letter 'S' to decrease collision detection radius
  898. if (e.keyCode === 83) {
  899. if (window.collisionRadiusMultiplier > 1) {
  900. window.collisionRadiusMultiplier--;
  901. console.log(
  902. 'collisionRadiusMultiplier set to: ' +
  903. window.collisionRadiusMultiplier);
  904. userInterface.savePreference(
  905. 'collisionRadiusMultiplier', window.collisionRadiusMultiplier);
  906. }
  907. }
  908. // Letter 'Z' to reset zoom
  909. if (e.keyCode === 90) {
  910. canvas.resetZoom();
  911. }
  912. // Letter 'Q' to quit to main menu
  913. if (e.keyCode === 81) {
  914. window.autoRespawn = false;
  915. userInterface.quit();
  916. }
  917. // 'ESC' to quickly respawn
  918. if (e.keyCode === 27) {
  919. bot.quickRespawn();
  920. }
  921. // Save nickname when you press "Enter"
  922. if (e.keyCode === 13) {
  923. userInterface.saveNick();
  924. }
  925. userInterface.onPrefChange();
  926. }
  927. },
  928.  
  929. // Manual mobile rendering
  930. toggleMobileRendering: function(mobileRendering) {
  931. window.mobileRender = mobileRendering;
  932. window.log('Mobile rendering set to: ' + window.mobileRender);
  933. userInterface.savePreference('mobileRender', window.mobileRender);
  934. // Set render mode
  935. if (window.mobileRender) {
  936. canvas.setBackground(
  937. 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs');
  938. window.render_mode = 1;
  939. window.want_quality = 0;
  940. window.high_quality = false;
  941. } else {
  942. canvas.setBackground();
  943. window.render_mode = 2;
  944. window.want_quality = 1;
  945. window.high_quality = true;
  946. }
  947. },
  948.  
  949. onPrefChange: function() {
  950. // Set static display options here.
  951. var oContent = [];
  952. var ht = userInterface.handleTextColor;
  953.  
  954. oContent.push('version: ' + GM_info.script.version);
  955. oContent.push('[T] Enable / disable bot: ' + ht(bot.isBotEnabled));
  956. oContent.push('[C] collision detection on/off: ' + ht(window.collisionDetection));
  957. oContent.push('[O] Mobile version on/off: ' + ht(window.mobileRender));
  958. oContent.push('[A/S] Radius multiplier (S-less, A-Greater): ' + window.collisionRadiusMultiplier);
  959. oContent.push('[I] Auto spawn on / off: ' + ht(window.autoRespawn));
  960. oContent.push('[Y] Visual debugging on / off: ' + ht(window.visualDebugging));
  961. oContent.push('[U] debug log: ' + ht(window.logDebugging));
  962. oContent.push('[Wheel(mouse)] Zoom (zoom in on top, zoom out down');
  963. oContent.push('[Z] Reset Zoom.');
  964. oContent.push('[ESC] Respawn');
  965. oContent.push('[Q] Exit to menu.');
  966.  
  967. userInterface.overlays.prefOverlay.innerHTML = oContent.join('<br/>');
  968. },
  969.  
  970. onFrameUpdate: function() {
  971. // Botstatus overlay
  972. var oContent = [];
  973.  
  974. if (window.playing && window.snake !== null) {
  975. oContent.push('fps: ' + userInterface.framesPerSecond.fps);
  976.  
  977. // Display the X and Y of the snake
  978. oContent.push('x: ' +
  979. (Math.round(window.snake.xx) || 0) + ' y: ' +
  980. (Math.round(window.snake.yy) || 0));
  981.  
  982. if (window.goalCoordinates) {
  983. oContent.push('target');
  984. oContent.push('x: ' + window.goalCoordinates.x + ' y: ' +
  985. window.goalCoordinates.y);
  986. if (window.goalCoordinates.sz) {
  987. oContent.push('sz: ' + window.goalCoordinates.sz);
  988. }
  989. }
  990.  
  991. if (window.bso !== undefined && userInterface.overlays.serverOverlay.innerHTML !==
  992. window.bso.ip + ':' + window.bso.po) {
  993. userInterface.overlays.serverOverlay.innerHTML =
  994. window.bso.ip + ':' + window.bso.po;
  995. }
  996. }
  997.  
  998. userInterface.overlays.botOverlay.innerHTML = oContent.join('<br/>');
  999.  
  1000.  
  1001. if (window.playing && window.visualDebugging) {
  1002. // Only draw the goal when a bot has a goal.
  1003. if (window.goalCoordinates && bot.isBotEnabled) {
  1004. var headCoord = {x: window.snake.xx, y: window.snake.yy};
  1005. canvas.drawLine(
  1006. headCoord,
  1007. window.goalCoordinates,
  1008. 'green');
  1009. canvas.drawCircle(window.goalCoordinates, 'red', true);
  1010. }
  1011. }
  1012. },
  1013.  
  1014. oefTimer: function() {
  1015. var start = Date.now();
  1016. canvas.maintainZoom();
  1017. original_oef();
  1018. original_redraw();
  1019.  
  1020. if (window.playing && bot.isBotEnabled && window.snake !== null) {
  1021. window.onmousemove = function() { };
  1022. bot.isBotRunning = true;
  1023. bot.collisionLoop();
  1024. } else if (bot.isBotEnabled && bot.isBotRunning) {
  1025. bot.isBotRunning = false;
  1026. if (window.lastscore && window.lastscore.childNodes[1]) {
  1027. bot.scores.push(parseInt(window.lastscore.childNodes[1].innerHTML));
  1028. bot.scores.sort(function(a, b) { return b - a; });
  1029. userInterface.updateStats();
  1030. }
  1031.  
  1032. if (window.autoRespawn) {
  1033. window.connect();
  1034. }
  1035. }
  1036.  
  1037. if (!bot.isBotEnabled || bot.isBotRunning) {
  1038. window.onmousemove = original_onmousemove;
  1039. }
  1040.  
  1041. userInterface.onFrameUpdate();
  1042. setTimeout(userInterface.oefTimer, (1000 / TARGET_FPS) - (Date.now() - start));
  1043. },
  1044.  
  1045. // Quit to menu
  1046. quit: function() {
  1047. if (window.playing && window.resetGame) {
  1048. window.want_close_socket = true;
  1049. window.dead_mtm = 0;
  1050. if (window.play_btn) {
  1051. window.play_btn.setEnabled(true);
  1052. }
  1053. window.resetGame();
  1054. }
  1055. },
  1056.  
  1057. // Update the relation between the screen and the canvas.
  1058. onresize: function() {
  1059. window.resize();
  1060. // Canvas different size from the screen (often bigger).
  1061. canvas.canvasRatio = {
  1062. x: window.mc.width / window.ww,
  1063. y: window.mc.height / window.hh
  1064. };
  1065. },
  1066.  
  1067. handleTextColor: function(enabled) {
  1068. return '<span style=\"color:' +
  1069. (enabled ? 'green;\">enabled' : 'red;\">disabled') + '</span>';
  1070. }
  1071. };
  1072. })();
  1073.  
  1074. // Main
  1075. (function() {
  1076. window.play_btn.btnf.addEventListener('click', userInterface.playButtonClickListener);
  1077. document.onkeydown = userInterface.onkeydown;
  1078. window.onmousedown = userInterface.onmousedown;
  1079. window.addEventListener('mouseup', userInterface.onmouseup);
  1080. window.onresize = userInterface.onresize;
  1081.  
  1082. // Hide top score
  1083. userInterface.hideTop();
  1084.  
  1085. // Overlays
  1086. userInterface.initOverlays();
  1087.  
  1088. // Load preferences
  1089. userInterface.loadPreference('logDebugging', true);
  1090. userInterface.loadPreference('visualDebugging', true);
  1091. userInterface.loadPreference('autoRespawn', true);
  1092. userInterface.loadPreference('mobileRender', true);
  1093. userInterface.loadPreference('collisionDetection', true);
  1094. userInterface.loadPreference('collisionRadiusMultiplier', 10);
  1095. window.nick.value = userInterface.loadPreference('savedNick', 'Slither.io Trainer Hack');
  1096.  
  1097. // Listener for mouse wheel scroll - used for setZoom function
  1098. document.body.addEventListener('mousewheel', canvas.setZoom);
  1099. document.body.addEventListener('DOMMouseScroll', canvas.setZoom);
  1100.  
  1101. // Set render mode
  1102. if (window.mobileRender) {
  1103. userInterface.toggleMobileRendering(false);
  1104. } else {
  1105. userInterface.toggleMobileRendering(true);
  1106. }
  1107.  
  1108. // Unblocks all skins without the need for FB sharing.
  1109. window.localStorage.setItem('edttsg', '1');
  1110.  
  1111. // Remove social
  1112. window.social.remove();
  1113.  
  1114. // Maintain fps
  1115. setInterval(userInterface.framesPerSecond.fpsTimer, 80);
  1116.  
  1117. // Start!
  1118. userInterface.oefTimer();
  1119. })();