voidwars

val teamColors = ["White", "Orange", "Magenta", "Light Blue", "Yellow", "Lime", "Pink", "Gray",
    "Light Gray", "Cyan", "Purple", "Brown", "Green", "Red", "Black"]
val teamWools = ["white", "orange", "magenta", "light_blue", "yellow", "lime", "pink", "gray",
    "light_gray", "cyan", "purple", "brown", "green", "red", "black"]

var running = false
var numTeams = 0
var removingFloor = false
var glowingPhase = false
var lootTimer = 0

var prestartTaskId = 0
var prepTaskId = 0
var floorTimerTaskId = 0
var lootTaskId = 0
var voidCheckTaskId = 0

@Katton.command(name = "vwsetspawn", permission = "admin")
function vwSetSpawn(sender, args) {
    val loc = sender.location
    val centeredX = Math.floor(loc["x"]) + 0.5
    val centeredZ = Math.floor(loc["z"]) + 0.5
    val centeredY = Math.floor(loc["y"])

    Katton.vars.script().set("spawn_x", centeredX)
    Katton.vars.script().set("spawn_y", centeredY)
    Katton.vars.script().set("spawn_z", centeredZ)

    sender.sendMessage("&avoidwars spawn point set")
}

function spawnLocation() {
    val x = Katton.vars.script().get("spawn_x")
    val y = Katton.vars.script().get("spawn_y")
    val z = Katton.vars.script().get("spawn_z")
    return { "x": x, "y": y, "z": z, "yaw": 0, "pitch": 0 }
}

function hasSpawn() {
    return Katton.vars.script().get("spawn_x") != null
}

@Katton.command(name = "voidwars", permission = "admin")
function voidwars(sender, args) {
    if (args.size() == 0) {
        sender.sendMessage("&cusage: /katton run voidwars <teamsize> or /katton run voidwars build")
        return
    }

    if (args[0] == "build") {
        buildArena(sender)
        return
    }

    if (running) {
        sender.sendMessage("&ca voidwars round is already running. do /katton run vwend first")
        return
    }

    if (!hasSpawn()) {
        sender.sendMessage("&cno spawn point set. run /katton run vwsetspawn first")
        return
    }

    val teamSize = args[0]
    if (teamSize == null) {
        sender.sendMessage("&cusage: /katton run voidwars <teamsize>")
        return
    }

    startVoidWars(sender, teamSize)
}

function buildArena(sender) {
    if (!hasSpawn()) {
        sender.sendMessage("&cno spawn point set. run /katton run vwsetspawn")
        return
    }

    sender.sendMessage("&ebuilding arena, please wait...")

    Katton.makeAdminExecuteCommand("clearplayer *")
    Katton.makeAdminExecuteCommand("building false")
    Katton.makeAdminExecuteCommand("pvp false")
    Katton.makeAdminExecuteCommand("hardcore false")
    Katton.makeAdminExecuteCommand("difficulty hard")
    Katton.makeAdminExecuteCommand("adminlock true")

    val spawn = spawnLocation()
    val baseX = spawn["x"]
    val baseY = spawn["y"]
    val baseZ = spawn["z"]

    val minX = baseX - 27
    val maxX = baseX + 27
    val minZ = baseZ - 27
    val maxZ = baseZ + 27
    val minY = baseY - 1
    val maxY = minY + 54

    Katton.after(3.seconds) {
        Katton.makeAdminExecuteCommand("//pos1 " + minX + "," + minY + "," + minZ)
        Katton.makeAdminExecuteCommand("//pos2 " + maxX + "," + maxY + "," + maxZ)
        Katton.makeAdminExecuteCommand("//set smooth_quartz")

        Katton.makeAdminExecuteCommand("//pos1 " + (minX + 1) + "," + (minY + 1) + "," + (minZ + 1))
        Katton.makeAdminExecuteCommand("//pos2 " + (maxX - 1) + "," + (maxY - 1) + "," + (maxZ - 1))
        Katton.makeAdminExecuteCommand("//set air")

    
        Katton.makeAdminExecuteCommand("//pos1 " + (minX + 1) + "," + minY + "," + (minZ + 1))
        Katton.makeAdminExecuteCommand("//pos2 " + (maxX - 1) + "," + minY + "," + (maxZ - 1))
        Katton.makeAdminExecuteCommand("//set light_blue_stained_glass")
        Katton.makeAdminExecuteCommand("//center blue_stained_glass")
    
        Katton.makeAdminExecuteCommand("//pos1 " + (minX + 1) + "," + maxY + "," + (minZ + 1))
        Katton.makeAdminExecuteCommand("//pos2 " + (maxX - 1) + "," + maxY + "," + (maxZ - 1))
        Katton.makeAdminExecuteCommand("//set light_blue_stained_glass")
    }


    sender.sendMessage("&aarena built!")
}

