GoBattle.io Combo Double Jump

Gobattle

// ==UserScript==
// @name         GoBattle.io Combo Double Jump
// @namespace    http://tampermonkey.net/
// @version      6.9
// @description  Gobattle 
// @match        https://gobattle.io/*
// @run-at       document-start
// @grant        none
// @license      MIT
// @author       Nightwave
// ==/UserScript==

(function () {
  'use strict';

  const TIMING = {
    dash:   { hold: 20, gap: 20 },     // ultra-fast double dash
    jump:   { hold: 70, gap: 60 },     // jump hold
    turn1:  { hold: 140, gap: 30 },    // first half of split turn
    turn2:  { hold: 140, gap: 50 },    // second half of split turn
    ultra:  { hold: 20, gap: 20 }      // Down Down → V ultra-fast
  };

  // Segment 1: Double Jump → Double Dash → Split Turn
  const SEG1 = [
    {key:'ArrowUp',   t:'jump'},      // first jump
    {key:'ArrowUp',   t:'jump'},      // second jump
    {key:'ArrowLeft', t:'dash'},
    {key:'ArrowLeft', t:'dash'},
    {key:'ArrowRight', t:'turn1'},
    {key:'ArrowRight', t:'turn2'}
  ];

  // Segment 2: Down Down → V ultra-fast
  const SEG2 = [
    {key:'ArrowDown', t:'ultra'},
    {key:'ArrowDown', t:'ultra'},
    {key:'KeyV',      t:'ultra'}
  ];

  const MIRROR = { ArrowLeft:'ArrowRight', ArrowRight:'ArrowLeft' };
  const mirrorSeq = seq => seq.map(s => ({...s, key: MIRROR[s.key]||s.key}));

  const origAdd = EventTarget.prototype.addEventListener;
  const downL = [], upL = [];
  EventTarget.prototype.addEventListener = function(type, fn, opt){
    if (type==='keydown' && typeof fn==='function') downL.push({t:this,f:fn});
    if (type==='keyup' && typeof fn==='function') upL.push({t:this,f:fn});
    return origAdd.call(this,type,fn,opt);
  };

  const codes = {
    ArrowLeft:[37,'ArrowLeft'], ArrowUp:[38,'ArrowUp'], ArrowRight:[39,'ArrowRight'], ArrowDown:[40,'ArrowDown'],
    KeyV:[86,'v']
  };

  function makeEvt(type, code){
    const c = codes[code] || [0, code];
    return {
      type,key:c[1],code,keyCode:c[0],which:c[0],repeat:false,
      altKey:false,ctrlKey:false,shiftKey:false,metaKey:false,
      target:document.activeElement||document.body,
      preventDefault(){},stopPropagation(){},stopImmediatePropagation(){}
    };
  }

  function fire(type,code){
    if (!code) return;
    const e = makeEvt(type,code);
    const list = type==='keydown'?downL:upL;
    for(const {t,f} of list){try{f.call(t,e);}catch{}}
  }

  const sleep=ms=>new Promise(r=>setTimeout(r,ms));

  async function tap(key,t){
    fire('keydown',key);
    await sleep(t.hold);
    fire('keyup',key);
    await sleep(t.gap);
  }

  async function runCombo(seq){
    const canvas=document.querySelector('canvas'); if(canvas) canvas.focus();
    for(const {key,t} of seq){ await tap(key,TIMING[t]); }
  }

  async function runFullCombo(seq1, seq2){
    await runCombo(seq1);
    await runCombo(seq2);
    // Release all keys to prevent stuck movement
    for(const k of ['ArrowLeft','ArrowRight','ArrowUp','ArrowDown','KeyV']) fire('keyup',k);
  }

  window.addEventListener('keydown',e=>{
    if(e.repeat)return;
    if(/INPUT|TEXTAREA/.test((e.target&&e.target.tagName)||''))return;
    if(e.key.toLowerCase()==='h'){e.preventDefault();runFullCombo(SEG1, SEG2);}
    if(e.key.toLowerCase()==='g'){e.preventDefault();runFullCombo(mirrorSeq(SEG1), mirrorSeq(SEG2));}
  },true);

})();