Bonk Panel

Have a better UI interface!

安装此脚本
作者推荐脚本

您可能也喜欢Bonk Deobfuscator

安装此脚本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Bonk Panel
// @version      0.4.2
// @author       KOOKY WARIROR
// @description  Have a better UI interface!
// @match        https://bonk.io/gameframe-release.html
// @icon		 https://bonk.io/graphics/tt/favicon-32x32.png
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/split.js/1.6.5/split.min.js
// @license      MIT
// @namespace    https://greasyfork.org/users/999838
// @grant        none
// ==/UserScript==

/*
Features
---------------------
- Command Helper (type "/" and it will show up)
- Show scores and rounds to win when in game
- Show system chat in game
- Show players in game
- Import skin
- Image overlay
- Choose Hex Colour (Skin Editor)

And more...
(discover it urself🕵️‍♀️)
*/

// SKINS
class BYTEBUFFER {
	constructor() {
		this.index = 0
		this.buffer = new ArrayBuffer(102400)
		this.view = new DataView(this.buffer)
		this.implicitClassAliasArray = []
		this.implicitStringArray = []
		this.bodgeCaptureZoneDataIdentifierArray = []
	}
	readByte() {
		let returnval = this.view.getUint8(this.index)
		this.index += 1
		return returnval
	}
	writeByte(val) {
		this.view.setUint8(this.index, val)
		this.index += 1
	}
	readInt() {
		let returnval = this.view.getInt32(this.index)
		this.index += 4
		return returnval
	}
	writeInt(val) {
		this.view.setInt32(this.index, val)
		this.index += 4
	}
	readShort() {
		let returnval = this.view.getInt16(this.index)
		this.index += 2
		return returnval
	}
	writeShort(val) {
		this.view.setInt16(this.index, val)
		this.index += 2
	}
	readBoolean() {
		return this.readByte() == 1
	}
	writeBoolean(val) {
		if (val) {
			this.writeByte(1)
		} else {
			this.writeByte(0)
		}
	}
	readFloat() {
		let returnval = this.view.getFloat32(this.index)
		this.index += 4
		return returnval
	}
	writeFloat(val) {
		this.view.setFloat32(this.index, val)
		this.index += 4
	}
	toBase64() {
		let tmpstring = ""
		let tmparray = new Uint8Array(this.buffer)
		for (let i = 0; i < this.index; i++) {
			tmpstring += String.fromCharCode(tmparray[i])
		}
		return window.btoa(tmpstring)
	}
	fromBase64(val1, val2) {
		let tmpatob = window.atob(val1)
		let tmplength = tmpatob.length
		let tmparray = new Uint8Array(tmplength)
		for (let i = 0; i < tmplength; i++) {
			tmparray[i] = tmpatob.charCodeAt(i)
		}
		if (val2 === true) {
			tmparray = window.pako.inflate(tmparray)
		}
		this.buffer = tmparray.buffer.slice(tmparray.byteOffset, tmparray.byteLength + tmparray.byteOffset)
		this.view = new DataView(this.buffer)
		this.index = 0
	}
}
class AVATAR {
	constructor() {
		this.layers = []
		this.bc = 4492031
	}
	randomBC(val) {
		this.bc = val.colors[Math.floor(Math.random() * val.colors.length)]
	}
	makeSafe() {
		if (!(this.bc >= 0 && this.bc <= 16777215)) {
			this.bc = 4492031
		}
		for (let i = 0; i < this.layers.length; i++) {
			let layer = this.layers[i]
			if (layer) {
				if (!(layer.id >= 1 && layer.id <= 115)) {
					layer.id = 1
				}
				if (!(layer.x >= -99999 && layer.x <= 99999)) {
					layer.x = 0
				}
				if (!(layer.y >= -99999 && layer.y <= 99999)) {
					layer.y = 0
				}
				if (!(layer.scale >= -10 && layer.scale <= 10)) {
					layer.scale = 0.25
				}
				if (!(layer.angle >= -9999 && layer.angle <= 9999)) {
					layer.angle = 0
				}
				if (typeof layer.flipX != "boolean") {
					layer.flipX = false
				}
				if (typeof layer.flipY != "boolean") {
					layer.flipY = false
				}
				if (!(layer.color >= 0 && layer.color <= 16777215)) {
					layer.color = 0
				}
			}
		}
		for (let i = 0; i < this.layers.length; i++) {
			if (this.layers[i] == null) {
				this.layers.splice(i, 1)
				i--
			}
		}
	}
	fromObject(val) {
		if (val) {
			if (val.layers && typeof val.layers == "object" && val.layers.length >= 0 && val.layers.length <= 16) {
				this.layers = val.layers
			}
			this.bc = val.bc
		}
		this.makeSafe()
	}
	toString() {
		let tmpbuffer = new BYTEBUFFER()
		tmpbuffer.writeByte(10)
		tmpbuffer.writeByte(7)
		tmpbuffer.writeByte(3)
		tmpbuffer.writeByte(97)
		tmpbuffer.writeShort(2)
		tmpbuffer.writeByte(9)
		tmpbuffer.writeByte(this.layers.length * 2 + 1)
		tmpbuffer.writeByte(1)
		for (let i = 0; i < this.layers.length; i++) {
			let layer = this.layers[i]
			tmpbuffer.writeByte(10)
			if (i == 0) {
				tmpbuffer.writeByte(7)
				tmpbuffer.writeByte(5)
				tmpbuffer.writeByte(97)
				tmpbuffer.writeByte(108)
			} else {
				tmpbuffer.writeByte(5)
			}
			tmpbuffer.writeShort(1)
			tmpbuffer.writeShort(layer.id)
			tmpbuffer.writeFloat(layer.scale)
			tmpbuffer.writeFloat(layer.angle)
			tmpbuffer.writeFloat(layer.x)
			tmpbuffer.writeFloat(layer.y)
			tmpbuffer.writeBoolean(layer.flipX)
			tmpbuffer.writeBoolean(layer.flipY)
			tmpbuffer.writeInt(layer.color)
		}
		tmpbuffer.writeInt(this.bc)
		return encodeURIComponent(tmpbuffer.toBase64())
	}
	fromString(val) {
		if (val == "") {
			return
		}
		try {
			let tmpdecoded = decodeURIComponent(val)
			let tmpbuffer = new BYTEBUFFER()
			tmpbuffer.fromBase64(tmpdecoded)
			function tmpfunction(functionval) {
				var P5t = [arguments]
				let tmpobj = {}
				if (functionval.readByte().toString(16) == "a") {
					if (functionval.readByte() == 7) {
						functionval.readByte()
						functionval.readByte()
						functionval.readByte()
					} else {
					}
					P5t[3] = functionval.readShort()
					tmpobj.id = functionval.readShort()
					tmpobj.scale = functionval.readFloat()
					tmpobj.angle = functionval.readFloat()
					tmpobj.x = functionval.readFloat()
					tmpobj.y = functionval.readFloat()
					tmpobj.flipX = functionval.readBoolean()
					tmpobj.flipY = functionval.readBoolean()
					tmpobj.color = functionval.readInt()
				} else {
					tmpobj = null
				}
				return tmpobj
			}
			let tmpbyte = tmpbuffer.readByte()
			let tmpbyte2 = tmpbuffer.readByte()
			let tmpbyte3 = tmpbuffer.readByte()
			let tmpbyte4 = tmpbuffer.readByte()
			let tmpbyte5 = tmpbuffer.readByte()
			let tmpbyteover2 = (tmpbuffer.readByte() - 1) / 2
			let tmpbyte6 = tmpbuffer.readByte()
			while (tmpbyte6 != 1) {
				let tmpnumber = 0
				if (tmpbyte6 == 3) {
					tmpnumber = tmpbuffer.readByte() - 48
				} else {
					if (tmpbyte6 == 5) {
						tmpnumber = (tmpbuffer.readByte() - 48) * 10 + (tmpbuffer.readByte() - 48)
					}
				}
				this.layers[tmpnumber] = tmpfunction(tmpbuffer)
				tmpbyte6 = tmpbuffer.readByte()
			}
			for (let i = 0; i < tmpbyteover2; i++) {
				this.layers[i] = tmpfunction(tmpbuffer)
			}
			if (tmpbuffer.readShort() >= 2) {
				this.bc = tmpbuffer.readInt()
			}
			this.makeSafe()
		} catch (M8n) {
			this.layers = []
			this.bc = 4492031
		}
	}
}
fetch("https://raw.githubusercontent.com/kookywarrior/bonkio-skins/main/skins.js")
	.then((response) => response.text())
	.then((response) => {
		eval(response)
	})
	.catch((err) => console.log(err))

// REMOVE ANNOYING STUFF
function removeEleByID(id) {
	let e = window.top.document.getElementById(id)
	if (e !== null) e.remove()
}
removeEleByID("descriptioncontainer")
removeEleByID("bonk_d_1")
removeEleByID("bonk_d_2")
window.top.document.body.getElementsByTagName("style")[0].innerHTML += `
#maingameframe { margin: 0 !important; margin-top: 0 !important; }
#adboxverticalleftCurse { display: none !important; }
#adboxverticalCurse { display: none !important; }
#bonkioheader { display: none !important; }
body { overflow: hidden !important; }`

// FIX CHAT STUFF
function hideEleByID(id) {
	let e = document.getElementById(id)
	if (e !== null) e.hidden = true
}
hideEleByID("newbonklobby_chat_lowerline")
hideEleByID("newbonklobby_chat_lowerinstruction")
hideEleByID("ingamechatbox")
hideEleByID("newbonklobby_chat_content")
document.getElementById("newbonklobby_chat_content").style.height = "calc(100% - 36px)"
document.body.appendChild(document.getElementById("newbonklobby_chat_input"))
document.body.appendChild(document.getElementById("ingamechatinputtext"))
document.getElementById("newbonklobby_chatbox").getElementsByClassName("newbonklobby_boxtop newbonklobby_boxtop_classic")[0].textContent =
	"More Coming Soon..."

// APPEND FPS TO TOP BAR
const FPS = document.createElement("div")
FPS.innerText = 0
FPS.style = `color: var(--bonk_theme_top_bar_text, #ffffff8f) !important;font-family:"futurept_b1";line-height:35px;display:inline-block;padding-left:15px;padding-right:15px;`
FPS.className = "niceborderright"
document.getElementById("pretty_top_bar").appendChild(FPS)
function fpsUpdate() {
	const updateDelay = 500
	let lastFpsUpdate = 0
	let frames = 0
	function updateFPS() {
		let now = Date.now()
		let elapsed = now - lastFpsUpdate
		if (elapsed < updateDelay) {
			++frames
		} else {
			FPS.innerText = `${Math.round(frames / (elapsed / 1000))} FPS`
			frames = 0
			lastFpsUpdate = now
		}
		window.requestAnimationFrame(updateFPS)
	}
	lastFpsUpdate = Date.now()
	window.requestAnimationFrame(updateFPS)
}
fpsUpdate()

// ADD MAIN CONTAINER
const container = document.createElement("div")
container.id = "bonkpanelcontainer"
document.getElementById("pagecontainer").insertBefore(container, document.getElementById("xpbarcontainer"))
container.appendChild(document.getElementById("bonkiocontainer"))

// ADD PANEL CONTAINER
const panelLeft = document.createElement("div")
panelLeft.style.visibility = "hidden"
panelLeft.className = "panelcontainer"
container.insertBefore(panelLeft, document.getElementById("bonkiocontainer"))
const panelRight = document.createElement("div")
panelRight.style.visibility = "hidden"
panelRight.className = "panelcontainer"
container.appendChild(panelRight)

