mace roulette

you've probably already seen this game in admin abuse

var active = false
var pvp = false
var round = 0
var deathsLastRound = 0
var players = []

var center = 0

var lastClosestUuid = ""
var roundWinnerUuid = ""
var spinYaw = 0.0
var spinSpeed = 40.0
var spinTicksLeft = 0.0
var currentSpawnRadius = 4.0

var progressStepsElapsed = 0.0

var spinTaskId = -1
var progressTaskId = -1
var voidCheckTaskId = -1

@Katton.command(name = "ygmr", permission = "admin")
function ygmr(sender, args) {
    if (args.size == 0) {
        sendHelp(sender)
        return
    }

    val sub = args[0]

    if (sub == "start") {
        cmdStart(sender)
    } else if (sub == "center") {
        cmdCenter(sender)
    } else if (sub == "stop") {
        cmdStop(sender)
    } else {
        sendHelp(sender)
    }
}

function sendHelp(sender) {
    sender.sendMessage("&dok listen here this is how u do mace roulette")
    sender.sendMessage("&dstand where u want the platform center then do &d&l/ygmr center")
    sender.sendMessage("&dthen do &d&l/ygmr start")
    sender.sendMessage("&danyone who is in survival/adventure will be added as a player")
    sender.sendMessage("&dthats it")
    sender.sendMessage("&7------")
    sender.sendMessage("&dcommands: /ygmr <start>/<center>/<stop>")
}

// subcommands
function cmdCenter(sender) {
    val loc = sender.location
    center = {
        "x": Math.floor(loc["x"]) + 0.5,
        "y": Math.floor(loc["y"]),
        "z": Math.floor(loc["z"]) + 0.5
    }
    sender.sendMessage("&acenter is set")
}

function cmdStart(sender) {
    if (active) {
        sender.sendMessage("&cmace roulette is already running!")
        return
    }
    
    if (center == 0) {
        sender.sendMessage("&cset the center first! do /katton run ygmr center")
        return
    }

    if (voidCheckTaskId < 0) {
        voidCheckTaskId = Katton.every(20) {
            voidCheck()
        }
    }

    collectPlayers()

    if (players.size < 2) {
        sender.sendMessage("&cnot enough valid players")
        return
    }

    active = true
    round = 0.0
    deathsLastRound = 0.0
    pvp = false

    for (uuid in players) {
        val p = findPlayer(uuid)
        if (p != null) {
            p.setInvulnerable(true)
        }
    }

    Katton.makeAdminExecuteCommand("pvp true")
    Katton.makeAdminExecuteCommand("dropping false")
    Katton.makeAdminExecuteCommand("hardcore true")
    Katton.makeAdminExecuteCommand("hunger false")
    Katton.makeAdminExecuteCommand("building false")

    broadcastArena("&c&lMACE ROULETTE &7is starting (created by urs truely, &cyayamelia &7:D)")
    startRound(true)
}

function cmdStop(sender) {
    endGame()
}

// round stuff
function startRound(starting) {
    if (!active) {
        return
    }

    if (players.size == 0) {
        broadcastArena("&c&lMACE ROULETTE &7ended with no winner!")
        endGame()
        return
    }

    if (players.size == 1) {
        val winner = findPlayer(players[0])
        broadcastArena("&c&lMACE ROULETTE &7is over! "+winner.name +" won!")
        if (winner != null) {
            winner.playSound("ui.toast.challenge_complete", 1.0, 1.0)
        }
        endGame()
        return
    }

    round = round + 1

    for (uuid in players) {
        val p = findPlayer(uuid)
        if (p != null) {
            p.clearInventory()
            if (starting) {
                p.sendTitle("&c&lRound " + round, "")
            } else {
                p.sendTitle("&c&lRound " + round, "&4" + deathsLastRound + " died last round")
            }
            p.playSound("block.note_block.pling", 1.0, 1.2)
        }
    }

    buildPlatformAndSpawnPlayers()
}

