It's a good script for moomoo.io, it's not the best, but I'll try to make it as good as possible. M - Auto mills. Q/F/V/N/H - Macro placers. Everything else works automatically! If some hats were not bought by yourself, then you can buy them in the store.
// ==UserScript== // @name 9mm [MooMoo.io] [v1.0.1] // @name:ru 9mm [MooMoo.io] [v1.0.1] // @namespace https://github.com/Nudo-o // @version 1.0.1 // @description It's a good script for moomoo.io, it's not the best, but I'll try to make it as good as possible. M - Auto mills. Q/F/V/N/H - Macro placers. Everything else works automatically! If some hats were not bought by yourself, then you can buy them in the store. // @description:ru Это хороший скрипт для moomoo.io, не самый лучший, но я постараюсь сделать его как можно лучше. M - Автоматические мельницы. Q/F/V/N/H - Макросы. Всё остальное работает автоматически! Если какие-то шапки не купились сами, то вы можете купить их в магазине. // @author @nudoo // @match *://moomoo.io/* // @match *://*.moomoo.io/* // @icon https://www.google.com/s2/favicons?sz=64&domain=moomoo.io // @require https://greasyfork.org/scripts/423602-msgpack/code/msgpack.js // @require https://update.greasyfork.org/scripts/480301/1322984/CowJS.js // @license MIT // @grant none // @run-at document-start // ==/UserScript== // LICENSE (MIT): https://www.tldrlegal.com/license/mit-license // The creation uses my library designed to simplify the work in creating scripts on MooMoo.io. You can get acquainted with it by following the link. // Cow.js - https://update.greasyfork.org/scripts/480301/1322984/CowJS.js (function() { "use strict" const { Cow, CowUtils, msgpack } = window const { packets, items } = Cow.config.designations const _placeItem = Cow.placeItem const _roundRect = CanvasRenderingContext2D.prototype.roundRect Cow.setCodec(msgpack) let nearEnemy = null let preplaceObjects = [] let lastPreplaceClear = 0 class AutoHeal { constructor() { this.checkIsHealed = false this.lastHeal = 0 } doFullHeal() { const { player } = Cow if (player.health === player.maxHealth) return const amount = player.items[0] === 0 ? 20 : player.items[0] === 1 ? 30 : 25 for (let i = player.health; i < player.maxHealth; i += amount) { this.doHeal() } } doHeal() { const { player } = Cow if (player.health === player.maxHealth) return const timeSinceHeal = Cow.ticker.ticks - (this.lastHeal || 0) if (timeSinceHeal >= 1) { /*Cow.delayedPlaceItem(() => Cow.placeItem(items.FOOD))*/ Cow.placeItem(items.FOOD) this.lastHeal = Cow.ticker.ticks } } update() { const { player } = Cow if (!player?.alive || player?.skinIndex === 45) return if (player.health === player.maxHealth) { if (player.shameCount >= 2) { tailor.autoBullTick = true } return } if (player.health <= 85 && nearEnemy) { const distance = CowUtils.getDistance(nearEnemy, player) if (distance <= 500) { tailor.autoEmpHat = true } } const timeSinceHit = Cow.ticker.ticks - (player.hitTime || 0) const timeSinceHeal = Cow.ticker.ticks - (this.lastHeal || 0) if (player.shameCount < 5) { if (timeSinceHeal >= 8) { this.doHeal() } else { if (timeSinceHit >= ((player.health <= 30) ? 0 : 2)) { if (player.health <= 40) { this.doFullHeal() } else if (timeSinceHit >= 1 && timeSinceHit >= 1) { this.doHeal() } if (player.health >= 65) { this.doHeal() } else if (player.health > 40 && player.health < 65) { this.doFullHeal() } } else if (timeSinceHit === 1) { if (player.health <= 30) { this.doHeal() this.doFullHeal() } } } } else { if (player.health < 40 && timeSinceHit >= 2) { this.doHeal() this.doHeal() } else if (player.health >= 40 && timeSinceHit >= 3 && timeSinceHeal >= 2) { this.doFullHeal() this.doHeal() } } } } class AntiInsta { constructor() { this.targetHealth = 35 this.lastHeal = 0 } doFullHeal() { const { player } = Cow if (player.health === player.maxHealth) return const amount = player.items[0] === 0 ? 20 : player.items[0] === 1 ? 30 : 25 for (let i = player.health; i < player.maxHealth; i += amount) { this.doHeal() } } doHeal() { const { player } = Cow if (player.health === player.maxHealth) return const timeSinceHeal = Cow.ticker.ticks - (this.lastHeal || 0) if (timeSinceHeal >= 0) { Cow.placeItem(items.FOOD) this.lastHeal = Cow.ticker.ticks } } update() { const { player } = Cow if (!player?.alive || player?.skinIndex === 45) return if (player.health > this.targetHealth || !nearEnemy) return const timeSinceHit = Cow.ticker.ticks - (player.hitTime || 0) const timeSinceHeal = Cow.ticker.ticks - (this.lastHeal || 0) if (player.shameCount <= 4) { if (timeSinceHeal >= 8) { this.doHeal() if (player.health <= 20) { this.doFullHeal() } this.doHeal() } else { if (timeSinceHit >= ((player.health <= 20) ? 0 : 1)) { if (player.health <= (this.targetHealth - 10)) { this.doHeal() this.doHeal() } else if (timeSinceHit >= 1 && timeSinceHit >= 1) { this.doHeal() this.doFullHeal() } if (player.health >= (this.targetHealth - 10)) { this.doHeal() this.doHeal() } else if (player.health > 5 && player.health < 15) { this.doFullHeal() this.doHeal() } } else if (timeSinceHit === 1) { this.doHeal() this.doHeal() } } if (player.health <= Math.max(this.targetHealth - 15, 10)) { this.doFullHeal() } else if (timeSinceHit >= 1 && timeSinceHeal >= 1) { this.doHeal() this.doHeal() } else { this.doHeal() } } else { if (player.health < (this.targetHealth - 5) && timeSinceHit >= 1) { this.doHeal() this.doHeal() } else if (player.health >= (this.targetHealth - 5) && timeSinceHit >= 2 && timeSinceHeal >= 1) { this.doHeal() } } } } class Tailor { constructor() { this.hatTicks = [] this.hasHats = [ 0 ] this.hasAccs = [ 0 ] this.lastHatTick = 0 this.autoEmpCD = null this.autoEmpSoldier = false this.autoSoldierEmp = false this.autoEmpHat = false this.autoBullTick = false this.auto####Hat = false } get canEquip() { return !autoSpikeSync.isActive && !autoClickHats.isActive } isHasTick(key) { return Boolean(this.hatTicks.filter((tick) => tick.key == key).length) } reset() { this.autoEmpSoldier = false this.autoSoldierEmp = false this.autoEmpHat = false this.autoBullTick = false this.auto####Hat = false } equipHat(id, onlyBuy) { const { player } = Cow if (!this.hasHats.includes(id) && id !== 0) { if (player.points >= Cow.items.hats.searchById(id).price) { Cow.sendPacket(packets.STORE_EQUIP, 1, id, 0, "by 9mm") return this.hasHats.push(id) } } if (onlyBuy) return if (this.hasHats.includes(id) && player.skinIndex !== id) { Cow.sendPacket(packets.STORE_EQUIP, 0, id, 0) this.lastHatID = id } } equipAcc(id, onlyBuy) { const { player } = Cow if (!this.hasAccs.includes(id) && id !== 0) { if (player.points >= Cow.items.accessories.searchById(id).price) { Cow.sendPacket(packets.STORE_EQUIP, 1, id, 1, "by 9mm") return this.hasAccs.push(id) } } if (onlyBuy) return if (this.hasAccs.includes(id) && player.tailIndex !== id) { Cow.sendPacket(packets.STORE_EQUIP, 0, id, 1) this.lastAccID = id } } equipBiomeHat() { if (!this.canEquip) return const { player } = Cow let hatID = 0 if (player.y2 > 6850 && player.y2 < 7550) { hatID = 31 } else if (player.y2 < 2400) { hatID = 15 } else { hatID = 12 } this.equipHat(hatID) if (!this.isHasTick("uneqip-tail")) { this.hatTicks.push({ key: "unknown", callback: () => { this.equipAcc(11) } }) } } unknownTicks(amount) { while (amount--) { this.hatTicks.push({ key: "unknown", callback: () => {} }) } } autoHats() { const { player } = Cow const dangerBuildings = calculator.getDangerBuildings(player) if (nearEnemy && player.health < player.maxHealth && this.canEquip) { const isPolearm = nearEnemy.weaponIndex === 4 const isSword = nearEnemy.weaponIndex === 3 const isKatana = nearEnemy.weaponIndex === 4 if (isPolearm || isSword || isKatana) { if (nearEnemy.weapons[1] === 10) { if (this.autoEmpSoldier) return this.autoEmpSoldier = true this.hatTicks = [] this.equipHat(22) setTimeout(() => { this.equipHat(6) setTimeout(() => { this.autoEmpSoldier = false }, 25) }, 90) } else { if (this.autoSoldierEmp) return this.autoSoldierEmp = true this.hatTicks = [] this.equipHat(6) setTimeout(() => { this.equipHat(22) setTimeout(() => { this.autoEmpSoldier = false }, 25) }, 90) } return } } if (this.autoEmpHat && (!this.autoEmpCD || Date.now() - this.autoEmpCD >= 500) && this.canEquip) { this.equipHat(22) if (!this.isHasTick("uneqip-hats")) { this.unknownTicks(1) this.hatTicks.push({ key: "uneqip-hats", callback: () => { this.autoEmpHat = false this.autoEmpCD = null } }) } this.autoEmpCD = Date.now() } else if (this.auto####Hat) { this.equipHat(40) if (!this.isHasTick("uneqip-hats")) { this.unknownTicks(3) this.hatTicks.push({ key: "uneqip-hats", callback: () => { this.auto####Hat = false } }) } } else if (dangerBuildings.length || nearEnemy && this.canEquip) { if (dangerBuildings.length) { this.equipHat(6) } else if (nearEnemy) { const weaponIndex = nearEnemy.weaponIndex const angle = CowUtils.getDirection(nearEnemy, player) const distance = CowUtils.getDistance(nearEnemy, player) - player.scale const isMeInAngle = true//getAngleDist(angle, tmpValues.nearEnemy.dir) <= Math.PI / 1.25 const weapon = Cow.items.weapons[weaponIndex] if (!weapon) return const isMeInRange = distance <= weapon.range * 3.25 if (isMeInRange && isMeInAngle) { if (nearEnemy.skinIndex === 7 && nearEnemy.tailIndex !== 11) { if (!this.isHasTick("auto-spike")) { this.equipHat(6) this.unknownTicks(2) this.hatTicks.push({ key: "auto-spike", callback: () => { this.equipHat(11) } }) } return } else { this.equipHat(6) return } } else { if (this.canEquip) return this.equipBiomeHat() } } } else { if (this.canEquip) this.equipBiomeHat() } } update() { this.autoHats() const { player } = Cow const timeSinceHatTick = Cow.ticker.ticks - (this.lastHatTick || 0) if (timeSinceHatTick >= 20 && !this.hatTicks.length && this.canEquip) { this.equipAcc(11, true) this.equipHat(6, true) this.equipHat(7, true) this.equipHat(22, true) this.equipHat(40, true) this.equipHat(53, true) this.lastHatTick = Cow.ticker.ticks } if (!this.canEquip) return (this.hatTicks = []) const hatTick = this.hatTicks[0] typeof hatTick?.callback === 'function' && hatTick.callback() this.hatTicks.shift() } } class AutoPlacer { constructor() { this.delay = 10 this.lastUpdate = null } update() { const { player } = Cow if (!player?.alive || !nearEnemy?.visible || autoSpikeSync.isActive) return if (Date.now() - this.lastUpdate < this.delay) return const trapConfig = Cow.items.list[player.items[items.TRAP]] const spikeConfig = Cow.items.list[player.items[items.SPIKE]] if (!trapConfig || !spikeConfig) return const visibleObjects = Cow.objectsManager.list.filter((gameObject) => gameObject.visible && gameObject.active && CowUtils.getDistance(gameObject, player) <= 300) const angle = CowUtils.getDistance(nearEnemy, player) const placeSpikeDistance = player.scale + spikeConfig.scale * 1.4 const distance = CowUtils.getDistance(player, nearEnemy) let distanceToPlace = distance nearEnemy.inTrap = false for (let i = 0; i < visibleObjects.length; i++) { const gameObject = visibleObjects[i] if (!gameObject.isItem || gameObject.id !== 15 || gameObject.owner?.sid === nearEnemy.sid) continue const scale = gameObject.scale || gameObject.getScale() const enemyDistanceToTrap = CowUtils.getDistance(nearEnemy, gameObject) - scale + window.config.collisionDepth const angleTrapToEnemy = CowUtils.getDirection(nearEnemy, gameObject) if (enemyDistanceToTrap > 0) continue nearEnemy.inTrap = true const offset = scale - Math.abs(enemyDistanceToTrap) + nearEnemy.scale / 2 + spikeConfig.scale const placeX = gameObject.x + offset * Math.cos(angleTrapToEnemy) const placeY = gameObject.y + offset * Math.sin(angleTrapToEnemy) distanceToPlace = CowUtils.getDistance(placeX, placeY, player.x, player.y) if (distanceToPlace <= placeSpikeDistance) { const angleToPlace = CowUtils.getDirection(placeX, placeY, player.x, player.y) Cow.placeItem(items.SPIKE, { angle: angleToPlace }) } break } const distanceToEnemy = CowUtils.getDistance(player, nearEnemy) - nearEnemy.scale const placeDistance = trapConfig.scale * 1.2 + player.scale if (!nearEnemy.inTrap) { if (trapConfig) { if (distanceToEnemy <= placeDistance && player.items[items.TRAP] === 15) { const angle = CowUtils.getDirection(nearEnemy, player) Cow.placeItem(items.TRAP, { angle }) } } } else if (distanceToPlace > placeSpikeDistance && distanceToEnemy <= 275) { if (player.items[items.TRAP] === 15) { Cow.placeItem(items.TRAP, { angle: angle }) } Cow.placeItem(items.SPIKE, { angle: -angle }) } this.lastUpdate = Date.now() } } class Macro { constructor() { this.assistPlaceX = null this.assistPlaceY = null this.lastAssistActive = null this.keys = { FOOD: 81, TRAP: 70, SPIKE: 86, MILL: 78, TURRET: 72 } } resetAssist() { this.assistPlaceX = null this.assistPlaceY = null this.lastAssistActive = Date.now() } update() { const { player } = Cow if (!player?.alive || isInputFocused()) return if (Date.now() - this.lastAssistActive >= 500) this.resetAssist() for (const key in this.keys) { if (!Cow.input.keyboard.activeKeys.get(this.keys[key])) continue const placeItemIndex = items[key.replace(/wind/, "").toUpperCase()] const placeItem = Cow.items.list[player.items[placeItemIndex]] if (!placeItem) continue let placeAngle = player.lookAngle if (placeItem?.scale) { const generalScale = (player.scale + placeItem.scale + (placeItem.placeOffset || 0)) const placeX = player.x + (generalScale * Math.cos(placeAngle)) const placeY = player.y + (generalScale * Math.sin(placeAngle)) const placePosition = { x: placeX, y: placeY } const interferingObject = Cow.objectsManager.checkItemLocation(placeX, placeY, placeItem.scale, 0.6, placeItem.id, false, true) if (interferingObject) { let nearObjects = Cow.objectsManager.list.filter((gameObject) => { const generalScale = placeItem.scale + (gameObject.isItem ? gameObject.scale : gameObject.getScale(0.6, false)) const inPlace = CowUtils.getDistance(gameObject, player) <= generalScale + player.scale * 2 + (placeItem.placeOffset || 0) return gameObject.visible && gameObject.active && inPlace }) nearObjects = nearObjects.sort((a, b) => { a = CowUtils.getDistance(a, placePosition) b = CowUtils.getDistance(b, placePosition) return a - b }) if (nearObjects.length) { let newPlaceX = placeX let newPlaceY = placeY for (const nearObject of nearObjects) { const angle = CowUtils.getDirection(player, nearObject) const scale = nearObject.isItem ? nearObject.scale : nearObject.getScale(.6, false) const offsetScale = scale / 2 + placeItem.scale / 2 * 1.25 const _x = newPlaceX + offsetScale const _y = newPlaceY + offsetScale newPlaceX = _x * Math.cos(angle - Math.atan(player.x - _x)) newPlaceY = _y * Math.sin(angle - Math.atan(player.y - _y)) const isCanPlace = Cow.objectsManager.checkItemLocation(newPlaceX, newPlaceY, placeItem.scale, 0.6, placeItem.id, false) if (isCanPlace) break nearObjects = nearObjects.sort((a, b) => { const newPlacePosition = { x: newPlaceX, y: newPlaceY } a = CowUtils.getDistance(a, newPlacePosition) b = CowUtils.getDistance(b, newPlacePosition) return a - b }) } this.assistPlaceX = newPlaceX this.assistPlaceY = newPlaceY this.lastAssistActive = Date.now() placeAngle = CowUtils.getDirection(player.x, player.y, newPlaceX, newPlaceY) } } } //Cow.delayedPlaceItem(() => { Cow.placeItem(placeItemIndex, { angle: placeAngle }) //}) } } } class AutoMills { constructor() { this.gaps = [ 1.115820407, 1.141422642 ] this.lastPlace = null this.isActive = false this.expectedMills = 0 this.autoResetTime = null const onKeyboard = () => { if (isInputFocused()) return this.isActive = !this.isActive this.expectedMills = 0 if (!this.isActive) this.lastPlace = null } Cow.onKeyboard(77, onKeyboard, { repeat: false }) } get gap() { const { player } = Cow return this.gaps[Number(player.items[items.MILL] !== 10)] } update() { const { player } = Cow if (!player?.alive || !this.isActive) return if (Date.now() - this.autoResetTime >= 500) { this.autoResetTime = null } else if (this.autoResetTime) { return } if (this.lastPlace && Date.now() - this.lastPlace < 100) return const millConfig = Cow.items.list[player.items[items.MILL]] const checkCanBuild = (angle) => { const scale = player.scale + millConfig.scale + (millConfig.placeOffset || 0) const placeX = player.x2 + scale * Math.cos(angle) const placeY = player.y2 + scale * Math.sin(angle) return Cow.objectsManager.checkItemLocation(placeX, placeY, millConfig.scale, .6, millConfig.id, false) } if (checkCanBuild(player.moveDir + this.gap)) { Cow.placeItem(items.MILL, { angle: player.moveDir + this.gap }) this.expectedMills += 1 } if (checkCanBuild(player.moveDir)) { Cow.placeItem(items.MILL, { angle: player.moveDir }) this.expectedMills += 1 } if (checkCanBuild(player.moveDir)) { Cow.placeItem(items.MILL, { angle: player.moveDir - this.gap }) this.expectedMills += 1 } if (this.expectedMills === 1) { this.autoResetTime = Date.now() } this.lastPlace = Date.now() } } class ReloadBars { constructor() { this.colors = { 1: [ "#cc5151", "#8ecc51" ], 2: "#accd51", 3: "#c4cd51", 4: "#cdae51", 5: "#cd8251", 6: "#cd5d51" } } getColor(reloadValue, isAlly) { let color = "" if (reloadValue >= 0.8 && reloadValue < 1) { color = this.colors[2] } else if (reloadValue >= 0.6 && reloadValue < 0.8) { color = this.colors[3] } else if (reloadValue >= 0.4 && reloadValue < 0.6) { color = this.colors[4] } else if (reloadValue >= 0.2 && reloadValue < 0.4) { color = this.colors[5] } else if (reloadValue < 0.2) { color = this.colors[6] } else { color = this.colors[1][Number(isAlly)] } return color } drawBar(widthMult, color, object, offsetX, offsetY, _width, radii) { const { healthBarWidth, healthBarPad } = window.config const { context } = Cow.renderer const width = _width || (healthBarWidth / 2 - healthBarPad / 2) const height = 17 context._roundRect = _roundRect context.save() context.fillStyle = "#3d3f42" context.translate(object.renderX + offsetX, object.renderY + offsetY) context.beginPath() context._roundRect(-width - healthBarPad, -height / 2, 2 * width + 2 * healthBarPad, height, Array.isArray(radii) ? radii[0] : radii) context.fill() context.restore() context.save() context.fillStyle = color context.translate(object.renderX + offsetX, object.renderY + offsetY) context.beginPath() context._roundRect(-width, -height / 2 + healthBarPad, 2 * width * widthMult, height - 2 * healthBarPad, Array.isArray(radii) ? radii[1] : radii - 1) context.fill() context.restore() } drawPrimaryBar(entity) { const primaryReload = Math.min(Math.max(entity.reloads.primary.count / entity.reloads.primary.max, 0), 1) const isAlly = entity.isMe || entity.isAlly const { healthBarWidth, healthBarPad } = window.config const width = (healthBarWidth / 2 - healthBarPad / 2) const addWidth = 0 const color = this.getColor(primaryReload, isAlly) const offset = -width * 1.19 + addWidth const radius = 8 const radii = [[ radius, 0, 0, radius ], [ radius - 1, 0, 0, radius - 1 ]] this.drawBar(primaryReload, color, entity, offset, entity.scale + window.config.nameY - 5, width + addWidth, radii) } drawSecondaryBar(entity) { const secondaryReload = Math.min(Math.max(entity.reloads.secondary.count / entity.reloads.secondary.max, 0), 1) const isAlly = entity.isMe || entity.isAlly const { healthBarWidth, healthBarPad } = window.config const width = (healthBarWidth / 2 - healthBarPad / 2) const addWidth = 0 const color = this.getColor(secondaryReload, isAlly) const offset = width * 1.19 - addWidth const radius = 8 const radii = [[ 0, radius, radius, 0 ], [ 0, radius - 1, radius - 1, 0 ]] this.drawBar(secondaryReload, color, entity, offset, entity.scale + window.config.nameY - 5, width + addWidth, radii) } drawTurretHatBar(entity) { const turretReload = Math.min(Math.max(entity.reloads.turret.count / entity.reloads.turret.max, 0), 1) const isAlly = entity.isMe || entity.isAlly const { healthBarWidth } = window.config const color = this.getColor(turretReload, isAlly) const radius = 8 this.drawBar(turretReload, color, entity, 0, entity.scale + window.config.nameY * 1.75 - 3, healthBarWidth, radius) } update() { const { player } = Cow if (!player?.alive) return Cow.playersManager.eachVisible((player) => { this.drawPrimaryBar(player) this.drawSecondaryBar(player) this.drawTurretHatBar(player) }) } } class Calculator { constructor() {} getLength(x, y) { const math = (Math.pow, Math.sqrt) return math(x * x + y * y) } findBuildingOnPosition(target, other) { const dx = target.x - other.x const dy = target.y - other.y const scale = target.scale + (other.getScale ? other.getScale() : other.scale) const length = this.getLength(dx, dy) return length - scale < 0 } getPredictor(target) { return { x: target.x2 + target.speed * Math.cos(target.moveDir - Math.PI), y: target.y2 + target.speed * Math.sin(target.moveDir - Math.PI), scale: target.scale } } getDangerBuildings(target) { const { player } = Cow if (!player?.alive || !target?.visible) return [] const predictor = this.getPredictor(target) return Cow.objectsManager.list.filter((gameObject) => { if (!gameObject.visible || !gameObject.isItem || !gameObject.visible) return const isSpike = [6, 7, 8, 9].includes(gameObject.id) return isSpike && !Cow.isAllianceMember(gameObject.owner?.sid) && this.findBuildingOnPosition(predictor, gameObject) }) } } class AutoBreak { constructor() { this.isBreaking = false this.weaponBeforeStart = null } stopBreaking() { if (!this.isBreaking) return const { player } = Cow this.isBreaking = false tailor.auto####Hat = false aimControl.stopAiming() Cow.sendPacket(packets.SELECT_BUILD, player.weapons[this.weaponBeforeStart], true) this.weaponBeforeStart = null } async update() { const { player } = Cow player.inTrap = false const nearTrap = Cow.objectsManager.list .filter((gameObject) => gameObject.visible && gameObject.active && gameObject.id === 15 && !Cow.isAllianceMember(gameObject.owner?.sid)) .sort((a, b) => { a = CowUtils.getDistance(a, player) b = CowUtils.getDistance(b, player) return a - b })[0] if (!nearTrap) return this.stopBreaking() const distance = CowUtils.getDistance(nearTrap, player) - nearTrap.scale + window.config.collisionDepth player.inTrap = distance <= 10 if (!player.inTrap) return this.stopBreaking() this.isBreaking = true tailor.auto####Hat = true if (!this.weaponBeforeStart) { this.weaponBeforeStart = Number(player.weaponIndex > 8) } const breakWeapon = player.weapons[1] === 10 ? player.weapons[1] : player.weapons[0] const equipWeapon = (id) => { if (player.weaponIndex !== breakWeapon) Cow.sendPacket(packets.SELECT_BUILD, breakWeapon, true) } if (nearEnemy) { const weapon = Cow.items.weapons[player.weaponIndex] const angle = CowUtils.getDirection(nearEnemy, player) const distance = CowUtils.getDistance(nearEnemy, player) - player.scale * 2 const isInAngle = CowUtils.getAngleDist(angle, player.dir) <= window.config.gatherAngle const isInRange = distance <= weapon.range if (isInAngle && isInRange && player.weapons[0] !== 8) { equipWeapon(player.weapons[0]) } else { equipWeapon(breakWeapon) } } else { equipWeapon(breakWeapon) } aimControl.startAiming(nearTrap) Cow.sendPacket(packets.ATTACK_STATE, 1, aimControl.aimAngle) Cow.sendPacket(packets.ATTACK_STATE, 0, aimControl.aimAngle) } } class AntiTrap extends AutoBreak { constructor() { super() } update() { super.update() } } class AimControl { constructor() { this.aimTarget = null this.isAiming = false this._aimAngle = null this.isSent = false } get aimAngle() { this.updateAimToTarget() return this._aimAngle } set aimAngle(_angle) { this._aimAngle = _angle } onSent() { this.isSent = true } updateAimToTarget() { if (!this.isAiming) return const { player } = Cow const angle = typeof this.aimTarget === 'number' ? this.aimTarget : CowUtils.getDirection(this.aimTarget, player) this.aimAngle = angle } startAiming(point) { this.aimTarget = point this.isAiming = true this.isSent = false this.updateAimToTarget() } stopAiming() { this.aimTarget = null this.isAiming = false this.aimAngle = null } } class AutoClickHats { constructor() { this.isActive = false this.isGathering = false this.isAutoAttacking = false this.timeout = null } onStartGather(isAutoAttack) { if (isAutoAttack) { this.isAutoAttacking = true } this.isGathering = true this.reset() } onStopGather(isAutoAttack) { if (isAutoAttack) { this.isAutoAttacking = false } if (!isAutoAttack && this.isAutoAttacking) return this.isGathering = false this.reset() } fullReset() { this.isGathering = false this.isAutoAttacking = false this.reset() } reset() { this.isActive = false this.clearTimeout() } clearTimeout() { clearTimeout(this.timeout) this.timeout = null } update() { if (autoSpikeSync.isActive) return this.reset() if (!this.isGathering || this.isActive || this.timeout) return const { player } = Cow const weapon = Cow.items.weapons[player.weaponIndex] let isTargetEnemy = false if (nearEnemy) { const angle = CowUtils.getDirection(nearEnemy, player) const distance = CowUtils.getDistance(nearEnemy, player) - player.scale * 2 const isInAngle = CowUtils.getAngleDist(angle, player.dir) <= window.config.gatherAngle const isInRange = distance <= weapon.range if (isInRange && isInAngle) { isTargetEnemy = nearEnemy this.isActive = true this.clearTimeout() player.tailIndex === 11 && tailor.equipAcc(0) tailor.equipHat(7) this.timeout = setTimeout(() => { tailor.equipHat(6) this.reset() }, weapon.speed / 1.5) } } if (isTargetEnemy) return const gameObjects = Cow.objectsManager.list.filter((gameObject) => gameObject.isItem && gameObject.visible && gameObject.active && CowUtils.getDistance(player, gameObject) <= 300) const nearGameObject = gameObjects.sort((a, b) => { a = CowUtils.getDistance(player, a) b = CowUtils.getDistance(player, b) return a - b })[0] if (nearGameObject) { const angle = CowUtils.getDirection(nearGameObject, player) const distance = CowUtils.getDistance(nearGameObject, player) - nearGameObject.scale - weapon.range if (distance > 0) return this.isActive = true this.clearTimeout() tailor.equipHat(40) this.timeout = setTimeout(() => { tailor.equipHat(6) this.reset() }, weapon.speed / 1.5) } } } class AutoSpikeSync { constructor() { this.isActive = false this.lastActive = null } getSpikes() { return Cow.objectsManager.list.filter((gameObject) => gameObject.visible && gameObject.active && gameObject.group?.name === "spikes") } async doSpikeSync(angle) { if (this.lastActive && Date.now() - this.lastActive < 1000) return const { player } = Cow this.isActive = true if (player.weapons[0] === 7) { this.isActive = false return Cow.placeItem(items.SPIKE, { angle }) } aimControl.stopAiming() aimControl.startAiming(angle) await CowUtils.delay(10) player.weaponIndex !== player.weapons[0] && Cow.sendPacket(packets.SELECT_BUILD, player.weapons[0], true) await CowUtils.delay(10) tailor.equipAcc(0) await CowUtils.delay(10) tailor.equipHat(7) await CowUtils.delay(25) Cow.sendPacket(packets.ATTACK_STATE, 1, angle, "by 9mm") Cow.sendPacket(packets.ATTACK_STATE, 0, null, "by 9mm") await CowUtils.delay(25) Cow.placeItem(items.SPIKE, { angle }) aimControl.stopAiming() this.isActive = false this.lastActive = Date.now() } update() { const { player, camera } = Cow const { context } = Cow.renderer if (!player?.alive || this.isActive || !nearEnemy) return const spikeObjects = this.getSpikes() if (!spikeObjects.length) return const nearSpikesToEnemy = spikeObjects.filter((spikeObject) => spikeObject.owner.sid !== nearEnemy.sid && CowUtils.getDistance(nearEnemy, spikeObject) <= 90 + nearEnemy.scale) if (!nearSpikesToEnemy.length || nearSpikesToEnemy.length > 3) return const midX = nearSpikesToEnemy.reduce((acc, spikeObject) => acc + spikeObject.x, 0) / nearSpikesToEnemy.length const midY = nearSpikesToEnemy.reduce((acc, spikeObject) => acc + spikeObject.y, 0) / nearSpikesToEnemy.length const angleMeToMid = CowUtils.getDirection(midX, midY, player.x, player.y) const angleEnemyToMid = CowUtils.getDirection(midX, midY, nearEnemy.x, nearEnemy.y) const angleMeToEnemy = CowUtils.getDirection(nearEnemy, player) const targetAngle = window.config.gatherAngle * (nearSpikesToEnemy.length === 2 ? 2 : 1) const distanceToEnemy = CowUtils.getDistance(nearEnemy, player) - player.scale * 2 if (CowUtils.getAngleDist(-angleEnemyToMid, angleMeToEnemy) > targetAngle) return renderGameObjectMark({ renderX: midX - camera.xOffset, renderY: midY - camera.yOffset, }, context, 1, distanceToEnemy > player.weapon.range ? "#941492" : "#941414", true) if (distanceToEnemy > player.weapon.range) return this.doSpikeSync(angleMeToMid) } } CowUtils.delay = function(ms) { return new Promise((resolve) => setTimeout(resolve, ms)) } Cow.placeItem = function() { const lastWeapon = Number(this.player.weaponIndex > 8) if (arguments[0] !== 0) { preplaceObjects.push({ id: arguments[0], angle: arguments[1]?.angle || this.player.dir }) } _placeItem.apply(this, arguments) const weaponId = this.player.weapons[lastWeapon] if (this.player.weaponIndex !== weaponId) this.sendPacket(packets.SELECT_BUILD, weaponId, true) } Cow.delayedPlaceItem = function(callback) { this.lastPlaceItem ??= 0 if (this.lastPlaceItem && (this.ticker.ticks - this.lastPlaceItem) < 1) return callback() this.lastPlaceItem = this.ticker.ticks } Cow.isAllianceMember = function(sid) { const { player } = Cow if (player && player.sid == sid) return true if (!player.team || sid < 0) return false for (var i = 0; i < Cow.alliancePlayers.length; i += 2) { if (sid !== Cow.alliancePlayers[i]) continue return true } return false } Cow.isCanGather = function(doer, other) { const distance = CowUtils.getDistance(doer, other) - other.scale const angle = CowUtils.getDirection(other, doer) const angleDistance = CowUtils.getAngleDist(angle, doer.dir2) const isInAngle = angleDistance <= window.config.gatherAngle const isInRange = distance <= doer.weapon.range return { range: isInRange, angle: isInAngle, both: isInRange && isInAngle } } Cow.onPacket(packets.INIT_DATA, (initData) => { Cow.alliances = initData.teams }) Cow.onPacket(packets.ADD_ALLIANCE, (alliance) => { Cow.alliances.push(alliance) }) Cow.onPacket(packets.DELETE_ALLIANCE, (sid) => { for (let i = Cow.alliances.length - 1; i >= 0; i--) { if (Cow.alliances[i].sid !== sid) continue Cow.alliances.splice(i, 1) } }) Cow.onPacket(packets.SET_ALLIANCE_PLAYERS, (players) => { Cow.alliancePlayers = players }) Cow.onPacket(packets.UPDATE_PLAYERS, () => { const { player } = Cow if (Cow.ticker.ticks - lastPreplaceClear >= 1) { preplaceObjects = [] lastPreplaceClear = Cow.ticker.ticks } }) Cow.onPacket(packets.ADD_PLAYER, (_, isYou) => { if (!isYou) return tailor.reset() autoClickHats.fullReset() toggleLoadingMenu(false) setTimeout(() => { updateItemsCount(true) }, 500) if (!Cow.isFirstEnterGame) { const firstClanInput = document.querySelector("#first_clan_input") Cow.isFirstEnterGame = true firstClanInput.value && Cow.sendPacket(packets.CREATE_ALLIANCE, firstClanInput.value) } }) Cow.onPacket(packets.UPDATE_ITEM_COUNTS, (index, value) => { const { player } = Cow player.itemCounts[index] = value updateItemsCount(false, index) }) Cow.onPacket(packets.UPDATE_UPGRADES, (index, value) => { updateItemsCount() }) const macro = new Macro() const calculator = new Calculator() const autoPlacer = new AutoPlacer() const tailor = new Tailor() const autoHeal = new AutoHeal() const antiInsta = new AntiInsta() const autoMills = new AutoMills() const reloadBars = new ReloadBars() const antiTrap = new AntiTrap() const aimControl = new AimControl() const autoClickHats = new AutoClickHats() const autoSpikeSync = new AutoSpikeSync() let lastRenderUpdate = 0 let fps = 0 let lastPingUpdate = null Cow.addRender("global", () => { fps += (1000 / Math.max(Date.now() - lastRenderUpdate, 1) - fps) / 10 lastRenderUpdate = Date.now() const { context } = Cow.renderer const { player } = Cow nearEnemy = Cow.getNearEnemy() for (const preplaceObject of preplaceObjects) { renderPreplace(preplaceObject, context) } Cow.objectsManager.eachVisible((gameObject) => { if (!gameObject.isItem || !gameObject.active) return const distance = CowUtils.getDistance(player, gameObject) - player.scale if (distance > 600) return const alpha = Math.min(1, Math.max(0, 1 - (distance / 600))) renderGameObjectMark(gameObject, context, alpha) }) macro.update() autoPlacer.update() autoClickHats.update() tailor.update() autoHeal.update() antiInsta.update() autoMills.update() reloadBars.update() antiTrap.update() autoSpikeSync.update() const pingDisplay = document.querySelector("#pingDisplay") if (pingDisplay && player?.alive) { if (!lastPingUpdate || (Date.now() - lastPingUpdate) >= 750 || !/FPS/.test(pingDisplay.innerHTML)) { pingDisplay.innerHTML = `Ping: ${window.pingTime}ms, FPS: ${fps.toFixed(0) || 0}` lastPingUpdate = Date.now() } } }) const oldSend = WebSocket.prototype.send WebSocket.prototype.send = function(data) { const binary = new Uint8Array(data) const decoded = msgpack.decode(binary) const { player } = Cow if (decoded[0] === packets.STORE_EQUIP && decoded[1][0] === 1 && decoded[1][1] !== 0) { if (decoded[1][3] !== "by 9mm" && player.points >= Cow.items.hats.searchById(decoded[1][1])?.price) { if (decoded[1][2] === 0) { tailor.hasHats.push(decoded[1][1]) } else { tailor.hasAccs.push(decoded[1][1]) } } } if (![9, 12, 13, 15].includes(player?.weaponIndex)) { if (decoded[0] === packets.ATTACK_STATE) { if (decoded[1][2] !== "by cowjs" && decoded[1][2] !== "by 9mm") { if (decoded[1][0] === 1) { autoClickHats.onStartGather() } else { autoClickHats.onStopGather() } } } if (decoded[0] === packets.AUTO_ATTACK) { if (!autoClickHats.isAutoAttacking) { autoClickHats.onStartGather(true) } else { autoClickHats.onStopGather(true) } } } if (decoded[0] === packets.SPAWN) { const nicknameInput = document.querySelector("#nickname_input") decoded[1][0] = { name: "9-".concat(nicknameInput.value.slice(0, 13)), moofoll: true, skin: decoded[1][0].skin } return oldSend.call(this, msgpack.encode(decoded)) } if (decoded[0] === packets.LOOK_DIR && aimControl.isAiming) { aimControl.updateAimToTarget() decoded[1][0] = aimControl.aimAngle oldSend.call(this, msgpack.encode(decoded)) return aimControl.onSent() } oldSend.apply(this, arguments) } function isInputFocused() { return document.activeElement.tagName === "INPUT" } function renderGameObjectMark(gameObject, context, alpha, color, isDebug) { const { player } = Cow const radius = 12 const innerRadius = !gameObject.maxHealth ? radius : (gameObject.health / gameObject.maxHealth) * radius color = color ? color : gameObject.owner.sid === player.sid ? "#8ecc51" : Cow.isAllianceMember(gameObject.owner.sid) ? "#cdaa51" : "#cc5151" context.save() context.globalAlpha = alpha context.fillStyle = color context.translate(gameObject.renderX, gameObject.renderY) context.beginPath() context.arc(0, 0, isDebug ? radius : Math.min(radius, Math.max(0, innerRadius)), 0, Math.PI * 2) context.fill() context.closePath() context.restore() context.save() context.globalAlpha = alpha context.strokeStyle = "#3d3f42" context.lineWidth = 5.5 context.translate(gameObject.renderX, gameObject.renderY) context.beginPath() context.arc(0, 0, radius, 0, Math.PI * 2) context.stroke() context.closePath() context.restore() } function renderPreplace(preplaceObject, context) { const { player } = Cow const item = Cow.items.list[Cow.player.items[preplaceObject.id]] const sprite = getItemSprite(item) const x = (player.scale + item.scale) * Math.cos(preplaceObject.angle) const y = (player.scale + item.scale) * Math.sin(preplaceObject.angle) const isCanPlace = Cow.objectsManager.checkItemLocation(player.x + x, player.y + y, item.scale, 0.6, item.id, false) if (!isCanPlace) return context.save() context.globalAlpha = .3 context.translate(player.renderX + x, player.renderY + y) context.rotate(preplaceObject.angle) context.drawImage(sprite, -(sprite.width / 2), -(sprite.height / 2)) context.restore() } function updateItemsCount(isFirst, itemIndex) { const { player } = Cow const allActionBarItems = [ ...document.querySelectorAll(".actionBarItem") ] allActionBarItems.forEach((actionBarItem) => { const id = parseInt(actionBarItem.id.replace(/\D/g, "")) - 16 const itemConfig = Cow.items.list[id] if (!itemConfig?.group) return const { group } = itemConfig if (!group.place) return actionBarItem.innerHTML = `<span class="item-count${!isFirst && group.id === itemIndex ? " scale-anim" : ""}">${player.itemCounts[group.id] || 0}</span>` }) } const itemSprites = {} function renderStar(ctxt, spikes, outer, inner) { const step = Math.PI / spikes let rot = Math.PI / 2 * 3 let x = 0 let y = 0 ctxt.beginPath() ctxt.moveTo(0, -outer) for (let i = 0; i < spikes; i++) { x = Math.cos(rot) * outer y = Math.sin(rot) * outer ctxt.lineTo(x, y) rot += step x = Math.cos(rot) * inner y = Math.sin(rot) * inner ctxt.lineTo(x, y) rot += step } ctxt.lineTo(0, -outer) ctxt.closePath() } function renderCircle(x, y, scale, tmpContext, dontStroke, dontFill) { tmpContext = tmpContext || Cow.renderer.context tmpContext.beginPath() tmpContext.arc(x, y, scale, 0, 2 * Math.PI) if (!dontFill) tmpContext.fill() if (!dontStroke) tmpContext.stroke() } function renderRect(x, y, w, h, ctxt, stroke) { ctxt.fillRect(x - (w / 2), y - (h / 2), w, h) if (!stroke) ctxt.strokeRect(x - (w / 2), y - (h / 2), w, h) } function renderRectCircle(x, y, s, sw, seg, ctxt, stroke) { ctxt.save() ctxt.translate(x, y) seg = Math.ceil(seg / 2) for (var i = 0; i < seg; i++) { renderRect(0, 0, s * 2, sw, ctxt, stroke) ctxt.rotate(Math.PI / seg) } ctxt.restore() } function renderTriangle(s, ctx) { ctx = ctx || Cow.renderer.context const h = s * (Math.sqrt(3) / 2) ctx.beginPath() ctx.moveTo(0, -h / 2) ctx.lineTo(-s / 2, h / 2) ctx.lineTo(s / 2, h / 2) ctx.lineTo(0, -h / 2) ctx.fill() ctx.closePath() } function getItemSprite(obj) { let tmpSprite = itemSprites[obj.id]; if (tmpSprite) return tmpSprite const tmpCanvas = document.createElement("canvas") const tmpContext = tmpCanvas.getContext("2d") const outlineWidth = 5.5 const outlineColor = "#525252" tmpCanvas.width = tmpCanvas.height = (obj.scale * 2.5) + outlineWidth + (Cow.items.list[obj.id].spritePadding || 0) tmpContext.strokeStyle = outlineColor tmpContext.lineWidth = outlineWidth tmpContext.translate((tmpCanvas.width / 2), (tmpCanvas.height / 2)) tmpContext.rotate(Math.PI / 2) if (/wall/.test(obj.name)) { const sides = (obj.name == "castle wall") ? 4 : 3 tmpContext.fillStyle = obj.name == "castle wall" ? "#83898e" : obj.name == "wood wall" ? "#a5974c" : "#939393" renderStar(tmpContext, sides, obj.scale * 1.1, obj.scale * 1.1) tmpContext.fill() tmpContext.stroke() tmpContext.fillStyle = obj.name == "castle wall" ? "#9da4aa" : obj.name == "wood wall" ? "#c9b758" : "#bcbcbc" renderStar(tmpContext, sides, obj.scale * 0.65, obj.scale * 0.65) tmpContext.fill() } else if (/spikes/.test(obj.name)) { const tmpScale = (obj.scale * 0.6) tmpContext.fillStyle = obj.name == "poison spikes" ? "#7b935d" : "#939393" renderStar(tmpContext, (obj.name == "spikes") ? 5 : 6, obj.scale, tmpScale) tmpContext.fill() tmpContext.stroke() tmpContext.fillStyle = "#a5974c" renderCircle(0, 0, tmpScale, tmpContext) tmpContext.fillStyle = "#c9b758" renderCircle(0, 0, tmpScale / 2, tmpContext, true) } else if (/mill/.test(obj.name)) { tmpContext.fillStyle = "#a5974c" renderCircle(0, 0, obj.scale, tmpContext) tmpContext.fillStyle = "#c9b758" renderRectCircle(0, 0, obj.scale * 1.5, 29, 4, tmpContext) tmpContext.fillStyle = "#a5974c" renderCircle(0, 0, obj.scale * 0.5, tmpContext) } else if (/mine/.test(obj.name)) { tmpContext.fillStyle = "#939393" renderStar(tmpContext, 3, obj.scale, obj.scale) tmpContext.fill() tmpContext.stroke() tmpContext.fillStyle = "#bcbcbc" renderStar(tmpContext, 3, obj.scale * 0.55, obj.scale * 0.65) tmpContext.fill() } else if (/sapling/.test(obj.name)) { for (let i = 0; i < 2; ++i) { const tmpScale = obj.scale * (!i ? 1 : 0.5) renderStar(tmpContext, 7, tmpScale, tmpScale * 0.7) tmpContext.fillStyle = (!i ? "#9ebf57" : "#b4db62") tmpContext.fill() !i && tmpContext.stroke() } } else if (/trap/.test(obj.name)) { tmpContext.fillStyle = "#a5974c" renderStar(tmpContext, 3, obj.scale * 1.1, obj.scale * 1.1) tmpContext.fill() tmpContext.stroke() tmpContext.fillStyle = outlineColor renderStar(tmpContext, 3, obj.scale * 0.65, obj.scale * 0.65) tmpContext.fill() } else if (/boost/.test(obj.name)) { tmpContext.fillStyle = "#7e7f82" renderRect(0, 0, obj.scale * 2, obj.scale * 2, tmpContext) tmpContext.fill() tmpContext.stroke() tmpContext.fillStyle = "#dbd97d" renderTriangle(obj.scale * 1, tmpContext) } else if (/turret/.test(obj.name)) { const tmpLen = 50 tmpContext.fillStyle = "#a5974c" renderCircle(0, 0, obj.scale, tmpContext) tmpContext.fill() tmpContext.stroke() tmpContext.fillStyle = "#939393" renderRect(0, -tmpLen / 2, obj.scale * 0.9, tmpLen, tmpContext) renderCircle(0, 0, obj.scale * 0.6, tmpContext) tmpContext.fill() tmpContext.stroke() } else if (/platform/.test(obj.name)) { const tmpCount = 4; const tmpS = obj.scale * 2 const tmpW = tmpS / tmpCount let tmpX = -(obj.scale / 2) tmpContext.fillStyle = "#cebd5f" for (let i = 0; i < tmpCount; ++i) { renderRect(tmpX - (tmpW / 2), 0, tmpW, obj.scale * 2, tmpContext) tmpContext.fill() tmpContext.stroke() tmpX += tmpS / tmpCount } } else if (/spawn/.test(obj.name)) { tmpContext.fillStyle = "#7e7f82" renderRect(0, 0, obj.scale * 2, obj.scale * 2, tmpContext) tmpContext.fill() tmpContext.stroke() tmpContext.fillStyle = "#71aad6" renderCircle(0, 0, obj.scale * 0.6, tmpContext) } else if (/blocker/.test(obj.name)) { tmpContext.fillStyle = "#7e7f82" renderCircle(0, 0, obj.scale, tmpContext) tmpContext.fill() tmpContext.stroke() tmpContext.rotate(Math.PI / 4) tmpContext.fillStyle = "#db6e6e" renderRectCircle(0, 0, obj.scale * 0.65, 20, 4, tmpContext, true) } else if (/teleport/.test(obj.name)) { tmpContext.fillStyle = "#7e7f82" renderCircle(0, 0, obj.scale, tmpContext) tmpContext.fill() tmpContext.stroke() tmpContext.rotate(Math.PI / 4) tmpContext.fillStyle = "#d76edb" renderCircle(0, 0, obj.scale * 0.5, tmpContext, true) } tmpSprite = tmpCanvas itemSprites[obj.id] = tmpSprite return tmpSprite } function waitForInterval(selector, callback) { const checker = setInterval(() => { const node = document.querySelector(selector) if (!node?.style) return callback() clearInterval(checker) }) setTimeout(() => { clearInterval(checker) }, 30000) return checker } waitForInterval("#gameUI", () => { createCustomHtmlAndCss() }) waitForInterval("#mainMenu", createCustomMainMenu) waitForInterval("#enterGame", () => { const enterGameBtn = document.querySelector("#enterGame") enterGameBtn.addEventListener = new Proxy(enterGameBtn.addEventListener, { apply(target, _this, args) { _this = document.querySelector("#play_button") return target.apply(_this, args) } }) }) Object.defineProperty(HTMLElement.prototype, "onclick", { set(callback) { this.addEventListener("click", arguments[0]) if (!/enterGame/.test(this.id)) return const playButton = document.querySelector("#play_button") playButton.addEventListener("click", arguments[0]) } }) function createCustomMainMenu() { const mainMenu = document.querySelector("#mainMenu") const style = document.createElement("style") style.insertAdjacentHTML("beforeend", ` .better-mm-holder { display: block; position: absolute; top: 0; z-index: 999999999; width: 100%; height: 100%; background: rgba(0, 0, 0, .75); overflow: hidden; pointer-events: all; } .better-mm-holder * { box-sizing: border-box; } .better-mm-holder ul, .better-mm-holder li { margin: 0; padding: 0; list-style: none; text-decoration: none; } .better-mm-wrapper { display: flex; flex-direction: column; justify-content: space-between; height: 100%; } .better-mm-header { display: flex; align-items: center; justify-content: center; width: 100%; height: 105px; min-height: 105px; background: #101010; border-bottom: 4px solid #1f0f29; } .mod-title { background: #1f0f29; color: #7575757a; font-size: 50px; transform: skewX(10deg); padding: 5px 30px; border-radius: 50% 20% 35% / 70%; box-shadow: 0px 0px 3px 1px #020002; animation: blob-anim 20s infinite ease-in-out; } @keyframes blob-anim { 0% { border-radius: 50% 20% 35% / 70%; } 25% { border-radius: 50% 20% 35% / 30% 30% 20%; } 50% { border-radius: 50% 20% 35% / 50% 50% 40%; } 100% { border-radius: 50% 20% 35% / 70%; } } .better-mm-container { display: flex; align-items: center; justify-content: center; gap: 20px; width: 100%; height: 100%; padding: 40px; } .bmm-container-box { display: flex; flex-direction: column; background: #101010; border: 4px solid #1f0f29; border-radius: 12px; padding: 5px; overflow-y: auto; } .bmm-container-box::-webkit-scrollbar { width: 8px; } .bmm-container-box::-webkit-scrollbar-track { width: 8px; } .bmm-container-box::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, .35); border-radius: 4px 30px 30px 4px; } .game-servers-box, .mod-changelog-box, .game-settings-box { min-height: 375px; max-height: 375px; width: 325px; } .items-list { display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 5px !important; } .items-list .item { display: flex; flex-direction: column; gap: 4px; background: #252525b8; width: 100%; min-height: max-content; border-radius: 6px; padding: 5px !important; } .items-list .item.light-background { background: #474747bd; } .changelog-item-header, .server-data-header { font-size: 16px; color: #d0d0d0; } .changelog-updates { display: flex; flex-direction: column; gap: 4px; padding: 0 4px; } .changelog-update-value { font-size: 14px; color: #a3a2a2; } .changelog-version-info { display: flex; flex-direction: column; gap: 2px; padding-left: 4px; border-left: 2px solid #a3a2a2; } .player-body-figure { stroke-width: 4; stroke: #3d3f42; transition: .3s fill; } .game-settings-box, .game-servers-box { overflow-y: hidden; } .game-servers-box .items-list { overflow-y: auto; } .game-servers-box .items-list::-webkit-scrollbar { width: 8px; } .game-servers-box .items-list::-webkit-scrollbar-track { width: 8px; } .game-servers-box .items-list::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, .35); border-radius: 20px; } .game-settings-box { display: flex; align-items: center; justify-content: space-between; } .player-settings { display: flex; align-items: center; justify-content: center; gap: 5px; padding: 10px; } .player-preview-wrapper { min-width: 50px; min-height: 50px; border-radius: 12px; background: #252525b8; cursor: pointer; } .player-data-wrapper { display: flex; flex-direction: column; gap: 8px; } .player-data-input { background: none; outline: 0; border: none; color: #d0d0d0; font-size: 14px; border-bottom: 2px solid #1f0f29; transition: .3s border-bottom; } .player-data-input:hover, .player-data-input:focus { border-bottom: 2px solid #2d143d; } .game-servers-update { display: flex; align-items: center; justify-content: center; color: #a3a2a2; font-size: 16px; width: 100%; min-height: 30px; background: #252525b8; border-radius: 6px; cursor: pointer; margin-bottom: 5px; transition: .3s color; } .game-servers-update:hover { color: #d0d0d0; } .server-data-wrapper { display: flex; align-items: center; justify-content: space-between; } .server-data-header, .server-data-ping { font-size: 14px; user-select: text; cursor: default; } .server-data-ping.red { color: #750d0d; } .server-data-ping.low-red { color: #852323; } .server-data-ping.yellow { color: #b3af0c; } .server-data-ping.green { color: #4bb30c; } .server-data-ping.low-green { color: #6c9f2b; } .server-data-actions { display: flex; align-items: center; gap: 4px; } .server-data-players { display: inline-block; user-select: none; color: #a3a2a2; width: 55px; } .server-open-btn { display: flex; align-items: center; justify-content: center; color: #d0d0d0; font-size: 14px; cursor: pointer; padding: 0 4px; background: #1f0f29; border-radius: 4px; } .loading-text, .disconnect-text { color: #d0d0d0; font-size: 35px; } .info-link { cursor: pointer; text-decoration: underline; } .link-logo { width: 25px; height: 25px; } .info-footer { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 8px; border-radius: 8px; } .info-footer-item { display: flex; align-items: center; } .info-footer-item.discord-item { user-select: text; } .info-footer-item.discord-item img { transform: scale(1.35); } .info-footer-item.discord-item::after { content: "nudoo"; position: fixed; color: #5a75ce; font-size: 14px; width: 0px; transform: scaleX(0) translateX(30px); text-decoration: none; opacity: 0; user-select: text; transition: transform .3s, width .3s, opacity .3s; } .info-footer-item.discord-item:hover::after { margin-left: 4px; transform: scaleX(1) translateX(30px); width: 40px; opacity: 1; } .game-settings { display: flex; flex-direction: column; } .player-settings-wrapper { display: flex; flex-direction: column; align-items: center; } .play-button, .game-setting-btn { display: flex; align-items: center; justify-content: center; font-size: 18px; padding: 0 4px; width: 100%; height: 30px; color: #7575757a; background: #1f0f29; border-radius: 6px; letter-spacing: 4px; cursor: pointer; transition: .3s background, .3s color; } .play-button:hover { background: #2d143d; color: #75757599; } .play-button:active, .game-setting-btn:active { transform: scale(.975); } .game-settings { display: grid; grid-template-columns: repeat(2, max-content); justify-items: center; justify-content: center; grid-gap: 5px; width: 100%; height: 100%; } .game-setting-btn { font-size: 14px; width: 140px; padding: 0 6px; letter-spacing: 0px; background: #1f0f29; } .game-setting-btn.enabled { background: #2d143d; color: #d0d0d0; } .select-skin-panel { position: absolute; padding: 4px; display: grid; grid-template-columns: repeat(5, max-content); gap: 4px; background: #252525b8; border: 4px solid #1f0f29; border-radius: 6px; z-index: 99999999999; } .skin-circle { cursor: pointer; width: 20px; height: 20px; border-radius: 6px; border: 3px solid #525252; transition: .3s border-radius; } .skin-circle:hover { border-radius: 50%; } .skin-circle.selected { border-radius: 50% !important; } .global-notification { position: absolute; top: 0; width: 100%; height: 100%; z-index: 9999999999999999999; display: flex; align-items: center; justify-content: center; } .global-notification-box { width: 400px; height: 400px; overflow: hidden; } `) document.head.appendChild(style) mainMenu.insertAdjacentHTML("beforeend", ` <div class="better-mm-holder" id="menuCardHolder"> <main class="better-mm-wrapper"> <header class="better-mm-header"> <span class="mod-title">9mm</span> </header> <container class="better-mm-container" id="better_mm_loading"> <span class="loading-text">Loading...</span> </container> <container class="better-mm-container hidden" id="better_mm_disconnect"> <span class="disconnect-text">Disconnected...</span> </container> <container class="better-mm-container hidden" id="better_mm_container"> <box class="bmm-container-box game-servers-box"> <div class="game-servers-update" id="game_servers_update">UPDATE</div> <ul class="items-list" id="game_servers"></ul> </box> <box class="bmm-container-box game-settings-box"> <div class="player-settings-wrapper"> <div class="player-settings"> <div class="player-preview-wrapper" id="player_preview"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="eIZvJ0eqgt61" viewBox="0 0 100 100" shape-rendering="geometricPrecision" text-rendering="geometricPrecision"> <ellipse class="player-body-figure" rx="10" ry="10" transform="translate(80 32.942071)" fill="#bf8f54"/> <ellipse class="player-body-figure" rx="10" ry="10" transform="translate(20 32.942071)" fill="#bf8f54"/> <ellipse class="player-body-figure" rx="30" ry="30" transform="translate(50 57)" fill="#bf8f54"/> </svg> </div> <div class="player-data-wrapper"> <input class="player-data-input" id="nickname_input" placeholder="Enter nickname..." maxlength="13"> <input class="player-data-input" id="first_clan_input" placeholder="Enter clan name..." maxlength="7"> </div> </div> <div class="play-button" id="play_button">PLAY</div> </div> <div class="game-settings"> <div class="game-setting-btn${localStorage.show_ping == "true" ? " enabled" : ""}" id="show_ping">Show ping/fps</div> <div class="game-setting-btn${localStorage.native_resolution == "true" ? " enabled" : ""}" id="native_resolution">Native resolution</div> <div class="game-setting-btn${localStorage.mill_rotate == "true" ? " enabled" : ""}" id="mill_rotate">Mill rotate</div> <div class="game-setting-btn${localStorage.remove_grid == "true" ? " enabled" : ""}" id="remove_grid">Remove grid</div> </div> <footer class="info-footer"> <a class="info-footer-item info-link" href="https://www.youtube.com/channel/UCpBgMEb1vFQnMcSz-kJ6i0Q" target="_blank"> <img class="link-logo" src="https://cdn.glitch.global/26ec0d7f-01b9-40b1-aff5-8aa4412693c4/youtube.png?v=1700800235511"> </a> <a class="info-footer-item info-link" href="https://github.com/Nudo-o/" target="_blank"> <img class="link-logo" src="https://cdn.glitch.global/26ec0d7f-01b9-40b1-aff5-8aa4412693c4/github-mark-white.png?v=1700800770200"> </a> <a class="info-footer-item info-link" href="https://greasyfork.org/ru/users/759782-nudo" target="_blank"> <img class="link-logo" src="https://cdn.glitch.global/26ec0d7f-01b9-40b1-aff5-8aa4412693c4/greasyfork.png?v=1700800851140"> </a> <div class="info-footer-item discord-item"> <img class="link-logo" src="https://cdn.glitch.global/26ec0d7f-01b9-40b1-aff5-8aa4412693c4/discord.png?v=1700801396017"> </div> </footer> </box> <box class="bmm-container-box mod-changelog-box"> <ul class="items-list" id="mod_changelog"></ul> </box> </container> </main> </div> <div class="select-skin-panel hidden" id="select_skin_panel"> <div class="skin-circle selected" activeSkin" style="background: #bf8f54;" skin_index="0"></div> <div class="skin-circle" style="background: #cbb091;" skin_index="1"></div> <div class="skin-circle" style="background: #896c4b;" skin_index="2"></div> <div class="skin-circle" style="background: #fadadc;" skin_index="3"></div> <div class="skin-circle" style="background: #ececec;" skin_index="4"></div> <div class="skin-circle" style="background: #c37373;" skin_index="5"></div> <div class="skin-circle" style="background: #4c4c4c;" skin_index="6"></div> <div class="skin-circle" style="background: #ecaff7;" skin_index="7"></div> <div class="skin-circle" style="background: #738cc3;" skin_index="8"></div> <div class="skin-circle" style="background: #8bc373;" skin_index="9"></div> </div> `) updateChangelog(getModVersions(), "#mod_changelog") const gameServersUpdateBtn = document.querySelector("#game_servers_update") const nicknameInput = document.querySelector("#nickname_input") const firstClanInput = document.querySelector("#first_clan_input") const playButton = document.querySelector("#play_button") const showPing = document.querySelector("#show_ping") const millRotate = document.querySelector("#mill_rotate") const nativeResolution = document.querySelector("#native_resolution") const removeGrid = document.querySelector("#remove_grid") const playerPreview = document.querySelector("#player_preview") const allSelectSkinElements = [ ...document.querySelectorAll(".skin-circle") ] const setNodeVisibility = (selector, key) => { const node = document.querySelector(selector) if (!node) return const state = JSON.parse(localStorage.getItem(key)) if (!node.hiddenInterval) { node.hiddenInterval = setInterval(() => { node.hidden = false }) } if (state) return node.classList.remove("hidden") node.classList.add("hidden") } const toggleGameSettingBtn = (toggler, key) => { if (!toggler) return toggler.classList.toggle("enabled") const state = toggler.classList.contains("enabled") localStorage.setItem(key, state.toString()) if (key === "show_ping") { setNodeVisibility("#pingDisplay", key) } } const setGameSettingBtnState = (toggler, key) => { if (!toggler) return const state = JSON.parse(localStorage.getItem(key)) if (key === "show_ping") { setNodeVisibility("#pingDisplay", key) } if (state) return toggler.classList.add("enabled") toggler.classList.remove("enabled") } setGameSettingBtnState(showPing, "show_ping") setGameSettingBtnState(millRotate, "mill_rotate") setGameSettingBtnState(nativeResolution, "native_resolution") setGameSettingBtnState(removeGrid, "remove_grid") nicknameInput.value = localStorage.getItem("9mm_name") || "" firstClanInput.value = localStorage.getItem("moo_first_clan") || "" nicknameInput.addEventListener("input", () => { localStorage.setItem("9mm_name", nicknameInput.value) }) firstClanInput.addEventListener("input", () => { localStorage.setItem("moo_first_clan", firstClanInput.value) }) showPing.addEventListener("click", toggleGameSettingBtn.bind(null, showPing, "show_ping")) millRotate.addEventListener("click", toggleGameSettingBtn.bind(null, millRotate, "mill_rotate")) nativeResolution.addEventListener("click", toggleGameSettingBtn.bind(null, nativeResolution, "native_resolution")) removeGrid.addEventListener("click", toggleGameSettingBtn.bind(null, removeGrid, "remove_grid")) playButton.addEventListener("click", enterGame) gameServersUpdateBtn.addEventListener("click", updateGameServers) playerPreview.addEventListener("click", toggleSelectSkin) window.addEventListener("resize", () => { toggleSelectSkin(null, true) }) allSelectSkinElements.forEach((selectSkinElement) => { selectSkinElement.addEventListener("mousedown", selectSkin) }) const checkGameLoading = setInterval(() => { const loadingText = document.querySelector("#loadingText") if (loadingText?.style.display !== "none") return if (localStorage.moo_skin) { selectSkin({ target: allSelectSkinElements[+localStorage.moo_skin] }) } toggleLoadingMenu(false) clearInterval(checkGameLoading) }) const checkGameDisconnect = setInterval(() => { const loadingText = document.querySelector("#loadingText") if (loadingText?.style.display === "none" || !/disconnect/.test(loadingText?.innerHTML)) return toggleDisconnectMenu(true) clearInterval(checkGameLoading) }) Cow.socket.onEvent("close", toggleDisconnectMenu.bind(null, true)) } function selectSkin(event) { const allSelectSkinElements = [ ...document.querySelectorAll(".skin-circle") ] allSelectSkinElements.forEach((selectSkinElement) => { selectSkinElement.classList.remove("selected") }) const skinIndex = parseInt(event.target.getAttribute("skin_index")) const playerBodyFigures = [ ...document.querySelectorAll(".player-body-figure") ] playerBodyFigures.forEach((playerBodyFigure) => { playerBodyFigure.style.fill = window.config.skinColors[skinIndex] }) event.target.classList.add("selected") window.selectSkinColor(skinIndex) localStorage.setItem("moo_skin", skinIndex) } function toggleSelectSkin(_, isResize, forceHide) { const playerPreview = document.querySelector("#player_preview") const selectSkinPanel = document.querySelector("#select_skin_panel") const boundings = playerPreview.getBoundingClientRect() const width = 162 const height = 72 if (forceHide) return selectSkinPanel.classList.add("hidden") !isResize && selectSkinPanel.classList.toggle("hidden") selectSkinPanel.style.left = `${boundings.x - width / 2 + boundings.width / 2}px` selectSkinPanel.style.top = `${boundings.y - height - 5}px` } function enterGame() { const enterGameBtn = document.querySelector("#enterGame") toggleSelectSkin(null, false, true) toggleLoadingMenu(true) setLoadingText("Connecting...") } function setLoadingText(text) { const bettermmLoading = document.querySelector("#better_mm_loading") const loadingText = bettermmLoading.querySelector(".loading-text") loadingText.innerHTML = text } function toggleDisconnectMenu(visibility) { const bettermmDisconnect = document.querySelector("#better_mm_disconnect") if (visibility) { bettermmDisconnect.classList.remove("hidden") toggleLoadingMenu(false) toggleSelectSkin(null, false, true) return toggleBettermmContainer(false) } bettermmDisconnect.classList.add("hidden") toggleBettermmContainer(true) toggleLoadingMenu(false) } function toggleLoadingMenu(visibility) { const bettermmLoading = document.querySelector("#better_mm_loading") if (visibility) { bettermmLoading.classList.remove("hidden") toggleSelectSkin(null, false, true) return toggleBettermmContainer(false) } bettermmLoading.classList.add("hidden") toggleBettermmContainer(true) } function toggleBettermmContainer(visibility) { const bettermmContainer = document.querySelector("#better_mm_container") if (visibility) { return bettermmContainer.classList.remove("hidden") } bettermmContainer.classList.add("hidden") } function getGameServers() { const currentMode = location.host.replace(/\.moomoo\.io/, "") const getRequestUrl = () => { if (/(sandbox|dev)/.test(currentMode)) { return `https://api-${currentMode}.moomoo.io/servers?v=1.22` } return "https://api.moomoo.io/servers" } return new Promise((resolve) => { fetch(getRequestUrl()).then((res) => res.text()).then((servers) => resolve(JSON.parse(servers))) }) } async function updateGameServers() { let servers = await getGameServers() const [ currentServerRegion, currentServerName ] = location.href.replace(/.+\=/, "").split(":") const gameServers = document.querySelector("#game_servers") const serversByRegions = {} gameServers.innerHTML = "" for (const server of servers) { if (!serversByRegions[server.region]) { serversByRegions[server.region] = [] } serversByRegions[server.region].push(server) } servers = Object.values(serversByRegions) for (let serversRegion of servers) { serversRegion = serversRegion.sort((a, b) => b.playerCount - a.playerCount) for (let i = 0; i < serversRegion.length; i++) { const server = serversRegion[i] const requestPingUrl = `https://${server.key}.${server.region}.moomoo.io/ping/9mm` const sentTime = Date.now() const currentMode = location.host.replace(/\.moomoo\.io/, "") const id = `${server.region}_${server.name}` const isCurrentServer = server.region === currentServerRegion && server.name === currentServerName gameServers.insertAdjacentHTML(isCurrentServer ? "afterbegin" : "beforeend", ` <li class="item${isCurrentServer ? " light-background" : ""}"> <div class="server-data-wrapper"> <header class="server-data-header"> <span class="server-data-players"> (${server.playerCount}/${server.playerCapacity}) </span>${window.regionsName[server.region]} ${server.name} </header> <div class="server-data-actions"> <span class="server-data-ping" id="${id}_ping"></span> ${!isCurrentServer ? `<div class="server-open-btn" id="${id}_open">GO!</div>` : ""} </div> </div> </li> `) const serverOpenBtn = document.querySelector(`#${id}_open`) if (serverOpenBtn) { serverOpenBtn.addEventListener("click", () => { window.open(`https://${currentMode !== "" ? currentMode + "." : ""}moomoo.io/?server=${server.region}:${server.name}`) }) } fetch(requestPingUrl).then(() => { const ping = Date.now() - sentTime const serverDataPing = document.querySelector(`#${server.region}_${server.name}_ping`) if (ping >= 500) { serverDataPing.classList.add("red") } else if (ping >= 350 && ping < 500) { serverDataPing.classList.add("low-red") } else if (ping >= 200 && ping < 350) { serverDataPing.classList.add("yellow") } else if (ping >= 100 && ping < 200) { serverDataPing.classList.add("low-green") } else { serverDataPing.classList.add("green") } serverDataPing.innerHTML = ping }) } } } function getModVersions() { return ` 1.0.1 (5/02/2024): > Added auto spike sync. > Added distance check for building markers. > Optimization for any keyboard. > Fixed a bug with infinite loading. 1.0.0 (4/02/2024): > Release. > Mini update auto mills. > Added custom main menu. > Added auto #### and bull hats. > Added building markers/hp. > Added items count. > Store was returned. 1.0.0b (2/02/2024): > Beta release. ` } async function updateChangelog(versions, appendNodeSelector, linesSlice = 0) { const changelog = document.querySelector(appendNodeSelector) const versionsList = versions.split(/\n/).slice(linesSlice).filter((line) => line !== "" && !/\s\s\-\s/.test(line)) for (let i = 0; i < versionsList.length; i++) { const versionsLine = versionsList[i] if (!/^\d/.test(versionsLine)) continue const currentVersion = versionsLine.replace(/\:.+$/, ":").replace(/\)\s.+\:/, ":") const parsedVersion = currentVersion.replace(/\(.+/, "").replace(/\s\-/, "") const versionDate = currentVersion.replace(/^.+\(/, "(").replace(/(\(|\)|\:)/g, "").split("/") const date = `${versionDate[1]}/${versionDate[0]}/${versionDate[2]}` const month = new Date(date).toLocaleString('en-GB', { month: 'long' }) const nextGameVersionIndex = versionsList.slice(i + 1).findIndex((line) => /^\d+\./.test(line)) const updatesList = versionsList.slice(i + 1, (i + 1) + nextGameVersionIndex) if (/0\.10/.test(parsedVersion) && linesSlice !== 0) { updatesList.push("> Initial Release") } if (/1\.0\.0b/.test(parsedVersion) && linesSlice === 0) { updatesList.push("> Beta release.") } changelog.insertAdjacentHTML("beforeend", ` <li class="item"> <header class="changelog-item-header">${parsedVersion} (${month} ${versionDate[0]}, ${versionDate[2]})</header> <div class="changelog-updates"> ${updatesList.map((update) => ` <div class="changelog-version-info"> <span class="changelog-update-value">${update.replace(/\>\s/, "")}</span> </div> `).join("")} </div> </li> `) } } function createCustomHtmlAndCss() { const style = document.createElement("style") style.insertAdjacentHTML("beforeend", ` .hidden { display: none !important; } .item-count { position: absolute; display: block; color: #fff; font-size: 16px; margin: 2px 5px; } .item-count.scale-anim { transform: scale(1); animation: item-count-scale-anim 1s; } @keyframes item-count-scale-anim { 0% { transform: scale(1); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } } .actionBarItem { text-align: end; } #actionBar { display: flex !important; justify-content: center; margin-bottom: 5px; } #menuContainer, #settingsButton, #partyButton, #linksContainer2, #joinPartyButton { display: none !important; } `) document.head.appendChild(style) } const maxScreenWidth = 1920 const maxScreenHeight = 1080 const { lineTo, moveTo } = CanvasRenderingContext2D.prototype const gridAlpha = 0.06 CanvasRenderingContext2D.prototype.moveTo = function(x, y) { if (localStorage.remove_grid == "false") return moveTo.apply(this, arguments) if (this.globalAlpha === gridAlpha) return return moveTo.apply(this, arguments) } CanvasRenderingContext2D.prototype.lineTo = function(x, y) { if (localStorage.remove_grid == "false") return lineTo.apply(this, arguments) if (this.globalAlpha === gridAlpha && (y === maxScreenHeight || x === maxScreenWidth)) return return lineTo.apply(this, arguments) } const turnSpeeds = { 9: .003, 10: .0016, 11: .0025, 12: .005, } Object.defineProperty(Object.prototype, "turnSpeed", { get() { if (![10, 11, 12].includes(this.id)) return turnSpeeds[this.id] return localStorage.mill_rotate == "true" ? turnSpeeds[this.id] : 0 }, set(value) { this[Symbol("turnSpeed")] = value } }) window.fetch = new Proxy(fetch, { apply(target, _this, args) { if (/\/ping/.test(args[0]) && !/\/9mm/.test(args[0])) { return target.apply(_this) } if (/\/9mm/.test(args[0])) { args[0] = args[0].replace(/\/9mm/, "") } return target.apply(_this, args) } }) console._error = console.error console.error = function(error) { // FK THIS FKING ERROR!!! FK U FAILED TO LOAD. I'VE BEEN FIXING YOU FOR 4 ####ING HOURS. if (error === "Failed to load.") { setInterval(() => { setLoadingText("Failed to load -___-") toggleBettermmContainer(false) toggleLoadingMenu(true) }) } this._error(error) } window.regionsName = { "us-east": "Miami", "us-west": "Silicon Valley", "gb": "London", "eu-west": "Frankfurt", "au": "Sydney", "sg": "Singapore" } })()