// ADD PANELS
for (let index = 1; index < 5; index++) {
	const panel = document.createElement("div")
	panel.id = `panel${index}`
	panel.className = "panel"
	const injectTO = index % 2 == 0 ? panelRight : panelLeft
	injectTO.appendChild(panel)
}
let leftSize = localStorage.getItem("panel-left-size") ? JSON.parse(localStorage.getItem("panel-left-size")) : [40, 60]
let rightSize = localStorage.getItem("panel-right-size") ? JSON.parse(localStorage.getItem("panel-right-size")) : [40, 60]
let leftSplit, rightSplit
setTimeout(() => {
	leftSplit = Split(["#panel1", "#panel3"], {
		sizes: leftSize,
		direction: "vertical",
		gutterSize: 15,
		snapOffset: 0,
		onDragEnd: function (e) {
			localStorage.setItem("panel-left-size", JSON.stringify(e))
		}
	})
	rightSplit = Split(["#panel2", "#panel4"], {
		sizes: rightSize,
		direction: "vertical",
		gutterSize: 15,
		snapOffset: 0,
		onDragEnd: function (e) {
			localStorage.setItem("panel-right-size", JSON.stringify(e))
		}
	})
}, 0)

// ADD COMMAND PANEL
let haveWS = false
const commandContainer = document.createElement("div")
commandContainer.id = "commandcontainer"
commandContainer.style.display = "none"
const commandBackground = document.createElement("div")
commandBackground.id = "commandbackground"
commandContainer.appendChild(commandBackground)
const commandWindow = document.createElement("div")
commandWindow.id = "commandpanel"
commandWindow.classList.add("windowShadow")
const commandTopBar = document.createElement("div")
commandTopBar.className = "newbonklobby_boxtop newbonklobby_boxtop_classic"
commandTopBar.textContent = "Command Helper"
commandWindow.appendChild(commandTopBar)
const commandListContainer = document.createElement("div")
commandListContainer.id = "commandlistcontainer"
commandListContainer.className = "chatcontainer"
commandWindow.appendChild(commandListContainer)
commandContainer.appendChild(commandWindow)
document.getElementById("bonkiocontainer").appendChild(commandContainer)
let COMMANDS = {
	kick: (id) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		id = parseInt(id)
		if (id == null || typeof id != "number") {
			FUNCTIONS.showSystemMessage("Failed, playerID not found", "#b53030")
			return
		}
		if (ROOM_VAR.myID == id) {
			FUNCTIONS.showSystemMessage("Failed, you can't use this command to yourself", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		if (ROOM_VAR.players[id] == null) {
			FUNCTIONS.showSystemMessage("Failed, player not found in this room", "#b53030")
			return
		}
		iosend([
			9,
			{
				banshortid: id,
				kickonly: true
			}
		])
	},
	ban: (id) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		id = parseInt(id)
		if (id == null || typeof id != "number") {
			FUNCTIONS.showSystemMessage("Failed, playerID not found", "#b53030")
			return
		}
		if (ROOM_VAR.myID == id) {
			FUNCTIONS.showSystemMessage("Failed, you can't use this command to yourself", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		if (ROOM_VAR.players[id] == null) {
			FUNCTIONS.showSystemMessage("Failed, player not found in this room", "#b53030")
			return
		}
		iosend([
			9,
			{
				banshortid: id,
				kickonly: false
			}
		])
	},
	mute: (id) => {
		id = parseInt(id)
		if (id == null || typeof id != "number") {
			FUNCTIONS.showSystemMessage("Failed, playerID not found", "#b53030")
			return
		}
		if (ROOM_VAR.myID == id) {
			FUNCTIONS.showSystemMessage("Failed, you can't use this command to yourself", "#b53030")
			return
		}
		if (ROOM_VAR.players[id] == null) {
			FUNCTIONS.showSystemMessage("Failed, player not found in this room", "#b53030")
			return
		}
		ROOM_VAR.players[id].mute = true
		PROCESSCOMMAND(`/mute '${ROOM_VAR.players[id].userName}'`)
	},
	unmute: (id) => {
		id = parseInt(id)
		if (id == null || typeof id != "number") {
			FUNCTIONS.showSystemMessage("Failed, playerID not found", "#b53030")
			return
		}
		if (ROOM_VAR.myID == id) {
			FUNCTIONS.showSystemMessage("Failed, you can't use this command to yourself", "#b53030")
			return
		}
		if (ROOM_VAR.players[id] == null) {
			FUNCTIONS.showSystemMessage("Failed, player not found in this room", "#b53030")
			return
		}
		ROOM_VAR.players[id].mute = false
		PROCESSCOMMAND(`/unmute '${ROOM_VAR.players[id].userName}'`)
	},
	balance: (id, size) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		id = parseInt(id)
		size = parseInt(size)
		if (id == null || typeof id != "number") {
			FUNCTIONS.showSystemMessage("Failed, playerID not found", "#b53030")
			return
		}
		if (size == null || typeof size != "number") {
			FUNCTIONS.showSystemMessage("Failed, size not found", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		if (ROOM_VAR.players[id] == null) {
			FUNCTIONS.showSystemMessage("Failed, player not found in this room", "#b53030")
			return
		}
		if (size < -100 || size > 100) {
			FUNCTIONS.showSystemMessage("Failed, size must be between -100 and 100", "#b53030")
			return
		}
		iosend([
			29,
			{
				sid: id,
				bal: size
			}
		])
	},
	balanceall: (size) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		size = parseInt(size)
		if (size == null || typeof size != "number") {
			FUNCTIONS.showSystemMessage("Failed, size not found", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		if (size < -100 || size > 100) {
			FUNCTIONS.showSystemMessage("Failed, size must be between -100 and 100", "#b53030")
			return
		}
		for (let i = 0; i < ROOM_VAR.players.length; i++) {
			const element = ROOM_VAR.players[i]
			if (element == null) continue
			iosend([
				29,
				{
					sid: i,
					bal: size
				}
			])
		}
	},
	roomname: (name) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		if (!name) {
			FUNCTIONS.showSystemMessage("Failed, name not found", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		iosend([
			52,
			{
				newName: name
			}
		])
	},
	roompass: (password) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		if (!password) {
			FUNCTIONS.showSystemMessage("Failed, password not found", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		iosend([
			53,
			{
				newPass: password
			}
		])
	},
	clearroompass: () => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		iosend([
			53,
			{
				newPass: ""
			}
		])
	},
	move: (id, teamid) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		id = parseInt(id)
		teamid = parseInt(teamid)
		if (id == null || typeof id != "number") {
			FUNCTIONS.showSystemMessage("Failed, playerID not found", "#b53030")
			return
		}
		if (teamid == null || typeof teamid != "number") {
			FUNCTIONS.showSystemMessage("Failed, teamID not found", "#b53030")
			return
		}
		if (![0, 1, 2, 3, 4, 5].includes(teamid)) {
			FUNCTIONS.showSystemMessage("Failed, team not found", "#b53030")
			return
		}
		if (ROOM_VAR.players[id] == null) {
			FUNCTIONS.showSystemMessage("Failed, player not found in this room", "#b53030")
			return
		}
		if (id == ROOM_VAR.myID) {
			iosend([
				6,
				{
					targetTeam: teamid
				}
			])
		} else if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		} else {
			iosend([
				26,
				{
					targetID: id,
					targetTeam: teamid
				}
			])
		}
		if (ROOM_VAR.myID == ROOM_VAR.hostID) {
			if (teamid < 0) {
				ROOM_VAR.stateFunction.hostHandlePlayerJoined(id, ROOM_VAR.players.length, teamid)
			} else {
				ROOM_VAR.stateFunction.hostHandlePlayerLeft(id)
			}
		}
	},
	moveall: (teamid) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		teamid = parseInt(teamid)
		if (teamid == null || typeof teamid != "number") {
			FUNCTIONS.showSystemMessage("Failed, teamID not found", "#b53030")
			return
		}
		if (![0, 1, 2, 3, 4, 5].includes(teamid)) {
			FUNCTIONS.showSystemMessage("Failed, team not found", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		for (let i = 0; i < ROOM_VAR.players.length; i++) {
			const element = ROOM_VAR.players[i]
			if (element == null) continue
			if (i == ROOM_VAR.myID) {
				iosend([
					6,
					{
						targetTeam: teamid
					}
				])
			} else {
				iosend([
					26,
					{
						targetID: i,
						targetTeam: teamid
					}
				])
			}
			if (teamid < 0) {
				ROOM_VAR.stateFunction.hostHandlePlayerJoined(i, ROOM_VAR.players.length, teamid)
			} else {
				ROOM_VAR.stateFunction.hostHandlePlayerLeft(i)
			}
		}
	},
	mode: (modeid) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		modeid = modeid.replace(" ", "")
		if (!modeid) {
			FUNCTIONS.showSystemMessage("Failed, modeID not found", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		iosend([
			20,
			{
				ga: modeid == "f" ? "f" : "b",
				mo: modeid
			}
		])
	},
	team: (option) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		option = option.replace(" ", "")
		if (!option) {
			FUNCTIONS.showSystemMessage("Failed, option not found", "#b53030")
			return
		}
		if (!["on", "off"].includes(option)) {
			FUNCTIONS.showSystemMessage("Failed, invalid option", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		iosend([
			32,
			{
				t: option == "on" ? true : false
			}
		])
	},
	lock: (option) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		option = option.replace(" ", "")
		if (!option) {
			FUNCTIONS.showSystemMessage("Failed, option not found", "#b53030")
			return
		}
		if (!["on", "off"].includes(option)) {
			FUNCTIONS.showSystemMessage("Failed, invalid option", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		iosend([
			7,
			{
				teamLock: option == "on" ? true : false
			}
		])
	},
	skin: (id) => {
		id = parseInt(id)
		if (id == null || typeof id != "number") {
			FUNCTIONS.showSystemMessage("Failed, playerID not found", "#b53030")
			return
		}
		if (ROOM_VAR.players[id] == null) {
			FUNCTIONS.showSystemMessage("Failed, player not found in this room", "#b53030")
			return
		}
		const a = document.createElement("a")
		const file = new Blob([JSON.stringify(ROOM_VAR.players[id].avatar)], { type: "text/plain" })
		a.href = URL.createObjectURL(file)
		a.download = `avatar_${ROOM_VAR.players[id].userName}_${Date.now()}`
		a.click()
	},
	link: () => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		document.getElementById("newbonklobby_linkbutton").click()
	},
	start: () => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		STARTGAME()
	},
	round: (number) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		number = parseInt(number)
		if (number == null || typeof number != "number") {
			FUNCTIONS.showSystemMessage("Failed, invalid number", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		iosend([
			21,
			{
				w: number
			}
		])
	},
	clear: () => {
		document.getElementById("messagecontainer").innerHTML = ""
	},
	clearsys: () => {
		document.getElementById("systemcontainer").innerHTML = ""
	},
	size: (option, topsize, bottomsize) => {
		option = option.replace(" ", "")
		topsize = parseInt(topsize)
		bottomsize = parseInt(bottomsize)
		if (!["left", "right"].includes(option)) {
			FUNCTIONS.showSystemMessage("Failed, invalid option", "#b53030")
			return
		}
		if (topsize == null || typeof topsize != "number" || bottomsize == null || typeof bottomsize != "number") {
			FUNCTIONS.showSystemMessage("Failed, invalid number", "#b53030")
			return
		}
		if (topsize + bottomsize != 100) {
			FUNCTIONS.showSystemMessage("Failed, the sum of top and bottom size must be 100", "#b53030")
			return
		}
		let sizes = [topsize, bottomsize]
		if (option == "left") {
			leftSplit.setSizes(sizes)
			localStorage.setItem("panel-left-size", JSON.stringify(sizes))
		} else {
			rightSplit.setSizes(sizes)
			localStorage.setItem("panel-right-size", JSON.stringify(sizes))
		}
	},
	fav: () => {
		PROCESSCOMMAND("/fav")
	},
	unfav: () => {
		PROCESSCOMMAND("/unfav")
	},
	givehost: (id) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		id = parseInt(id)
		if (id == null || typeof id != "number") {
			FUNCTIONS.showSystemMessage("Failed, playerID not found", "#b53030")
			return
		}
		if (ROOM_VAR.myID == id) {
			FUNCTIONS.showSystemMessage("Failed, you can't use this command to yourself", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		if (ROOM_VAR.players[id] == null) {
			FUNCTIONS.showSystemMessage("Failed, player not found in this room", "#b53030")
			return
		}
		iosend([
			34,
			{
				id: id
			}
		])
	},
	countdown: (option) => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		option = option.replace(" ", "")
		if (!["1", "2", "3"].includes(option)) {
			FUNCTIONS.showSystemMessage("Failed, invalid option", "#b53030")
			return
		}
		iosend([36, { num: parseInt(option) }])
	},
	abort: () => {
		if (ROOM_VAR.quick) {
			FUNCTIONS.showSystemMessage("Failed, unavailable in quick play", "#b53030")
			return
		}
		if (ROOM_VAR.myID != ROOM_VAR.hostID) {
			FUNCTIONS.showSystemMessage("Failed, you must be room host", "#b53030")
			return
		}
		iosend([37])
	},
	xp: () => {
		FUNCTIONS.showSystemMessage(`${ROOM_VAR.xpEarned}xp earned`, "#0955c7")
	},
	earnxp: () => {
		iosend([38])
	},
	ready: (option) => {
		option = option.replace(" ", "")
		if (!["true", "false"].includes(option)) {
			FUNCTIONS.showSystemMessage("Failed, invalid option", "#b53030")
			return
		}
		iosend([16, { ready: option === "true" }])
	},
	zoom: (number) => {
		number = parseFloat(number)
		if (number == null || typeof number != "number") {
			FUNCTIONS.showSystemMessage("Failed, invalid number", "#b53030")
			return
		}
		scaler = number
		RESCALESTAGE()
	}
}
let SEARCH = {
	addInput: (addText = "", setInstead = false) => {
		if (addText == "") return
		if (setInstead) {
			document.getElementById("bonkpanelchatinput").value = addText
		} else {
			document.getElementById("bonkpanelchatinput").value += addText
		}
		FUNCTIONS.showCommandHelper(document.getElementById("bonkpanelchatinput").value)
	},
	remove: () => {
		commandListContainer.innerHTML = ""
	},
	commands: (filterText = "") => {
		SEARCH.remove()
		COMMANDLIST.forEach((e) => {
			if (!e.name.startsWith(filterText)) return
			if (ROOM_VAR.hostID != ROOM_VAR.myID && e.host) return
			if (ROOM_VAR.quick && e.noQuick) return

			let params = ""
			e.param.forEach((a) => {
				params += ` <${a}>`
			})

			const card = document.createElement("div")
			card.className = "windowShadow commandscardcontainer"
			card.addEventListener("click", () => {
				SEARCH.addInput("/" + e.name + " ", true)
				document.getElementById("bonkpanelchatinput").focus()
			})
			if (document.getElementById("bonkpanelchatinput").onkeydown == null) {
				document.getElementById("bonkpanelchatinput").onkeydown = function (event) {
					if (event.code == "Tab") {
						event.preventDefault()
						document.getElementById("bonkpanelchatinput").onkeydown = null
						SEARCH.addInput("/" + e.name + " ", true)
						document.getElementById("bonkpanelchatinput").focus()
					}
				}
			}

			const title = document.createElement("div")
			title.className = "commandscardtitle"
			title.textContent = e.name + params
			card.appendChild(title)

			const description = document.createElement("div")
			description.className = "commandscarddescription"
			description.textContent = e.des
			card.appendChild(description)

			commandListContainer.appendChild(card)
		})
	},
	players: (command) => {
		SEARCH.remove()

		for (let i = 0; i < ROOM_VAR.players.length; i++) {
			if (command.withoutMe && i == ROOM_VAR.myID) continue
			if (command.checkHost && ROOM_VAR.myID != ROOM_VAR.hostID && i != ROOM_VAR.myID) continue

			const element = ROOM_VAR.players[i]
			if (element == null) continue

			const card = document.createElement("div")
			card.className = "windowShadow commandscardcontainer"
			card.style.flexDirection = "row"
			card.addEventListener("click", () => {
				SEARCH.addInput(i.toString() + " ")
				document.getElementById("bonkpanelchatinput").focus()
			})
			if (document.getElementById("bonkpanelchatinput").onkeydown == null) {
				document.getElementById("bonkpanelchatinput").onkeydown = function (event) {
					if (event.code == "Tab") {
						event.preventDefault()
						document.getElementById("bonkpanelchatinput").onkeydown = null
						SEARCH.addInput(i.toString() + " ")
						document.getElementById("bonkpanelchatinput").focus()
					}
				}
			}

			const img = document.createElement("div")
			img.classList.add("commandplayerimgcontainer")
			card.appendChild(img)

			const textcontainer = document.createElement("div")
			textcontainer.style = `width: calc(100% - 100px); display: flex; flex-direction: column; justify-content: space-evenly`
			const name = document.createElement("div")
			name.className = "commandscardtitle"
			name.textContent = element.userName
			textcontainer.appendChild(name)
			const id = document.createElement("div")
			id.style.marginBottom = "0"
			id.className = "commandscarddescription"
			id.textContent = `ID: ${i.toString()}`
			textcontainer.appendChild(id)
			const levelorguest = document.createElement("div")
			levelorguest.className = "commandscarddescription"
			levelorguest.textContent = element.guest ? "Guest" : `Level ${element.level}`
			textcontainer.appendChild(levelorguest)
			card.appendChild(textcontainer)
			commandListContainer.appendChild(card)
			if (ROOM_VAR.commandAvatarCache[element.userName] && ROOM_VAR.commandAvatarCache[element.userName][1]) {
				img.appendChild(ROOM_VAR.commandAvatarCache[element.userName][1].cloneNode(true))
			} else {
				try {
					FUNCTIONS.createAvatarImage(element.avatar, 1, img, "newbonklobby_chat_msg_avatar", 100, 100, ROOM_VAR.commandAvatarCache, i, 1, 1, 0.25)
				} catch (error) {
					console.error(error)
				}
			}
		}
	},
	teams: () => {
		SEARCH.remove()

		Array.from([
			["Spectate", 0],
			["Free for all", 1],
			["Red", 2],
			["Blue", 3],
			["Green", 4],
			["Yellow", 5]
		]).forEach((e) => {
			const card = document.createElement("div")
			card.className = "windowShadow commandscardcontainer"
			card.addEventListener("click", () => {
				SEARCH.addInput(e[1].toString() + " ")
				document.getElementById("bonkpanelchatinput").focus()
			})
			if (document.getElementById("bonkpanelchatinput").onkeydown == null) {
				document.getElementById("bonkpanelchatinput").onkeydown = function (event) {
					if (event.code == "Tab") {
						event.preventDefault()
						document.getElementById("bonkpanelchatinput").onkeydown = null
						SEARCH.addInput(e[1].toString() + " ")
						document.getElementById("bonkpanelchatinput").focus()
					}
				}
			}
			const title = document.createElement("div")
			title.className = "commandscardtitle"
			title.textContent = e[0]
			card.appendChild(title)
			const id = document.createElement("div")
			id.className = "commandscarddescription"
			id.textContent = "ID: " + e[1].toString()
			card.appendChild(id)
			commandListContainer.appendChild(card)
		})
	},
	modes: () => {
		SEARCH.remove()

		Array.from([
			["Classic", "b"],
			["Arrows", "ar"],
			["Death arrows", "ard"],
			["Grapple", "sp"],
			["Football", "f"],
			["VTOL", "v"]
		]).forEach((e) => {
			const card = document.createElement("div")
			card.className = "windowShadow commandscardcontainer"
			card.addEventListener("click", () => {
				SEARCH.addInput(e[1].toString() + " ")
				document.getElementById("bonkpanelchatinput").focus()
			})
			if (document.getElementById("bonkpanelchatinput").onkeydown == null) {
				document.getElementById("bonkpanelchatinput").onkeydown = function (event) {
					if (event.code == "Tab") {
						event.preventDefault()
						document.getElementById("bonkpanelchatinput").onkeydown = null
						SEARCH.addInput(e[1].toString() + " ")
						document.getElementById("bonkpanelchatinput").focus()
					}
				}
			}
			const title = document.createElement("div")
			title.className = "commandscardtitle"
			title.textContent = e[0]
			card.appendChild(title)
			const id = document.createElement("div")
			id.className = "commandscarddescription"
			id.textContent = "ID: " + e[1].toString()
			card.appendChild(id)
			commandListContainer.appendChild(card)
		})
	},
	option: (command) => {
		SEARCH.remove()

		Array.from(command.option).forEach((e) => {
			const card = document.createElement("div")
			card.className = "windowShadow commandscardcontainer"
			card.addEventListener("click", () => {
				SEARCH.addInput(e + " ")
				document.getElementById("bonkpanelchatinput").focus()
			})
			if (document.getElementById("bonkpanelchatinput").onkeydown == null) {
				document.getElementById("bonkpanelchatinput").onkeydown = function (event) {
					if (event.code == "Tab") {
						event.preventDefault()
						document.getElementById("bonkpanelchatinput").onkeydown = null
						SEARCH.addInput(e + " ")
						document.getElementById("bonkpanelchatinput").focus()
					}
				}
			}
			const title = document.createElement("div")
			title.style.paddingBottom = "8px"
			title.className = "commandscardtitle"
			title.textContent = e
			card.appendChild(title)
			commandListContainer.appendChild(card)
		})
	},
	nothing: () => {
		SEARCH.remove()
	}
}
let COMMANDLIST = [
	{
		name: "kick",
		des: "Removes a player from the game",
		param: ["player:ID"],
		func: ["players"],
		withoutMe: true,
		host: true,
		noQuick: true
	},
	{
		name: "ban",
		des: "Removes and prevents a player from joining the game",
		param: ["player:ID"],
		func: ["players"],
		withoutMe: true,
		host: true,
		noQuick: true
	},
	{
		name: "mute",
		des: "Prevents the chat messages of a player from registering on your screen",
		param: ["player:ID"],
		func: ["players"],
		withoutMe: true,
		host: false
	},
	{
		name: "unmute",
		des: "Allows for future chat messages of a player to register on your screen again",
		param: ["player:ID"],
		func: ["players"],
		withoutMe: true,
		host: false
	},
	{
		name: "balance",
		des: "Changes the size of a player",
		param: ["player:ID", "size:number"],
		func: ["players", "nothing"],
		host: true,
		noQuick: true
	},
	{
		name: "balanceall",
		des: "Change the size of all players",
		param: ["size:number"],
		func: ["nothing"],
		host: true,
		noQuick: true
	},
	{
		name: "roomname",
		des: "Changes the name of the room",
		param: ["name:string"],
		func: ["nothing"],
		host: true,
		noQuick: true
	},
	{
		name: "roompass",
		des: "Changes the password of the room",
		param: ["name:string"],
		func: ["nothing"],
		host: true,
		noQuick: true
	},
	{
		name: "clearroompass",
		des: "The room no longer need a password to join",
		param: [],
		func: [],
		host: true,
		noQuick: true
	},
	{
		name: "move",
		des: "Move a player to another team",
		param: ["player:ID", "team:ID"],
		func: ["players", "teams"],
		checkHost: true,
		host: false,
		noQuick: true
	},
	{
		name: "moveall",
		des: "Move all players to a team",
		param: ["team:ID"],
		func: ["teams"],
		host: true,
		noQuick: true
	},
	{
		name: "mode",
		des: "Changes the game mode of the room",
		param: ["mode:ID"],
		func: ["modes"],
		host: true,
		noQuick: true
	},
	{
		name: "team",
		des: "Enable or disable teams",
		param: ["teams:option"],
		func: ["option"],
		option: ["on", "off"],
		host: true,
		noQuick: true
	},
	{
		name: "lock",
		des: "Allow or prevent players from moving themselves",
		param: ["lock:option"],
		func: ["option"],
		option: ["on", "off"],
		host: true,
		noQuick: true
	},
	{
		name: "link",
		des: "Copy the auto join link to your clipboard",
		param: [],
		func: [],
		host: false,
		noQuick: true
	},
	{
		name: "skin",
		des: "Download the skin of a player",
		param: ["player:ID"],
		func: ["players"],
		host: false
	},
	{
		name: "start",
		des: "Instantly start the game without countdown",
		param: [],
		func: [],
		host: true,
		noQuick: true
	},
	{
		name: "round",
		des: "Set the rounds to win",
		param: ["rounds:number"],
		func: ["nothing"],
		host: true,
		noQuick: true
	},
	{
		name: "clear",
		des: "Clears the chat",
		param: [],
		func: [],
		host: false
	},
	{
		name: "clearsys",
		des: "Clears the system chat",
		param: [],
		func: [],
		host: false
	},
	{
		name: "size",
		des: ["Set the size of the panels"],
		param: ["panel:option", "topsize:number", "bottomsize:number"],
		func: ["option", "nothing", "nothing"],
		option: ["left", "right"],
		host: false
	},
	{
		name: "fav",
		des: ["Adds the current map to your favourites list"],
		param: [],
		func: [],
		host: false
	},
	{
		name: "unfav",
		des: ["Removes the current map from your favourites list"],
		param: [],
		func: [],
		host: false
	},
	{
		name: "givehost",
		des: ["Gives host to a player"],
		param: ["player:id"],
		func: ["players"],
		withoutMe: true,
		host: true,
		noQuick: true
	},
	{
		name: "countdown",
		des: ["Sends a countdown message"],
		param: ["countdown:option"],
		func: ["option"],
		option: ["1", "2", "3"],
		host: true,
		noQuick: true
	},
	{
		name: "abort",
		des: ["Sends an abort countdown message"],
		param: [],
		func: [],
		host: true,
		noQuick: true
	},
	{
		name: "xp",
		des: ["Tells you how much xp you earned in this room"],
		param: [],
		func: [],
		host: false
	},
	{
		name: "earnxp",
		des: ["Instantly gain xp"],
		param: [],
		func: [],
		host: false
	},
	{
		name: "ready",
		des: ["Change your ready state"],
		param: ["state:option"],
		func: ["option"],
		option: ["true", "false"],
		host: false
	},
	{
		name: "zoom",
		des: "Set the scale of the stage",
		param: ["scale:number"],
		func: ["nothing"],
		host: false
	}
]

// SCENE
let ROOM_VAR = {
	xpEarned: 0,
	myID: 0,
	hostID: 0,
	autoJoinID: null,
	autoJoinPassBypass: null,
	players: [],
	quick: false,
	bal: [],
	tmpImgTextPing: [],
	state: null,
	stateFunction: null,
	chatAvatarCache: [],
	commandAvatarCache: [],
	playersAvatarCache: []
}
let FUNCTIONS = {
	showCommandHelper: (value = "") => {
		if (value == "" || !value.startsWith("/") || value.startsWith("/ ")) {
			commandContainer.style.display = "none"
			return
		}
		const stage = value.split(" ")
		if (stage.length == 1) {
			commandContainer.style.display = "flex"
			commandTopBar.textContent = stage[0].substring(1) || "Command Helper"
			SEARCH.commands(stage[0].substring(1))
		} else {
			let index = COMMANDLIST.findIndex((z) => z.name === stage[0].substring(1))
			if (index == -1) {
				commandContainer.style.display = "none"
			} else {
				commandContainer.style.display = "flex"
				let command = COMMANDLIST[index]
				let params = ""
				for (let i = 0; i < stage.length - 1; i++) {
					if (command.param[i]) {
						params += ` <${command.param[i]}>`
					}
				}
				if (params == "") {
					SEARCH["nothing"]()
				} else if (command.func[stage.length - 2]) {
					SEARCH[command.func[stage.length - 2]](command)
				} else {
					SEARCH["nothing"]()
				}
				commandTopBar.textContent = command.name + params
			}
		}
	},
	processCommand: (value = "") => {
		if (value == "/" || value.startsWith("/ ")) {
			FUNCTIONS.showSystemMessage("Failed, invalid command", "#b53030")
			commandContainer.style.display = "none"
			return
		}
		const stage = value.substring(1).split(" ")
		if (COMMANDS[stage[0]] == null) {
			FUNCTIONS.showSystemMessage("Failed, invalid command", "#b53030")
			commandContainer.style.display = "none"
			return
		}
		if (stage.length == 1) {
			COMMANDS[stage[0]]()
		} else {
			let index = COMMANDLIST.findIndex((z) => z.name === stage[0])
			if (index == -1) {
				FUNCTIONS.showSystemMessage("Failed, invalid command", "#b53030")
			} else {
				let command = COMMANDLIST[index]
				if (command.param.length == 0) {
					COMMANDS[stage[0]]()
				} else {
					function splitWithTail(str, delim, count) {
						var parts = str.split(delim)
						var tail = parts.slice(count).join(delim)
						var result = parts.slice(0, count)
						result.push(tail)
						return result
					}
					let val = splitWithTail(value, " ", command.param.length)
					val.shift()
					COMMANDS[stage[0]](...val)
				}
			}
		}
		commandContainer.style.display = "none"
	},
	createAvatarImage: (
		avatar,
		team_number,
		appendlocation,
		classList,
		image_width,
		image_height,
		cache_storage,
		cache_id,
		cache_team,
		shadow_thickness,
		opacity
	) => {
		const hexToHSL = (hex) => {
			// Convert hex string to RGB values
			let r = parseInt(hex.slice(1, 3), 16)
			let g = parseInt(hex.slice(3, 5), 16)
			let b = parseInt(hex.slice(5, 7), 16)

			// Normalize RGB values
			r /= 255
			g /= 255
			b /= 255

			// Find the minimum and maximum RGB values
			let min = Math.min(r, g, b)
			let max = Math.max(r, g, b)
			let diff = max - min

			// Initialize HSL values
			let h, s, l

			// Calculate hue
			if (diff === 0) {
				h = 0
			} else if (max === r) {
				h = ((g - b) / diff) % 6
			} else if (max === g) {
				h = (b - r) / diff + 2
			} else {
				h = (r - g) / diff + 4
			}
			h = Math.round(60 * h)
			if (h < 0) {
				h += 360
			}

			// Calculate lightness
			l = (min + max) / 2

			// Calculate saturation
			s = diff === 0 ? 0 : diff / (1 - Math.abs(2 * l - 1))
			s = +(s * 100).toFixed(1)
			l = +(l * 100).toFixed(1)

			// Return HSL values as an object
			return { h, s, l }
		}
		const HSLtoHex = (hue, saturation, lightness) => {
			saturation /= 100
			lightness /= 100
			let s = (1 - Math.abs(2 * lightness - 1)) * saturation,
				h = s * (1 - Math["abs"](((hue / 60) % 2) - 1)),
				l = lightness - s / 2,
				red = 0,
				green = 0,
				blue = 0
			if (0 <= hue && hue < 60) {
				red = s
				green = h
				blue = 0
			} else if (60 <= hue && hue < 120) {
				red = h
				green = s
				blue = 0
			} else if (120 <= hue && hue < 180) {
				red = 0
				green = s
				blue = h
			} else if (180 <= hue && hue < 240) {
				red = 0
				green = h
				blue = s
			} else if (240 <= hue && hue < 300) {
				red = h
				green = 0
				blue = s
			} else if (300 <= hue && hue < 360) {
				red = s
				green = 0
				blue = h
			}
			red = Math.round((red + l) * 255)
			green = Math.round((green + l) * 255)
			blue = Math.round((blue + l) * 255)
			const rgbToHex = (r, g, b) =>
				"#" +
				[r, g, b]
					.map((x) => {
						const hex = x.toString(16)
						return hex.length === 1 ? "0" + hex : hex
					})
					.join("")
			return rgbToHex(red, green, blue)
		}
		const hueify = (hexcolour, hue_value) => {
			let hsl = hexToHSL("#" + hexcolour.toString(16)["padStart"](6, "0"))
			hsl.h = hue_value
			return HSLtoHex(hsl.h, hsl.s, hsl.l)
		}
		const teamify = (colour) => {
			if (team_number == 2) {
				return hueify(colour, 4)
			} else if (team_number == 3) {
				return hueify(colour, 207)
			} else if (team_number == 4) {
				return hueify(colour, 122)
			} else if (team_number == 5) {
				return hueify(colour, 54)
			}
			return "#" + colour.toString(16)["padStart"](6, "0")
		}
		let svgcode = `
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="36" height="36">
<clipPath id="clipCircle"><circle cx="18" cy="18" r="15"/></clipPath>`
		if (shadow_thickness > 0) {
			svgcode += `<circle fill="#000000" fill-opacity="${opacity}" cx="${18 + shadow_thickness}" cy="${18 + shadow_thickness}" r="15"/>`
		}
		svgcode += `
<circle fill="${teamify(avatar.bc)}" cx="18" cy="18" r="15"/>
<g id="base" clip-path="url(#clipCircle)">`
		avatar.layers
			.slice()
			.reverse()
			.forEach((layer) => {
				svgcode += window.bonkpanel_skins[layer.id - 1]
					.match(/<g.+<\/g>/gs)[0]
					.replace(/fill=".+?"/, `fill="${teamify(layer.color)}"`)
					.replace(
						/transform=".+?"/,
						`
			transform="matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
			translate(${layer.x + 18}, ${layer.y + 18})
			rotate(${layer.angle})
			scale(${layer.scale})
			scale(${layer.flipX ? -1 : 1}, ${layer.flipY ? -1 : 1})"
			`
					)
			})
		svgcode += "</g>"
		if (team_number >= 2 && team_number <= 5) {
			svgcode += `<circle clip-path="url(#clipCircle)" fill="none" cx="18" cy="18" r="15" stroke-width="1.8" stroke="#`
			if (team_number == 2) {
				svgcode += `f44336"/>`
			} else if (team_number == 3) {
				svgcode += `2196f3"/>`
			} else if (team_number == 4) {
				svgcode += `4caf50"/>`
			} else if (team_number == 5) {
				svgcode += `ffeb3b"/>`
			}
		}
		svgcode += "</svg>"
		const image = document.createElement("img")
		image.src = `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svgcode)))}`
		image.style.width = image_width + "px"
		image.style.height = image_height + "px"
		if (document.body.contains(appendlocation)) {
			while (appendlocation.firstChild) {
				appendlocation.removeChild(appendlocation.firstChild)
			}
			appendlocation.appendChild(image)
			if (classList != "") {
				image.classList.add(classList)
			}
		}
		if (cache_storage) {
			if (!cache_storage[cache_id]) {
				cache_storage[cache_id] = []
			}
			cache_storage[cache_id][cache_team] = image
		}
		return image
	},
	urlify: (text) => {
		let urlRegex = /(((https?:\/\/)|(www\.))[^\s]+)/g
		return text.replace(urlRegex, function (url, b, c) {
			let url2 = c == "www." ? "https://" + url : url
			return '<a href="' + url2 + '" target="_blank">' + url + "</a>"
		})
	},
	showSystemMessage: (message, colour, inChat = false) => {
		const systemcontainer = inChat ? document.getElementById("messagecontainer") : document.getElementById("systemcontainer")
		if (systemcontainer == null) return

		const needToScroll = systemcontainer.scrollTop + systemcontainer.clientHeight >= systemcontainer.scrollHeight - 5
		const msgcontainer = document.createElement("div")
		const msg = document.createElement("span")
		msg.style.color = colour
		msg.classList.add("newbonklobby_chat_status")
		msg.appendChild(document.createTextNode("* " + message))
		msgcontainer.appendChild(msg)
		systemcontainer.appendChild(msgcontainer)
		if (systemcontainer.childElementCount > 250) {
			systemcontainer.removeChild(systemcontainer.firstChild)
		}
		if (needToScroll) {
			systemcontainer.scrollTop = systemcontainer.scrollHeight
		}
	},
	showChatMessage: (message, ID) => {
		const messagecontainer = document.getElementById("messagecontainer")
		if (messagecontainer == null) return

		const messager = ROOM_VAR.players[ID]
		if (messager == null) return
		if (messager.mute) return

		const needToScroll = messagecontainer.scrollTop + messagecontainer.clientHeight >= messagecontainer.scrollHeight - 5
		const messageholder = document.createElement("div")
		messagecontainer.appendChild(messageholder)

		const colourbox = document.createElement("div")
		colourbox.classList.add("newbonklobby_chat_msg_colorbox")
		messageholder.appendChild(colourbox)

		if (ROOM_VAR.chatAvatarCache[messager.userName] && ROOM_VAR.chatAvatarCache[messager.userName][1]) {
			colourbox.appendChild(ROOM_VAR.chatAvatarCache[messager.userName][1].cloneNode(true))
		} else {
			try {
				FUNCTIONS.createAvatarImage(messager.avatar, 1, colourbox, "newbonklobby_chat_msg_avatar", 12, 12, ROOM_VAR.chatAvatarCache, ID, 1, 2, 0.1)
			} catch (error) {
				console.error(error)
			}
		}

		const name = document.createElement("span")
		name.classList.add("newbonklobby_chat_msg_name")
		name.innerText = `${messager.userName}: `
		messageholder.append(name)

		const msg = document.createElement("span")
		msg.classList.add("newbonklobby_chat_msg_txt")
		msg.innerHTML = FUNCTIONS.urlify(message)
		messageholder.appendChild(msg)

		if (needToScroll.childElementCount > 300) {
			needToScroll.removeChild(needToScroll.firstChild)
		}
		if (needToScroll) {
			messagecontainer.scrollTop = messagecontainer.scrollHeight
		}
	}
}
let SCENES = {
	system: (panelID = "panel1") => {
		document.getElementById(panelID).innerHTML = ""

		const container = document.createElement("div")
		container.id = "systemcontainer"
		container.className = "chatcontainer"
		document.getElementById(panelID).appendChild(container)
	},
	chat: (panelID = "panel3") => {
		document.getElementById(panelID).innerHTML = ""

		const container = document.createElement("div")
		container.id = "messagecontainer"
		container.className = "chatcontainer"
		container.style.height = "calc(100% - 26px)"
		document.getElementById(panelID).appendChild(container)

		const lowerline = document.createElement("div")
		lowerline.id = "bonkpanellowerline"
		document.getElementById(panelID).appendChild(lowerline)

		const lowerinstruction = document.createElement("div")
		lowerinstruction.id = "bonkpanellowerinstruction"
		lowerinstruction.innerText = "Press enter to send a message"
		lowerinstruction.style.display = "block"
		document.getElementById(panelID).appendChild(lowerinstruction)

		const input = document.createElement("input")
		input.id = "bonkpanelchatinput"
		input.type = "text"
		input.setAttribute("autocomplete", "off")
		input.setAttribute("aria-autocomplete", "none")
		input.style.pointerEvents = "none"
		input.addEventListener("input", (e) => {
			input.onkeydown = null
			FUNCTIONS.showCommandHelper(e.target.value)
		})
		document.getElementById(panelID).appendChild(input)
	},
	leaderboard: (panelID = "panel2") => {
		document.getElementById(panelID).innerHTML = ""
		const container = document.createElement("div")
		container.style = "width: 100%; height: 100%; display: flex; flex-direction: column; scroll-y: auto; font-family: futurept_b1;"

		if (inLobby || ROOM_VAR.state == null || ROOM_VAR.state[4]?.wl == null) return
		const div = document.createElement("div")
		div.style = "font-size: 17px; padding: 5px 5px 0px; overflow: hidden;"
		div.textContent = "Rounds to win: " + ROOM_VAR.state[4]?.wl
		container.appendChild(div)

		if (ROOM_VAR.state[0].scores.length <= 0) return
		let leaderboardData = []
		for (let i = 0; i < ROOM_VAR.state[0].scores.length; i++) {
			const score = ROOM_VAR.state[0].scores[i]
			if (score == null) continue
			let scoreOwner = ""
			if (ROOM_VAR.state[4].tea) {
				switch (i) {
					case 2:
						scoreOwner = "Red Team"
						break
					case 3:
						scoreOwner = "Blue Team"
						break
					case 4:
						scoreOwner = "Green Team"
						break
					case 5:
						scoreOwner = "Yellow Team"
						break
				}
			} else if (ROOM_VAR.players[i]?.userName) {
				scoreOwner = ROOM_VAR.players[i].userName
			}
			if (scoreOwner.length == 0) continue
			leaderboardData.push({
				owner: scoreOwner,
				score: score
			})
		}
		leaderboardData.sort((a, b) => b.score - a.score)
		leaderboardData.forEach((e) => {
			const div = document.createElement("div")
			div.style = "font-size: 17px; padding: 5px 5px 0px; overflow: hidden;"
			div.textContent = e.owner + ": " + e.score
			container.appendChild(div)
		})

		document.getElementById(panelID).appendChild(container)
	},
	players: (panelID = "panel4") => {
		document.getElementById(panelID).innerHTML = ""

		const container = document.createElement("div")
		container.id = "playerscontainer"
		container.style = "width: 100%; height: 100%; display: flex; flex-direction: column; scroll-y: auto;"

		ROOM_VAR.tmpImgTextPing = []
		for (let i = 0; i < ROOM_VAR.players.length; i++) {
			const player = ROOM_VAR.players[i]
			if (player == null) continue

			const playerContainer = document.createElement("div")
			playerContainer.className = "newbonklobby_playerentry"
			playerContainer.style =
				"border-left: 4px solid var(--bonk_theme_primary_background, #e2e2e2) !important; border-right: 4px solid var(--bonk_theme_primary_background, #e2e2e2) !important; border-top: 4px solid var(--bonk_theme_primary_background, #e2e2e2) !important; background-color: var(--bonk_theme_primary_background, #e2e2e2); cursor: auto;"

			const avatar = document.createElement("div")
			avatar.className = "newbonklobby_playerentry_avatar"
			avatar.style = `opacity: ${player.team === 0 ? "0.5" : "1"};`
			if (ROOM_VAR.playersAvatarCache?.[player.userName]?.[player.team]) {
				avatar.innerHTML = ROOM_VAR.commandAvatarCache[player.userName][player.team]
			} else {
				try {
					avatar.innerHTML = FUNCTIONS.createAvatarImage(
						player.avatar,
						player.team,
						null,
						"",
						36,
						36,
						ROOM_VAR.playersAvatarCache,
						i,
						player.team,
						1.1,
						0.3
					).outerHTML
				} catch (error) {
					console.error(error)
				}
			}
			playerContainer.appendChild(avatar)

			const name = document.createElement("div")
			name.className = "newbonklobby_playerentry_name"
			name.textContent = player.userName
			playerContainer.appendChild(name)

			const level = document.createElement("div")
			level.className = "newbonklobby_playerentry_level"
			level.textContent = player.guest ? "Guest" : `Level ${player.level}`
			playerContainer.appendChild(level)

			const size = document.createElement("div")
			let sizeclass = ""
			let sizetext = ""
			if (ROOM_VAR.bal[i] && ROOM_VAR.bal[i] != 0) {
				if (ROOM_VAR.bal[i] > 0) {
					sizeclass = " newbonklobby_playerentry_balance_buff"
					sizetext = "+" + ROOM_VAR.bal[i] + "%"
				} else {
					sizeclass = " newbonklobby_playerentry_balance_nerf"
					sizetext = ROOM_VAR.bal[i] + "%"
				}
			}
			size.className = "newbonklobby_playerentry_balance" + sizeclass
			size.textContent = sizetext
			playerContainer.appendChild(size)

			const pingImg = document.createElement("img")
			pingImg.src = "graphics/ping_5.png"
			pingImg.className = "newbonklobby_playerentry_ping"
			playerContainer.appendChild(pingImg)

			const pingText = document.createElement("div")
			pingText.className = "newbonklobby_playerentry_pingtext"
			playerContainer.appendChild(pingText)

			const hostImg = document.createElement("img")
			hostImg.src = "graphics/host_0.png"
			hostImg.className = "newbonklobby_playerentry_host"
			playerContainer.appendChild(hostImg)

			ROOM_VAR.tmpImgTextPing[i] = {
				img: pingImg,
				text: pingText,
				host: hostImg
			}

			if (player.ready) {
				const ready = document.createElement("img")
				ready.className = "newbonklobby_playerentry_ready"
				ready.src = "graphics/readytick.png"
				playerContainer.appendChild(ready)
			}

			container.appendChild(playerContainer)
		}
		SCENES.updatePlayersPing()

		document.getElementById(panelID).appendChild(container)
	},
	updatePlayersPing: () => {
		for (let i = 0; i < ROOM_VAR.players.length; i++) {
			const player = ROOM_VAR.players[i]
			const element = ROOM_VAR.tmpImgTextPing[i]
			if (element == null || player == null) continue

			let imgSrc = 1
			if (player.ping <= 100) {
				imgSrc = 5
			}
			if (player.ping > 100 && player.ping <= 200) {
				imgSrc = 4
			}
			if (player.ping > 200 && player.ping <= 300) {
				imgSrc = 3
			}
			if (player.ping > 300 && player.ping <= 400) {
				imgSrc = 2
			}
			if (player.ping > 400) {
				imgSrc = 1
			}
			if (player.tabbed) {
				imgSrc = "tab"
			}

			if (element.lastSet != imgSrc) {
				element.lastSet = imgSrc
				element.img.src = "graphics/ping_" + imgSrc + ".png"
			}
			if (imgSrc == "tab") {
				element.text.textContent = "Tab"
			} else if (player.ping === undefined) {
				element.text.textContent = "-ms"
			} else {
				element.text.textContent = player.ping + "ms"
			}
			if (ROOM_VAR.hostID == i) {
				if (element.hostLastSet != imgSrc) {
					element.hostLastSet = imgSrc
					element.host.src = "graphics/host_" + imgSrc + ".png"
				}
			} else {
				if (element.hostLastSet !== 0) {
					element.hostLastSet = 0
					element.host.src = "graphics/host_0.png"
				}
			}
		}
	}
}
let inLobby = true

// ADD STYLE
const style = document.createElement("style")
style.innerHTML += /*css*/ `
	*:focus {
		outline: none;
	}
	.newbonklobby_chat_msg_colorbox {
		user-select: none;
	}
	#bonkiocontainer {
		margin: 10px !important;
		flex: 0 0 auto;
	}
	#bonkpanelcontainer {
		width: 100%;
		height: 100%;
		display: flex;
		align-items: center;
		justify-content: space-between;
	}
	#newbonklobby_chat_input {
		width: 0px !important;
		height: 0px !important;
	}
	#ingamechatinputtext {
		width: 0px !important;
		height: 0px !important;
	}
	#skinColourPickerContainerButton {
		display: flex;
		justify-content: space-between;
	}
	#skinColourPickerContainerButton > div {
		padding: 0 10;
		width: inherit;
		margin-top: 7px;
		margin-bottom: 2px;
		display: inline-block;
		height: 25px;
		line-height: 25px;
	}
	#imageOverlayContainer {
		position: absolute;
		margin: auto;
		left: 0;
		right: 0;
		top: 55px;
		width: 245px;
		height: 245px;
		cursor: grab;
		pointer-events: none;
		overflow: hidden;
	}
	#imageOverlay {
		width: 100%;
		aspect-ratio: 1;
		position: absolute;
		top: 0;
		left: 0;
		background-repeat: no-repeat;
		background-size: 100%;
		background-position: center;
		background-image: none;
		transform: scale(100%);
		opacity: 50%;
	}
	.panelcontainer {
		flex: 1;
		height: 100%;
		background-color: var(--bonk_theme_primary_background, #e2e2e2) !important;
		color: var(--bonk_theme_primary_text, #000000) !important;
	}
	.gutter {
		background-color: var(--bonk_theme_window_color, #009688) !important;
		background-repeat: no-repeat;
		background-position: 50%;
	}
	.gutter.gutter-vertical {
		background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII=');
		cursor: row-resize;
	}
	.panel {
		position: relative;
	}
	#commandcontainer {
		position: absolute;
		width: 100%;
		height: 100%;
		top: 0;
		display: flex;
		align-items: center;
		justify-content: center;
		font-family: "futurept_b1";
	}
	#commandbackground {
		width: 100%;
		height: 100%;
		background-color: black;
		opacity: 0.6;
	}
	#commandpanel {
		position: absolute;
		background-color: var(--greyWindowBGColor);
		width: 80%;
		height: 70%;
		border-radius: 7px;
	}
	#commandtoptext {
		font-family: "futurept_b1";
		color: #ffffff;
		text-align: center;
		font-size: 20px;
		line-height: 32px;
		padding: 0px;
		margin: 0px;
	}
	#commandlistcontainer {
		height: calc(100% - 36px) !important;
	}
	.commandscardcontainer {
		background-color: #eeeeee;
		color: #222222;
		border-radius: 5px;
		user-select: none;
		margin: 10px;
		display: flex;
		flex-direction: column;
		cursor: pointer;
	}
	.commandscardtitle {
		display: block;
		text-align: left;
		font-size: 20px;
		padding-left: 10px;
		padding-top: 8px;
	}
	.commandscarddescription {
		display: block;
		text-align: left;
		font-size: 15px;
		padding-left: 10px;
		padding-right: 10px;
		line-height: 20px;
		white-space: pre-wrap;
		margin-bottom: 5px;
	}
	.commandplayerimgcontainer {
		width: 100px;
		height: 100px;
	}
	.chatcontainer {
		width: 100%;
		height: 100%;
		box-sizing: border-box;
		margin: auto;
		overflow-y: scroll;
		overflow-x: hidden;
		word-break: break-word;
		user-select: text;
		padding: 4px;
	}
	.chatcontainer::-webkit-scrollbar {width: 0.6em;}
	.chatcontainer::-webkit-scrollbar-thumb {background-color: var(--bonk_theme_scrollbar_thumb, #757575) !important;}
	.chatcontainer::-webkit-scrollbar-track {background-color: var(--bonk_theme_scrollbar_background) !important;}
	#bonkpanellowerline {
		background-color: #a5acb0;
		width: 97%;
		height: 1px;
	}
	#bonkpanellowerinstruction {
		width: 97%;
		height: 24px;
		margin: auto;
		color: #656565;
		font-size: 16px;
		font-family: "futurept_b1";
		pointer-events: none;
	}
	#bonkpanelchatinput {
		width: 97%;
		height: 24px;
		margin: auto;
		border: 0px solid;
		background: none;
		font-family: "futurept_b1";
		font-size: 16px;
		color: #171717;
		pointer-events: none;
	}
`
document.body.appendChild(style)

// MONKEY PATCH SHAKE TWEEN OBJECT
var canvasStage = null
const symbolshakeTweenObject = Symbol("shakeTweenObject")
Object.defineProperty(Object.prototype, "shakeTweenObject", {
	get() {
		return this[symbolshakeTweenObject]
	},
	set(value) {
		canvasStage = this
		const original = this.createGradientBackground
		this.createGradientBackground = function () {
			original.call(this, arguments)
			RESCALESTAGE()
		}
		this[symbolshakeTweenObject] = value
	},
	configurable: true
})
var scaler = 1
function RESCALESTAGE() {
	if (canvasStage == null || canvasStage.stage == null) return
	canvasStage.stage.scale.x = scaler
	canvasStage.stage.scale.y = scaler

	var tmpWidth = 730 * canvasStage.scaleRatio
	var tmpHeight = 500 * canvasStage.scaleRatio
	canvasStage.stage.x = tmpWidth / 2 - (tmpWidth * scaler) / 2
	canvasStage.stage.y = tmpHeight / 2 - (tmpHeight * scaler) / 2

	var superWidth = (780 * canvasStage.scaleRatio) / scaler
	var superHeight = (550 * canvasStage.scaleRatio) / scaler

	canvasStage.bgGradient.beginFill(0x3b536b)
	canvasStage.bgGradient.drawRect(0, 0, superWidth, superHeight)
	canvasStage.bgGradient.x = -(superWidth - tmpWidth) / 2
	canvasStage.bgGradient.y = -(superHeight - tmpHeight) / 2
}

// MONKEY PATCH AVATAR SHOW
let showFunction = () => {}
const symbolShow = Symbol("show")
Object.defineProperty(Object.prototype, "show", {
	get() {
		return this[symbolShow]
	},
	set(value) {
		if (typeof value == "function") {
			const original = value
			value = function () {
				if (arguments[0]?.bc) {
					showFunction = original
				}
				return original.apply(this, arguments)
			}
		}

		this[symbolShow] = value
	},
	configurable: true
})

// MONKEY PATCH showColorPicker
var showColorPicker = null
var showColorPickerArguments = []
const symbolshowColorPicker = Symbol("showColorPicker")
Object.defineProperty(Object.prototype, "showColorPicker", {
	get() {
		return this[symbolshowColorPicker]
	},
	set(value) {
		if (typeof value == "function") {
			const original = value
			value = function () {
				showColorPicker = original
				showColorPickerArguments = arguments
				return original.apply(this, arguments)
			}
		}
		this[symbolshowColorPicker] = value
	}
})

// MONKEY PATCH hostHandlePlayerJoined
const symbolhostHandlePlayerJoined = Symbol("hostHandlePlayerJoined")
Object.defineProperty(Object.prototype, "hostHandlePlayerJoined", {
	get() {
		return this[symbolStep]
	},
	set(value) {
		ROOM_VAR.stateFunction = this
		this[symbolStep] = value
	},
	configurable: true
})

// MONKEY PATCH STEP
const symbolStep = Symbol("step")
Object.defineProperty(Object.prototype, "step", {
	get() {
		return this[symbolStep]
	},
	set(value) {
		if (typeof value == "function") {
			const original = value
			value = function () {
				if (arguments[0]?.scores) {
					if (ws) {
						ROOM_VAR.state = arguments
					}
				}
				return original.apply(this, arguments)
			}
		}
		this[symbolStep] = value
	},
	configurable: true
})

// MONKEY PATCH APPEND CHILD
let originalAppendChild = Element.prototype.appendChild
Element.prototype.appendChild = function () {
	if (this == document.getElementById("newbonklobby_chat_content") || this == document.getElementById("ingamechatcontent")) {
		if (
			(arguments[0].firstChild?.className == "newbonklobby_chat_status" && inLobby) ||
			(arguments[0].firstChild?.className == "ingamechatstatus" && !inLobby)
		) {
			let text = arguments[0].textContent
			if (text.startsWith("* Map added to favourites")) {
				FUNCTIONS.showSystemMessage("Map added to favourites", "#b53030")
			} else if (text.startsWith("* Couldn't favourite map because it isn't public")) {
				FUNCTIONS.showSystemMessage("Failed, couldn't favourite map because it isn't public", "#b53030")
			} else if (text.startsWith("* This map is already in your favourites!")) {
				FUNCTIONS.showSystemMessage("This map is already in your favourites", "#b53030")
			} else if (text.startsWith("* Couldn't favourite, something went wrong")) {
				FUNCTIONS.showSystemMessage("Failed, something went wrong", "#b53030")
			} else if (text.startsWith("* You must be logged in and the map must be a Bonk 2 map")) {
				FUNCTIONS.showSystemMessage("Failed, you must be logged in and the map must be a Bonk 2 map", "#b53030")
			} else if (text.startsWith("* Map removed from favourites")) {
				FUNCTIONS.showSystemMessage("Map removed from favourites", "#b53030")
			} else if (text.startsWith("* Couldn't unfavourite map because it isn't public")) {
				FUNCTIONS.showSystemMessage("Failed, couldn't unfavourite map because it isn't public", "#b53030")
			} else if (text.startsWith("* This map isn't in your favourites!")) {
				FUNCTIONS.showSystemMessage("This map isn't in your favourites", "#b53030")
			} else if (text.startsWith("* Couldn't unfavourite, something went wrong")) {
				FUNCTIONS.showSystemMessage("Failed, something went wrong", "#b53030")
			} else if (text.startsWith("* No replays in Football mode")) {
				FUNCTIONS.showSystemMessage("Failed, no replays in football mode", "#b53030")
			} else if (text.startsWith("* Please wait at least")) {
				FUNCTIONS.showSystemMessage(text.substring(2).replace("Please", "Failed,"), "#b53030")
			} else if (text.startsWith("* Recording failed")) {
				FUNCTIONS.showSystemMessage("Failed, something went wrong", "#b53030")
			} else if (text.startsWith("* Replay must be at least")) {
				FUNCTIONS.showSystemMessage(text.substring(2).replace("Replay", "Failed, replay"), "#b53030")
			} else if (text.startsWith("* The last")) {
				FUNCTIONS.showSystemMessage(text.substring(2), "#b53030")
			} else if (text.startsWith("* You and")) {
				FUNCTIONS.showSystemMessage(text.substring(2), "#00675d")
			} else if (text.endsWith("accepted your friend request ")) {
				FUNCTIONS.showSystemMessage(text.substring(2), "#00675d")
			} else if (text.startsWith("* Your clipboard has been set to:")) {
				FUNCTIONS.showSystemMessage(`Link copied`, "#0955c7")
			}
		} else if (arguments[0].firstChild?.className == "newbonklobby_chat_status") {
			let text = arguments[0].textContent
			if (text.startsWith("* You and")) {
				FUNCTIONS.showSystemMessage(text.substring(2), "#00675d")
			} else if (text.endsWith("accepted your friend request ")) {
				FUNCTIONS.showSystemMessage(text.substring(2), "#00675d")
			} else if (text.startsWith("* Your clipboard has been set to:")) {
				FUNCTIONS.showSystemMessage(`Link copied`, "#0955c7")
			}
		}
		if (this == document.getElementById("newbonklobby_chat_content") && arguments[0].firstChild?.className == "newbonklobby_chat_msg_name") {
			const systemcontainer = document.getElementById("systemcontainer")
			if (systemcontainer) {
				const needToScroll = systemcontainer.scrollTop + systemcontainer.clientHeight >= systemcontainer.scrollHeight - 5
				systemcontainer.appendChild(arguments[0])
				if (systemcontainer.childElementCount > 250) {
					systemcontainer.removeChild(systemcontainer.firstChild)
				}
				if (needToScroll) {
					systemcontainer.scrollTop = systemcontainer.scrollHeight
				}
				return
			}
		}
		if (this == document.getElementById("newbonklobby_chat_content") && arguments[0].lastChild?.textContent == "[Accept]") {
			const systemcontainer = document.getElementById("systemcontainer")
			if (systemcontainer) {
				const needToScroll = systemcontainer.scrollTop + systemcontainer.clientHeight >= systemcontainer.scrollHeight - 5
				systemcontainer.appendChild(arguments[0])
				if (systemcontainer.childElementCount > 250) {
					systemcontainer.removeChild(systemcontainer.firstChild)
				}
				if (needToScroll) {
					systemcontainer.scrollTop = systemcontainer.scrollHeight
				}
				return
			}
		}
	} else if (
		(arguments[0].textContent == "Unmute" || arguments[0].textContent == "Mute") &&
		(arguments[0].className == "newbonklobby_playerentry_menu_button brownButton buttonShadow newbonklobby_playerentry_menu_button_warn" ||
			arguments[0].className == "newbonklobby_playerentry_menu_button brownButton buttonShadow brownButton_classic")
	) {
		arguments[0].addEventListener("click", () => {
			const tmpelement = document.getElementsByClassName("newbonklobby_playerentry_menuhighlighted")[0]
			if (tmpelement) {
				const tmpname = tmpelement.getElementsByClassName("newbonklobby_playerentry_name")[0].textContent
				for (let i = 0; i < ROOM_VAR.players.length; i++) {
					if (ROOM_VAR.players[i].userName === tmpname) {
						ROOM_VAR.players[i].mute = !ROOM_VAR.players[i].mute
						break
					}
				}
			}
		})
	} else if (this == document.body && arguments[0].tagName == "DIV") {
		return
	}

	return originalAppendChild.apply(this, arguments)
}

// MONKEY PATCH ARRAY PUSH
let STARTGAME = null
let PROCESSCOMMAND = null
let originalArrayPush = Array.prototype.push
Array.prototype.push = function () {
	if (arguments[0]?.eventName === "startGame") {
		STARTGAME = arguments[0].callback
	} else if (arguments[0]?.eventName === "processCommand") {
		PROCESSCOMMAND = arguments[0].callback
	}
	originalArrayPush.apply(this, arguments)
}

// MONKEY PATCH EVENT LISTENER
let lockKeyboard = true
let _listeners = []
let originalAddEventListener = EventTarget.prototype.addEventListener
EventTarget.prototype.addEventListener = function (type, listener, useCapture) {
	if (this == window && type == "keydown") {
		let originalListener = listener
		lockKeyboard = document.activeElement !== document.getElementById("bonkpanelchatinput")
		listener = function () {
			if (lockKeyboard) {
				originalListener.apply(null, arguments)
			}
		}
	}
	_listeners.push({ type: type, listener: listener })
	originalAddEventListener.apply(this, [type, listener, useCapture])
}
let originalRemoveEventListener = EventTarget.prototype.removeEventListener
EventTarget.prototype.removeEventListener = function (type, listener) {
	if (this == window && type == "keydown") {
		_listeners.forEach((e) => {
			if (e.type == type) {
				originalRemoveEventListener.apply(window, [type, e.listener])
			}
		})
		_listeners = []
	}
	originalRemoveEventListener.apply(this, [type, listener])
}
let chatInputEvent = null
setTimeout(() => {
	let originalOn = $.fn.on
	$.fn.on = function (types, func) {
		if (ws && types == "keydown") {
			chatInputEvent = func
			return false
		} else {
			return originalOn.apply(this, arguments)
		}
	}
}, 0)

// CUSTOM EVENT LISTENER
document.addEventListener("keydown", (e) => {
	if (ws == null) return
	if (e.code != "Enter") return
	let chatinput = document.getElementById("bonkpanelchatinput")
	let instruction = document.getElementById("bonkpanellowerinstruction")
	if (inLobby) {
		if (document.activeElement == document.getElementById("maploadwindowsearchinput")) {
			document.getElementById("maploadwindowsearchbutton").click()
		} else if (document.activeElement == chatinput) {
			if (chatinput.value.startsWith("/")) {
				FUNCTIONS.processCommand(chatinput.value)
			} else if (chatinput.value != "") {
				iosend([10, { message: chatinput.value }])
			}
			chatinput.value = ""
			chatinput.blur()
			instruction.style.display = "block"
			chatinput.style.pointerEvents = "none"
		} else {
			chatinput.focus()
			instruction.style.display = "none"
			chatinput.style.pointerEvents = "auto"
		}
	} else {
		if (document.activeElement == chatinput) {
			if (chatinput.value.startsWith("/")) {
				FUNCTIONS.processCommand(chatinput.value)
			} else if (chatinput.value != "") {
				iosend([10, { message: chatinput.value }])
			}
			chatinput.value = ""
			chatinput.blur()
			instruction.style.display = "block"
			chatinput.style.pointerEvents = "none"
			lockKeyboard = true
		} else {
			chatinput.focus()
			instruction.style.display = "none"
			chatinput.style.pointerEvents = "auto"
			lockKeyboard = false
		}
	}
})
document.addEventListener("mouseup", (e) => {
	if (ws && !inLobby) {
		if (commandContainer.style.display != "none") {
			lockKeyboard = false
		} else if (typeof window.getSelection != "undefined" && window.getSelection().toString() != "") {
			lockKeyboard = false
		} else if (e.target.tagName == "INPUT") {
			lockKeyboard = false
		} else {
			lockKeyboard = true
		}
	}
})
document.addEventListener("mousedown", (e) => {
	if (ws && !inLobby) {
		if (commandContainer.style.display != "none") {
			lockKeyboard = false
		} else if (e.target == document.getElementById("bonkpanelcontainer") || e.target == document.querySelector("#gamerenderer > canvas")) {
			lockKeyboard = true
		} else if (e.target.tagName == "INPUT") {
			lockKeyboard = false
		}
	}
})

// ADD SKIN BUTTONS
const skinButtonsContainer = document.createElement("div")
skinButtonsContainer.style = "width: 100%; height: 30px; bottom: 15px; position: absolute; display: flex; justify-content: space-around;"
document.getElementById("skineditor_previewbox").appendChild(skinButtonsContainer)
const tmpEle = document.createElement("input")
tmpEle.type = "file"
tmpEle.style.display = "none"
tmpEle.addEventListener("change", () => {
	let fr = new FileReader()
	fr.onload = function () {
		const result = JSON.parse(fr.result)
		const tmpAvatar = new AVATAR()
		tmpAvatar.bc = result.bc
		tmpAvatar.layers = result.layers
		showFunction(tmpAvatar)
	}
	fr.readAsText(tmpEle.files[0])
})
document.body.appendChild(tmpEle)

var overlayEditing = false
var pos1 = 0,
	pos2 = 0,
	pos3 = 0,
	pos4 = 0,
	overlayScale = 100,
	overlayOpacity = 50
const tmpEle2 = document.createElement("input")
tmpEle2.type = "file"
tmpEle2.style.display = "none"
tmpEle2.addEventListener("change", () => {
	let fr = new FileReader()
	fr.onload = function () {
		importImageOverlay.style.backgroundImage =
			"url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDhweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iNDhweCIgZmlsbD0iI0ZGRkZGRiI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTkgMTYuMTdMNC44MyAxMmwtMS40MiAxLjQxTDkgMTkgMjEgN2wtMS40MS0xLjQxTDkgMTYuMTd6Ii8+PC9zdmc+)"
		imageOverlayContainer.style.pointerEvents = "auto"
		imageOverlay.style.backgroundImage = `url(${fr.result})`
		imageOverlay.style.top = "0px"
		imageOverlay.style.left = "0px"
		overlayScale = 100
		overlayOpacity = 50
		imageOverlay.style.transform = `scale(${overlayScale}%)`
		imageOverlay.style.opacity = `${overlayOpacity}%`
		imageOverlayContainer.onmousedown = function (event) {
			pos3 = event.clientX
			pos4 = event.clientY
			window.onmousemove = function (e) {
				pos1 = pos3 - e.clientX
				pos2 = pos4 - e.clientY
				pos3 = e.clientX
				pos4 = e.clientY
				imageOverlay.style.top = imageOverlay.offsetTop - pos2 + "px"
				imageOverlay.style.left = imageOverlay.offsetLeft - pos1 + "px"
			}
			window.onmouseup = () => {
				window.onmousemove = null
				window.onmouseup = null
			}
		}
		imageOverlayContainer.onwheel = function (event) {
			if (event.shiftKey) {
				if (event.deltaY < 0) {
					overlayOpacity = Math.min(overlayOpacity + 1, 100)
				} else {
					overlayOpacity = Math.max(overlayOpacity - 1, 0)
				}
				imageOverlay.style.opacity = `${overlayOpacity}%`
			} else {
				if (event.deltaY < 0) {
					overlayScale += 1
				} else {
					overlayScale -= 1
				}
				imageOverlay.style.transform = `scale(${overlayScale}%)`
			}
		}
		document.getElementById("skineditor_previewbox_skincontainer").style.pointerEvents = "none"
		overlayEditing = true
	}
	fr.readAsDataURL(tmpEle2.files[0])
})
const importSkin = document.createElement("div")
importSkin.className = "brownButton brownButton_classic"
importSkin.style.width = "30px"
importSkin.style.height = "30px"
importSkin.addEventListener("click", () => {
	tmpEle.click()
})
importSkin.style.backgroundImage =
	"url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyNy4zLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCA0OCA0OCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDggNDg7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOiNGRkZGRkY7fQ0KPC9zdHlsZT4NCjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xMSw0MGMtMC44LDAtMS41LTAuMy0yLjEtMC45QzguMywzOC41LDgsMzcuOCw4LDM3di03LjFoM1YzN2gyNnYtNy4xaDNWMzdjMCwwLjgtMC4zLDEuNS0wLjksMi4xDQoJQzM4LjUsMzkuNywzNy44LDQwLDM3LDQwSDExeiBNMjIuNSwzMi4zVjEzLjhsLTYsNmwtMi4xLTIuMUwyNCw4bDkuNyw5LjZsLTIuMiwyLjFsLTYtNnYxOC41SDIyLjV6Ii8+DQo8L3N2Zz4NCg==)"
const importImageOverlay = document.createElement("div")
importImageOverlay.className = "brownButton brownButton_classic"
importImageOverlay.style.width = "30px"
importImageOverlay.style.height = "30px"
importImageOverlay.style.backgroundSize = "80%"
importImageOverlay.style.backgroundPosition = "center"
importImageOverlay.style.backgroundRepeat = "no-repeat"
importImageOverlay.style.backgroundImage =
	"url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDhweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iNDhweCIgZmlsbD0iI0ZGRkZGRiI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTIyIDE4VjJINnYxNmgxNnptLTExLTZsMi4wMyAyLjcxTDE2IDExbDQgNUg4bDMtNHpNMiA2djE2aDE2di0ySDRWNkgyeiIvPjwvc3ZnPg==)"
importImageOverlay.addEventListener("click", () => {
	if (overlayEditing) {
		importImageOverlay.style.backgroundImage =
			"url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDhweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iNDhweCIgZmlsbD0iI0ZGRkZGRiI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTIyIDE4VjJINnYxNmgxNnptLTExLTZsMi4wMyAyLjcxTDE2IDExbDQgNUg4bDMtNHpNMiA2djE2aDE2di0ySDRWNkgyeiIvPjwvc3ZnPg==)"
		imageOverlayContainer.style.pointerEvents = null
		document.getElementById("skineditor_previewbox_skincontainer").style.pointerEvents = null
		overlayEditing = false
	} else {
		imageOverlay.style.backgroundImage = null
		tmpEle2.click()
	}
})
document.getElementById("skineditor_cancelbutton").style.position = "initial"
document.getElementById("skineditor_savebutton").style.position = "initial"
skinButtonsContainer.appendChild(document.getElementById("skineditor_cancelbutton"))
skinButtonsContainer.appendChild(document.getElementById("skineditor_savebutton"))
skinButtonsContainer.appendChild(importImageOverlay)
skinButtonsContainer.appendChild(importSkin)

const skinColourPickerContainerButton = document.createElement("div")
skinColourPickerContainerButton.id = "skinColourPickerContainerButton"
const skinHexColourPicker = document.createElement("div")
skinHexColourPicker.className = "brownButton brownButton_classic buttonShadow"
skinHexColourPicker.innerText = "#"
skinHexColourPicker.onclick = () => {
	const hexCode = window.prompt("Hex Code", "#")
	try {
		showColorPickerArguments[0] = parseInt(hexCode.replace("#", ""), 16)
		showColorPicker(...showColorPickerArguments)
	} catch (error) {}
}
skinColourPickerContainerButton.appendChild(skinHexColourPicker)
skinColourPickerContainerButton.appendChild(document.getElementById("skineditor_colorpicker_cancelbutton"))
skinColourPickerContainerButton.appendChild(document.getElementById("skineditor_colorpicker_savebutton"))
document.getElementById("skineditor_colorpicker").appendChild(skinColourPickerContainerButton)

// SKIN IMAGE OVERLAY
const imageOverlayContainer = document.createElement("div")
imageOverlayContainer.id = "imageOverlayContainer"
const imageOverlay = document.createElement("div")
imageOverlay.id = "imageOverlay"
imageOverlayContainer.appendChild(imageOverlay)
document.getElementById("skineditor_previewbox").appendChild(imageOverlayContainer)

// WS SENDER AND RECEIVER
let wsextra = []
let ws = null
let originalSend = window.WebSocket.prototype.send
window.WebSocket.prototype.send = function (args) {
	if (this.url.includes(".bonk.io/socket.io/?EIO=3&transport=websocket&sid=")) {
		if (typeof args == "string" && !wsextra.includes(this)) {
			if (!ws) {
				ws = this
			}
			try {
				if (args.startsWith("42[")) {
					let data = JSON.parse(/42(.*)/.exec(args)[1])
					handleOwnData(data, this)
				}
			} catch (error) {
				console.log(args)
				console.log(error)
			}
		}
	} else if (args.includes("rport")) {
		return
	}
	if (this.url.includes(".bonk.io/socket.io/?EIO=3&transport=websocket&sid=") && !this.injected) {
		this.injected = true

		let originaReceiveMessage = this.onmessage
		this.onmessage = (e) => {
			if (!wsextra.includes(this)) {
				if (typeof e.data == "string") {
					try {
						if (e.data.startsWith("42[")) {
							handleData(JSON.parse(/42(.*)/.exec(e.data)[1]), this)
						}
					} catch (error) {
						console.log(e.data)
						console.log(error)
					}
				}
			}
			return originaReceiveMessage.call(this, e)
		}

		let originalClose = this.onclose
		this.onclose = function () {
			if (wsextra.includes(this)) {
				wsextra.splice(wsextra.indexOf(this), 1)
			} else {
				ws = null
				panelLeft.style.visibility = "hidden"
				panelRight.style.visibility = "hidden"
			}
			return originalClose.call(this)
		}
	}
	return originalSend.call(this, args)
}
function iosend(data) {
	if (ws == null) return
	ws.send(`42${JSON.stringify(data)}`)
}
function ioreceive(data) {
	if (ws == null) return
	ws.onmessage({ data: `42${JSON.stringify(data)}` })
}

// HANDLE DATA
function handleData(data, websocket) {
	switch (data[0]) {
		case 1:
			// got ping data
			originalSend.call(websocket, `42[1,{"id":${data[1]}}]`)
			for (const id in data[1]) {
				if (ROOM_VAR.players[id]) {
					ROOM_VAR.players[id].ping = data[1][id]
				}
			}
			SCENES.updatePlayersPing()
			break
		case 2:
			// room Created
			inLobby = true
			ROOM_VAR.quick = false
			ROOM_VAR.bal = []
			ROOM_VAR.chatAvatarCache = []
			ROOM_VAR.commandAvatarCache = []
			ROOM_VAR.playersAvatarCache = []
			ROOM_VAR.xpEarned = 0
			SCENES.system()
			SCENES.chat()
			SCENES.players()
			SCENES.leaderboard()
			break
		case 3:
			// joined Room
			panelLeft.style.visibility = "visible"
			panelRight.style.visibility = "visible"
			ROOM_VAR.players = data[3]
			ROOM_VAR.hostID = data[2]
			ROOM_VAR.quick = false
			ROOM_VAR.xpEarned = 0
			ROOM_VAR.chatAvatarCache = []
			ROOM_VAR.commandAvatarCache = []
			ROOM_VAR.playersAvatarCache = []
			SCENES.system()
			SCENES.chat()
			SCENES.players()
			if (ROOM_VAR.players[data[1]].userName == document.getElementById("pretty_top_name").textContent) {
				ROOM_VAR.myID = data[1]
				ws = websocket
			} else {
				wsextra.push(websocket)
			}
			break
		case 4:
			// new player joined
			FUNCTIONS.showSystemMessage(data[3] + " has joined the game", "#b53030", true)
			ROOM_VAR.players[data[1]] = {
				peerID: data[2],
				userName: data[3],
				guest: data[4],
				level: data[5],
				team: data[6],
				avatar: data[7]
			}
			SCENES.players()
			break
		case 5:
			// someone left
			FUNCTIONS.showSystemMessage(ROOM_VAR.players[data[1]].userName + " has left the game", "#b53030", true)
			ROOM_VAR.players[data[1]] = null
			SCENES.players()
			break
		case 6:
			// host left
			ROOM_VAR.hostID = data[2]
			if (data[2] == -1) {
				FUNCTIONS.showSystemMessage(`${ROOM_VAR.players[data[1]].userName} has left the game and closed the room`, "#b53030", true)
			} else {
				FUNCTIONS.showSystemMessage(
					`${ROOM_VAR.players[data[1]].userName} has left the game and ${ROOM_VAR.players[data[2]].userName} is now the game host`,
					"#b53030",
					true
				)
			}
			if (data[2] == ROOM_VAR.myID) {
				FUNCTIONS.showSystemMessage("You are now the host of this game", "#800d6e")
			}
			ROOM_VAR.players[data[1]] = null
			SCENES.players()
			break
		case 8:
			// ready change
			ROOM_VAR.players[data[1]].ready = data[2]
			SCENES.players()
			break
		case 13:
			// return to lobby
			inLobby = true
			SCENES.leaderboard()
			break
		case 15:
			// game started
			inLobby = false
			SCENES.leaderboard()
			break
		case 16:
			// status (something like chat rate limited)
			if (["rate_limit_ready", "chat_rate_limit"].includes(data[1])) {
				FUNCTIONS.showSystemMessage("You're doing that too much!", "#cc4444")
			} else if (data[1] == "teams_locked") {
				FUNCTIONS.showSystemMessage("Failed, teams have been locked, only the host can assign teams", "#cc4444")
			}
			break
		case 18:
			// team changed
			ROOM_VAR.players[data[1]].team = data[2]
			SCENES.players()
			break
		case 19:
			// team lock changed
			if (data[1]) {
				FUNCTIONS.showSystemMessage("Teams have been locked, only the host can assign teams", "#b53030")
			} else {
				FUNCTIONS.showSystemMessage("Teams have been unlocked", "#b53030")
			}
			break
		case 20:
			// chat messages
			FUNCTIONS.showChatMessage(data[2], data[1])
			break
		case 21:
			// game settings first load
			inLobby = true
			SCENES.leaderboard()
			ROOM_VAR.bal = data[1].bal
			break
		case 24:
			// kicked
			FUNCTIONS.showSystemMessage(`${ROOM_VAR.players[data[1]].userName} was ${data[2] ? "kicked" : "banned"}!`, "#b53030")
			break
		case 26:
			// change gamo
			let mode = "Classic"
			switch (data[2]) {
				case "b":
					mode = "Classic"
					break
				case "ar":
					mode = "Arrows"
					break
				case "ard":
					mode = "Death Arrows"
					break
				case "sp":
					mode = "Grapple"
					break
				case "f":
					mode = "Football"
					break
				case "v":
					mode = "VTOL"
					break
			}
			FUNCTIONS.showSystemMessage("Game mode changed to: " + mode, "#800d6e")
			break
		case 27:
			// change win lose
			FUNCTIONS.showSystemMessage("Rounds to win changed to: " + data[1], "#800d6e")
			break
		case 32:
			// afk warn
			FUNCTIONS.showSystemMessage("STOP AFK, WAKE UP!!!", "#b53030")
			break
		case 36:
			// balance
			ROOM_VAR.bal[data[1]] = data[2]
			SCENES.players()
			break
		case 39:
			// team settings change
			let onoroff = data[1] ? "on" : "off"
			FUNCTIONS.showSystemMessage(`Teams ${onoroff}`, "#800d6e")
			break
		case 40:
			// arm record
			if (data[1] != ROOM_VAR.myID) {
				FUNCTIONS.showSystemMessage(`${ROOM_VAR.players[data[1]].userName} requests record gameplay`, "#b53030")
			}
			break
		case 41:
			// host changed
			FUNCTIONS.showSystemMessage(
				ROOM_VAR.players[data[1].oldHost].userName +
					" has given host privileges to " +
					ROOM_VAR.players[data[1].newHost].userName +
					", who is now the game host",
				"#b53030"
			)
			if (data[1].newHost == ROOM_VAR.myID) {
				FUNCTIONS.showSystemMessage("You are now the host of this game", "#800d6e")
			}
			ROOM_VAR.hostID = data[1].newHost
			SCENES.players()
			break
		case 43:
			// countdown
			FUNCTIONS.showSystemMessage("Game starting in " + data[1], "#0955c7")
			break
		case 44:
			// countdown aborted
			FUNCTIONS.showSystemMessage("Countdown aborted!", "#0955c7")
			break
		case 45:
			// player leveled up
			ROOM_VAR.players[data[1].sid].level = data[1].lv
			SCENES.players()
			break
		case 46:
			// gained xp
			ROOM_VAR.xpEarned += 100
			break
		case 48:
			// show in game data if just joined
			inLobby = false
			SCENES.leaderboard()
			ROOM_VAR.bal = data[1].gs.bal
			ROOM_VAR.quick = data[1].gs.q
			break
		case 49:
			// autojoin
			ROOM_VAR.autoJoinID = data[1]
			ROOM_VAR.autoJoinPassBypass = data[2]
			break
		case 52:
			// tabbed update
			ROOM_VAR.players[data[1]].tabbed = data[2]
			SCENES.updatePlayersPing()
			break
		case 58:
			// room name changed
			FUNCTIONS.showSystemMessage("Room name changed to: " + data[1], "#800d6e")
			break
		case 59:
			// room password changed
			if (data[1]) {
				FUNCTIONS.showSystemMessage("A new password has been set for this room", "#800d6e")
			} else {
				FUNCTIONS.showSystemMessage("This room no longer requires a password to join.", "#800d6e")
			}
			break
	}
}

// HANDLE OWN DATA
function handleOwnData(data, websocket) {
	switch (data[0]) {
		case 5:
			ROOM_VAR.quick = data[1].gs.q
			break
		case 14:
			// Own quit game
			inLobby = true
			SCENES.leaderboard()
			break
		case 12:
			// Created room
			ROOM_VAR.myID = 0
			ROOM_VAR.hostID = 0
			ws = websocket
			panelLeft.style.visibility = "visible"
			panelRight.style.visibility = "visible"
			ROOM_VAR.players = []
			let tmpdata = data[1]
			tmpdata.userName = document.getElementById("pretty_top_name").innerText
			if (!data[1].guest) {
				tmpdata.level = parseInt(document.getElementById("pretty_top_level").innerText.substring(3))
			}
			ROOM_VAR.players.push(tmpdata)
			break
		case 20:
			// Change GAMO
			ioreceive([26, data[1].ga, data[1].mo])
			break
		case 21:
			// Change win lose
			ioreceive([27, data[1].w])
			break
		case 32:
			// Change teamchain
			ioreceive([39, data[1].t])
			break
		case 29:
			// Change balance
			ioreceive([36, data[1].sid, data[1].bal])
			break
	}
}

setInterval(() => {
	SCENES.leaderboard()
}, 1000)