Arras.io Gamepad support

Allows you to use your Gamepad to play arras.io! Press F1 to toggle between active and disabled. Controls: Right thumb stick to move your tank, Left thumb stick to aim, RT to shoot, LT for right click, A for autofire, B for autospin, X for override and Y for taking control of other tanks. Setup "commandKeys" in the script to match your keybindings ingame.

2024-12-01 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        Arras.io Gamepad support
// @namespace   Violentmonkey Scripts
// @match       *://arras.io/*
// @grant       none
// @version     1.1
// @author      alexandra
// @description Allows you to use your Gamepad to play arras.io! Press F1 to toggle between active and disabled. Controls: Right thumb stick to move your tank, Left thumb stick to aim, RT to shoot, LT for right click, A for autofire, B for autospin, X for override and Y for taking control of other tanks. Setup "commandKeys" in the script to match your keybindings ingame.
// @run-at      document-start
// @license     MIT
// ==/UserScript==

let commandKeys = { // change this if you want
  autofire: "KeyE",
  autospin: "KeyC",
  override: "KeyR",
  takeControl: "KeyF"
}

let enabled = false

window.addEventListener("keydown", (e) => {
  if (e.code === "F1") {
    e.preventDefault()
    enabled = !enabled
  }
})

let windowListener = window.addEventListener
window.addEventListener = function(...args) {
  let type = args[0]
  if (type === "keydown" || type === "keyup") {
    let callback = args[1]

    args[1] = function(event) {
      return callback.call(this, {
        isTrusted: true,
        key: event.key,
        code: event.code,
        preventDefault: function(){}
      })
    }
  }
  return windowListener.apply(this, args)
}

let cnvsElementListener = HTMLDivElement.prototype.addEventListener
HTMLDivElement.prototype.addEventListener = function(...args) {
  let type = args[0]
  if (type === "mousedown" || type === "mouseup" || type === "mousemove") {
    let callback = args[1]

    args[1] = function(event) {
      return callback.call(this, {
        isTrusted: true,
        clientX: event.clientX,
        clientY: event.clientY,
        button: event.button,
        preventDefault: function(){}
      })
    }
  }
  return cnvsElementListener.apply(this, args)
}

function keyEvent(key, type) {
  window.dispatchEvent(new KeyboardEvent(type ? "keydown" : "keyup", {
    code: key
  }))
}

function mouseEvent(x, y, button, type) {
  let event = new MouseEvent(type, {
    clientX: x,
    clientY: y,
    button: button,
  })
  document.querySelector("#canvas").dispatchEvent(event)
}

function eightDirMoveTo(cx, cy, ex, ey) {
  if (ex > cx) {
    keyEvent("ArrowLeft", false)
    keyEvent("ArrowRight", true)
  } else if (ex < cx) {
    keyEvent("ArrowLeft", true)
    keyEvent("ArrowRight", false)
  } else {
    keyEvent("ArrowLeft", false)
    keyEvent("ArrowRight", false)
  }

  if (ey > cy) {
    keyEvent("ArrowUp", false)
    keyEvent("ArrowDown", true)
  } else if (ey < cy) {
    keyEvent("ArrowUp", true)
    keyEvent("ArrowDown", false)
  } else {
    keyEvent("ArrowUp", false)
    keyEvent("ArrowDown", false)
  }
}

let stsText = document.createElement("div")
stsText.style.position = "absolute"
stsText.style.margin = "15px"
stsText.style.color = "white"
stsText.style.fontSize = "20px"
stsText.style.fontWeight = "bold"
stsText.style.fontFamily = "Ubuntu"
stsText.textContent = "No Gamepad connected!"
stsText.style.textShadow = "-1px 0 black, 0 1px black, 1px 0 black, 0 -1px black"
stsText.style.pointerEvents = "none"

let enText = document.createElement("div")
enText.style.position = "absolute"
enText.style.margin = "15px"
enText.style.color = "white"
enText.style.fontSize = "15px"
enText.style.fontWeight = "bold"
enText.style.fontFamily = "Ubuntu"
enText.textContent = "Disabled!"
enText.style.textShadow = "-1px 0 black, 0 1px black, 1px 0 black, 0 -1px black"
enText.style.top = "20px"
enText.style.pointerEvents = "none"

let crosshair = document.createElement("img")
crosshair.style.position = "absolute"
crosshair.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAXCAYAAADgKtSgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACjSURBVEhL1Y5BDsUgCEQ9SJe9/816Bqs2kAHFIrGLvmSSysD7Py2QIdvJ13H+S05ClFPCsKRKdbAvWUJI8W11JS74mL4XZq9YBzgn2nswHzJb9sx1J5gtmvIao2ORXuoS6J5id1iOgaJLoBPoJUT8I6DNjE4wW/TMdddhLY/k7W386BB9wHHMXPAhHuvviJjoRBjsS8KwZKdUg/LtfCuHOEjpBh0tTqQ2XaPuAAAAAElFTkSuQmCC"
crosshair.style.pointerEvents = "none"
crosshair.style.visibility = "hidden"
crosshair.style.margin = 0

