9mm [MooMoo.io]

It's a good script for the game, 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! You don't need to buy anything in the store, the script will buy everything by itself!

目前为 2024-02-01 提交的版本。查看 最新版本

// ==UserScript==
// @name            9mm [MooMoo.io]
// @name:ru         9mm [MooMoo.io]
// @namespace       https://github.com/Nudo-o
// @version         1.0.0b
// @description     It's a good script for the game, 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! You don't need to buy anything in the store, the script will buy everything by itself!
// @description:ru  Это хороший сценарий для игры, не самый лучший, но я постараюсь сделать его как можно лучше. 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://update.greasyfork.org/scripts/480301/1320686/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/1320686/CowJS.js

/*
- 02.02.2024 (v1.0.0):
1. RELEASE.
*/

(function() {
    "use strict"

    const { Cow, CowUtils } = window
    const { packets, items } = Cow.config.designations
    const _placeItem = Cow.placeItem
    const _roundRect = CanvasRenderingContext2D.prototype.roundRect

    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 >= 0) {
                Cow.delayedPlaceItem(() => 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.lastHatID = null
            this.lastAccID = null

            this.autoEmpCD = null
            this.attackHatsCD = null

            this.autoEmpSoldier = false
            this.autoSoldierEmp = false
            this.autoEmpHat = false
            this.autoBullTick = false
            this.autoTankHat = false

            this.autoBullTickTimeout = null

            Cow.onPacket(packets.ADD_PLAYER, (_, isYou) => {
                if (!isYou) return

                this.attackHatActive = false
                this.autoAttackState = false
                this.autoAttackHatTick = 0
            })
        }

        isHasTick(key) {
            return Boolean(this.hatTicks.filter((tick) => tick.key == key).length)
        }

        equipHat(id) {
            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)

                    return this.hasHats.push(id)
                }
            }

            if (this.hasHats.includes(id) && player.skinIndex !== id) {
                Cow.sendPacket(packets.STORE_EQUIP, 0, id, 0)

                this.lastHatID = id
            }
        }

        equipAcc(id) {
            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)

                    return this.hasAccs.push(id)
                }
            }

            if (this.hasAccs.includes(id) && player.tailIndex !== id) {
                Cow.sendPacket(packets.STORE_EQUIP, 0, id, 1)

                this.lastAccID = id
            }
        }

        equipBiomeHat() {
            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
            }

            if (!this.isHasTick("auto-biome")) {
                this.hatTicks.push({
                    key: "auto-biome",
                    callback: () => {
                        this.equipHat(hatID)
                        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) {
                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.autoBullTick) {
                this.hatTicks = []

                this.equipHat(7)

                if (!this.autoBullTickTimeout) {
                    this.autoBullTickTimeout = setTimeout(() => {
                        this.autoBullTick = false
                        this.autoBullTickTimeout = null
                    }, 111.111)
                }

                return
            }*/

            if (this.autoEmpHat && (!this.autoEmpCD || Date.now() - this.autoEmpCD >= 500)) {
                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.autoTankHat) {
                this.equipHat(40)

                if (!this.isHasTick("uneqip-hats")) {
                    this.unknownTicks(4)

                    this.hatTicks.push({
                        key: "uneqip-hats",
                        callback: () => {
                            this.autoTankHat = false
                        }
                    })
                }

                this.attackHatsCD = Date.now()
            } else if (dangerBuildings.length || nearEnemy) {
                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 {
                        return this.equipBiomeHat()
                    }
                }
            } else {
                return this.equipBiomeHat()
            }
        }

        update() {
            this.autoHats()

            const { player } = Cow

            const timeSinceHatTick = Cow.ticker.ticks - (this.lastHatTick || 0)

            if (timeSinceHatTick >= 0 && this.hatTicks.length) {
                const hatTick = this.hatTicks[0]

                typeof hatTick?.callback === 'function' && hatTick.callback()

                this.hatTicks.shift()

                this.lastHatTick = Cow.ticker.ticks
            }
        }
    }

    class AutoPlacer {
        constructor() {
            this.delay = 0
            this.lastUpdate = null
        }

        update() {
            const { player } = Cow

            if (!player?.alive || !nearEnemy?.visible) 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)
            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: "KeyQ",
                TRAP: "KeyF",
                SPIKE: "KeyV",
                MILL: "KeyN",
                TURRET: "KeyH"
            }
        }

        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

            Cow.onKeyboard("KeyM", () => {
                if (isInputFocused()) return

                this.isActive = !this.isActive

                if (!this.isActive) this.lastPlace = null
            }, {
                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 (this.lastPlace) {
                const millConfig = Cow.items.list[player.items[items.MILL]]
                const distance = CowUtils.getDistance(player, this.lastPlace) - player.scale
                const placeDistance = millConfig.scale * 1.6

                if (distance < placeDistance) return
            }

            //Cow.delayedPlaceItem(() => {
            Cow.placeItem(items.MILL, {
                angle: player.moveDir + this.gap
            })

            Cow.placeItem(items.MILL, {
                angle: player.moveDir
            })

            Cow.placeItem(items.MILL, {
                angle: player.moveDir - this.gap
            })

            this.lastPlace = {
                x: player.x,
                y: player.y
            }
            //})
        }
    }

    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
        }

        stopBreaking() {
            if (!this.isBreaking) return

            this.isBreaking = false
            tailor.autoTankHat = false

            aimControl.stopAiming()
        }

        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 <= 0

            if (!player.inTrap) return this.stopBreaking()

            this.isBreaking = true
            tailor.autoTankHat = true

            const breakWeapon = player.weapons[1] === 10 ? player.weapons[1] : player.weapons[0]

            if (player.weaponIndex !== breakWeapon) Cow.sendPacket(packets.SELECT_BUILD, breakWeapon, true)

            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
        }
    }

    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
        }
    })

    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()

    Cow.addRender("global", () => {
        const { context } = Cow.renderer
        const { player } = Cow

        nearEnemy = Cow.getNearEnemy()

        for (const preplaceObject of preplaceObjects) {
            renderPreplace(preplaceObject, context)
        }

        macro.update()
        autoPlacer.update()
        tailor.update()
        autoHeal.update()
        antiInsta.update()
        autoMills.update()
        reloadBars.update()
        antiTrap.update()
    })

    const oldSend = WebSocket.prototype.send

    WebSocket.prototype.send = function(data) {
        const binary = new Uint8Array(data)
        const decoded = Cow.codec.decoder.decode(binary)

        if (decoded[0] === packets.SPAWN) {
            decoded[1][0].moofoll = true
            decoded[1][0].name = "9-".concat(decoded[1][0].name)

            return oldSend.call(this, Cow.codec.encoder.encode(decoded))
        }

        if (decoded[0] === packets.LOOK_DIR && aimControl.isAiming) {
            aimControl.updateAimToTarget()

            decoded[1][0] = aimControl.aimAngle

            oldSend.call(this, Cow.codec.encoder.encode(decoded))

            return aimControl.onSent()
        }

        oldSend.apply(this, arguments)
    }

    function isInputFocused() {
        return document.activeElement.tagName === "INPUT"
    }

    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()
    }

    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 removeButton(buttonId) {
        const button = document.querySelector(buttonId)

        button.style.visibility = "hidden"
    }

    const checkButtons = setInterval(() => {
        const storeButton = document.getElementById("storeButton")

        if (!storeButton?.style) return

        createCustomHtmlAndCss()
        removeButton("#storeButton")
        removeButton("#chatButton")
        clearInterval(checkButtons)
    })

    function createCustomHtmlAndCss() {
        const style = document.createElement("style")

        style.insertAdjacentHTML("beforeend", `
        #gameName::after {
            content: "9mm";
            display: inline-block;
            position: absolute;
            font-size: 40px;
            transform: rotate(45deg) translate(-10px, 60px) scale(1);
            text-shadow: 0 1px 0 #c4c4c4, 0 2px 0 #c4c4c4, 0 3px 0 #c4c4c4;
            animation: scale-anim 3s infinite ease-in-out;
        }

        @keyframes scale-anim {
            0% {
                transform: rotate(45deg) translate(-10px, 60px) scale(1);
            }

            50% {
                transform: rotate(45deg) translate(-10px, 60px) scale(1.1);
            }

            100% {
                transform: rotate(45deg) translate(-10px, 60px) scale(1);
            }
        }
        `)

        document.head.appendChild(style)
    }
})()