GoBattle.io Theme Panel

Add a floating panel to recolor GoBattle.io with tint overlays and filters (hue, saturation, brightness, contrast).

// ==UserScript==
// @name         GoBattle.io Theme Panel
// @namespace    https://greasyfork.org/users/your-username
// @version      1.1
// @description  Add a floating panel to recolor GoBattle.io with tint overlays and filters (hue, saturation, brightness, contrast).
// @author       YourName
// @match        *://gobattle.io/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  const STORAGE_KEY = 'gobattle_theme';
  const defaults = {
    tint: '#00aaff',
    blend: 'multiply',
    opacity: 0.35,
    hue: 0,
    saturate: 120,
    bright: 100,
    contrast: 100,
    applyAll: false,
    panelPos: { left: null, top: 80, right: 20 }
  };

  function loadSettings() {
    try {
      return Object.assign({}, defaults, JSON.parse(localStorage.getItem(STORAGE_KEY)));
    } catch {
      return { ...defaults };
    }
  }
  function saveSettings() {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
  }

  let settings = loadSettings();

  // Panel CSS
  const style = document.createElement('style');
  style.textContent = `
    #gb-theme-panel {
      width: 260px;
      position: fixed;
      background: #111;
      color: #eee;
      padding: 10px;
      border-radius: 10px;
      border: 1px solid #444;
      font-family: sans-serif;
      box-shadow: 0 4px 20px rgba(0,0,0,0.6);
      z-index: 100000;
      user-select: none;
    }
    #gb-panel-header { font-weight: bold; font-size: 13px; margin: 0 0 6px; cursor: move; text-align:center; }
    #gb-theme-panel label { display:block; font-size:12px; margin-top:6px; }
    #gb-theme-panel input[type="color"], 
    #gb-theme-panel select, 
    #gb-theme-panel input[type="range"], 
    #gb-theme-panel input[type="checkbox"] { width: 100%; }
    .gb-overlay { position: fixed; pointer-events: none; z-index: 99990; }
  `;
  document.head.appendChild(style);

  // Panel HTML
  const panel = document.createElement('div');
  panel.id = 'gb-theme-panel';
  panel.style.top = `${settings.panelPos.top}px`;
  if (settings.panelPos.left !== null) {
    panel.style.left = `${settings.panelPos.left}px`;
  } else {
    panel.style.right = `${settings.panelPos.right}px`;
  }

  panel.innerHTML = `
    <div id="gb-panel-header">KING MAMBA PANNEL</div>
    <label>Tint <input id="gb-tint" type="color" value="${settings.tint}"></label>
    <label>Blend
      <select id="gb-blend">
        <option>multiply</option><option>screen</option><option>overlay</option>
        <option>color</option><option>lighten</option><option>darken</option>
      </select>
    </label>
    <label>Opacity <input id="gb-opacity" type="range" min="0" max="100" value="${Math.round(settings.opacity*100)}"></label>
    <label>Hue <input id="gb-hue" type="range" min="0" max="360" value="${settings.hue}"></label>
    <label>Saturate <input id="gb-saturate" type="range" min="0" max="300" value="${settings.saturate}"></label>
    <label>Brightness <input id="gb-bright" type="range" min="0" max="200" value="${settings.bright}"></label>
    <label>Contrast <input id="gb-contrast" type="range" min="0" max="200" value="${settings.contrast}"></label>
    <label><input id="gb-applyall" type="checkbox"${settings.applyAll ? ' checked' : ''}> Apply to all canvases</label>
  `;
  document.body.appendChild(panel);

  // Drag panel
  (function () {
    const header = panel.querySelector('#gb-panel-header');
    let dragging = false, sx=0, sy=0, sl=0, st=0;
    header.addEventListener('mousedown', e => {
      dragging = true; sx=e.clientX; sy=e.clientY;
      const r=panel.getBoundingClientRect(); sl=r.left; st=r.top;
      panel.style.right='auto'; panel.style.left=`${sl}px`;
      document.addEventListener('mousemove', move);
      document.addEventListener('mouseup', up);
    });
    function move(e){ if(!dragging) return; panel.style.left=`${sl+(e.clientX-sx)}px`; panel.style.top=`${st+(e.clientY-sy)}px`;
      settings.panelPos.left=parseInt(panel.style.left); settings.panelPos.top=parseInt(panel.style.top);}
    function up(){dragging=false; document.removeEventListener('mousemove',move); document.removeEventListener('mouseup',up); saveSettings();}
  })();

  // Overlay
  const overlays = new Map();
  function applySettings() {
    const canvases = Array.from(document.querySelectorAll('canvas'));
    if (!canvases.length) return;
    const targets = settings.applyAll ? canvases : [canvases.sort((a,b)=>b.width*b.height - a.width*a.height)[0]];
    targets.forEach(c => {
      if (!overlays.has(c)) {
        const ov=document.createElement('div'); ov.className='gb-overlay'; document.body.appendChild(ov); overlays.set(c,ov);
      }
      const ov=overlays.get(c), r=c.getBoundingClientRect();
      ov.style.left=`${r.left}px`; ov.style.top=`${r.top}px`;
      ov.style.width=`${r.width}px`; ov.style.height=`${r.height}px`;
      ov.style.backgroundColor=settings.tint;
      ov.style.mixBlendMode=settings.blend;
      ov.style.opacity=settings.opacity;
      c.style.filter=`hue-rotate(${settings.hue}deg) saturate(${settings.saturate}%) brightness(${settings.bright}%) contrast(${settings.contrast}%)`;
    });
  }

  // Controls
  const el = id => panel.querySelector(id);
  function update(){
    settings.tint=el('#gb-tint').value;
    settings.blend=el('#gb-blend').value;
    settings.opacity=parseInt(el('#gb-opacity').value)/100;
    settings.hue=parseInt(el('#gb-hue').value);
    settings.saturate=parseInt(el('#gb-saturate').value);
    settings.bright=parseInt(el('#gb-bright').value);
    settings.contrast=parseInt(el('#gb-contrast').value);
    settings.applyAll=el('#gb-applyall').checked;
    saveSettings();
    applySettings();
  }
  panel.querySelectorAll('input,select').forEach(e=>e.addEventListener('input',update));

  // Run
  setInterval(applySettings, 1500);
  applySettings();
})();