function buildPlatformAndSpawnPlayers() {
    val world = Katton.world()
    val count = players.size
    val radius = Math.min(9, Math.max(4, Math.round(Math.sqrt(count) * 2.5)))

    val cx = center["x"]
    val cy = center["y"]
    val cz = center["z"]

    val blockX = Math.floor(cx)
    val blockZ = Math.floor(cz)
    val clearRadius = radius + 2

    val minX = blockX - clearRadius
    val maxX = blockX + clearRadius
    val minZ = blockZ - clearRadius
    val maxZ = blockZ + clearRadius

    for (x in minX..maxX) {
        for (z in minZ..maxZ) {
            val dx = x - blockX
            val dz = z - blockZ
            val distSq = Math.round(Math.sqrt((dx * dx) + (dz * dz)))

            if (distSq <= radius) {
                world.setBlock({"x": x, "y": cy - 1, "z": z}, "smooth_stone")
            } else {
                world.setBlock({"x": x, "y": cy - 1, "z": z}, "air")
            }
            world.setBlock({"x": x, "y": cy, "z": z}, "air")
            world.setBlock({"x": x, "y": cy + 1, "z": z}, "air")
            world.setBlock({"x": x, "y": cy + 2, "z": z}, "air")
        }
    }

    world.setBlock({"x": blockX, "y": cy - 1, "z": blockZ}, "lodestone")

    for (uuid in players) {
        val p = findPlayer(uuid)
        if (p != null) {
            p.setMovementSpeed(0)
            p.setJumpStrength(0)
        }
    }

    currentSpawnRadius = Math.max(1.5, radius - 1.5)

    Katton.after(5) {
        placePlayersAndMace(count)
    }
}

function placePlayersAndMace(count) {
    if (!active) {
        return
    }

    val angleStep = 360 / count
    var currentAngle = 0.0

    for (uuid in players) {
        val p = findPlayer(uuid)
        if (p != null) {
            val offX = currentSpawnRadius * Math.cos(currentAngle)
            val offZ = currentSpawnRadius * Math.sin(currentAngle)
            val loc = {
                "x": center["x"] + offX,
                "y": center["y"],
                "z": center["z"] + offZ,
                "yaw": currentAngle + 90,
                "pitch": 0
            }
            p.teleport(loc)
            p.setHealth(20)
        }
        currentAngle = currentAngle + angleStep
    }

    val world = Katton.world()
    spinYaw = Math.randomInt(0, 360)

    val spawnLoc = {"x": center["x"], "y": center["y"] + 0.05, "z": center["z"], "yaw": spinYaw, "pitch": 90}
    val mace = world.spawnEntity("item_display", spawnLoc)
    mace.setItem(Katton.item("mace"))
    mace.addTag("mace_spin")
    mace.setTeleportDuration(1)

    Katton.after(60) {
        beginSpin()
    }
}

function beginSpin() {
    if (!active) {
        return
    }

    spinTicksLeft = 100.0
    spinSpeed = 40.0
    lastClosestUuid = ""

    spinTaskId = Katton.every(1) {
        spinStep()
    }
}

function spinStep() {
    if (!active) {
        Katton.cancel(spinTaskId)
        killMace()
        return
    }

    spinTicksLeft = spinTicksLeft - 1

    spinYaw = spinYaw + spinSpeed
    if (spinYaw >= 360) {
        spinYaw = spinYaw - 360
    }
    if (spinSpeed > 2) {
        spinSpeed = spinSpeed - 0.4
    }

    val mace = findMace()
    if (mace != null) {
        val spinLoc = {
            "x": center["x"],
            "y": center["y"] + 0.05,
            "z": center["z"],
            "yaw": spinYaw,
            "pitch": 90
        }
        mace.teleport(spinLoc)
    }

    val mathYaw = spinYaw + 45
    val pointX = center["x"] + (currentSpawnRadius * Math.cos(mathYaw))
    val pointZ = center["z"] + (currentSpawnRadius * Math.sin(mathYaw))
    val switchDistance = 0.8

    var closestUuid = lastClosestUuid
    var minDistSq = switchDistance * switchDistance

    for (uuid in players) {
        val p = findPlayer(uuid)
        if (p != null) {
            val loc = p.location
            val ddx = loc["x"] - pointX
            val ddz = loc["z"] - pointZ

            val distSq = (ddx * ddx) + (ddz * ddz)

            if (distSq <= minDistSq) {
                minDistSq = distSq
                closestUuid = uuid
            }
        }
    }

    if (closestUuid != lastClosestUuid) {
        val prevClosest = findPlayer(lastClosestUuid)
        if (prevClosest != null) {
            prevClosest.setGlowing(false)
        }

        for (uuid in players) {
            val p = findPlayer(uuid)
            if (p != null) {
                p.playSound("block.copper_bulb.turn_off", 0.3, 1.0)
            }
        }

        val newClosest = findPlayer(closestUuid)
        if (newClosest != null) {
            newClosest.setGlowing(true)
        }

        lastClosestUuid = closestUuid
    }

    if (spinTicksLeft <= 0) {
        Katton.cancel(spinTaskId)
        roundWinnerUuid = closestUuid
        Katton.after(15) {
            revealWinner()
        }
    }
}