let movArrow = document.createElement("img")
movArrow.style.position = "absolute"
movArrow.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAlCAYAAAAA7LqSAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEaSURBVFhH1ZYJCgMhEAQ1//+zYUQXSTxanWsLFiEIWvaMJiiQyidKLKMUPQGRNSVFZimwr2slUmFb/1NGK9h6xzqRlqu9WCfScpWOqkhKy70ey9Q42Wp1RhWJEaqirVKjySoSxG8igBAsY9ojYKlBB23e7IAMsZzk4tYiGTCdId0eAU9JhNOLwNM7kjlNx51IBZR5JrkVIcASz5Pc9ciIVe+4TqRldbivEXl9IiSA/JVxLbLzprgVQVIoX8adCFpKZXygH1TuWuRKPxGoqIkQI5mdXhhhXlocEoSpCFhKW6aqfcKVQsvW5E3+DodEbhp6hqoIwPF+PL0jV4fqIRGWPVgnwnaQVomwryspQvRkRNaUFiFaGaH1QvgCHmRUMUA6MH0AAAAASUVORK5CYII="
movArrow.style.pointerEvents = "none"
movArrow.style.visibility = "hidden"
movArrow.style.margin = 0
movArrow.style.padding = 0
movArrow.style.transformOrigin = "center center"
movArrow.width = 40

document.addEventListener("DOMContentLoaded", () => {
  document.body.appendChild(movArrow)
  document.body.appendChild(crosshair)
  document.body.appendChild(stsText)
  document.body.appendChild(enText)
})

let gamepadControls = {
  A: 0,
  B: 0,
  X: 0,
  Y: 0
}

// buttons[0] A
// buttons[1] B
// buttons[2] X
// buttons[3] Y
// buttons[4] LB
// buttons[5] RB
// buttons[6] LT
// buttons[7] RT

let gamepad
setInterval(() => {

  let gamepads = navigator.getGamepads().find((x) => x !== null)
  if (gamepads) {
    gamepad = gamepads
    stsText.textContent = "Gamepad connected!"
  } else {
    gamepad = undefined
    stsText.textContent = "No Gamepad connected!"
  }

  if (enabled) {
    enText.textContent = "Enabled!"
    if (gamepad) {
      crosshair.style.visibility = "visible"
      movArrow.style.visibility = "visible"
      let target = gamepad.axes.slice(0, 2).map((x) => Math.round(x))
      let cursorPos = gamepad.axes.slice(2, 4).map((x) => x + 1)
      cursorPos[0] *= window.innerWidth / 2
      cursorPos[1] *= window.innerHeight / 2
      if (gamepad.buttons[6].pressed) {
        keyEvent("ShiftLeft", true)
      } else {
        keyEvent("ShiftLeft", false)
      }

      if (gamepad.buttons[0].pressed) {
        gamepadControls.A++
      } else {
        gamepadControls.A = 0
      }
      if (gamepad.buttons[1].pressed) {
        gamepadControls.B++
      } else {
        gamepadControls.B = 0
      }
      if (gamepad.buttons[2].pressed) {
        gamepadControls.X++
      } else {
        gamepadControls.X = 0
      }
      if (gamepad.buttons[3].pressed) {
        gamepadControls.Y++
      } else {
        gamepadControls.Y = 0
      }

      if (gamepadControls.A === 1) {
        keyEvent(commandKeys.autofire, true)
        keyEvent(commandKeys.autofire, false)
      }

      if (gamepadControls.B === 1) {
        keyEvent(commandKeys.autospin, true)
        keyEvent(commandKeys.autospin, false)
      }

      if (gamepadControls.X === 1) {
        keyEvent(commandKeys.override, true)
        keyEvent(commandKeys.override, false)
      }

      if (gamepadControls.Y === 1) {
        keyEvent(commandKeys.takeControl, true)
        keyEvent(commandKeys.takeControl, false)
      }

      crosshair.style.left = cursorPos[0] - 11.5 + "px"
      crosshair.style.top = cursorPos[1] - 11.5 + "px"

      if (target[0] || target[1]) {
        let dir = Math.atan2(target[1], target[0])
        movArrow.style.transform = `rotate(${dir}rad)`
        movArrow.style.left = ((window.innerWidth / 2) + Math.cos(dir) * 75) - 20 + "px"
        movArrow.style.top = ((window.innerHeight / 2) + Math.sin(dir) * 75) - 15 + "px"
      } else {
        movArrow.style.visibility = "hidden"
      }

      eightDirMoveTo(0, 0, target[0], target[1])
      mouseEvent(
        cursorPos[0],
        cursorPos[1],
        0,
        gamepad.buttons[7].pressed ? "mousedown" : "mouseup"
      )
    } else {
      crosshair.style.visibility = "hidden"
      movArrow.style.visibility = "hidden"
    }
  } else {
    enText.textContent = "Disabled!"
    crosshair.style.visibility = "hidden"
    movArrow.style.visibility = "hidden"
  }
})


















// empty space wooo