function startVoidWars(sender, teamSizeArg) {
    val teamSize = Math.floor(Math.parseNumber(teamSizeArg))

    val pool = Katton.players.all()
    val totalEntities = pool.size()
    val teamCountFloat = totalEntities / teamSize
    val teamCount = Math.floor(teamCountFloat)

    if (teamCount < 2) {
        sender.sendMessage("&cnot enough players to form at least 2 teams of size " + teamSize)
        return
    }
    if (teamCount > 15) {
        sender.sendMessage("&cthis setup requires " + teamCount + " teams, but there are only 15 colors")
        return
    }

    Katton.makeAdminExecuteCommand("hardcore true")

    running = true
    numTeams = teamCount
    removingFloor = false
    glowingPhase = false
    lootTimer = 0

    startBackgroundLoops()

    Katton.vars.script().set("alive_teams", teamCount)

    for (i in 0..(teamCount - 1)) {
        Katton.vars.script().set("team_color_" + i, teamColors[i])
        Katton.vars.script().set("team_wool_" + i, teamWools[i])
        Katton.vars.script().set("team_alive_" + i, true)
        Katton.vars.script().set("alive_on_team_" + i, 0)

        Katton.makeAdminExecuteCommand("team create " + (i + 1))
    }

    var currentTeam = 0
    for (player in pool) {
        Katton.vars.player(player.uuid).set("team_id", currentTeam)
        Katton.vars.player(player.uuid).set("in_void", false)
        Katton.vars.player(player.uuid).set("pending_loot", [])

        var count = Katton.vars.script().get("alive_on_team_" + currentTeam)
        Katton.vars.script().set("alive_on_team_" + currentTeam, count + 1)

        Katton.makeAdminExecuteCommand("team join " + player.name + " " + (currentTeam + 1))

        val wool = teamWools[currentTeam]
        player.give(Katton.item(wool + "_wool", 64))
        player.give(Katton.item("shears"))
        player.give(Katton.item("cooked_beef", 16))

        player.teleport(spawnLocation())

        val teamColorName = teamColors[currentTeam]
        player.sendMessage("&ayou are on &lteam " + teamColorName + "&a! get ready to fight...")

        currentTeam = currentTeam + 1
        if (currentTeam >= teamCount) {
            currentTeam = 0
        }
    }

    Katton.makeAdminExecuteCommand("healall")
    for (player in Katton.players.all()) {
        player.sendMessage("&e&lvoidwars &7- &a" + teamSize + "v" + teamSize + " &7round starting now with &b"
            + teamCount + " &7teams!")
    }

    runPrestartCountdown(3)
}

function runPrestartCountdown(secondsLeft) {
    if (!running) {
        return
    }

    for (player in Katton.players.all()) {
        player.sendTitle("&egame starting In...", "&b" + secondsLeft + "s")
    }

    if (secondsLeft <= 0) {
        Katton.makeAdminExecuteCommand("building true")
        runPrepCountdown(10)
        return
    }

    prestartTaskId = Katton.after(1.seconds) {
        runPrestartCountdown(secondsLeft - 1)
    }
}