function revealWinner() {
    if (!active) {
        return
    }

    killMace()

    val closest = findPlayer(roundWinnerUuid)
    if (closest != null) {
        closest.setGlowing(false)
    }

    var winnerName = "someone"
    if (closest != null) {
        winnerName = closest.name
    }
    broadcastArena("&6" + winnerName + " &ehas been selected!")

    val mmace = Katton.item("mace")
    mmace.enchant("wind_burst", 1)

    if (closest != null) {
        closest.give(mmace)
        closest.give(Katton.item("wind_charge"))
    }

    for (uuid in players) {
        val p = findPlayer(uuid)
        if (p != null) {
            p.playSound("entity.player.levelup", 1.0, 1.0)
            p.sendTitle("", "&c&lFIGHT!")
            p.playSound("entity.ender_dragon.shoot", 0.8, 0.5)
            p.setInvulnerable(false)
            p.resetMovementSpeed(0)
            p.resetJumpStrength(0)
        }
    }

    pvp = true
    deathsLastRound = 0.0
    progressStepsElapsed = 0.0

    progressTaskId = Katton.every(10) {
        progressBarStep()
    }
}

function progressBarStep() {
    if (!active) {
        Katton.cancel(progressTaskId)
        return
    }
    if (progressStepsElapsed >= 21) {
        Katton.cancel(progressTaskId)
        finishFightWindow()
        return
    }

    val greenCount = progressStepsElapsed
    val grayCount = 20 - greenCount

    var bar = "&a"
    for (i in 1..greenCount) {
        bar = bar + "⬛"
    }
    bar = bar + "&7"
    for (i in 1..grayCount) {
        bar = bar + "⬛"
    }

    val world = Katton.world()
    for (p in world.players) {
        p.sendActionBar(bar)
        p.playSound("block.stone_pressure_plate.click_on", 0.2, 2.0)
    }

    progressStepsElapsed = progressStepsElapsed + 1
}

function finishFightWindow() {
    if (!active) {
        return
    }

    pvp = false

    for (uuid in players) {
        val p = findPlayer(uuid)
        if (p != null) {
            p.setInvulnerable(true)
        }
    }

    if (roundWinnerUuid != "") {
        val winner = findPlayer(roundWinnerUuid)
        if (winner != null) {
            winner.clearInventory()
        }
    }

    for (uuid in players) {
        val p = findPlayer(uuid)
        if (p != null) {
            p.playSound("block.bell.use", 1.0, 0.5)
        }
    }

    Katton.after(40) {
        startRound(false)
    }
}

function endGame() {
    active = false
    pvp = false

    if (spinTaskId >= 0) {
        Katton.cancel(spinTaskId)
        spinTaskId = -1
    }
    if (progressTaskId >= 0) {
        Katton.cancel(progressTaskId)
        progressTaskId = -1
    }

    for (uuid in players) {
        val p = findPlayer(uuid)
        if (p != null) {
            p.setMovementSpeed(0.1)
            p.setJumpStrength(0.42)
            p.clearInventory()
            p.setInvulnerable(false)
            p.setGlowing(false)
        }
    }

    killMace()

    val world = Katton.world()
    world.playSound("block.beacon.deactivate", center, 1.0, 0.8)

    players.clear()
}

function voidCheck() {
    if (!active) {
        return
    }

    for (uuid in players) {
        val p = findPlayer(uuid)
        if (p != null && p.location["y"] <= center["y"] - 2) {
            p.setHealth(0)
        }
    }
}

// helpers
function collectPlayers() {
    players.clear()
    for (p in Katton.players.survival()) {
        players.add(p.uuid)
    }
}

function findPlayer(uuid) {
    for (p in Katton.players.survival()) {
        if (p.uuid == uuid) {
            return p
        }
    }
    return null
}

function findMace() {
    val world = Katton.world()
    for (e in world.entities) {
        if (e.hasTag("mace_spin")) {
            return e
        }
    }
    return null
}

function killMace() {
    val world = Katton.world()
    for (e in world.entities) {
        if (e.hasTag("mace_spin")) {
            e.kill()
        }
    }
}

function broadcastArena(text) {
    for (p in Katton.players.all()) {
        p.sendMessage(text)
    }
}

// listeners
@Katton.listen("player_quit")
function onPlayerQuit(event) {
    players.remove(event.player.uuid)
}

@Katton.listen("player_death")
function onPlayerDeath(event) {
    if (!active) {
        return
    }

    val victim = event.player
    if (!players.contains(victim.uuid)) {
        return
    }

    broadcastArena("&c&lMACE ROULETTE &7" + victim.name + " died")
    players.remove(victim.uuid)
    deathsLastRound = deathsLastRound + 1
    event.clearDrops()
}