Gota.io Features by Donut + Linesplit

Linesplit up, down, left and right • Diagonal linesplit with just one key • Hotkeys to show/hide skins, names, mass, food, chat, minimap, score panel, party panel and leaderboard • Auto respawn in Team Scrimmage • Leave a scrimmage match before it ends • You can change the keys as you wish

目前为 2021-04-30 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Gota.io Features by Donut + Linesplit
  3. // @namespace https://www.youtube.com/channel/UCIpCflcKEN9YgaO9qDahpRg
  4. // @version 1.7.12
  5. // @description Linesplit up, down, left and right • Diagonal linesplit with just one key • Hotkeys to show/hide skins, names, mass, food, chat, minimap, score panel, party panel and leaderboard • Auto respawn in Team Scrimmage • Leave a scrimmage match before it ends • You can change the keys as you wish
  6. // @match *://gota.io/web*
  7. // @author Donut
  8. // @grant none
  9. // @run-at document-end
  10. // ==/UserScript==
  11.  
  12. (function(){
  13. if (window.top != window.self) return;
  14.  
  15. if (!('code' in KeyboardEvent.prototype)) {
  16. return alert("Sorry, your browser is incompatible with Gota.io Features by Donut. You're recommended to install the newest version of Google Chrome or Mozilla Firefox browsers.");
  17. }
  18.  
  19. function validateVersion(v) {
  20. return !!v && !v.split('.').some(function(part) {
  21. return !/^\d+$/.test(part);
  22. });
  23. }
  24.  
  25. function compareVersions(v1, v2) {
  26. var v1parts = v1.split('.').map(Number);
  27. var v2parts = v2.split('.').map(Number);
  28.  
  29. while (v1parts.length < v2parts.length) v1parts.push(0);
  30. while (v2parts.length < v1parts.length) v2parts.push(0);
  31.  
  32. for (var i = 0; i < v1parts.length; ++i) {
  33. if (v1parts[i] > v2parts[i]) return 1;
  34. else if (v1parts[i] < v2parts[i]) return -1;
  35. }
  36. return 0;
  37. }
  38.  
  39. var importantUpdates = ['1.5.3', '1.6', '1.6.3', '1.7.0', '1.7.7'];
  40. importantUpdates.sort(compareVersions);
  41.  
  42. var version = '1.7.12', storageVersion = localStorage['donut-version'];
  43. var storageVersionValid = validateVersion(storageVersion);
  44. var notify = !storageVersionValid || compareVersions(importantUpdates[importantUpdates.length - 1], storageVersion) == 1;
  45. if (!notify) localStorage['donut-version'] = version;
  46.  
  47. var styles = {
  48. '.g-recaptcha, .grecaptcha-badge': {
  49. 'display': 'none !important'
  50. },
  51. '.bottom-btn': {
  52. 'height': '32px'
  53. },
  54. '.donut-features-table': {
  55. 'margin': 'auto',
  56. 'width': 'max-content',
  57. 'border-collapse': 'collapse'
  58. },
  59. '.donut-features-table td:nth-child(1)': {
  60. 'text-align': 'left'
  61. },
  62. '.donut-features-table th, .donut-features-table td': {
  63. 'position': 'relative',
  64. 'padding': '2px 8px'
  65. },
  66. '.donut-features-table input[type="text"]': {
  67. 'box-sizing': 'border-box',
  68. 'width': '104px',
  69. 'height': '22px',
  70. 'text-align': 'center'
  71. },
  72. '.donut-features-table select': {
  73. 'position': 'absolute',
  74. 'top': '2px',
  75. 'bottom': '2px',
  76. 'left': '8px',
  77. 'box-sizing': 'border-box',
  78. 'padding': '0',
  79. 'width': '104px',
  80. 'height': '22px',
  81. 'font-size': '0',
  82. 'background': 'transparent',
  83. 'border': 'none',
  84. 'outline': '0'
  85. },
  86. '.donut-features-table option': {
  87. 'font-size': '13.3333px'
  88. },
  89. '.donut-features-div': {
  90. 'margin': 'auto',
  91. 'padding-top': '4px',
  92. 'width': 'max-content'
  93. },
  94. '.donut-checkbox': {
  95. 'position': 'relative',
  96. 'top': '1px',
  97. 'margin': '0 5px'
  98. },
  99. '.donut-channel-link': {
  100. 'box-sizing': 'border-box',
  101. 'display': 'block',
  102. 'margin': '3px auto 0',
  103. 'padding': '4px 8px 4px 5.5px',
  104. 'width': '85px',
  105. 'height': '24px',
  106. 'color': '#fefefe',
  107. 'font-family': 'Arial, Helvetica, sans-serif',
  108. 'font-size': '12px',
  109. 'line-height': 'normal',
  110. 'text-align': 'center',
  111. 'text-decoration': 'none',
  112. 'background-color': '#e62117',
  113. 'border': 'solid 1px transparent',
  114. 'border-radius': '2px',
  115. 'white-space': 'nowrap',
  116. 'vertical-align': 'middle',
  117. 'box-shadow': '0 1px 0 rgba(0,0,0,0.05)'
  118. },
  119. '.donut-channel-link::before': {
  120. 'content': '""',
  121. 'position': 'relative',
  122. 'top': '-1px',
  123. 'display': 'inline-block',
  124. 'margin-right': '6px',
  125. 'width': '16px',
  126. 'height': '12px',
  127. 'background': 'no-repeat url(//s.ytimg.com/yts/imgbin/www-hitchhiker-vfl-Nn88d.png) -721px -88px',
  128. 'background-size': 'auto',
  129. 'vertical-align': 'middle'
  130. },
  131. '.donut-channel-link>span': {
  132. 'display': 'inline-block',
  133. '-moz-box-sizing': 'border-box',
  134. 'box-sizing': 'border-box'
  135. },
  136. '.donut-channel-link:hover': {
  137. 'background-color': '#cc181e'
  138. },
  139. '.donut-channel-link:active': {
  140. 'background-color': '#b31217'
  141. }
  142. };
  143.  
  144. if (notify) {
  145. styles['.donut-features-btn::before'] = {
  146. 'content': '"!"',
  147. 'position': 'absolute',
  148. 'top': '-4px',
  149. 'right': '-4px',
  150. 'width': '15px',
  151. 'height': '15px',
  152. 'color': 'white',
  153. 'font-family': 'Open Sans',
  154. 'font-size': '12px',
  155. 'text-align': 'center',
  156. 'line-height': '15px',
  157. 'background-color': 'red',
  158. 'border-radius': '50%',
  159. 'overflow': 'hidden',
  160. '-webkit-user-select': 'none',
  161. '-moz-user-select': 'none',
  162. '-ms-user-select': 'none',
  163. 'user-select': 'none',
  164. };
  165. }
  166.  
  167. function insertRule(selector) {
  168. var rule = selector + '{';
  169. for (var property in styles[selector]) rule += property + ':' + styles[selector][property] + ';';
  170. rule += '}';
  171. stylesheet.insertRule(rule, stylesheet.cssRules.length);
  172. }
  173.  
  174. var style = document.createElement('style');
  175. document.head.appendChild(style);
  176. var stylesheet = style.sheet;
  177. for (var selector in styles) insertRule(selector);
  178.  
  179. var gota = {
  180. main: document.getElementById('main'),
  181. left: document.getElementsByClassName('main-bottom-left')[0],
  182. right: document.getElementsByClassName('main-bottom-right')[0],
  183. stats: document.getElementsByClassName('main-bottom-stats')[0],
  184. btnPlay: document.getElementById('btn-play'),
  185. btnSpec: document.getElementById('btn-spec'),
  186. scorePanel: document.getElementById('score-panel'),
  187. partyPanel: document.getElementById('party-panel'),
  188. leaderboardPanel: document.getElementById('leaderboard-panel'),
  189. extraPanel: document.getElementById('extra-panel'),
  190. chatPanel: document.getElementById('chat-panel'),
  191. chatInput: document.getElementById('chat-input'),
  192. mainScrimmage: document.getElementById('main-scrimmage'),
  193. scrimmageModeSelect: document.getElementById('scrimmage-mode-select'),
  194. btnQueue: document.getElementById('btn-queue'),
  195. scrimmageBtnLeave: document.getElementById('scrimmage-btn-leave'),
  196. btnLeaveMatch: document.getElementById('btn-leave-match'),
  197. playerCells: document.getElementById('playerCells'),
  198. scoreMouse: document.getElementById('score-mouse'),
  199. sShowSkins: document.getElementById('sShowSkins'),
  200. sShowNames: document.getElementById('sShowNames'),
  201. cShowMass: document.getElementById('cShowMass'),
  202. cHideFood: document.getElementById('cHideFood'),
  203. cHideChat: document.getElementById('cHideChat'),
  204. cHideMinimap: document.getElementById('cHideMinimap'),
  205. cAutoDecline: document.getElementById('cAutoDecline'),
  206. cAutoRespawn: document.getElementById('cAutoRespawn')
  207. };
  208.  
  209. gota.stats.style.width = '140px';
  210. gota.stats.style.height = '143px';
  211.  
  212. var leftButton = document.createElement('button');
  213. leftButton.className = 'gota-btn bottom-btn';
  214. leftButton.style.margin = '0 3px';
  215. leftButton.style.float = 'none';
  216. leftButton.style.setProperty('border-radius', '10px 3px 3px 10px', 'important');
  217.  
  218. var rightButton = leftButton.cloneNode();
  219. rightButton.style.setProperty('border-radius', '3px 10px 10px 3px', 'important');
  220.  
  221. var btn = rightButton.cloneNode();
  222. btn.className += ' donut-features-btn';
  223. btn.style.position = 'relative';
  224. btn.style.boxSizing = 'border-box';
  225. btn.style.margin = '5px 0';
  226. btn.style.setProperty('border-radius', '3px 10px 10px 3px', 'important');
  227. btn.style.whiteSpace = 'nowrap';
  228. btn.style.overflow = 'visible';
  229. btn.style.float = 'left';
  230. btn.innerText = 'Features by Donut';
  231. btn.addEventListener('click', function() {
  232. blackout.style.opacity = '0';
  233. blackout.style.display = 'block';
  234. resize();
  235. blackout.style.opacity = '1';
  236. if (notify) {
  237. localStorage['donut-version'] = version;
  238. stylesheet.insertRule('.donut-features-btn::before{content:none;}', stylesheet.cssRules.length);
  239. }
  240. });
  241. gota.right.appendChild(btn);
  242.  
  243. var blackout = document.createElement('div');
  244. blackout.className = 'donut-blackout';
  245. blackout.style.position = 'fixed';
  246. blackout.style.top = '0';
  247. blackout.style.right = '0';
  248. blackout.style.bottom = '0';
  249. blackout.style.left = '0';
  250. blackout.style.display = 'none';
  251. blackout.style.background = 'rgba(0,0,0,.5)';
  252. blackout.style.overflow = 'auto';
  253. blackout.style.zIndex = '100';
  254. document.body.appendChild(blackout);
  255.  
  256. var win = document.createElement('div');
  257. win.style.position = 'relative';
  258. win.style.left = '50%';
  259. win.style.padding = '15px';
  260. win.style.width = 'max-content';
  261. win.style.color = 'white';
  262. win.style.fontFamily = 'Arial, Helvetica, sans-serif';
  263. win.style.fontSize = '16px';
  264. win.style.lineHeight = '22px';
  265. win.style.textAlign = 'center';
  266. win.style.backgroundColor = '#363636';
  267. win.style.borderRadius = '10px';
  268. win.innerHTML = `<table class='donut-features-table'><tbody><tr><th>Feature</th><th>Default</th><th>Custom key</th></tr>
  269. <tr><td>Show/hide skins</td><td>K</td><td><input type="text" spellcheck="false" data-donut-feature="skins"></td></tr>
  270. <tr><td>Show/hide names</td><td>N</td><td><input type="text" spellcheck="false" data-donut-feature="names"></td></tr>
  271. <tr><td>Show/hide mass</td><td>M</td><td><input type="text" spellcheck="false" data-donut-feature="mass"></td></tr>
  272. <tr><td>Show/hide food</td><td>F</td><td><input type="text" spellcheck="false" data-donut-feature="food"></td></tr>
  273. <tr><td>Show/hide chat</td><td>H</td><td><input type="text" spellcheck="false" data-donut-feature="chat"></td></tr>
  274. <tr><td colspan="2">Triple split (8x)</td><td><input type="text" spellcheck="false" data-donut-feature="tripleSplit"></td></tr>
  275. <tr><td colspan="2">Hexa split (64x)</td><td><input type="text" spellcheck="false" data-donut-feature="hexaSplit"></td></tr>
  276. <tr><td colspan="2">Show/hide minimap</td><td><input type="text" spellcheck="false" data-donut-feature="minimap"></td></tr>
  277. <tr><td colspan="2">Show/hide score panel</td><td><input type="text" spellcheck="false" data-donut-feature="scorePanel"></td></tr>
  278. <tr><td colspan="2">Show/hide party panel</td><td><input type="text" spellcheck="false" data-donut-feature="partyPanel"></td></tr>
  279. <tr><td colspan="2">Show/hide leaderboard</td><td><input type="text" spellcheck="false" data-donut-feature="leaderboard"></td></tr>
  280. <tr><td colspan="2" style="padding-right:32px;">Toggle <em>Decline party invites</em></td><td><input type="text" spellcheck="false" data-donut-feature="autoDecline"></td></tr>
  281. <tr><td colspan="2">Toggle <em>Auto respawn</em></td><td><input type="text" spellcheck="false" data-donut-feature="autoRespawn"></td></tr>
  282. <tr><th colspan="3">Linesplit</th></tr>
  283. <tr><td>Freeze in the middle</td><td>S</td><td><input type="text" spellcheck="false" data-donut-feature="freeze"></td></tr>
  284. <tr><td colspan="2">Freeze to linesplit up</td><td><input type="text" spellcheck="false" data-donut-feature="freezeUp"></td></tr>
  285. <tr><td colspan="2">Freeze to linesplit down</td><td><input type="text" spellcheck="false" data-donut-feature="freezeDown"></td></tr>
  286. <tr><td colspan="2"><div style="display:inline-block;text-decoration:underline dotted;cursor:help;" title="Horizontal linesplits are not guaranteed to work. Use at your own risk!">Freeze to linesplit left</div></td><td><input type="text" spellcheck="false" data-donut-feature="freezeLeft"></td></tr>
  287. <tr><td colspan="2"><div style="display:inline-block;text-decoration:underline dotted;cursor:help;" title="Horizontal linesplits are not guaranteed to work. Use at your own risk!">Freeze to linesplit right</div></td><td><input type="text" spellcheck="false" data-donut-feature="freezeRight"></td></tr>
  288. <tr><td colspan="2">Diagonal linesplit (8x)</td><td><input type="text" spellcheck="false" data-donut-feature="linesplit"></td></tr>
  289. <tr><td colspan="2">Freeze mode</td><td><input type="text" spellcheck="false" readonly><select id="donut-freezeMode" data-donut-feature="freezeMode"><option value="hold">Hold down</option><option value="toggle">Toggle</option></select></td></tr>
  290. <tr><th colspan="3">Team Scrimmage</th></tr>
  291. <tr><td>Leave a match</td><td>L</td><td><input type="text" spellcheck="false" data-donut-feature="leave"></td></tr>
  292. <tr><td colspan="2">Queue</td><td><input type="text" spellcheck="false" data-donut-feature="queue"></td></tr>
  293. <tr><td colspan="2">Default gamemode</td><td><input type="text" spellcheck="false" readonly><select id="donut-scrimmageMode" data-donut-feature="scrimmageMode"><option value="0">Team 2v2</option><option value="1">Team 2v2 [MegaSplit]</option><option value="2">Team 2v2 [Extreme]</option><option value="3">Duel 1v1</option></select></td></tr></tbody></table>
  294. <div class="donut-features-div">You should only use the <em>Leave a match</em> hotkey<br>when playing with random teammates.</div>
  295. <div class="donut-features-div">If you want to disable a hotkey, type Delete.</div>
  296. <div class="donut-features-div"><label><input type="checkbox" class="donut-checkbox" id="donut-hideScorePanel" data-donut-feature="hideScorePanel">Hide Score Panel</label><br>
  297. <label><input type="checkbox" class="donut-checkbox" id="donut-hidePartyPanel" data-donut-feature="hidePartyPanel">Hide Party Panel</label><br>
  298. <label><input type="checkbox" class="donut-checkbox" id="donut-hideLeaderboard" data-donut-feature="hideLeaderboard">Hide Leaderboard</label><br>
  299. <label><input type="checkbox" class="donut-checkbox" id="donut-scrimmageAutoRespawn" data-donut-feature="scrimmageAutoRespawn">Scrimmage Auto Respawn</label><br>
  300. <label><input type="checkbox" class="donut-checkbox" id="donut-startWithScrimmage" data-donut-feature="startWithScrimmage">Join Scrimmage as I open the game</label><br>
  301. <label><input type="checkbox" class="donut-checkbox" id="donut-leaveExperimental" data-donut-feature="leaveExperimental">Use my <em>Leave a match</em> hotkey to leave<br>my team in the other gamemodes</label></div>
  302. <div class="donut-features-div">You can support the developer by checking out<br>his YouTube channel:</div>
  303. <a href="https://www.youtube.com/channel/UCIpCflcKEN9YgaO9qDahpRg" target="_blank" class="donut-channel-link"><span>YouTube</span></a>`;
  304. if (storageVersionValid && notify) {
  305. if (compareVersions(storageVersion, '1.7.0') < 0) win.innerHTML = `<div class="donut-features-div" style="padding:0 0 4px 0;font:italic 18px 'Open Sans';">Check out the new linesplit features below!</div>` + win.innerHTML;
  306. else if (compareVersions(storageVersion, '1.7.7') < 0) {
  307. win.innerHTML = `<div style="padding-bottom:12px;border-bottom:1px solid #b3b3b3;margin-bottom:12px;font-family:'Segoe UI', 'Open Sans', sans-serif;">
  308. <div class="donut-features-div" style="padding:0 0 4px 0;">You can now select <strong>Team 2v2 [Extreme]</strong><br>as your default scrimmage gamemode.</div>
  309. <div class="donut-features-div" style="padding:0 0 4px 0;">Your freeze mode (see Linesplit section below)<br>now applies to the <strong>Freeze Mouse</strong> keybind<br>you can set in Gota's Options menu.</div>
  310. </div>` + win.innerHTML;
  311. }
  312. }
  313. blackout.appendChild(win);
  314.  
  315. function resize() {
  316. if (blackout.style.display == 'block') {
  317. if (window.innerHeight < win.offsetHeight + 20) {
  318. win.style.top = '0';
  319. win.style.margin = '10px';
  320. win.style.transform = 'translate(-50%, 0%)';
  321. } else {
  322. win.style.top = '50%';
  323. win.style.margin = '0';
  324. win.style.transform = 'translate(-50%, -50%)';
  325. }
  326. }
  327. }
  328. window.addEventListener('resize', resize);
  329.  
  330. function close() {
  331. blackout.style.display = 'none';
  332. win.style.display = 'block';
  333. reportWin.style.display = 'none';
  334. }
  335.  
  336. document.addEventListener('click', function(e) {
  337. if (blackout.style.display == 'block'
  338. && e.target != win && !win.contains(e.target)
  339. && e.target != reportWin && !reportWin.contains(e.target)
  340. ) close();
  341. }, true);
  342.  
  343. var footer = document.createElement('div');
  344. footer.style.marginTop = '12px';
  345. win.appendChild(footer);
  346.  
  347. var done = leftButton.cloneNode();
  348. done.style.setProperty('border-radius', '10px 3px 3px 10px', 'important');
  349. done.innerText = 'Done';
  350. done.addEventListener('click', function() {
  351. blackout.style.display = 'none';
  352. });
  353. footer.appendChild(done);
  354.  
  355. var reportBtn = rightButton.cloneNode();
  356. reportBtn.style.setProperty('border-radius', '3px 10px 10px 3px', 'important');
  357. reportBtn.innerText = 'Report bug';
  358. reportBtn.addEventListener('click', function() {
  359. win.style.display = 'none';
  360. reportWin.style.display = 'block';
  361. });
  362. footer.appendChild(reportBtn);
  363.  
  364. var reportWin = win.cloneNode();
  365. reportWin.style.top = '50%';
  366. reportWin.style.display = 'none';
  367. reportWin.style.margin = '0';
  368. reportWin.style.width = '500px';
  369. reportWin.style.transform = 'translate(-50%, -50%)';
  370. reportWin.style.fontFamily = '"Segoe UI", "Open Sans", Arial, Helvetica, sans-serif';
  371. reportWin.innerHTML = `<p style="margin:0.12em 0 0.72em;">You will be redirected to a channel in Gota's official Discord server where you can report issues and bugs you come across in the game, including those caused by use of Gota.io Features by Donut.</p>
  372. <p style="margin:0.72em 0 0;font-size:18px;font-weight:bold;">Please include the following sentence in your report:</p><pre style="margin-top:0.1em;user-select:text;">I am using Features by Donut version ${version}.</pre>`;
  373. blackout.appendChild(reportWin);
  374.  
  375. var reportFooter = footer.cloneNode();
  376. reportWin.appendChild(reportFooter);
  377.  
  378. var reportGoBack = leftButton.cloneNode();
  379. reportGoBack.innerText = 'Go Back';
  380. reportGoBack.addEventListener('click', function() {
  381. reportWin.style.display = 'none';
  382. win.style.display = 'block';
  383. });
  384. reportFooter.appendChild(reportGoBack);
  385.  
  386. var reportContinue = rightButton.cloneNode();
  387. reportContinue.innerText = 'Continue';
  388. reportContinue.addEventListener('click', function() {
  389. window.open('https://discord.gg/QJknuQF', '_blank');
  390. });
  391. reportFooter.appendChild(reportContinue);
  392.  
  393. var processedKeyCodes = {
  394. Escape: 'Esc',
  395. Minus: '-',
  396. Equal: '=',
  397. BracketLeft: '[',
  398. BracketRight: ']',
  399. Control: 'Ctrl',
  400. Semicolon: ';',
  401. Quote: "'",
  402. Backquote: '`',
  403. Backslash: '\\',
  404. Comma: ',',
  405. Period: '.',
  406. Slash: '/',
  407. NumpadMultiply: 'Numpad *',
  408. CapsLock: 'Caps Lock',
  409. ScrollLock: 'Scroll Lock',
  410. Numpad7: 'Numpad 7',
  411. Numpad8: 'Numpad 8',
  412. Numpad9: 'Numpad 9',
  413. NumpadSubtract: 'Numpad -',
  414. Numpad4: 'Numpad 4',
  415. Numpad5: 'Numpad 5',
  416. Numpad6: 'Numpad 6',
  417. NumpadAdd: 'Numpad +',
  418. Numpad1: 'Numpad 1',
  419. Numpad2: 'Numpad 2',
  420. Numpad3: 'Numpad 3',
  421. Numpad0: 'Numpad 0',
  422. NumpadDecimal: 'Numpad .',
  423. NumpadEqual: 'Numpad =',
  424. NumpadEnter: 'Enter',
  425. NumpadDivide: 'Numpad /',
  426. NumLock: 'Num Lock',
  427. ArrowUp: 'Arrow Up',
  428. PageUp: 'Page Up',
  429. ArrowLeft: 'Arrow Left',
  430. ArrowRight: 'Arrow Right',
  431. ArrowDown: 'Arrow Down',
  432. PageDown: 'Page Down',
  433. Meta: 'Win / \u2318',
  434. OS: 'Win / \u2318'
  435. };
  436.  
  437. function processKeyCode(code) {
  438. if (code.indexOf('Arrow') && code.indexOf('Bracket')) code = code.replace(/Key|Digit|Left|Right/, '');
  439. if (code in processedKeyCodes) return processedKeyCodes[code];
  440. return code;
  441. }
  442.  
  443. var defaultKeys = {
  444. skins: 'K',
  445. names: 'N',
  446. mass: 'M',
  447. food: 'F',
  448. chat: 'H',
  449. tripleSplit: null,
  450. hexaSplit: null,
  451. minimap: null,
  452. scorePanel: null,
  453. partyPanel: null,
  454. leaderboard: null,
  455. autoDecline: null,
  456. autoRespawn: null,
  457. freeze: 'S',
  458. freezeUp: null,
  459. freezeDown: null,
  460. freezeLeft: null,
  461. freezeRight: null,
  462. linesplit: null,
  463. leave: 'L',
  464. queue: null
  465. };
  466.  
  467. var inputs = document.querySelectorAll('.donut-features-table input[type="text"][data-donut-feature]'), errorKeys = 'Gota.io Features by Donut: An error occurred. We had to reset your custom keys.';
  468. function fillInputs(keys) {
  469. for (var i = 0; i < inputs.length; i++) {
  470. var feature = inputs[i].dataset.donutFeature, key = keys[feature];
  471. if (typeof key == 'undefined' && feature in defaultKeys) {
  472. key = keys[feature] = defaultKeys[feature];
  473. localStorage['donut-keys'] = JSON.stringify(keys);
  474. }
  475. if (key === null) continue;
  476. inputs[i].value = key;
  477. }
  478. }
  479.  
  480. function tryLocalStorage(index, defaultObj, func, error) {
  481. var obj; index = 'donut-' + index;
  482. try {
  483. if (!localStorage[index]) throw null;
  484. obj = JSON.parse(localStorage[index]);
  485. if (func) func(obj);
  486. } catch (e) {
  487. obj = JSON.parse(JSON.stringify(defaultObj));
  488. if (e && error) console.error(error);
  489. localStorage[index] = JSON.stringify(obj);
  490. if (func) func(obj);
  491. }
  492. return obj;
  493. }
  494.  
  495. var keys = tryLocalStorage('keys', defaultKeys, fillInputs, errorKeys);
  496.  
  497. function handleInputKeydown(e) {
  498. e.preventDefault();
  499. e.stopPropagation();
  500. var feature = this.dataset.donutFeature, code = processKeyCode(e.code);
  501. if (code && code != keys[feature] && code != 'Unidentified') {
  502. if (code == 'Delete') {
  503. this.value = '';
  504. keys[feature] = null;
  505. } else {
  506. for (var k in keys) {
  507. if (keys[k] == code) {
  508. keys[k] = null;
  509. for (var l = 0; l < inputs.length; l++) {
  510. if (inputs[l].dataset.donutFeature == k) {
  511. inputs[l].value = '';
  512. break;
  513. }
  514. }
  515. }
  516. }
  517. this.value = keys[feature] = code;
  518. }
  519. localStorage['donut-keys'] = JSON.stringify(keys);
  520. this.blur();
  521. }
  522. }
  523.  
  524. for (var j = 0; j < inputs.length; j++) {
  525. inputs[j].addEventListener('keydown', handleInputKeydown);
  526. }
  527.  
  528. function selectOption(select) {
  529. var index = select.selectedIndex;
  530. select.selectedIndex = (index + (index == 1) + 1) % select.options.length;
  531. $(select).change();
  532. }
  533.  
  534. function triggerCheckbox(checkbox) {
  535. $(checkbox).prop('checked', !$(checkbox).prop('checked')).change();
  536. }
  537.  
  538. function triggerDonutCheckbox(checkbox) {
  539. checkbox.checked = !checkbox.checked;
  540. checkbox.dispatchEvent(new Event('change'));
  541. }
  542.  
  543. function fadeOutMain() {
  544. if (gota.main.style.display == 'block') window.onkeydown({keyCode: 27, which: 27});
  545. }
  546.  
  547. var alertWin = win.cloneNode();
  548. alertWin.style.top = '10px';
  549. alertWin.style.display = 'none';
  550. alertWin.style.lineHeight = '20px';
  551. alertWin.style.backgroundColor = 'rgba(42,42,42,.9)';
  552. alertWin.style.boxShadow = '0 2px 3px rgba(0,0,0,.25)';
  553. alertWin.style.transform = 'translate(-50%, 0%)';
  554. alertWin.style.zIndex = '3';
  555. document.body.appendChild(alertWin);
  556.  
  557. var alertFadeOutTimer = 0;
  558. function Alert(html, timeout) {
  559. alertWin.innerHTML = html;
  560. $(alertWin).fadeIn(500);
  561. if (alertFadeOutTimer) clearTimeout(alertFadeOutTimer);
  562. alertFadeOutTimer = setTimeout(function() {
  563. $(alertWin).fadeOut(500);
  564. alertFadeOutTimer = 0;
  565. }, timeout);
  566. }
  567.  
  568. var freezeKeys = {
  569. freeze: {
  570. id: 1,
  571. x: 0.5,
  572. y: 0.5
  573. },
  574. freezeUp: {
  575. id: 2,
  576. x: 0.5,
  577. y: 0
  578. },
  579. freezeDown: {
  580. id: 3,
  581. x: 0.5,
  582. y: 1
  583. },
  584. freezeLeft: {
  585. id: 4,
  586. x: 0,
  587. y: 0.5
  588. },
  589. freezeRight: {
  590. id: 5,
  591. x: 1,
  592. y: 0.5
  593. }
  594. };
  595.  
  596. function findFreezeKey(code) {
  597. var found = Object.keys(freezeKeys).find(function(val) {
  598. return code == keys[val];
  599. });
  600. return found ? freezeKeys[found] : undefined;
  601. }
  602.  
  603. var x = 0, y = 0, originalMousemove, freezeState = 0, freezeKeyDown = false;
  604. setTimeout(function delay() {
  605. if (window.onmousemove) {
  606. originalMousemove = window.onmousemove;
  607. window.onmousemove = function(e) {
  608. x = e.clientX; y = e.clientY;
  609. if (freezeState == 0) originalMousemove(e);
  610. };
  611. } else {
  612. setTimeout(delay, 200);
  613. }
  614. }, 200);
  615.  
  616. function split(times = 1) {
  617. for (var i = 0; i < times; i++) {
  618. $(window).trigger($.Event('keydown', {keyCode: 32, which: 32}));
  619. }
  620. }
  621.  
  622. function isModifier(e, code) {
  623. return (
  624. (code == 'Alt' && !e.ctrlKey && !e.metaKey && !e.shiftKey)
  625. || (code == 'Ctrl' && !e.altKey && !e.metaKey && !e.shiftKey)
  626. || (code == 'Win / \u2318' && !e.altKey && !e.ctrlKey && !e.shiftKey)
  627. || (code == 'Shift' && !e.altKey && !e.ctrlKey && !e.metaKey)
  628. );
  629. }
  630.  
  631. window.addEventListener('keydown', function(e) {
  632. if (e.code == undefined) return;
  633. var code = processKeyCode(e.code), modifier = isModifier(e, code);
  634. if (modifier || !e.altKey && !e.ctrlKey && !e.metaKey) {
  635. if (!modifier) {
  636. var key = e.which || e.keyCode || 0;
  637. if (key == 13 && document.activeElement && document.activeElement.id == 'name-box') team2v2();
  638. else if (key == 27) {
  639. if (!firstEscape) firstEscape = true;
  640. if (alertWin.offsetHeight) $(alertWin).fadeOut(500);
  641. }
  642. }
  643. if ((modifier || !e.shiftKey) && document.activeElement.tagName != 'INPUT' && document.activeElement.tagName != 'TEXTAREA') {
  644. switch (code) {
  645. case keys.skins:
  646. selectOption(gota.sShowSkins);
  647. break;
  648. case keys.names:
  649. selectOption(gota.sShowNames);
  650. break;
  651. case keys.mass:
  652. triggerCheckbox(gota.cShowMass);
  653. break;
  654. case keys.food:
  655. triggerCheckbox(gota.cHideFood);
  656. break;
  657. case keys.chat:
  658. triggerCheckbox(gota.cHideChat);
  659. break;
  660. case keys.tripleSplit:
  661. split(3);
  662. break;
  663. case keys.hexaSplit:
  664. split(6);
  665. break;
  666. case keys.minimap:
  667. triggerCheckbox(gota.cHideMinimap);
  668. break;
  669. case keys.scorePanel:
  670. triggerDonutCheckbox(checkboxes.hideScorePanel);
  671. break;
  672. case keys.partyPanel:
  673. triggerDonutCheckbox(checkboxes.hidePartyPanel);
  674. break;
  675. case keys.leaderboard:
  676. triggerDonutCheckbox(checkboxes.hideLeaderboard);
  677. break;
  678. case keys.autoDecline:
  679. triggerCheckbox(gota.cAutoDecline);
  680. Alert('Auto Decline Party Invites: <strong>' + (gota.cAutoDecline.checked ? 'On' : 'Off') + '</strong>', 2500);
  681. break;
  682. case keys.autoRespawn:
  683. var checkbox = done2v2 ? checkboxes.scrimmageAutoRespawn : gota.cAutoRespawn;
  684. if (done2v2) triggerDonutCheckbox(checkbox);
  685. else triggerCheckbox(checkbox);
  686. Alert('Auto Respawn' + (done2v2 ? ' in Team Scrimmage' : '') + ': <strong>' + (checkbox.checked ? 'On' : 'Off') + '</strong>', 2500);
  687. break;
  688. case keys.linesplit:
  689. split(3);
  690. if (gota.main.style.display == 'none') {
  691. gota.main.style.visibility = 'hidden';
  692. gota.main.style.display = 'block';
  693. setTimeout(function() {
  694. gota.main.style.display = 'none';
  695. gota.main.style.visibility = 'visible';
  696. }, 1050);
  697. }
  698. break;
  699. case keys.queue:
  700. if (gota.scrimmageBtnLeave.style.display == 'block') $(gota.btnLeaveMatch).click();
  701. else if (gota.mainScrimmage.style.display == 'block') $(gota.btnQueue).click();
  702. fadeOutMain();
  703. break;
  704. case keys.leave:
  705. if (switches.leaveExperimental || scrimmage) {
  706. var hidden = gota.chatPanel.style.display == 'none';
  707. if (hidden) {
  708. gota.chatPanel.style.opacity = '0';
  709. gota.chatPanel.style.display = 'block';
  710. }
  711. $(gota.chatInput).val('/leave').focus().trigger($.Event('keyup', {keyCode: 13, which: 13})).blur();
  712. scrimmage = false;
  713. if (hidden) {
  714. gota.chatPanel.style.display = 'none';
  715. gota.chatPanel.style.opacity = '1';
  716. }
  717. fadeOutMain();
  718. }
  719. break;
  720. default:
  721. if (originalMousemove) {
  722. var freezeKey = findFreezeKey(code);
  723. if (freezeKey) {
  724. if (freezeState != freezeKey.id) {
  725. freezeState = freezeKey.id;
  726. freezeKeyDown = true;
  727. originalMousemove({
  728. clientX: window.innerWidth / 2,
  729. clientY: window.innerHeight / 2
  730. });
  731. setTimeout(function() {
  732. originalMousemove({
  733. clientX: window.innerWidth * freezeKey.x,
  734. clientY: window.innerHeight * freezeKey.y
  735. });
  736. }, 100);
  737. } else if (options.freezeMode == 'toggle' && !freezeKeyDown) {
  738. freezeState = 0;
  739. originalMousemove({clientX: x, clientY: y});
  740. }
  741. }
  742. }
  743. }
  744. }
  745. }
  746. });
  747.  
  748. window.addEventListener('keyup', function(e) {
  749. if (e.code == undefined) return;
  750. var code = processKeyCode(e.code);
  751. if ((isModifier(e, code) || !e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) && document.activeElement.tagName != 'INPUT' && document.activeElement.tagName != 'TEXTAREA') {
  752. if (originalMousemove) {
  753. if (options.freezeMode == 'hold') {
  754. var freezeKey = findFreezeKey(code);
  755. if (freezeKey && freezeState == freezeKey.id) {
  756. freezeState = 0;
  757. originalMousemove({clientX: x, clientY: y});
  758. }
  759. } else {
  760. freezeKeyDown = false;
  761. }
  762. }
  763. }
  764. });
  765.  
  766. function isMouseFrozen() {
  767. return gota.scoreMouse.style.display == 'block';
  768. }
  769.  
  770. var originalKeydown;
  771. setTimeout(function delay() {
  772. if (window.onkeydown) {
  773. originalKeydown = window.onkeydown;
  774. window.onkeydown = function(e) {
  775. try {
  776. if (options.freezeMode == 'toggle' || !isMouseFrozen() || e.which != JSON.parse(localStorage.keybinds).kFreezeMouse) {
  777. originalKeydown(e);
  778. }
  779. } catch (err) {
  780. originalKeydown(e);
  781. }
  782. };
  783.  
  784. window.addEventListener('keyup', function(e) {
  785. if (options.freezeMode == 'hold' && isMouseFrozen() && e.which == JSON.parse(localStorage.keybinds).kFreezeMouse) {
  786. originalKeydown(e);
  787. }
  788. });
  789. } else {
  790. setTimeout(delay, 200);
  791. }
  792. }, 200);
  793.  
  794. var defaultSwitches = {
  795. hideScorePanel: false,
  796. hidePartyPanel: false,
  797. hideLeaderboard: false,
  798. scrimmageAutoRespawn: true,
  799. startWithScrimmage: false,
  800. leaveExperimental: false
  801. };
  802.  
  803. var checkboxes = {};
  804. for (var feature in defaultSwitches) {
  805. checkboxes[feature] = document.getElementById('donut-' + feature);
  806. }
  807.  
  808. var errorSwitches = 'Gota.io Features by Donut: An error occurred. We had to reset your settings.';
  809. function fillCheckboxes(switches) {
  810. for (var feature in checkboxes) {
  811. var Switch = switches[feature];
  812. if (typeof Switch == 'undefined') {
  813. Switch = switches[feature] = defaultSwitches[feature];
  814. localStorage['donut-switches'] = JSON.stringify(switches);
  815. }
  816. if (typeof Switch == 'boolean') checkboxes[feature].checked = Switch;
  817. else throw errorSwitches;
  818. }
  819. }
  820.  
  821. var switches = tryLocalStorage('switches', defaultSwitches, fillCheckboxes, errorSwitches);
  822.  
  823. var anchorObd = document.querySelector('.options-table>thead:nth-of-type(6)'), obd = anchorObd.cloneNode(true);
  824. obd.firstElementChild.firstElementChild.textContent = 'Options by Donut';
  825. anchorObd.parentNode.insertBefore(obd, anchorObd);
  826.  
  827. var tbody = document.createElement('tbody');
  828. anchorObd.parentNode.insertBefore(tbody, anchorObd);
  829.  
  830. function duplicateCheckbox(feature, text) {
  831. var td = document.createElement('td');
  832. td.colSpan = '4';
  833. td.style.position = 'relative';
  834. td.style.paddingRight = '22px';
  835. td.style.whiteSpace = 'normal';
  836. td.innerHTML = text;
  837.  
  838. var checkbox = document.createElement('input');
  839. checkbox.type = 'checkbox';
  840. checkbox.className = 'checkbox-options';
  841. checkbox.dataset.donutFeature = feature;
  842. checkbox.style.position = 'absolute';
  843. checkbox.style.top = '1px';
  844. checkbox.style.right = '1px';
  845. td.appendChild(checkbox);
  846.  
  847. var tr = document.createElement('tr');
  848. tr.appendChild(td);
  849. tbody.appendChild(tr);
  850.  
  851. checkbox.checked = switches[feature];
  852. return checkbox;
  853. }
  854.  
  855. var duplicates = {};
  856. for (feature in defaultSwitches) {
  857. var bro = checkboxes[feature], text = bro.parentNode.innerText;
  858. if (feature == 'leaveExperimental') text = 'Use my <em>Leave a match</em> hotkey to leave my team in the other gamemodes';
  859. duplicates[feature] = duplicateCheckbox(feature, text);
  860. }
  861.  
  862. function handleCheckboxChange(e) {
  863. var feature = this.dataset.donutFeature;
  864. switches[feature] = this.checked;
  865. localStorage['donut-switches'] = JSON.stringify(switches);
  866. checkboxes[feature].checked = duplicates[feature].checked = this.checked;
  867. switch (feature) {
  868. case 'hideScorePanel':
  869. if (this.checked) hideScorePanel();
  870. else {
  871. gota.partyPanel.style.position = 'relative';
  872. gota.partyPanel.style.top = '10px';
  873. gota.scorePanel.style.opacity = '1';
  874. }
  875. break;
  876. case 'hidePartyPanel':
  877. visibility(gota.partyPanel, !this.checked);
  878. break;
  879. case 'hideLeaderboard':
  880. visibility(gota.leaderboardPanel, !this.checked);
  881. visibility(gota.extraPanel, !this.checked);
  882. break;
  883. case 'scrimmageAutoRespawn':
  884. if (this.checked && scrimmage) scrimRespawn();
  885. }
  886. }
  887.  
  888. for (feature in checkboxes) {
  889. checkboxes[feature].addEventListener('change', handleCheckboxChange);
  890. duplicates[feature].addEventListener('change', handleCheckboxChange);
  891. }
  892.  
  893. function hideScorePanel() {
  894. gota.scorePanel.style.opacity = '0';
  895. gota.partyPanel.style.position = 'absolute';
  896. gota.partyPanel.style.top = '0';
  897. }
  898. if (switches.hideScorePanel) hideScorePanel();
  899.  
  900. function visibility(element, visible) {
  901. element.style.visibility = visible ? 'visible' : 'hidden';
  902. }
  903. if (switches.hideLeaderboard) {
  904. visibility(gota.leaderboardPanel, false);
  905. visibility(gota.extraPanel, false);
  906. }
  907. if (switches.hidePartyPanel) visibility(gota.partyPanel, false);
  908.  
  909. var info = tryLocalStorage('info', {
  910. scrimmageModeAdjusted: storageVersionValid && compareVersions(storageVersion, '1.7.11') < 0 ? 0 : 1
  911. });
  912.  
  913. function adjustScrimmageMode() {
  914. info.scrimmageModeAdjusted++;
  915. localStorage['donut-info'] = JSON.stringify(info);
  916. }
  917.  
  918. var defaultOptions = {
  919. freezeMode: 'hold',
  920. scrimmageMode: '2'
  921. };
  922.  
  923. var selects = [document.getElementById('donut-freezeMode'), document.getElementById('donut-scrimmageMode')];
  924. function fillSelects(options) {
  925. for (var i = 0; i < selects.length; i++) {
  926. var feature = selects[i].dataset.donutFeature, option = options[feature];
  927. var scrimmageModeAdjustRequired = feature == 'scrimmageMode' && info.scrimmageModeAdjusted === 0;
  928. if (scrimmageModeAdjustRequired) {
  929. info.scrimmageModeAdjusted++;
  930. localStorage['donut-info'] = JSON.stringify(info);
  931. }
  932. if (typeof option == 'undefined' && feature in defaultOptions) {
  933. scrimmageModeAdjustRequired = false;
  934. option = options[feature] = defaultOptions[feature];
  935. localStorage['donut-options'] = JSON.stringify(options);
  936. }
  937. if (scrimmageModeAdjustRequired && storageVersionValid && compareVersions(storageVersion, '1.7.11') < 0 && +option > 0) {
  938. option = options[feature] = +option - 1 + '';
  939. localStorage['donut-options'] = JSON.stringify(options);
  940. }
  941. var el = selects[i].querySelector('option[value="' + option + '"]');
  942. if (el) {
  943. selects[i].selectedIndex = el.index;
  944. selects[i].previousElementSibling.value = el.textContent;
  945. } else throw null;
  946. }
  947. }
  948.  
  949. var options = tryLocalStorage('options', defaultOptions, fillSelects);
  950.  
  951. for (var l = 0; l < selects.length; l++) {
  952. selects[l].addEventListener('change', function(e) {
  953. var feature = this.dataset.donutFeature;
  954. options[feature] = this.options[this.selectedIndex].value;
  955. localStorage['donut-options'] = JSON.stringify(options);
  956. this.previousElementSibling.value = this.options[this.selectedIndex].textContent;
  957. });
  958. }
  959.  
  960. var scrimmage = false, respawnCheckInterval = 0, respawnTimer = 0;
  961. gota.btnQueue.addEventListener('click', function() {
  962. scrimmage = true;
  963. if (switches.scrimmageAutoRespawn) scrimRespawn();
  964. });
  965.  
  966. function scrimmageFalse() {
  967. scrimmage = false;
  968. if (respawnTimer) {
  969. clearTimeout(respawnTimer);
  970. respawnTimer = respawnCheckInterval = 0;
  971. }
  972. }
  973. gota.btnLeaveMatch.addEventListener('click', scrimmageFalse);
  974.  
  975. function scrimRespawn() {
  976. var cells = gota.playerCells, cellsNum = parseInt(cells.innerText, 10);
  977. if (respawnCheckInterval) clearInterval(respawnCheckInterval);
  978. respawnCheckInterval = setInterval(function() {
  979. if (switches.scrimmageAutoRespawn && scrimmage) {
  980. var temp = parseInt(cells.innerText, 10);
  981. if (temp != cellsNum) {
  982. cellsNum = temp;
  983. if (cellsNum === 0) {
  984. if (gota.mainScrimmage.style.display == 'none') {
  985. respawnTimer = setTimeout(function() {
  986. if (switches.scrimmageAutoRespawn && scrimmage) {
  987. $(gota.btnPlay).click();
  988. respawnTimer = 0;
  989. scrimRespawn();
  990. }
  991. }, 10000);
  992. }
  993. clearInterval(respawnCheckInterval);
  994. respawnCheckInterval = 0;
  995. }
  996. }
  997. } else {
  998. clearInterval(respawnCheckInterval);
  999. respawnCheckInterval = 0;
  1000. }
  1001. }, 500);
  1002. }
  1003.  
  1004. var firstEscape = false;
  1005. function startScrimmage() {
  1006. var region = document.getElementsByClassName('server-active')[0].getAttribute('region');
  1007. var id = region == 'eu' ? 's_Beta' : region == 'na' ? 's_Jet' : 's_Citrus';
  1008. var interval = setInterval(function() {
  1009. if (document.getElementsByClassName('server-selected').length) {
  1010. clearInterval(interval);
  1011. var server = document.getElementById(id);
  1012. if (!server) return;
  1013. $(server).click();
  1014. $(gota.btnPlay).click();
  1015. setTimeout(function() {
  1016. if (!firstEscape && !done2v2) Alert('It looks like the server is not responding.<br>You may want to press <strong>Esc</strong> to go back to the game menu.', 20000);
  1017. }, 5000);
  1018. }
  1019. }, 250);
  1020. }
  1021. if (switches.startWithScrimmage) startScrimmage();
  1022.  
  1023. var scrimmageModeSet = false;
  1024. function isTeamScrimmage() {
  1025. var server = document.getElementsByClassName('server-selected')[0];
  1026. return server && server.lastElementChild.textContent == 'Scrimmage';
  1027. }
  1028.  
  1029. var done2v2 = false, checkigTean2v2 = false;
  1030. function team2v2() {
  1031. if (!checkigTean2v2) {
  1032. if (!isTeamScrimmage()) {
  1033. scrimmageFalse();
  1034. done2v2 = false;
  1035. } else if (!done2v2) {
  1036. checkigTean2v2 = true;
  1037. var interval = setInterval(function() {
  1038. if (gota.mainScrimmage.style.display == 'block') {
  1039. if (!scrimmageModeSet) {
  1040. $(gota.scrimmageModeSelect).val(options.scrimmageMode).change();
  1041. scrimmageModeSet = true;
  1042. }
  1043. clearTimeout(timeout);
  1044. clearInterval(interval);
  1045. done2v2 = true;
  1046. checkigTean2v2 = false;
  1047. if (switches.startWithScrimmage && alertWin.offsetHeight) alertWin.style.display = 'none';
  1048. }
  1049. }, 100), timeout = setTimeout(function() {
  1050. clearInterval(interval);
  1051. checkigTean2v2 = false;
  1052. }, 25000);
  1053. }
  1054. }
  1055. }
  1056.  
  1057. gota.btnPlay.addEventListener('click', team2v2);
  1058. gota.btnSpec.addEventListener('click', team2v2);
  1059. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址