function runPrepCountdown(secondsLeft) {
    if (!running) {
        return
    }

    if (secondsLeft <= 0) {
        beginRound()
        return
    }

    for (player in Katton.players.all()) {
        if (secondsLeft > 5) {
            player.sendTitle("&a&l" + secondsLeft, "")
        } else {
            player.sendTitle("&c&l" + secondsLeft, "")
        }
    }

    prepTaskId = Katton.after(1.seconds) {
        runPrepCountdown(secondsLeft - 1)
    }
}

function beginRound() {
    if (!running) {
        return
    }

    for (player in Katton.players.all()) {
        player.sendMessage("&c&lgo!")
    }

    Katton.makeAdminExecuteCommand("pvp true")

    Katton.vars.script().set("glow_time_counter", 120)

    runFloorCountdown(20)
}

function runFloorCountdown(secondsLeft) {
    if (!running) {
        return
    }

    if (secondsLeft <= 0) {
        dropFloor()
        return
    }

    for (player in Katton.players.all()) {
        if (secondsLeft > 5) {
            player.sendMessage("&efloor collapses in: &a" + secondsLeft + "s")
        } else {
            player.sendMessage("&c&lFLOOR COLLAPSES IN: " + secondsLeft + "s")
        }
    }

    val glowCounter = Katton.vars.script().get("glow_time_counter")
    if (!glowingPhase) {
        val newCounter = glowCounter - 1
        Katton.vars.script().set("glow_time_counter", newCounter)
        if (newCounter <= 0) {
            glowingPhase = true
        }
    }

    floorTimerTaskId = Katton.after(1.seconds) {
        runFloorCountdown(secondsLeft - 1)
    }
}

function dropFloor() {
    if (!running) {
        return
    }

    removingFloor = true
    for (player in Katton.players.all()) {
        player.sendMessage("&c&lthe bottom floor has dropped! dont't fall into the void!")
    }
    Katton.makeAdminExecuteCommand("pvp true")

    val spawn = spawnLocation()
    val baseX = spawn["x"]
    val baseY = spawn["y"]
    val baseZ = spawn["z"]

    val minX = baseX - 26
    val maxX = baseX + 26
    val minZ = baseZ - 26
    val maxZ = baseZ + 26
    val floorY = baseY - 1

    Katton.makeAdminExecuteCommand("//pos1 " + minX + "," + floorY + "," + minZ)
    Katton.makeAdminExecuteCommand("//pos2 " + maxX + "," + floorY + "," + maxZ)
    Katton.makeAdminExecuteCommand("//set air")

    removingFloor = false

    val pool1 = ["cobweb", "cobweb", "cobweb", "cobweb", "cobweb", "cobweb", "cobweb", "cobweb",
        "stone", "copper_ingot", "copper_ingot", "copper_ingot", "copper_ingot", "copper_ingot",
        "copper_ingot", "copper_ingot", "copper_ingot", "ender_pearl", "iron_ingot", "iron_ingot",
        "diamond", "diamond", "lava_bucket"]
    val pool2 = ["ender_pearl", "iron_ingot", "iron_ingot", "iron_ingot", "iron_ingot", "diamond",
        "diamond", "diamond", "diamond", "cobweb", "cobweb", "cobweb", "cobweb", "cobweb",
        "cobweb", "cobweb", "cobweb", "copper_ingot", "lava_bucket", "tnt", "arrow", "arrow",
        "arrow", "arrow"]

    Katton.vars.script().set("pool1", pool1)
    Katton.vars.script().set("pool2", pool2)
    Katton.vars.script().set("is_first_drop", true)

    runLootRound()
}

function runLootRound() {
    if (!running) {
        return
    }
    runLootTimer(20)
}

function runLootTimer(secondsLeft) {
    if (!running) {
        return
    }

    if (secondsLeft <= 0) {
        deliverLoot()
        return
    }

    lootTimer = secondsLeft
    Katton.vars.script().set("loot_timer", secondsLeft)

    for (player in Katton.players.all()) {
        if (Katton.vars.player(player.uuid).get("in_void") != true) {
            player.sendActionBar("&6item drop in: &f" + secondsLeft + "s")
        }
    }

    lootTaskId = Katton.after(1.seconds) {
        runLootTimer(secondsLeft - 1)
    }
}

