MooMoo.io Custom Store

Customize store

安装此脚本
作者推荐脚本

您可能也喜欢MooMoo.io Insta Helper

安装此脚本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         MooMoo.io Custom Store
// @description  Customize store
// @author       KOOKY WARRIOR
// @match        *://*.moomoo.io/*
// @icon         https://moomoo.io/img/favicon.png?v=1
// @require      https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.10.2/Sortable.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/msgpack-lite/0.1.26/msgpack.min.js
// @require 	 https://greasyfork.org/scripts/478839-moomoo-io-packet-code/code/MooMooio%20Packet%20Code.js
// @run-at       document-start
// @grant        unsafeWindow
// @license      MIT
// @version      1.1.1
// @namespace    https://greasyfork.org/users/999838
// ==/UserScript==
/*
- Right-click the Store Button to access the editing options
- Press "B" to toggle store menu
- To change the order of your hats and accessories, simply drag and drop them to the desired position
- Add a blank space to give your collection some extra style
- Don't need a particular hat or accessory? Delete it with just a click
- Export your changes or import customizations
*/

;(async () => {
	unsafeWindow.customStore = true
	const elementID = (id) => {
		return document.getElementById(id)
	}
	const myPlayer = {
		sid: null,
		tails: {},
		skins: {},
		tailIndex: 0,
		skinIndex: 0,
		team: null
	}
	var inGame = false
	var alliances = []
	var alliancePlayers = []
	var totalAllianceEle = 0
	var currentAllianceEle = 0
	let init = false
	await new Promise(async (resolve) => {
		let { send } = WebSocket.prototype

		WebSocket.prototype.send = function (...x) {
			send.apply(this, x)
			this.send = send
			if (!init) {
				init = true
				this.addEventListener("message", (e) => {
					if (!e.origin.includes("moomoo.io") && !unsafeWindow.privateServer) return
					const [packet, data] = msgpack.decode(new Uint8Array(e.data))
					switch (packet) {
						case PACKETCODE.RECEIVE.updateStoreItems:
							if (data[2]) {
								if (!data[0]) {
									myPlayer.tails[data[1]] = 1
								} else {
									myPlayer.tailIndex = data[1]
								}
							} else {
								if (!data[0]) {
									myPlayer.skins[data[1]] = 1
								} else {
									myPlayer.skinIndex = data[1]
								}
							}
							if (elementID("storeMenu").style.display == "block") {
								generateStoreList()
							}
							break
						case PACKETCODE.RECEIVE.setupGame:
							myPlayer.sid = data[0]
							inGame = true
							break
						case PACKETCODE.RECEIVE.killPlayer:
							inGame = false
							break
						case PACKETCODE.RECEIVE.addAlliance:
							alliances.push(data[0])
							totalAllianceEle = myPlayer.team != null ? alliancePlayers.length / 2 : alliances.length
							currentAllianceEle = Math.min(totalAllianceEle - 5, currentAllianceEle + 5)
							if (elementID("allianceMenu").style.display == "block") {
								showAllianceMenu()
							}
							break
						case PACKETCODE.RECEIVE.setPlayerTeam:
							myPlayer.team = data[0]
							myPlayer.isOwner = data[1]
							currentAllianceEle = 0
							totalAllianceEle = myPlayer.team != null ? alliancePlayers.length / 2 : alliances.length
							if (elementID("allianceMenu").style.display == "block") {
								showAllianceMenu()
							}
							break
						case PACKETCODE.RECEIVE.setAlliancePlayers:
							alliancePlayers = data[0]
							totalAllianceEle = myPlayer.team != null ? alliancePlayers.length / 2 : alliances.length
							currentAllianceEle = Math.min(totalAllianceEle - 5, currentAllianceEle + 5)
							if (elementID("allianceMenu").style.display == "block") {
								showAllianceMenu()
							}
							break
						case PACKETCODE.RECEIVE.deleteAlliance:
							for (var i = alliances.length - 1; i >= 0; i--) {
								if (alliances[i].sid == data[0]) {
									alliances.splice(i, 1)
									break
								}
							}
							totalAllianceEle = myPlayer.team != null ? alliancePlayers.length / 2 : alliances.length
							currentAllianceEle = Math.min(totalAllianceEle - 5, currentAllianceEle + 5)
							if (elementID("allianceMenu").style.display == "block") {
								showAllianceMenu()
							}
							break
						case PACKETCODE.RECEIVE.setInitData:
							alliances = data[0].teams
							totalAllianceEle = myPlayer.team != null ? alliancePlayers.length / 2 : alliances.length
							break
					}
				})
			}
			resolve(this)
		}
	})

	function waitForElm(selector) {
		return new Promise((resolve) => {
			if (document.querySelector(selector)) {
				return resolve(document.querySelector(selector))
			}

			const observer = new MutationObserver((mutations) => {
				if (document.querySelector(selector)) {
					resolve(document.querySelector(selector))
					observer.disconnect()
				}
			})

			observer.observe(document.body, {
				childList: true,
				subtree: true
			})
		})
	}

	const customStoreHolder = document.createElement("div")
	customStoreHolder.id = "customStoreHolder"
	const customStoreScrollBar = document.createElement("div")
	customStoreScrollBar.id = "customStoreScrollBar"
	waitForElm("#storeHolder").then((storeHolder) => {
		const style = document.createElement("style")
		style.innerHTML = `
            #customStoreHolder {
                pointer-events: all;
                width: 400px;
                display: inline-block;
                background-color: rgba(0, 0, 0, 0.25);
                -webkit-border-radius: 4px;
                -moz-border-radius: 4px;
                border-radius: 4px;
                color: #fff;
                padding: 10px;
                height: 200px;
                max-height: calc(100vh - 200px);
                overflow-y: scroll;
                -webkit-overflow-scrolling: touch;
            }
			.storeItem {
				font-size: 24px;
			}
			.hatPreview {
				width: 45px;
				height: 45px;
			}
			.joinAlBtn {
				font-size: 24px;
			}
			.itemPrice {
				font-size: 24px;
			}
			#customStoreScrollBar {
				display: none;
				position: absolute;
				width: 3px;
				background: white;
				left: calc(50% + 210px - 3px);
				border-radius: 10px;
			}
        `
		document.head.appendChild(style)
		storeHolder.parentNode.insertBefore(customStoreHolder, storeHolder.nextSibling)
		storeHolder.parentNode.insertBefore(customStoreScrollBar, storeHolder.nextSibling)
		storeHolder.style.display = "none"
	})

	var currentStoreIndex = 0
	var store = {
		hats: [
			{
				id: 51,
				name: "Moo Cap",
				price: 0,
				scale: 120,
				desc: "coolest mooer around"
			},
			{
				id: 50,
				name: "Apple Cap",
				price: 0,
				scale: 120,
				desc: "apple farms remembers"
			},
			{
				id: 28,
				name: "Moo Head",
				price: 0,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 29,
				name: "Pig Head",
				price: 0,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 30,
				name: "Fluff Head",
				price: 0,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 36,
				name: "Pandou Head",
				price: 0,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 37,
				name: "Bear Head",
				price: 0,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 38,
				name: "Monkey Head",
				price: 0,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 44,
				name: "Polar Head",
				price: 0,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 35,
				name: "Fez Hat",
				price: 0,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 42,
				name: "Enigma Hat",
				price: 0,
				scale: 120,
				desc: "join the enigma army"
			},
			{
				id: 43,
				name: "Blitz Hat",
				price: 0,
				scale: 120,
				desc: "hey everybody i'm blitz"
			},
			{
				id: 49,
				name: "Bob XIII Hat",
				price: 0,
				scale: 120,
				desc: "like and subscribe"
			},
			{
				id: 57,
				name: "Pumpkin",
				price: 50,
				scale: 120,
				desc: "Spooooky"
			},
			{
				id: 8,
				name: "Bummle Hat",
				price: 100,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 2,
				name: "Straw Hat",
				price: 500,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 15,
				name: "Winter Cap",
				price: 600,
				scale: 120,
				desc: "allows you to move at normal speed in snow",
				coldM: 1
			},
			{
				id: 5,
				name: "Cowboy Hat",
				price: 1000,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 4,
				name: "Ranger Hat",
				price: 2000,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 18,
				name: "Explorer Hat",
				price: 2000,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 31,
				name: "Flipper Hat",
				price: 2500,
				scale: 120,
				desc: "have more control while in water",
				watrImm: true
			},
			{
				id: 1,
				name: "Marksman Cap",
				price: 3000,
				scale: 120,
				desc: "increases arrow speed and range",
				aMlt: 1.3
			},
			{
				id: 10,
				name: "Bush Gear",
				price: 3000,
				scale: 160,
				desc: "allows you to disguise yourself as a bush"
			},
			{
				id: 48,
				name: "Halo",
				price: 3000,
				scale: 120,
				desc: "no effect"
			},
			{
				id: 6,
				name: "Soldier Helmet",
				price: 4000,
				scale: 120,
				desc: "reduces damage taken but slows movement",
				spdMult: 0.94,
				dmgMult: 0.75
			},
			{
				id: 23,
				name: "Anti Venom Gear",
				price: 4000,
				scale: 120,
				desc: "makes you immune to poison",
				poisonRes: 1
			},
			{
				id: 13,
				name: "Medic Gear",
				price: 5000,
				scale: 110,
				desc: "slowly regenerates health over time",
				healthRegen: 3
			},
			{
				id: 9,
				name: "Miners Helmet",
				price: 5000,
				scale: 120,
				desc: "earn 1 extra gold per resource",
				extraGold: 1
			},
			{
				id: 32,
				name: "Musketeer Hat",
				price: 5000,
				scale: 120,
				desc: "reduces cost of projectiles",
				projCost: 0.5
			},
			{
				id: 7,
				name: "Bull Helmet",
				price: 6000,
				scale: 120,
				desc: "increases damage done but drains health",
				healthRegen: -5,
				dmgMultO: 1.5,
				spdMult: 0.96
			},
			{
				id: 22,
				name: "Emp Helmet",
				price: 6000,
				scale: 120,
				desc: "turrets won't attack but you move slower",
				antiTurret: 1,
				spdMult: 0.7
			},
			{
				id: 12,
				name: "Booster Hat",
				price: 6000,
				scale: 120,
				desc: "increases your movement speed",
				spdMult: 1.16
			},
			{
				id: 26,
				name: "Barbarian Armor",
				price: 8000,
				scale: 120,
				desc: "knocks back enemies that attack you",
				dmgK: 0.6
			},
			{
				id: 21,
				name: "Plague Mask",
				price: 10000,
				scale: 120,
				desc: "melee attacks deal poison damage",
				poisonDmg: 5,
				poisonTime: 6
			},
			{
				id: 46,
				name: "Bull Mask",
				price: 10000,
				scale: 120,
				desc: "bulls won't target you unless you attack them",
				bullRepel: 1
			},
			{
				id: 14,
				name: "Windmill Hat",
				topSprite: true,
				price: 10000,
				scale: 120,
				desc: "generates points while worn",
				pps: 1.5
			},
			{
				id: 11,
				name: "Spike Gear",
				topSprite: true,
				price: 10000,
				scale: 120,
				desc: "deal damage to players that damage you",
				dmg: 0.45
			},
			{
				id: 53,
				name: "Turret Gear",
				topSprite: true,
				price: 10000,
				scale: 120,
				desc: "you become a walking turret",
				turret: {
					proj: 1,
					range: 700,
					rate: 2500
				},
				spdMult: 0.7
			},
			{
				id: 20,
				name: "Samurai Armor",
				price: 12000,
				scale: 120,
				desc: "increased attack speed and fire rate",
				atkSpd: 0.78
			},
			{
				id: 58,
				name: "Dark Knight",
				price: 12000,
				scale: 120,
				desc: "restores health when you deal damage",
				healD: 0.4
			},
			{
				id: 27,
				name: "Scavenger Gear",
				price: 15000,
				scale: 120,
				desc: "earn double points for each kill",
				kScrM: 2
			},
			{
				id: 40,
				name: "Tank Gear",
				price: 15000,
				scale: 120,
				desc: "increased damage to buildings but slower movement",
				spdMult: 0.3,
				bDmg: 3.3
			},
			{
				id: 52,
				name: "Thief Gear",
				price: 15000,
				scale: 120,
				desc: "steal half of a players gold when you kill them",
				goldSteal: 0.5
			},
			{
				id: 55,
				name: "Bloodthirster",
				price: 20000,
				scale: 120,
				desc: "Restore Health when dealing damage. And increased damage",
				healD: 0.25,
				dmgMultO: 1.2
			},
			{
				id: 56,
				name: "Assassin Gear",
				price: 20000,
				scale: 120,
				desc: "Go invisible when not moving. Can't eat. Increased speed",
				noEat: true,
				spdMult: 1.1,
				invisTimer: 1000
			}
		],
		accessories: [
			{
				id: 12,
				name: "Snowball",
				price: 1000,
				scale: 105,
				xOff: 18,
				desc: "no effect"
			},
			{
				id: 9,
				name: "Tree Cape",
				price: 1000,
				scale: 90,
				desc: "no effect"
			},
			{
				id: 10,
				name: "Stone Cape",
				price: 1000,
				scale: 90,
				desc: "no effect"
			},
			{
				id: 3,
				name: "Cookie Cape",
				price: 1500,
				scale: 90,
				desc: "no effect"
			},
			{
				id: 8,
				name: "Cow Cape",
				price: 2000,
				scale: 90,
				desc: "no effect"
			},
			{
				id: 11,
				name: "Monkey Tail",
				price: 2000,
				scale: 97,
				xOff: 25,
				desc: "Super speed but reduced damage",
				spdMult: 1.35,
				dmgMultO: 0.2
			},
			{
				id: 17,
				name: "Apple Basket",
				price: 3000,
				scale: 80,
				xOff: 12,
				desc: "slowly regenerates health over time",
				healthRegen: 1
			},
			{
				id: 6,
				name: "Winter Cape",
				price: 3000,
				scale: 90,
				desc: "no effect"
			},
			{
				id: 4,
				name: "Skull Cape",
				price: 4000,
				scale: 90,
				desc: "no effect"
			},
			{
				id: 5,
				name: "Dash Cape",
				price: 5000,
				scale: 90,
				desc: "no effect"
			},
			{
				id: 2,
				name: "Dragon Cape",
				price: 6000,
				scale: 90,
				desc: "no effect"
			},
			{
				id: 1,
				name: "Super Cape",
				price: 8000,
				scale: 90,
				desc: "no effect"
			},
			{
				id: 7,
				name: "Troll Cape",
				price: 8000,
				scale: 90,
				desc: "no effect"
			},
			{
				id: 14,
				name: "Thorns",
				price: 10000,
				scale: 115,
				xOff: 20,
				desc: "no effect"
			},
			{
				id: 15,
				name: "Blockades",
				price: 10000,
				scale: 95,
				xOff: 15,
				desc: "no effect"
			},
			{
				id: 20,
				name: "Devils Tail",
				price: 10000,
				scale: 95,
				xOff: 20,
				desc: "no effect"
			},
			{
				id: 16,
				name: "Sawblade",
				price: 12000,
				scale: 90,
				spin: true,
				xOff: 0,
				desc: "deal damage to players that damage you",
				dmg: 0.15
			},
			{
				id: 13,
				name: "Angel Wings",
				price: 15000,
				scale: 138,
				xOff: 22,
				desc: "slowly regenerates health over time",
				healthRegen: 3
			},
			{
				id: 19,
				name: "Shadow Wings",
				price: 15000,
				scale: 138,
				xOff: 22,
				desc: "increased movement speed",
				spdMult: 1.1
			},
			{
				id: 18,
				name: "Blood Wings",
				price: 20000,
				scale: 178,
				xOff: 26,
				desc: "restores health when you deal damage",
				healD: 0.2
			},
			{
				id: 21,
				name: "Corrupt X Wings",
				price: 20000,
				scale: 178,
				xOff: 26,
				desc: "deal damage to players that damage you",
				dmg: 0.25
			}
		]
	}
	for (let i = 0; i < store.hats.length; ++i) {
		if (store.hats[i].price <= 0) {
			myPlayer.skins[store.hats[i].id] = 1
		}
	}
	for (let i = 0; i < store.accessories.length; ++i) {
		if (store.accessories[i].price <= 0) {
			myPlayer.tails[store.accessories[i].id] = 1
		}
	}
	var checkRealStore = JSON.parse(localStorage.getItem("realStore"))
	if (checkRealStore == null) {
		localStorage.setItem("realStore", JSON.stringify(store))
	}
	var customStore = JSON.parse(localStorage.getItem("customStore"))
	if (customStore == null) {
		customStore = store
		localStorage.setItem("customStore", JSON.stringify(store))
	}

	var totalShopEle = customStore.hats.length
	var currentShopEle = 0
	function updateScroll() {
		if (customStoreButton.style.background == "red") return
		const elements = document.querySelectorAll("#customStoreHolder > .storeItem")
		const storeArray = []
		for (let i = 0; i < elements.length; i++) {
			if (currentShopEle <= i && i < currentShopEle + 4) {
				elements[i].style.display = null
				storeArray.push(tmpArray[i].blank ? "blank" : tmpArray[i].id)
			} else {
				elements[i].style.display = "none"
			}
		}

		const elementHeight = 220 / elements.length
		customStoreScrollBar.style.height = `${elementHeight * 4}px`
		customStoreScrollBar.style.marginTop = `${elementHeight * currentShopEle}px`
		customStoreScrollBar.style.display = elements.length <= 4 ? "none" : "block"
		if (unsafeWindow.recorder) {
			unsafeWindow.updateStoreData = [currentStoreIndex, storeArray, elements.length, currentShopEle]
			unsafeWindow.sendToLocal("addData", [
				Date.now().toString(),
				{ type: "updateStore", data: [currentStoreIndex, storeArray, elements.length, currentShopEle] }
			])
		}
	}
	customStoreHolder.addEventListener("wheel", (event) => {
		if (event.wheelDelta > 0) {
			currentShopEle = Math.max(0, currentShopEle - 4)
		} else {
			currentShopEle = Math.min(totalShopEle - 4, currentShopEle + 4)
		}
		updateScroll()
	})

	var sortable = null,
		tmpArray
	function generateStoreList() {
		if (inGame) {
			while (customStoreHolder.hasChildNodes()) {
				customStoreHolder.removeChild(customStoreHolder.lastChild)
			}
			var index = currentStoreIndex
			tmpArray = index ? customStore.accessories : customStore.hats
			totalShopEle = tmpArray.length
			addEdit.style.display = customStoreButton.style.background == "red" ? null : "none"
			reloadEdit.style.display = customStoreButton.style.background == "red" ? null : "none"
			importBut.style.display = customStoreButton.style.background == "red" ? null : "none"
			exportBut.style.display = customStoreButton.style.background == "red" ? null : "none"
			customStoreHolder.style.overflowY = customStoreButton.style.background == "red" ? null : "hidden"
			Array.from(tmpArray).forEach((ele) => {
				let tmp = document.createElement("div")
				tmp.id = "storeDisplay" + ele.id
				tmp.className = "storeItem"
				customStoreHolder.appendChild(tmp)

				let childtmp
				if (!ele.blank) {
					tmp.onmouseout = () => {
						unsafeWindow.showItemInfo()
					}
					tmp.onmouseover = () => {
						unsafeWindow.showItemInfo(ele, false, true)
					}

					childtmp = document.createElement("img")
					childtmp.className = "hatPreview"
					childtmp.src = "../img/" + (index ? "accessories/access_" : "hats/hat_") + ele.id + (ele.topSprite ? "_p" : "") + ".png"
					tmp.appendChild(childtmp)

					childtmp = document.createElement("span")
					childtmp.textContent = ele.name
					tmp.appendChild(childtmp)
				} else {
					childtmp = document.createElement("div")
					childtmp.className = "hatPreview"
					tmp.appendChild(childtmp)
				}

				if (customStoreButton.style.background == "red") {
					childtmp = document.createElement("div")
					childtmp.className = "joinAlBtn"
					childtmp.style = "margin-top: 5px"
					childtmp.textContent = "Delete"
					tmp.appendChild(childtmp)
					childtmp.onclick = () => {
						let arr = index ? customStore.accessories : customStore.hats
						const objWithIdIndex = arr.findIndex((obj) => obj.id === ele.id)
						if (objWithIdIndex > -1) {
							arr.splice(objWithIdIndex, 1)
						}
						localStorage.setItem("customStore", JSON.stringify(customStore))
						generateStoreList()
					}
				} else if (!ele.blank) {
					if (index ? !myPlayer.tails[ele.id] : !myPlayer.skins[ele.id]) {
						childtmp = document.createElement("div")
						childtmp.className = "joinAlBtn"
						childtmp.style = "margin-top: 5px"
						childtmp.textContent = "Buy"
						childtmp.onclick = () => {
							unsafeWindow.storeBuy(ele.id, index)
						}
						tmp.appendChild(childtmp)

						childtmp = document.createElement("span")
						childtmp.className = "itemPrice"
						childtmp.textContent = ele.price
						tmp.appendChild(childtmp)
					} else if ((index ? myPlayer.tailIndex : myPlayer.skinIndex) == ele.id) {
						childtmp = document.createElement("div")
						childtmp.className = "joinAlBtn"
						childtmp.style = "margin-top: 5px"
						childtmp.textContent = "Unequip"
						childtmp.onclick = () => {
							unsafeWindow.storeEquip(0, index)
						}
						tmp.appendChild(childtmp)
					} else {
						childtmp = document.createElement("div")
						childtmp.className = "joinAlBtn"
						childtmp.style = "margin-top: 5px"
						childtmp.textContent = "Equip"
						childtmp.onclick = () => {
							unsafeWindow.storeEquip(ele.id, index)
						}
						tmp.appendChild(childtmp)
					}
				}
			})
			updateScroll()
			if (customStoreButton.style.background == "red") {
				if (sortable != null) {
					sortable.destroy()
				}
				sortable = new Sortable.create(customStoreHolder, {
					animation: 150,
					onUpdate: (event) => {
						let arr = index ? customStore.accessories : customStore.hats
						if (event.newIndex >= arr.length) {
							var k = event.newIndex - arr.length + 1
							while (k--) {
								arr.push(undefined)
							}
						}
						arr.splice(event.newIndex, 0, arr.splice(event.oldIndex, 1)[0])
						localStorage.setItem("customStore", JSON.stringify(customStore))
					}
				})
			} else {
				if (sortable != null) {
					sortable.destroy()
					sortable = null
				}
			}
		}
	}

	const customStoreButton = document.createElement("div")
	customStoreButton.id = "customStoreButton"
	customStoreButton.className = "uiElement gameButton"
	customStoreButton.innerHTML = `<i class="material-icons" style="font-size:40px; vertical-align:middle"></i>`
	customStoreButton.onclick = () => {
		if (elementID("storeMenu").style.display != "block") {
			elementID("storeMenu").style.display = "block"
			elementID("allianceMenu").style.display = "none"
			elementID("chatBox").value = ""
			elementID("chatHolder").style.display = "none"
			generateStoreList()
		} else {
			elementID("storeMenu").style.display = "none"
			customStoreButton.style.background = null
		}
	}
	customStoreButton.oncontextmenu = (event) => {
		event.preventDefault()
		if (elementID("storeMenu").style.display != "block") {
			elementID("storeMenu").style.display = "block"
			elementID("allianceMenu").style.display = "none"
			elementID("chatBox").value = ""
			elementID("chatHolder").style.display = "none"
			if (customStoreButton.style.background != "red") {
				customStoreButton.style.background = "red"
			}
			generateStoreList()
		} else {
			elementID("storeMenu").style.display = "none"
			customStoreButton.style.background = null
		}
	}

	waitForElm("#storeButton").then((storeButton) => {
		const style = document.createElement("style")
		style.innerHTML = `
            #customStoreButton {
                right: 330px;
            }
            @media only screen and (max-width: 896px) {
                #customStoreButton {
                    top: inherit;
                    right: 60px;
                }
            }
        `
		document.head.appendChild(style)
		storeButton.parentNode.insertBefore(customStoreButton, storeButton.nextSibling)
		storeButton.hidden = true
	})

	waitForElm("#storeMenu > div:nth-child(1) > div:nth-child(1)").then((storeTab1) => {
		storeTab1.addEventListener("click", () => {
			currentStoreIndex = 0
			currentShopEle = 0
			generateStoreList()
		})
	})
	const addEdit = document.createElement("div")
	addEdit.className = "storeTab"
	addEdit.textContent = "Add Blank"
	addEdit.style.display = "none"
	addEdit.style.marginLeft = "10px"
	const reloadEdit = document.createElement("div")
	reloadEdit.className = "storeTab"
	reloadEdit.textContent = "Reload"
	reloadEdit.style.display = "none"
	reloadEdit.style.marginLeft = "10px"
	const importBut = document.createElement("div")
	importBut.className = "storeTab"
	importBut.textContent = "Import"
	importBut.style.marginLeft = "10px"
	const exportBut = document.createElement("div")
	exportBut.className = "storeTab"
	exportBut.textContent = "Export"
	exportBut.style.marginLeft = "10px"
	waitForElm("#storeMenu > div:nth-child(1) > div:nth-child(2)").then((storeTab2) => {
		storeTab2.addEventListener("click", () => {
			currentStoreIndex = 1
			currentShopEle = 0
			generateStoreList()
		})

		storeTab2.parentNode.appendChild(addEdit)
		addEdit.onclick = () => {
			let arr = currentStoreIndex ? customStore.accessories : customStore.hats
			let id = Math.max(...arr.map((el) => el.id)) + 1001

			let min = customStoreHolder.getBoundingClientRect().top + 10
			let top,
				index = 0
			let childrens = customStoreHolder.childNodes
			for (var i = 0; i < childrens.length; i++) {
				top = Math.abs(childrens[i].getBoundingClientRect().top)
				if (top <= min) {
					index = i + 1
				}
			}
			arr.splice(index, 0, { id: id, blank: true })
			localStorage.setItem("customStore", JSON.stringify(customStore))
			generateStoreList()
		}

		storeTab2.parentNode.appendChild(reloadEdit)
		reloadEdit.onclick = () => {
			let realStore = JSON.parse(localStorage.getItem("realStore"))
			currentStoreIndex ? (customStore.accessories = realStore.accessories) : (customStore.hats = realStore.hats)
			localStorage.setItem("customStore", JSON.stringify(customStore))
			currentShopEle = 0
			generateStoreList()
		}

		storeTab2.parentNode.appendChild(importBut)
		importBut.onclick = () => {
			const tmpEle = document.createElement("input")
			tmpEle.type = "file"
			tmpEle.style.display = "none"
			document.body.appendChild(tmpEle)
			tmpEle.addEventListener("change", async () => {
				let data = await new Response(tmpEle.files[0]).json()
				customStore = data
				localStorage.setItem("customStore", JSON.stringify(data))
				tmpEle.remove()
				currentShopEle = 0
				generateStoreList()
			})
			tmpEle.click()
		}

		storeTab2.parentNode.appendChild(exportBut)
		exportBut.onclick = () => {
			let dataStr = JSON.stringify(customStore)
			let dataUri = "data:application/jsoncharset=utf-8," + encodeURIComponent(dataStr)

			let exportFileDefaultName = `customStore_${Date.now()}.json`

			let linkElement = document.createElement("a")
			linkElement.setAttribute("href", dataUri)
			linkElement.setAttribute("download", exportFileDefaultName)
			linkElement.click()
		}
	})

	unsafeWindow.addEventListener("keydown", (event) => {
		if (event.code == "Escape") {
			customStoreButton.style.background = null
		} else if (event.code == "KeyB" && document.activeElement.tagName != "INPUT" && inGame) {
			if (elementID("storeMenu").style.display != "block") {
				elementID("storeMenu").style.display = "block"
				elementID("allianceMenu").style.display = "none"
				elementID("chatBox").value = ""
				elementID("chatHolder").style.display = "none"
				generateStoreList()
			} else {
				elementID("storeMenu").style.display = "none"
				customStoreButton.style.background = null
			}
		}
	})

	const customAllianceHolder = document.createElement("div")
	customAllianceHolder.id = "customAllianceHolder"
	const customAllianceScrollBar = document.createElement("div")
	customAllianceScrollBar.id = "customAllianceScrollBar"
	waitForElm("#allianceHolder").then((allianceHolder) => {
		const style = document.createElement("style")
		style.innerHTML = `
            #customAllianceHolder {
                pointer-events: all;
				height: 200px;
				max-height: calc(100vh - 260px);
				overflow-y: hidden;
				-webkit-overflow-scrolling: touch;
				width: 350px;
				display: inline-block;
				text-align: left;
				padding: 10px;
				background-color: rgba(0, 0, 0, 0.25);
				-webkit-border-radius: 4px;
				-moz-border-radius: 4px;
				border-radius: 4px;
            }
			.allianceItem {
				height: 30px;
				font-size: 24px;
			}
			#allianceNoTribe {
				height: 30px;
				font-size: 24px;
				padding: 5px;
				color: #fff;
			}
			#customAllianceScrollBar {
				display: none;
				position: absolute;
				width: 3px;
				background: white;
				left: calc(50% + 185px - 3px);
				border-radius: 10px;
			}
        `
		document.head.appendChild(style)
		allianceHolder.parentNode.insertBefore(customAllianceHolder, allianceHolder.nextSibling)
		allianceHolder.parentNode.insertBefore(customAllianceScrollBar, allianceHolder.nextSibling)
		allianceHolder.style.display = "none"
	})

	customAllianceHolder.addEventListener("wheel", (event) => {
		if (event.wheelDelta > 0) {
			currentAllianceEle = Math.max(0, currentAllianceEle - 5)
		} else {
			currentAllianceEle = Math.min(totalAllianceEle - 5, currentAllianceEle + 5)
		}
		updateAllianceScroll()
	})
	function updateAllianceScroll() {
		const elements = document.querySelectorAll("#customAllianceHolder > .allianceItem")
		const allianceArray = []
		var tmpi = 0
		for (let i = 0; i < elements.length; i++) {
			if (currentAllianceEle <= i && i < currentAllianceEle + 5) {
				elements[i].style.display = null
				allianceArray.push({
					sid: myPlayer.team ? alliancePlayers[tmpi] : null,
					text: myPlayer.team ? alliancePlayers[tmpi + 1] : alliances[i]
				})
			} else {
				elements[i].style.display = "none"
			}

			tmpi += 2
		}

		if (allianceArray.length <= 0 && document.getElementById("allianceNoTribe") == null) {
			let tmp = document.createElement("div")
			tmp.id = "allianceNoTribe"
			tmp.textContent = "No Tribes Yet"
			customAllianceHolder.appendChild(tmp)
		}

		const elementHeight = 220 / elements.length
		customAllianceScrollBar.style.height = `${elementHeight * 5}px`
		customAllianceScrollBar.style.marginTop = `${elementHeight * currentAllianceEle}px`
		customAllianceScrollBar.style.display = elements.length <= 5 ? "none" : "block"

		if (unsafeWindow.recorder) {
			unsafeWindow.updateAllianceData = [myPlayer.team, allianceArray, elements.length, currentAllianceEle]
			unsafeWindow.sendToLocal("addData", [
				Date.now().toString(),
				{ type: "updateAlliance", data: [myPlayer.team, allianceArray, elements.length, currentAllianceEle] }
			])
		}
	}

	waitForElm("#allianceButton").then((ele) => {
		ele.addEventListener("click", () => {
			showAllianceMenu()
		})
	})

	function showAllianceMenu() {
		if (inGame) {
			if (unsafeWindow.recorder) {
				unsafeWindow.sendToLocal("addData", [Date.now().toString(), { type: "changeInputText", data: ["allianceInput", ""] }])
			}
			while (customAllianceHolder.hasChildNodes()) {
				customAllianceHolder.removeChild(customAllianceHolder.lastChild)
			}
			if (myPlayer.team) {
				for (let i = 0; i < alliancePlayers.length; i += 2) {
					let tmp = document.createElement("div")
					tmp.id = "allianceItem" + alliancePlayers[i]
					tmp.className = "allianceItem"
					tmp.style = "color:" + (alliancePlayers[i] == myPlayer.sid ? "#fff" : "rgba(255,255,255,0.6)")
					let tmp2 = document.createElement("span")
					tmp2.innerText = alliancePlayers[i + 1]
					tmp2.style.position = "absolute"
					tmp.appendChild(tmp2)
					customAllianceHolder.appendChild(tmp)

					if (myPlayer.isOwner && alliancePlayers[i] != myPlayer.sid) {
						let alliancePlayersArray = alliancePlayers
						let childtmp = document.createElement("div")
						childtmp.className = "joinAlBtn"
						childtmp.textContent = "Kick"
						childtmp.onclick = function () {
							unsafeWindow.kickFromClan(alliancePlayersArray[i])
						}
						tmp.appendChild(childtmp)
					}
				}
			} else if (alliances.length) {
				for (let i = 0; i < alliances.length; ++i) {
					let tmp = document.createElement("div")
					tmp.id = "allianceItem" + alliances[i].owner
					tmp.className = "allianceItem"
					tmp.style = "color:" + (alliances[i].sid == myPlayer.team ? "#fff" : "rgba(255,255,255,0.6)")
					let tmp2 = document.createElement("span")
					tmp2.innerText = alliances[i].sid
					tmp2.style.position = "absolute"
					tmp.appendChild(tmp2)
					customAllianceHolder.appendChild(tmp)

					let childtmp = document.createElement("div")
					childtmp.className = "joinAlBtn"
					childtmp.textContent = "Join"
					childtmp.onclick = function () {
						unsafeWindow.sendJoin(i)
					}
					tmp.appendChild(childtmp)
				}
			}
			updateAllianceScroll()
		}
	}
})()