Greasy Fork is available in English.

Bonk Panel

Have a better UI interface!

安装此脚本?
作者推荐脚本

您可能也喜欢Bonk Deobfuscator

安装此脚本
// ==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)