function deliverLoot() {
    if (!running) {
        return
    }

    val isFirstDrop = Katton.vars.script().get("is_first_drop")
    var chosenItem = null

    if (isFirstDrop == true) {
        chosenItem = "oak_planks"
        Katton.vars.script().set("is_first_drop", false)
    } else {
        val pool1 = Katton.vars.script().get("pool1")
        if (pool1 != null && pool1.size() > 0) {
            chosenItem = Katton.pick(pool1)
            pool1.remove(chosenItem)
            Katton.vars.script().set("pool1", pool1)
        } else {
            val pool2 = Katton.vars.script().get("pool2")
            if (pool2 != null && pool2.size() > 0) {
                chosenItem = Katton.pick(pool2)
                pool2.remove(chosenItem)
                Katton.vars.script().set("pool2", pool2)
            } else {
                for (player in Katton.players.all()) {
                    player.sendMessage("&eall loot items distributed!")
                }
            }
        }
    }

    if (chosenItem != null) {
        for (player in Katton.players.all()) {
            if (player.game_mode == "spectator") {
                var pendingList = Katton.vars.player(player.uuid).get("pending_loot")
                if (pendingList == null) {
                    pendingList = []
                }
                pendingList.append(chosenItem)
                Katton.vars.player(player.uuid).set("pending_loot", pendingList)
            }
        }

        val chosenItemItem = Katton.item(chosenItem)
        for (player in Katton.players.all()) {
            player.give(chosenItemItem)
            player.sendMessage("&6&lLOOT! &eeveryone has received " + chosenItem + "!")
        }
    }

    runLootRound()
}

function handlePlayerDeath(player) {
    if (!running) {
        return
    }

    val teamId = Katton.vars.player(player.uuid).get("team_id")
    if (teamId == null) {
        return
    }

    if (Katton.vars.player(player.uuid).get("in_void") == true) {
        return
    }

    player.sendTitle("&c&ldead", "")
    Katton.vars.player(player.uuid).set("in_void", true)
    
    player.setHealth(0)
    player.teleport(spawnLocation())

    var aliveOnTeam = Katton.vars.script().get("alive_on_team_" + teamId)
    if (aliveOnTeam == null) {
        aliveOnTeam = 0
    }
    aliveOnTeam = Math.max(0, aliveOnTeam - 1)
    Katton.vars.script().set("alive_on_team_" + teamId, aliveOnTeam)

    if (aliveOnTeam <= 0) {
        Katton.vars.script().set("team_alive_" + teamId, false)
        checkWinCondition()
    } else {
        player.sendMessage("&eyou died! you will respawn in 30 seconds")
        runRespawnTimer(player, 30, teamId)
    }
}

function checkWinCondition() {
    var aliveTeams = 0
    var winningTeamId = -1
    for (i in 0..(numTeams - 1)) {
        val teamStillIn = Katton.vars.script().get("team_alive_" + i)
        if (teamStillIn == true) {
            aliveTeams = aliveTeams + 1
            winningTeamId = i
        }
    }

    if (aliveTeams <= 1) {
        if (winningTeamId >= 0) {
            val winColor = Katton.vars.script().get("team_color_" + winningTeamId)
            for (p in Katton.players.all()) {
                p.sendMessage("&e&lteam " + winColor + " wins voidwars!")
            }
        } else {
            for (p in Katton.players.all()) {
                p.sendMessage("&7&ldraw! no teams survived")
            }
        }
        endVoidWars()
    }
}

function runRespawnTimer(player, secondsLeft, teamId) {
    if (!running) {
        return
    }

    val aliveOnTeam = Katton.vars.script().get("alive_on_team_" + teamId)
    if (aliveOnTeam == null || aliveOnTeam <= 0) {
        player.sendActionBar("&cyour team was eliminated")
        return
    }

    if (secondsLeft > 0) {
        player.sendActionBar("&ereviving in: &c" + secondsLeft + "s")
        Katton.after(1.seconds) {
            runRespawnTimer(player, secondsLeft - 1, teamId)
        }
    } else {
        var aliveTeammates = []
        for (p in Katton.players.all()) {
            if (Katton.vars.player(p.uuid).get("team_id") == teamId && Katton.vars.player(p.uuid).get("in_void") != true) {
                aliveTeammates.append(p)
            }
        }

        if (aliveTeammates.size() > 0) {
            val targetTeammate = Katton.pick(aliveTeammates)

            Katton.makeAdminExecuteCommand("survival " + player.name)
            Katton.after(5.ticks) {
                player.teleport(targetTeammate.location)
            }
            Katton.after(10.ticks) {
                Katton.vars.player(player.uuid).set("in_void", false)
            }
            
            var currentAlive = Katton.vars.script().get("alive_on_team_" + teamId)
            Katton.vars.script().set("alive_on_team_" + teamId, currentAlive + 1)

            player.sendMessage("&ayou have been revived and teleported to " + targetTeammate.name + "!")
            targetTeammate.sendMessage("&a" + player.name + " has respawned at your location!")

            val pendingList = Katton.vars.player(player.uuid).get("pending_loot")
            if (pendingList != null && pendingList.size() > 0) {
                for (item in pendingList) { 
                    player.give(Katton.item(item))
                    player.sendMessage("&6received pending loot: " + item)
                }
                Katton.vars.player(player.uuid).set("pending_loot", [])
            }

            val wool = teamWools[teamId]
            player.give(Katton.item(wool + "_wool", 64))
            player.give(Katton.item("shears"))
            player.give(Katton.item("cooked_beef", 16))

        } else {
            player.sendMessage("&cno teammates left alive to spawn on!")
            checkWinCondition() 
        }
    }
}

function endVoidWars() {
    running = false
    Katton.cancel(prestartTaskId)
    Katton.cancel(prepTaskId)
    Katton.cancel(floorTimerTaskId)
    Katton.cancel(lootTaskId)
    Katton.cancel(voidCheckTaskId)

    val spawn = spawnLocation()
    val baseX = spawn["x"]
    val baseY = spawn["y"]
    val baseZ = spawn["z"]

    val minX = baseX - 27
    val maxX = baseX + 27
    val minZ = baseZ - 27
    val maxZ = baseZ + 27
    val minY = baseY - 1
    val maxY = minY + 54

    val spawn2 = spawnLocation()
    val baseX2 = spawn2["x"]
    val baseY2 = spawn2["y"]
    val baseZ2 = spawn2["z"]
    val minX2 = baseX2 - 26
    val maxX2 = baseX2 + 26
    val minZ2 = baseZ2 - 26
    val maxZ2 = baseZ2 + 26
    val minY2 = baseY2
    val maxY2 = minY2 + 52
    val floorY = baseY2 - 1

    Katton.makeAdminExecuteCommand("//pos1 " + minX2 + "," + minY2 + "," + minZ2)
    Katton.makeAdminExecuteCommand("//pos2 " + maxX2 + "," + maxY2 + "," + maxZ2)
    Katton.makeAdminExecuteCommand("//set air")

    Katton.makeAdminExecuteCommand("//pos1 " + minX2 + "," + floorY + "," + minZ2)
    Katton.makeAdminExecuteCommand("//pos2 " + maxX2 + "," + floorY + "," + maxZ2)
    Katton.makeAdminExecuteCommand("//set light_blue_stained_glass")
    Katton.makeAdminExecuteCommand("//center blue_stained_glass")

    Katton.after(2.seconds) {
        for (player in Katton.players.all()) {
            player.teleport(spawnLocation())
        }
    
        for (i in 0..(numTeams - 1)) {
            Katton.vars.script().set("team_alive_" + i, null)
            Katton.vars.script().set("alive_on_team_" + i, null)
        }
    
        for (player in Katton.players.all()) {
            player.sendMessage("&avoidwars is over!")
        }
    }
}

@Katton.command(name = "vwend", permission = "admin")
function vwEnd(sender, args) {
    if (!running) {
        sender.sendMessage("&cno voidwars round is currently running")
        return
    }

    Katton.makeAdminExecuteCommand("building false")

    val spawn = spawnLocation()
    val baseX = spawn["x"]
    val baseY = spawn["y"]
    val baseZ = spawn["z"]
    val minX = baseX - 26
    val maxX = baseX + 26
    val minZ = baseZ - 26
    val maxZ = baseZ + 26
    val minY = baseY
    val maxY = minY + 52
    val floorY = baseY - 1

    Katton.makeAdminExecuteCommand("//pos1 " + minX + "," + minY + "," + minZ)
    Katton.makeAdminExecuteCommand("//pos2 " + maxX + "," + maxY + "," + maxZ)
    Katton.makeAdminExecuteCommand("//set air")

    Katton.makeAdminExecuteCommand("//pos1 " + minX + "," + floorY + "," + minZ)
    Katton.makeAdminExecuteCommand("//pos2 " + maxX + "," + floorY + "," + maxZ)
    Katton.makeAdminExecuteCommand("//set light_blue_stained_glass")
    Katton.makeAdminExecuteCommand("//center blue_stained_glass")

    endVoidWars()
}

function startBackgroundLoops() {
    voidCheckTaskId = Katton.every(1.seconds) {
        if (running) {
            val spawn = spawnLocation()
            val killY = spawn["y"] - 10

            for (player in Katton.players.all()) {
                val inVoid = Katton.vars.player(player.uuid).get("in_void")
                val loc = player.location
                if (loc["y"] < killY) {
                    if (inVoid != true) {
                        handlePlayerDeath(player)
                    }
                }
            }
        }
    }
}

@Katton.listen("player_death")
function onDeath(event) {
    if (running) {
        handlePlayerDeath(event.player)
    }
}

@Katton.listen("player_respawn")
function onRespawn(event) {
    if (running) {
        val player = event.player
        val inVoid = Katton.vars.player(player.uuid).get("in_void")
        Katton.after(5.ticks) {
            event.player.teleport(spawnLocation())
        }
        if (inVoid == true) {
            player.setHealth(0)
        }
    }
}

@Katton.listen("block_break")
function onBreak(event) {
    if (running) {
        if (event.block == "quartz" || event.block == "smooth_quartz" || event.block == "light_blue_stained_glass") {
            if (!removingFloor) {
                event.cancel()
                event.player.sendMessage("&cyou cannot break the arena structure!")
            }
        }
    }
}

@Katton.listen("block_place")
function onPlace(event) {
    if (running) {
        val player = event.player
        val block = event.block

        val currentTeam = Katton.vars.player(player.uuid).get("team_id")
        if (currentTeam == null) {
            return
        }
        
        val wool = teamWools[currentTeam]
        val placedwool = wool + "_wool"
        Katton.after(2.ticks) {
            if (block == placedwool) {
                // could just use player.give, however it wouldnt replace offhand
                if (player.tool.type == block) {
                    player.setTool(Katton.item(wool + "_wool", 64))
                }
                if (player.offhand_tool.type == block) {
                    player.setOffhandTool(Katton.item(wool + "_wool", 64))
                }
            }
        }
    }
}


@Katton.listen("item_drop")
function onDrop(event) {
    if (running) {
        val player = event.player
        val item = event.item
        if (item != null) {
            if (item.type.contains("wool")) {
                event.cancel()
                event.player.sendMessage("&cyou cannot drop this block!")
            }
        }
    }
}

try it yourself

/katton import
/katton run vwsetspawn // (do this in a empty spot)
/katton run voidwars build // (this will build the arena in the spawn spot you set)
/katton run voidwars <integer> // (change integer to be any number like 1, 2, 3, etc. this is how many teams so if its 1 it will be a 1v1v1v1v.. depending on how many people there are)

you can end it at anytime by doing

/katton run vwend