Lots of things
parent
cbf1e64db9
commit
95391de3b7
|
@ -1,2 +1,3 @@
|
||||||
/build/
|
/build/
|
||||||
/.gradle/
|
/.gradle/
|
||||||
|
/.idea/
|
||||||
|
|
|
@ -31,5 +31,10 @@
|
||||||
<option name="name" value="maven2" />
|
<option name="name" value="maven2" />
|
||||||
<option name="url" value="https://repo.aikar.co/content/groups/aikar/" />
|
<option name="url" value="https://repo.aikar.co/content/groups/aikar/" />
|
||||||
</remote-repository>
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="BintrayJCenter" />
|
||||||
|
<option name="name" value="BintrayJCenter" />
|
||||||
|
<option name="url" value="https://jcenter.bintray.com/" />
|
||||||
|
</remote-repository>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -14,6 +14,9 @@
|
||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
<file type="web" url="file://$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="PWA">
|
||||||
|
<option name="wasEnabledAtLeastOnce" value="true" />
|
||||||
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_16" default="true" project-jdk-name="16" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_16" default="true" project-jdk-name="16" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.5.31"
|
kotlin("jvm") version "1.6.10"
|
||||||
id("com.github.johnrengelman.shadow") version "7.1.0"
|
id("com.github.johnrengelman.shadow") version "7.1.0"
|
||||||
|
kotlin("plugin.serialization") version "1.6.10"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "nl.kallestruik"
|
group = "nl.kallestruik"
|
||||||
|
@ -11,6 +12,7 @@ version = "2.0"
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
|
jcenter()
|
||||||
|
|
||||||
maven("https://papermc.io/repo/repository/maven-public/")
|
maven("https://papermc.io/repo/repository/maven-public/")
|
||||||
maven("https://repo.aikar.co/content/groups/aikar/")
|
maven("https://repo.aikar.co/content/groups/aikar/")
|
||||||
|
@ -19,7 +21,9 @@ repositories {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("co.aikar:acf-paper:0.5.0-SNAPSHOT")
|
implementation("co.aikar:acf-paper:0.5.0-SNAPSHOT")
|
||||||
compileOnly("com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT")
|
compileOnly("com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT")
|
||||||
|
compileOnly("nl.kallestruik:DLib:1.4.1")
|
||||||
compileOnly(kotlin("stdlib-jdk8"))
|
compileOnly(kotlin("stdlib-jdk8"))
|
||||||
|
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.compileJava {
|
tasks.compileJava {
|
||||||
|
|
|
@ -1,25 +1,174 @@
|
||||||
package nl.kallestruik.darena
|
package nl.kallestruik.darena
|
||||||
|
|
||||||
import nl.kallestruik.darena.managers.ConfigManager
|
import co.aikar.commands.BukkitCommandExecutionContext
|
||||||
import nl.kallestruik.darena.managers.PointsManager
|
import co.aikar.commands.InvalidCommandArgument
|
||||||
import nl.kallestruik.darena.managers.TeamManager
|
import co.aikar.commands.MessageType
|
||||||
|
import co.aikar.commands.PaperCommandManager
|
||||||
|
import nl.kallestruik.darena.arenas.Arena
|
||||||
|
import nl.kallestruik.darena.commands.CommandDArena
|
||||||
|
import nl.kallestruik.darena.exceptions.TeamNotFoundException
|
||||||
|
import nl.kallestruik.darena.listeners.*
|
||||||
|
import nl.kallestruik.darena.managers.*
|
||||||
|
import nl.kallestruik.darena.types.Reloadable
|
||||||
|
import nl.kallestruik.darena.types.Team
|
||||||
|
import org.bukkit.ChatColor
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class DArena : JavaPlugin() {
|
//TODO: Add documentation on a wiki page or something for this whole thing.
|
||||||
|
|
||||||
|
class DArena: JavaPlugin() {
|
||||||
|
private lateinit var commandManager: PaperCommandManager
|
||||||
private lateinit var configManager: ConfigManager
|
private lateinit var configManager: ConfigManager
|
||||||
private lateinit var pointsManager: PointsManager
|
private lateinit var pointsManager: PointsManager
|
||||||
private lateinit var teamManager: TeamManager
|
private lateinit var teamManager: TeamManager
|
||||||
|
private lateinit var arenaManager: ArenaManager
|
||||||
|
private lateinit var editManager: EditManager
|
||||||
|
private lateinit var gameManager: GameManager
|
||||||
|
|
||||||
|
private lateinit var reloadable: Map<String, Reloadable>
|
||||||
|
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
|
commandManager = PaperCommandManager(this)
|
||||||
|
commandManager.enableUnstableAPI("help")
|
||||||
|
commandManager.enableUnstableAPI("brigadier")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
configManager = ConfigManager(File(dataFolder, "config.yml"))
|
configManager = ConfigManager(File(dataFolder, "config.yml"))
|
||||||
configManager.load()
|
configManager.load()
|
||||||
|
|
||||||
teamManager = TeamManager(File(dataFolder, "teams.yml"))
|
teamManager = TeamManager(File(dataFolder, "teams.yml"))
|
||||||
teamManager.load()
|
teamManager.load()
|
||||||
|
|
||||||
pointsManager = PointsManager(teamManager, File(dataFolder, "points.yml"))
|
pointsManager = PointsManager(teamManager, File(dataFolder, "points.yml"))
|
||||||
|
pointsManager.load()
|
||||||
|
|
||||||
|
arenaManager = ArenaManager(teamManager, pointsManager, configManager, this, File(dataFolder, "arenas"), File(dataFolder, "arena.schema.json"))
|
||||||
|
arenaManager.loadArenas()
|
||||||
|
|
||||||
|
editManager = EditManager(arenaManager, this)
|
||||||
|
arenaManager.editManager = editManager
|
||||||
|
|
||||||
|
gameManager = GameManager(arenaManager, File(dataFolder, "games.yml"))
|
||||||
|
gameManager.load()
|
||||||
|
|
||||||
|
reloadable = mapOf(
|
||||||
|
Pair("arenas", arenaManager),
|
||||||
|
Pair("config", configManager),
|
||||||
|
Pair("games", gameManager),
|
||||||
|
)
|
||||||
|
|
||||||
|
setupCommandContexts()
|
||||||
|
setupCommandCompletions()
|
||||||
|
setupACFFormatting()
|
||||||
|
|
||||||
|
commandManager.registerCommand(CommandDArena(arenaManager, editManager, pointsManager, teamManager, gameManager))
|
||||||
|
|
||||||
|
server.pluginManager.registerEvents(CheckpointListener(arenaManager), this)
|
||||||
|
server.pluginManager.registerEvents(TeamListener(teamManager), this)
|
||||||
|
server.pluginManager.registerEvents(KillListener(arenaManager), this)
|
||||||
|
server.pluginManager.registerEvents(FireballListener(arenaManager, configManager), this)
|
||||||
|
server.pluginManager.registerEvents(FallListener(arenaManager), this)
|
||||||
|
server.pluginManager.registerEvents(BorderListener(arenaManager), this)
|
||||||
|
server.pluginManager.registerEvents(BuildRestrictionListener(arenaManager), this)
|
||||||
|
server.pluginManager.registerEvents(SectionListener(arenaManager), this)
|
||||||
|
server.pluginManager.registerEvents(CheckpointSoundListener(arenaManager), this)
|
||||||
|
server.pluginManager.registerEvents(CheckpointMessageListener(arenaManager), this)
|
||||||
|
server.pluginManager.registerEvents(GrapplingHookListener(arenaManager), this)
|
||||||
|
server.pluginManager.registerEvents(EditListener(editManager, this), this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisable() {
|
override fun onDisable() {
|
||||||
|
teamManager.save()
|
||||||
|
pointsManager.save()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Move this to a more suitable location
|
||||||
|
private fun setupACFFormatting() {
|
||||||
|
commandManager.setFormat(
|
||||||
|
MessageType.INFO,
|
||||||
|
ChatColor.GRAY,
|
||||||
|
ChatColor.YELLOW,
|
||||||
|
ChatColor.RED
|
||||||
|
)
|
||||||
|
commandManager.setFormat(
|
||||||
|
MessageType.SYNTAX,
|
||||||
|
ChatColor.YELLOW,
|
||||||
|
ChatColor.GREEN,
|
||||||
|
ChatColor.WHITE
|
||||||
|
)
|
||||||
|
commandManager.setFormat(
|
||||||
|
MessageType.ERROR,
|
||||||
|
ChatColor.RED,
|
||||||
|
ChatColor.DARK_GREEN,
|
||||||
|
ChatColor.GREEN
|
||||||
|
)
|
||||||
|
commandManager.setFormat(
|
||||||
|
MessageType.HELP,
|
||||||
|
ChatColor.YELLOW,
|
||||||
|
ChatColor.AQUA,
|
||||||
|
ChatColor.GRAY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Move this to a more suitable location
|
||||||
|
private fun setupCommandContexts() {
|
||||||
|
commandManager.commandContexts.registerContext(
|
||||||
|
Arena::class.java
|
||||||
|
) { context: BukkitCommandExecutionContext ->
|
||||||
|
return@registerContext arenaManager.getArena(context.popFirstArg())
|
||||||
|
?: throw InvalidCommandArgument("Arena does not exist!")
|
||||||
|
}
|
||||||
|
|
||||||
|
commandManager.commandContexts.registerContext(
|
||||||
|
Team::class.java
|
||||||
|
) { context: BukkitCommandExecutionContext ->
|
||||||
|
try {
|
||||||
|
return@registerContext teamManager.getTeamByName(context.popFirstArg())
|
||||||
|
} catch (e: TeamNotFoundException) {
|
||||||
|
throw InvalidCommandArgument("Arena does not exist!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commandManager.commandContexts.registerContext(
|
||||||
|
Reloadable::class.java
|
||||||
|
) { context: BukkitCommandExecutionContext ->
|
||||||
|
return@registerContext reloadable[context.popFirstArg()]
|
||||||
|
?: throw InvalidCommandArgument("That is not a reloadable component!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Move this to a more suitable location
|
||||||
|
private fun setupCommandCompletions() {
|
||||||
|
// Arena
|
||||||
|
commandManager.commandCompletions.registerAsyncCompletion("arena") {
|
||||||
|
arenaManager.getAllArenaNames()
|
||||||
|
}
|
||||||
|
|
||||||
|
commandManager.commandCompletions.setDefaultCompletion("arena", Arena::class.java)
|
||||||
|
|
||||||
|
// Team
|
||||||
|
commandManager.commandCompletions.registerAsyncCompletion("team") {
|
||||||
|
teamManager.getAllTeamNames()
|
||||||
|
}
|
||||||
|
|
||||||
|
commandManager.commandCompletions.setDefaultCompletion("team", Team::class.java)
|
||||||
|
|
||||||
|
// teamOptions
|
||||||
|
commandManager.commandCompletions.registerCompletion("teamOptions") {
|
||||||
|
listOf(
|
||||||
|
"color",
|
||||||
|
"prefix"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reloadable
|
||||||
|
commandManager.commandCompletions.registerAsyncCompletion("reloadable") {
|
||||||
|
reloadable.keys
|
||||||
|
}
|
||||||
|
|
||||||
|
commandManager.commandCompletions.setDefaultCompletion("reloadable", Reloadable::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +1,192 @@
|
||||||
package nl.kallestruik.darena.arenas
|
package nl.kallestruik.darena.arenas
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
import nl.kallestruik.darena.arenas.world.ArenaWorld
|
import nl.kallestruik.darena.arenas.world.ArenaWorld
|
||||||
|
import nl.kallestruik.darena.exceptions.ArenaStartAbortedException
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.managers.ConfigManager
|
||||||
|
import nl.kallestruik.darena.managers.PointsManager
|
||||||
|
import nl.kallestruik.darena.managers.TeamManager
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import nl.kallestruik.darena.types.arena.ProcessedArenaCheckpoint
|
||||||
|
import nl.kallestruik.darena.types.border.MCBorder
|
||||||
|
import nl.kallestruik.darena.types.countdown.AsyncCountdown
|
||||||
|
import nl.kallestruik.darena.types.countdown.AsyncCountdownWithSyncTask
|
||||||
import nl.kallestruik.darena.util.ArenaUtil
|
import nl.kallestruik.darena.util.ArenaUtil
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import nl.kallestruik.darena.util.RenderUtil
|
||||||
|
import org.bukkit.Color
|
||||||
|
import org.bukkit.GameMode
|
||||||
|
import org.bukkit.World
|
||||||
|
import org.bukkit.entity.Player
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
import org.bukkit.potion.PotionEffect
|
import org.bukkit.scheduler.BukkitRunnable
|
||||||
import org.bukkit.potion.PotionEffectType
|
|
||||||
|
|
||||||
class Arena(
|
class Arena(
|
||||||
private val config: ArenaConfig,
|
val config: ArenaConfig,
|
||||||
private val arenaUtil: ArenaUtil,
|
|
||||||
private val plugin: JavaPlugin,
|
private val plugin: JavaPlugin,
|
||||||
private val world: ArenaWorld = ArenaWorld(config.name, plugin),
|
private val configManager: ConfigManager,
|
||||||
|
private val arenaManager: ArenaManager,
|
||||||
|
private val teamManager: TeamManager,
|
||||||
|
private val pointsManager: PointsManager,
|
||||||
|
val world: ArenaWorld = ArenaWorld(config.name, configManager, plugin),
|
||||||
) {
|
) {
|
||||||
private lateinit var session: ArenaSession
|
var session: ArenaSession? = null
|
||||||
// Simple stuff done: 0.001474s
|
var pointsSession: PointsSession? = null
|
||||||
// createArena start: 0.0001026s
|
|
||||||
// Loaded schematic: 0.000162s
|
fun start(loadingTimeOverride: Int, spectatorTimeOverride: Int, beforeStartTimeOverride: Int) {
|
||||||
// Created new world: 3.122034s
|
val loadingTime = if (loadingTimeOverride != -1) loadingTimeOverride else config.countdown.loadingTime
|
||||||
// Loaded world: 0.0075989s
|
val spectatorTime = if (spectatorTimeOverride != -1) spectatorTimeOverride else config.countdown.spectatorTime
|
||||||
// Placed arena: 2.3205267s
|
val beforeStartTime = if (beforeStartTimeOverride != -1) beforeStartTimeOverride else config.countdown.beforeStartTime
|
||||||
// Fixed light: 4.2093585s
|
|
||||||
// createArena done: 0.0002953
|
|
||||||
// Countdown done: 5.0126158
|
|
||||||
// Fully done: 0.0982557
|
|
||||||
|
|
||||||
fun start() {
|
|
||||||
//TODO: Redo everything in here.
|
|
||||||
// Create a new session
|
// Create a new session
|
||||||
session = ArenaSession()
|
session = ArenaSession()
|
||||||
|
pointsSession = PointsSession(teamManager, pointsManager, this)
|
||||||
// Add all participants and spectators
|
// Add all participants and spectators
|
||||||
session.participants.addAll(arenaUtil.getPossibleParticipants());
|
session!!.originalParticipants.addAll(ArenaUtil.getPossibleParticipants())
|
||||||
session.spectators.addAll(arenaUtil.getPossibleSpectators());
|
session!!.participants.addAll(session!!.originalParticipants)
|
||||||
// Reset the world
|
session!!.spectators.addAll(ArenaUtil.getPossibleSpectators())
|
||||||
world.reset()
|
session!!.allPlayers.addAll(session!!.originalParticipants.union(session!!.spectators))
|
||||||
|
|
||||||
|
session!!.currentCountdown = AsyncCountdownWithSyncTask(plugin, loadingTime, "Teleporting in", { //TODO: Make the subtitle configurable
|
||||||
|
// Reset the world
|
||||||
|
world.reset()
|
||||||
|
}, {
|
||||||
|
session!!.processSpawnRules(config.spawns, config.spawnRules, config.loadouts)
|
||||||
|
session!!.processSpawnPools(config.spawnPools)
|
||||||
|
session!!.processCheckpoints(config.checkpoints)
|
||||||
|
|
||||||
|
startBorders()
|
||||||
|
|
||||||
// Place all spectators in the arena
|
// Spawn everyone as a spectator so they can explore the map.
|
||||||
session.spectators.forEach {
|
for (player in session!!.allPlayers) {
|
||||||
// config.spectatorSpawn.spawn(world, it)
|
player.gameMode = GameMode.SPECTATOR
|
||||||
}
|
}
|
||||||
|
|
||||||
// Randomize spawns
|
val spectatorSpawnRule = session!!.spawnRules["spectator"] ?: throw ArenaStartAbortedException("Missing spawn rule \"spectator\". This rule is required.")
|
||||||
session.initialSpawns.addAll(config.spawns.filter {
|
|
||||||
//TODO: make this filter only spawns that meet the intitial spawn condidtions. Or just replace this whole method.
|
|
||||||
return@filter true
|
|
||||||
}.keys)
|
|
||||||
session.initialSpawns.shuffle()
|
|
||||||
|
|
||||||
// Spawn all the players
|
spectatorSpawnRule.spawnPlayers(session!!.allPlayers, world)
|
||||||
session.participants.forEachIndexed { index, player ->
|
|
||||||
// TODO: Freeze players in place (for in arena countdown) (if countdown is 0 dont freeze them)
|
session!!.currentCountdown = AsyncCountdown(plugin, spectatorTime, "Starting in", alwaysAsActionBar = true) { //TODO: Make the subtitle configurable
|
||||||
config.spawns[session.initialSpawns[index % session.initialSpawns.size]]!!.spawn(world, player)
|
for (participant in session!!.participants) {
|
||||||
}
|
if (config.features.contains(ArenaFeature.GAMEMODE_ADVENTURE))
|
||||||
// TODO:
|
participant.gameMode = GameMode.ADVENTURE
|
||||||
|
else
|
||||||
|
participant.gameMode = GameMode.SURVIVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
val initialSpawnRule = session!!.spawnRules["initial"] ?: throw ArenaStartAbortedException("Missing spawn rule \"initial\". This rule is required.")
|
||||||
|
initialSpawnRule.spawnPlayers(session!!.participants, world)
|
||||||
|
|
||||||
|
// Mark the game as in progress. This makes the event listeners all trigger from this point onwards.
|
||||||
|
session!!.isInProgress = true
|
||||||
|
session!!.currentCountdown = AsyncCountdown(plugin, beforeStartTime, "Beginning in", alwaysAsTitle = true) { //TODO: Make the subtitle configurable
|
||||||
|
|
||||||
|
config.blockRemoveZone?.removeBlocks(world)
|
||||||
|
|
||||||
|
startTimers()
|
||||||
|
}.start(session!!.allPlayers)
|
||||||
|
}.start(session!!.allPlayers)
|
||||||
|
}).start(session!!.allPlayers)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reset() {
|
fun startTimers() {
|
||||||
world.reset()
|
session!!.startTime = System.currentTimeMillis()
|
||||||
|
if (config.endConditions.time != -1) {
|
||||||
|
session!!.timeLeft = config.endConditions.time
|
||||||
|
session!!.endTimeTask = object : BukkitRunnable() {
|
||||||
|
override fun run() {
|
||||||
|
arenaManager.end()
|
||||||
|
}
|
||||||
|
}.runTaskLater(plugin, config.endConditions.time * 20L)
|
||||||
|
}
|
||||||
|
|
||||||
|
session!!.timerTask = object : BukkitRunnable() {
|
||||||
|
override fun run() {
|
||||||
|
session!!.timeLeft--
|
||||||
|
updateActionbar()
|
||||||
|
}
|
||||||
|
}.runTaskTimer(plugin, 20, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startBorders() {
|
||||||
|
config.borders.vertical.getOrNull(0)?.execute(MCBorder(world), plugin)
|
||||||
|
config.borders.top.getOrNull(0)?.execute(session!!.borderTop, plugin)
|
||||||
|
config.borders.bottom.getOrNull(0)?.execute(session!!.borderBottom, plugin)
|
||||||
|
|
||||||
|
if (hasFeature(ArenaFeature.HORIZONTAL_BORDERS)) {
|
||||||
|
session!!.borderTask = BorderRunnable(world.world, session!!).runTaskTimer(plugin, 0, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateActionbar() {
|
||||||
|
var actionBar = Component.empty()
|
||||||
|
if (hasFeature(ArenaFeature.SHOW_TIMER))
|
||||||
|
actionBar = actionBar.append(Component
|
||||||
|
.text("Time left: ${ArenaUtil.formatTime(session!!.timeLeft)}"))
|
||||||
|
val allPlayers = session!!.originalParticipants.union(session!!.spectators)
|
||||||
|
for (player in allPlayers) {
|
||||||
|
player.sendActionBar(actionBar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun end() {
|
||||||
|
for (player in session!!.originalParticipants) {
|
||||||
|
ArenaUtil.clearPlayer(player)
|
||||||
|
player.gameMode = GameMode.SURVIVAL
|
||||||
|
session!!.spectators.remove(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
world.empty()
|
||||||
|
if (pointsSession != null)
|
||||||
|
pointsSession!!.end()
|
||||||
|
|
||||||
|
session?.timerTask?.cancel()
|
||||||
|
session?.endTimeTask?.cancel()
|
||||||
|
session?.currentCountdown?.cancel()
|
||||||
|
session?.borderTask?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasFeature(feature: ArenaFeature) = config.features.contains(feature)
|
||||||
|
|
||||||
|
fun sendMessage(component: Component) {
|
||||||
|
for (player in session!!.allPlayers) {
|
||||||
|
player.sendMessage(component)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun makeSpectator(player: Player) {
|
||||||
|
Logger.trace(Arena::class, "makeSpectator(player: ${player.name})")
|
||||||
|
player.gameMode = GameMode.SPECTATOR
|
||||||
|
session!!.participants.remove(player)
|
||||||
|
session!!.spectators.add(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun respawn(player: Player) {
|
||||||
|
val lastCheckpoint: ProcessedArenaCheckpoint? = session!!.lastCheckpoint[player.uniqueId]
|
||||||
|
val spawnRule = if (lastCheckpoint != null)
|
||||||
|
lastCheckpoint.spawnRule!!
|
||||||
|
else
|
||||||
|
session!!.spawnRules["initial"]!!
|
||||||
|
|
||||||
|
spawnRule.spawnPlayer(player, world)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BorderRunnable(
|
||||||
|
private val world: World,
|
||||||
|
private val session: ArenaSession
|
||||||
|
): BukkitRunnable() {
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
RenderUtil.drawXZPlane(world,
|
||||||
|
-100, -100,
|
||||||
|
100, 100,
|
||||||
|
session.borderTop.height, Color.RED)
|
||||||
|
|
||||||
|
RenderUtil.drawXZPlane(world,
|
||||||
|
-100, -100,
|
||||||
|
100, 100,
|
||||||
|
session.borderBottom.height, Color.RED)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +1,85 @@
|
||||||
package nl.kallestruik.darena.arenas
|
package nl.kallestruik.darena.arenas
|
||||||
|
|
||||||
import nl.kallestruik.darena.types.arena.ArenaLoadout
|
import kotlinx.serialization.*
|
||||||
import nl.kallestruik.darena.types.arena.ArenaCheckpoint
|
import kotlinx.serialization.json.Json
|
||||||
import nl.kallestruik.darena.types.arena.ArenaPoints
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
import nl.kallestruik.darena.types.arena.ArenaSpawn
|
import nl.kallestruik.darena.types.arena.*
|
||||||
import nl.kallestruik.darena.types.arena.ArenaSpawnRule
|
import nl.kallestruik.darena.util.Logger
|
||||||
import nl.kallestruik.darena.util.ConfigHelper
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import org.bukkit.configuration.InvalidConfigurationException
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration
|
|
||||||
import kotlin.collections.mutableMapOf
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ArenaConfig(
|
data class ArenaConfig(
|
||||||
|
@Description(["The name of the arena."])
|
||||||
var name: String,
|
var name: String,
|
||||||
var file: File,
|
@Description(["The spawns that the arena will use. These alone are not enough to make a player spawn (See spawnRules)."])
|
||||||
var spawns: MutableMap<String, ArenaSpawn> = mutableMapOf(),
|
var spawns: MutableMap<String, ArenaSpawn> = mutableMapOf(),
|
||||||
|
@Description(["The loadouts for the arena. These determine the items a player spawns with and what potion effects they get."])
|
||||||
var loadouts: MutableMap<String, ArenaLoadout> = mutableMapOf(),
|
var loadouts: MutableMap<String, ArenaLoadout> = mutableMapOf(),
|
||||||
var checkpoints: MutableMap<String, ArenaCheckpoint> = mutableMapOf(),
|
@Description(["The checkpoints for the arena. Both normal checkpoints and finish points are defined here."])
|
||||||
|
var checkpoints: MutableList<ArenaCheckpoint> = mutableListOf(),
|
||||||
|
@Description([
|
||||||
|
"The points that can be obtained by doing certain actions.",
|
||||||
|
"These points are always awarded top down with the last repeating.",
|
||||||
|
"For example when you set kill to 10, 5, and 0, the first player to get a kill will receive 10 points, the second 5, and every player after that will receive 0."])
|
||||||
var points: ArenaPoints = ArenaPoints(),
|
var points: ArenaPoints = ArenaPoints(),
|
||||||
var spawnRules: MutableMap<String, ArenaSpawnRule> = mutableMapOf()
|
@Description([
|
||||||
|
"The spawn rules used by the arena. These rules are used by many different components to link multiple spawns together with a set of items.",
|
||||||
|
"There are two special spawn rules that are required for the operation of the plugin. These are:",
|
||||||
|
"initial - Used by the plugin to do the initial round of spawning.",
|
||||||
|
"spectator - Used by the plugin to spawn the spectators."])
|
||||||
|
var spawnRules: MutableMap<String, ArenaSpawnRule> = mutableMapOf(),
|
||||||
|
@Description(["The list of features that are enabled in the arena."])
|
||||||
|
var features: MutableSet<ArenaFeature> = mutableSetOf(),
|
||||||
|
@Description(["" +
|
||||||
|
"The different borders used by the arena. These are defined using border script. Below a short explanation of the different keyword:",
|
||||||
|
"CENTER <x> <z> - Set the center of the border to x,z. (ONLY AVAILABLE FOR VERTICAL BORDERS)",
|
||||||
|
"SET <i> - On vertical borders set the with to i. On horizontal borders set the height to i.",
|
||||||
|
"TRANSITION <i> <t> - Same as SET <i>, but over t seconds.",
|
||||||
|
"WAIT <t> - Wait for t seconds."])
|
||||||
|
var borders: ArenaBorders = ArenaBorders(),
|
||||||
|
@SerialName("end-conditions")
|
||||||
|
@Description(["The end conditions of the arena."])
|
||||||
|
var endConditions: ArenaEndConditions = ArenaEndConditions(),
|
||||||
|
@SerialName("spawn-pools")
|
||||||
|
@Description(["The spawn pools that can be used by the arena. These are used to define groups of spawn rules that can be chosen at random by checkpoints."])
|
||||||
|
var spawnPools: MutableMap<String, ArenaSpawnPool> = mutableMapOf(),
|
||||||
|
@SerialName("break-allowed")
|
||||||
|
@Description(["The blocks that players are allowed to break in the arena. Unless specified otherwise all blocks can be broken."])
|
||||||
|
var breakAllowed: ArenaAllowList = ArenaAllowList(),
|
||||||
|
@SerialName("place-allowed")
|
||||||
|
@Description(["The blocks that players are allowed to place in the arena. Unless specified otherwise all blocks can be broken."])
|
||||||
|
var placeAllowed: ArenaAllowList = ArenaAllowList(),
|
||||||
|
@Description(["The different countdowns used by the arena."])
|
||||||
|
var countdown: ArenaCountdown = ArenaCountdown(),
|
||||||
|
@SerialName("block-remove-zone")
|
||||||
|
@Description(["A area of blocks to remove at the start of the game."])
|
||||||
|
var blockRemoveZone: ArenaBlockRemoveZone? = null,
|
||||||
) {
|
) {
|
||||||
fun save(toString: Boolean = false): String? {
|
@Transient
|
||||||
val config = YamlConfiguration()
|
lateinit var file: File
|
||||||
config.set("name", name)
|
|
||||||
|
|
||||||
ConfigHelper.saveMap(ConfigHelper.getOrCreateSection(config, "spawns"), spawns)
|
fun save(toString: Boolean = false): String? {
|
||||||
ConfigHelper.saveMap(ConfigHelper.getOrCreateSection(config, "loadouts"), loadouts)
|
val json = Json {
|
||||||
ConfigHelper.saveMap(ConfigHelper.getOrCreateSection(config, "checkpoints"), checkpoints)
|
prettyPrint = true
|
||||||
points.save(ConfigHelper.getOrCreateSection(config, "points"))
|
}.encodeToString(this)
|
||||||
ConfigHelper.saveMap(ConfigHelper.getOrCreateSection(config, "spawnRules"), spawnRules)
|
|
||||||
|
|
||||||
if (toString) {
|
if (toString) {
|
||||||
return config.saveToString()
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
config.save(file)
|
file.writeText(json, Charsets.UTF_8)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@Throws(InvalidConfigurationException::class)
|
|
||||||
fun load(file: File): ArenaConfig {
|
fun load(file: File): ArenaConfig {
|
||||||
val config = ConfigHelper.getOrCreateConfig(file, "template/arena.yml")
|
Logger.trace(ArenaConfig::class, "load(file: ${file.path})")
|
||||||
if (!config.contains("name")) {
|
|
||||||
throw InvalidConfigurationException("The arena configuration file '${file.name}' does not contain the required attribute 'name'")
|
|
||||||
}
|
|
||||||
|
|
||||||
val arenaConfig = ArenaConfig(config.getString("name")!!, file)
|
val config = Json.decodeFromString<ArenaConfig>(file.readText(Charsets.UTF_8))
|
||||||
|
config.file = file
|
||||||
|
|
||||||
if (config.contains("spawns")) {
|
return config
|
||||||
arenaConfig.spawns = ConfigHelper.loadMap(config.getConfigurationSection("spawns")!!, ArenaSpawn)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.contains("loadouts")) {
|
|
||||||
arenaConfig.loadouts = ConfigHelper.loadMap(config.getConfigurationSection("loadouts")!!, ArenaLoadout)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.contains("checkpoints")) {
|
|
||||||
arenaConfig.checkpoints = ConfigHelper.loadMap(config.getConfigurationSection("checkpoints")!!, ArenaCheckpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.contains("points")) {
|
|
||||||
arenaConfig.points = ArenaPoints.load(config.getConfigurationSection("points")!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.contains("spawnRules")) {
|
|
||||||
arenaConfig.spawnRules = ConfigHelper.loadMap(config.getConfigurationSection("spawnRules")!!, ArenaSpawnRule)
|
|
||||||
}
|
|
||||||
|
|
||||||
return arenaConfig
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,81 @@
|
||||||
package nl.kallestruik.darena.arenas
|
package nl.kallestruik.darena.arenas
|
||||||
|
|
||||||
import nl.kallestruik.darena.types.arena.ArenaSpawn
|
import nl.kallestruik.darena.exceptions.ArenaStartAbortedException
|
||||||
|
import nl.kallestruik.darena.types.arena.*
|
||||||
|
import nl.kallestruik.darena.types.border.HorizontalBorder
|
||||||
|
import nl.kallestruik.darena.types.countdown.Countdown
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
import java.util.UUID
|
import org.bukkit.scheduler.BukkitTask
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
data class ArenaSession(
|
data class ArenaSession(
|
||||||
val participants: MutableList<Player> = ArrayList(),
|
val originalParticipants: MutableList<Player> = mutableListOf(),
|
||||||
val spectators: MutableList<Player> = ArrayList(),
|
val participants: MutableList<Player> = mutableListOf(),
|
||||||
val completedObjective: MutableList<UUID> = ArrayList(),
|
val spectators: MutableList<Player> = mutableListOf(),
|
||||||
val deaths: MutableList<UUID> = ArrayList(),
|
val allPlayers: MutableList<Player> = mutableListOf(),
|
||||||
val initialSpawns: MutableList<String> = ArrayList(),
|
val completedObjective: MutableList<UUID> = mutableListOf(),
|
||||||
)
|
val deaths: MutableList<UUID> = mutableListOf(),
|
||||||
|
val spawnRules: MutableMap<String, ProcessedArenaSpawnRule> = mutableMapOf(),
|
||||||
|
val spawnPools: MutableMap<String, ProcessedArenaSpawnPool> = mutableMapOf(),
|
||||||
|
val checkpoints: MutableList<ProcessedArenaCheckpoint> = mutableListOf(),
|
||||||
|
val lastCheckpoint: MutableMap<UUID, ProcessedArenaCheckpoint> = mutableMapOf(),
|
||||||
|
val reachedCheckpoints: MutableMap<UUID, MutableSet<String>> = mutableMapOf(),
|
||||||
|
val lastDamage: MutableMap<UUID, UUID> = mutableMapOf(),
|
||||||
|
val borderTop: HorizontalBorder = HorizontalBorder(),
|
||||||
|
val borderBottom: HorizontalBorder = HorizontalBorder(),
|
||||||
|
val finishedSectionsByPlayer: MutableMap<UUID, Int> = mutableMapOf(),
|
||||||
|
val finishedPerSection: MutableMap<Int, Int> = mutableMapOf(),
|
||||||
|
var startTime: Long = 0,
|
||||||
|
var playersFinished: Int = 0,
|
||||||
|
var timeLeft: Int = -1,
|
||||||
|
var endTimeTask: BukkitTask? = null,
|
||||||
|
var timerTask: BukkitTask? = null,
|
||||||
|
var borderTask: BukkitTask? = null,
|
||||||
|
var isInProgress: Boolean = false,
|
||||||
|
var currentCountdown: Countdown? = null,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun processSpawnRules(spawns: Map<String, ArenaSpawn>, rules: Map<String, ArenaSpawnRule>, loadouts: Map<String, ArenaLoadout>) {
|
||||||
|
for (rule in rules) {
|
||||||
|
val list = mutableListOf<ArenaSpawn>()
|
||||||
|
for (spawn in rule.value.spawns) {
|
||||||
|
if (spawn in spawns)
|
||||||
|
list.add(spawns[spawn]!!)
|
||||||
|
else
|
||||||
|
throw ArenaStartAbortedException("Spawn $spawn is used in spawn rule ${rule.key} but is not defined. Please resolve this issue before trying to start this arena.")
|
||||||
|
}
|
||||||
|
|
||||||
|
list.shuffle()
|
||||||
|
|
||||||
|
if (!loadouts.containsKey(rule.value.loadout) && rule.value.loadout != "none")
|
||||||
|
throw ArenaStartAbortedException("Loadout ${rule.value.loadout} is used in spawn rule ${rule.key} but is not defined. Please resolve this issue before trying to start this arena.")
|
||||||
|
|
||||||
|
spawnRules[rule.key] = ProcessedArenaSpawnRule(rule.value, list, loadouts[rule.value.loadout])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processSpawnPools(pools: Map<String, ArenaSpawnPool>) {
|
||||||
|
for (pool in pools) {
|
||||||
|
val list = mutableListOf<ProcessedArenaSpawnRule>()
|
||||||
|
for (rule in pool.value.rules) {
|
||||||
|
if (rule in spawnRules)
|
||||||
|
list.add(spawnRules[rule]!!)
|
||||||
|
else
|
||||||
|
throw ArenaStartAbortedException("Spawn rule $rule is used in spawn pool ${pool.key} but is not defined. Please resolve this issue before trying to start this arena.")
|
||||||
|
}
|
||||||
|
|
||||||
|
list.shuffle()
|
||||||
|
|
||||||
|
spawnPools[pool.key] = ProcessedArenaSpawnPool(pool.value.label, pool.value.times, list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processCheckpoints(checkpoints: List<ArenaCheckpoint>) {
|
||||||
|
for (checkpoint in checkpoints) {
|
||||||
|
val spawnRule = spawnRules[checkpoint.spawnRule]
|
||||||
|
val spawnPool = spawnPools[checkpoint.spawnPool]
|
||||||
|
|
||||||
|
this.checkpoints.add(ProcessedArenaCheckpoint(checkpoint, spawnRule, spawnPool))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
package nl.kallestruik.darena.arenas
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.format.TextColor
|
||||||
|
import nl.kallestruik.darena.exceptions.TeamNotFoundException
|
||||||
|
import nl.kallestruik.darena.managers.PointsManager
|
||||||
|
import nl.kallestruik.darena.managers.TeamManager
|
||||||
|
import nl.kallestruik.darena.types.Team
|
||||||
|
import nl.kallestruik.darena.types.arena.ProcessedArenaCheckpoint
|
||||||
|
import nl.kallestruik.darena.types.arena.ProcessedArenaSpawnPool
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
class PointsSession(
|
||||||
|
private val teamManager: TeamManager,
|
||||||
|
private val pointsManager: PointsManager,
|
||||||
|
private val arena: Arena,
|
||||||
|
) {
|
||||||
|
private val checkpointCompletionCount: MutableMap<String, Int> = mutableMapOf()
|
||||||
|
private var kills: Int = 0
|
||||||
|
// Team -> {reason -> amount}
|
||||||
|
private val points: MutableMap<Team, MutableMap<String, Int>> = HashMap()
|
||||||
|
|
||||||
|
@Throws(TeamNotFoundException::class)
|
||||||
|
fun givePointsToPlayer(uuid: UUID, amount: Int, reason: String) {
|
||||||
|
Logger.trace(PointsSession::class, "givePointsToPlayer(uuid: $uuid, amount: $amount, reason: $reason)")
|
||||||
|
Logger.debug(PointsSession::class, "Giving $amount points to player with uuid $uuid for reason $reason")
|
||||||
|
val team = teamManager.getTeamForPlayer(uuid) ?: return
|
||||||
|
|
||||||
|
givePointsToTeam(team, amount, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givePointsToTeam(team: Team, amount: Int, reason: String) {
|
||||||
|
Logger.trace(PointsSession::class, "givePointsToTeam(team: $team, amount: $amount, reason: $reason)")
|
||||||
|
Logger.debug(PointsSession::class, "Giving $amount points to team ${team.name} for reason $reason")
|
||||||
|
val pointsMap = points.getOrDefault(team, mutableMapOf())
|
||||||
|
pointsMap[reason] = (pointsMap[reason] ?: 0) + amount
|
||||||
|
points[team] = pointsMap
|
||||||
|
}
|
||||||
|
|
||||||
|
fun giveCheckpointPoints(player: Player, checkpoint: ProcessedArenaCheckpoint) {
|
||||||
|
Logger.debug(PointsSession::class, "Giving checkpoint points to player ${player.name} for reaching checkpoint ${checkpoint.label}")
|
||||||
|
val checkpointPointList = arena.config.points.checkpoints[checkpoint.label] ?: return
|
||||||
|
val checkpointPoints = checkpointPointList[min(checkpointCompletionCount[checkpoint.label] ?: 0, checkpointPointList.size - 1)]
|
||||||
|
givePointsToPlayer(player.uniqueId, checkpointPoints, "Reaching checkpoint") //TODO: Make these messages configurable
|
||||||
|
checkpointCompletionCount[checkpoint.label] = (checkpointCompletionCount[checkpoint.label] ?: 0) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun giveKillPoints(killer: Player) {
|
||||||
|
val killPointList = arena.config.points.kill
|
||||||
|
val killPoints = killPointList[min(kills, killPointList.size -1 )]
|
||||||
|
givePointsToPlayer(killer.uniqueId, killPoints, "Kill")//TODO: Make these messages configurable
|
||||||
|
kills++
|
||||||
|
}
|
||||||
|
|
||||||
|
fun giveLMSPoints() {
|
||||||
|
var lmsCounter = 0
|
||||||
|
// First award points to the players still alive
|
||||||
|
val lmsPoints = arena.config.points.lastManStanding
|
||||||
|
for (player in arena.session!!.participants) {
|
||||||
|
givePointsToPlayer(player.uniqueId, lmsPoints[min(lmsCounter, lmsPoints.size - 1)], "Placement") //TODO: Make these messages configurable
|
||||||
|
lmsCounter++
|
||||||
|
}
|
||||||
|
for (uuid in arena.session!!.deaths.reversed()) {
|
||||||
|
givePointsToPlayer(uuid, lmsPoints[min(lmsCounter, lmsPoints.size - 1)], "Placement") //TODO: Make these messages configurable
|
||||||
|
lmsCounter++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun end() {
|
||||||
|
giveLMSPoints()
|
||||||
|
|
||||||
|
val map = mutableMapOf<Team, Pair<Int, Component>>()
|
||||||
|
|
||||||
|
for (entry in points) {
|
||||||
|
val team = entry.key;
|
||||||
|
var detailComponent = Component.text("${team.name} - Points\n")
|
||||||
|
var totalPoints = 0
|
||||||
|
for (reason in entry.value) {
|
||||||
|
if (reason.value <= 0)
|
||||||
|
continue
|
||||||
|
|
||||||
|
totalPoints += reason.value
|
||||||
|
detailComponent = detailComponent.append(Component.text("\n[+${reason.value}] ${reason.key}"))
|
||||||
|
}
|
||||||
|
if (totalPoints > 0)
|
||||||
|
map[team] = Pair(totalPoints, detailComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
val sortedMap = map.toList().sortedByDescending { (_, v) -> v.first }.toMap()
|
||||||
|
|
||||||
|
//TODO: Make these message configurable
|
||||||
|
arena.sendMessage(Component.text("============[ Summary ]============"))
|
||||||
|
for (entry in sortedMap) {
|
||||||
|
// Give points to the team.
|
||||||
|
pointsManager.givePointsToTeam(entry.key, entry.value.first)
|
||||||
|
arena.sendMessage(Component.empty()
|
||||||
|
.append(teamManager.getTeamComponent(entry.key))
|
||||||
|
.append(Component
|
||||||
|
.text(": "))
|
||||||
|
.append(Component
|
||||||
|
.text(pointsManager.getPointsForTeam(entry.key)))
|
||||||
|
.append(Component
|
||||||
|
.text(" [+${entry.value.first}]")
|
||||||
|
.hoverEvent(entry.value.second.asHoverEvent())
|
||||||
|
.color(TextColor.color(0x55FFFF)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givePoolPoints(player: Player, spawnPool: ProcessedArenaSpawnPool, checkpointNumber: Int) {
|
||||||
|
Logger.debug(PointsSession::class, "Giving pool points to player ${player.name} for reaching checkpoint $checkpointNumber in the pool ${spawnPool.label}")
|
||||||
|
val poolPointList = (arena.config.points.pools[spawnPool.label] ?: return)[checkpointNumber - 1]
|
||||||
|
val poolPoints = poolPointList[min(arena.session!!.finishedPerSection[checkpointNumber] ?: 0, poolPointList.size - 1)]
|
||||||
|
givePointsToPlayer(player.uniqueId, poolPoints, "Reaching checkpoint") //TODO: Make these messages configurable
|
||||||
|
arena.session!!.finishedPerSection[checkpointNumber] = (arena.session!!.finishedPerSection[checkpointNumber] ?: 0) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -3,46 +3,68 @@ package nl.kallestruik.darena.arenas.world
|
||||||
import nl.kallestruik.darena.exceptions.ArenaWorldCreationException
|
import nl.kallestruik.darena.exceptions.ArenaWorldCreationException
|
||||||
import nl.kallestruik.darena.exceptions.ArenaWorldLoadException
|
import nl.kallestruik.darena.exceptions.ArenaWorldLoadException
|
||||||
import nl.kallestruik.darena.exceptions.ArenaWorldSaveException
|
import nl.kallestruik.darena.exceptions.ArenaWorldSaveException
|
||||||
|
import nl.kallestruik.darena.managers.ConfigManager
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.Location
|
||||||
import org.bukkit.World
|
import org.bukkit.World
|
||||||
|
import org.bukkit.WorldCreator
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import java.io.File
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
|
||||||
|
|
||||||
class ArenaWorld(
|
class ArenaWorld(
|
||||||
val name: String,
|
val name: String,
|
||||||
val plugin: JavaPlugin
|
val configManager: ConfigManager,
|
||||||
|
val plugin: JavaPlugin,
|
||||||
) {
|
) {
|
||||||
var world: World
|
lateinit var world: World
|
||||||
|
val worldFolder = File(Bukkit.getServer().worldContainer, name)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
load()
|
load()
|
||||||
world = Bukkit.createWorld(ArenaWorldCreator(name))
|
createWorld()
|
||||||
|
world.isAutoSave = false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(ArenaWorldCreationException::class)
|
||||||
|
private fun createWorld() {
|
||||||
|
world = WorldCreator.name(name).generator(ArenaChunkGenerator()).createWorld()
|
||||||
?: throw ArenaWorldCreationException("Exception while creating bukkit world for arena \"$name\".")
|
?: throw ArenaWorldCreationException("Exception while creating bukkit world for arena \"$name\".")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(ArenaWorldLoadException::class)
|
@Throws(ArenaWorldLoadException::class)
|
||||||
fun reset() {
|
fun reset() {
|
||||||
//TODO: Teleport everyone out.
|
empty()
|
||||||
|
Bukkit.unloadWorld(world, false)
|
||||||
load()
|
load()
|
||||||
|
createWorld()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(ArenaWorldSaveException::class)
|
@Throws(ArenaWorldSaveException::class)
|
||||||
fun save() {
|
fun save() {
|
||||||
|
world.save()
|
||||||
try {
|
try {
|
||||||
val savePath = Path.of(plugin.dataFolder.path, "worlds", name)
|
val savePath = Path.of(plugin.dataFolder.path, "worlds", name)
|
||||||
Files.walk(savePath).use { walk ->
|
|
||||||
walk.sorted(Comparator.reverseOrder()).forEach { path ->
|
|
||||||
Files.delete(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Files.walk(world.worldFolder.toPath()).use { walk ->
|
if (savePath.exists())
|
||||||
|
Files.walk(savePath).use { walk ->
|
||||||
|
walk.sorted(Comparator.reverseOrder()).forEach { path ->
|
||||||
|
Files.delete(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.walk(worldFolder.toPath()).use { walk ->
|
||||||
walk.forEach { source ->
|
walk.forEach { source ->
|
||||||
val destination = Path.of(
|
val destination = Path.of(
|
||||||
savePath.toString(), source.toString()
|
savePath.toString(), source.toString()
|
||||||
.substring(world.worldFolder.path.length)
|
.substring(worldFolder.path.length)
|
||||||
)
|
)
|
||||||
|
// Don't copy uid.dat files because they stop bukkit from loading the world.
|
||||||
|
if (source.endsWith("uid.dat"))
|
||||||
|
return@forEach
|
||||||
|
|
||||||
Files.copy(source, destination)
|
Files.copy(source, destination)
|
||||||
}
|
}
|
||||||
|
@ -56,7 +78,10 @@ class ArenaWorld(
|
||||||
private fun load() {
|
private fun load() {
|
||||||
try {
|
try {
|
||||||
val loadPath = Path.of(plugin.dataFolder.path, "worlds", name)
|
val loadPath = Path.of(plugin.dataFolder.path, "worlds", name)
|
||||||
Files.walk(world.worldFolder.toPath()).use { walk ->
|
if (!loadPath.exists())
|
||||||
|
return
|
||||||
|
|
||||||
|
Files.walk(worldFolder.toPath()).use { walk ->
|
||||||
walk.sorted(Comparator.reverseOrder()).forEach { path ->
|
walk.sorted(Comparator.reverseOrder()).forEach { path ->
|
||||||
Files.delete(path)
|
Files.delete(path)
|
||||||
}
|
}
|
||||||
|
@ -65,9 +90,12 @@ class ArenaWorld(
|
||||||
Files.walk(loadPath).use { walk ->
|
Files.walk(loadPath).use { walk ->
|
||||||
walk.forEach { source ->
|
walk.forEach { source ->
|
||||||
val destination = Path.of(
|
val destination = Path.of(
|
||||||
world.worldFolder.path, source.toString()
|
worldFolder.path, source.toString()
|
||||||
.substring(loadPath.toString().length)
|
.substring(loadPath.toString().length)
|
||||||
)
|
)
|
||||||
|
// Don't copy uid.dat files because they stop bukkit from loading the world.
|
||||||
|
if (source.endsWith("uid.dat"))
|
||||||
|
return@forEach
|
||||||
|
|
||||||
Files.copy(source, destination)
|
Files.copy(source, destination)
|
||||||
}
|
}
|
||||||
|
@ -76,4 +104,11 @@ class ArenaWorld(
|
||||||
throw ArenaWorldLoadException("There was an issue load the world for arena \"$name\"!", e)
|
throw ArenaWorldLoadException("There was an issue load the world for arena \"$name\"!", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun empty() {
|
||||||
|
for (player in world.players) {
|
||||||
|
player.spigot().respawn()
|
||||||
|
player.teleport(Location(Bukkit.getWorld(configManager.spawnWorldName), configManager.spawnPos.x + 0.5, configManager.spawnPos.y + 0.5, configManager.spawnPos.z + 0.5))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +0,0 @@
|
||||||
package nl.kallestruik.darena.arenas.world
|
|
||||||
|
|
||||||
data class ArenaWorldConfig(
|
|
||||||
val name: String,
|
|
||||||
)
|
|
|
@ -1,16 +0,0 @@
|
||||||
package nl.kallestruik.darena.arenas.world
|
|
||||||
|
|
||||||
import org.bukkit.WorldCreator
|
|
||||||
import org.bukkit.generator.ChunkGenerator
|
|
||||||
|
|
||||||
class ArenaWorldCreator(
|
|
||||||
name: String
|
|
||||||
): WorldCreator(name) {
|
|
||||||
private var generator: ChunkGenerator = ArenaChunkGenerator()
|
|
||||||
|
|
||||||
override fun generator(): ChunkGenerator {
|
|
||||||
return generator
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,354 @@
|
||||||
|
package nl.kallestruik.darena.commands
|
||||||
|
|
||||||
|
import co.aikar.commands.BaseCommand
|
||||||
|
import co.aikar.commands.CommandHelp
|
||||||
|
import co.aikar.commands.annotation.*
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.event.ClickEvent
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor
|
||||||
|
import net.kyori.adventure.text.format.TextColor
|
||||||
|
import nl.kallestruik.darena.arenas.Arena
|
||||||
|
import nl.kallestruik.darena.exceptions.ArenaCreationException
|
||||||
|
import nl.kallestruik.darena.exceptions.ArenaEditException
|
||||||
|
import nl.kallestruik.darena.exceptions.TeamExistsException
|
||||||
|
import nl.kallestruik.darena.managers.*
|
||||||
|
import nl.kallestruik.darena.types.EditSetting
|
||||||
|
import nl.kallestruik.darena.types.Reloadable
|
||||||
|
import nl.kallestruik.darena.types.Team
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import org.bukkit.ChatColor
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
@CommandAlias("darena|arena")
|
||||||
|
class CommandDArena(
|
||||||
|
private val arenaManager: ArenaManager,
|
||||||
|
private val editManager: EditManager,
|
||||||
|
private val pointsManager: PointsManager,
|
||||||
|
private val teamManager: TeamManager,
|
||||||
|
private val gameManager: GameManager,
|
||||||
|
): BaseCommand() {
|
||||||
|
|
||||||
|
@HelpCommand
|
||||||
|
fun doHelp(sender: CommandSender, help: CommandHelp) {
|
||||||
|
Logger.trace(CommandDArena::class, "doHelp(sender: $sender, help: $help)")
|
||||||
|
help.showHelp()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("start|s")
|
||||||
|
@Description("Start an arena")
|
||||||
|
@CommandPermission("darena.start")
|
||||||
|
fun onStart(sender: CommandSender, arena: Arena, @Default("-1") loadingTimeOverride: Int, @Default("-1") spectatorTimeOverride: Int, @Default("-1") beforeStartTimeOverride: Int) {
|
||||||
|
Logger.trace(CommandDArena::class, "onStart(sender: $sender)")
|
||||||
|
arenaManager.start(arena, loadingTimeOverride, spectatorTimeOverride, beforeStartTimeOverride)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("end")
|
||||||
|
@Description("End the current arena")
|
||||||
|
@CommandPermission("darena.end")
|
||||||
|
fun onEnd(sender: CommandSender) {
|
||||||
|
Logger.trace(CommandDArena::class, "onEnd(sender: $sender)")
|
||||||
|
arenaManager.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("list")
|
||||||
|
@Description("List all arenas")
|
||||||
|
@CommandPermission("darena.list")
|
||||||
|
fun onList(sender: CommandSender) {
|
||||||
|
Logger.trace(CommandDArena::class, "onList(sender: $sender)")
|
||||||
|
sender.sendMessage(Component.text("============[ Arenas ]============"))
|
||||||
|
for (gameEntry in gameManager.games) {
|
||||||
|
sender.sendMessage(Component
|
||||||
|
.text("${gameEntry.key}:")
|
||||||
|
.hoverEvent(Component.text(gameEntry.value.description))
|
||||||
|
)
|
||||||
|
for (arena in gameEntry.value.arenas) {
|
||||||
|
sender.sendMessage(Component.empty()
|
||||||
|
.append(Component
|
||||||
|
.text(" ${arena.config.name} "))
|
||||||
|
.append(Component
|
||||||
|
.text("[start]")
|
||||||
|
.clickEvent(ClickEvent.runCommand("/darena start ${arena.config.name}"))
|
||||||
|
.color(NamedTextColor.GREEN))
|
||||||
|
.append(Component
|
||||||
|
.text(" "))
|
||||||
|
.append(Component
|
||||||
|
.text("[edit]")
|
||||||
|
.clickEvent(ClickEvent.runCommand("/darena edit load ${arena.config.name}"))
|
||||||
|
.color(NamedTextColor.AQUA))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("leaderboard|top")
|
||||||
|
@CommandAlias("leaderboard")
|
||||||
|
@Description("Show the points leaderboard")
|
||||||
|
@CommandPermission("darena.leaderboard")
|
||||||
|
fun onLeaderboard(sender: CommandSender) {
|
||||||
|
Logger.trace(CommandDArena::class, "onLeaderboard(sender: $sender)")
|
||||||
|
pointsManager.sendLeaderboard(sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("reload")
|
||||||
|
@Description("Reload the config")
|
||||||
|
@CommandPermission("darena.reload")
|
||||||
|
fun onReload(sender: CommandSender, reloadable: Reloadable) {
|
||||||
|
Logger.trace(CommandDArena::class, "onReload(sender: $sender, reloadable: $reloadable)")
|
||||||
|
reloadable.reload()
|
||||||
|
sender.sendMessage("${ChatColor.YELLOW}[?] Successfully reloaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("new")
|
||||||
|
@Description("Create a new arena")
|
||||||
|
@CommandPermission("darena.new")
|
||||||
|
fun onNew(sender: CommandSender, name: String) {
|
||||||
|
Logger.trace(CommandDArena::class, "onNew(sender: $sender, name: $name)")
|
||||||
|
try {
|
||||||
|
arenaManager.createArena(name)
|
||||||
|
sender.sendMessage("${ChatColor.YELLOW}[?] Arena $name created successfully. You can edit it with /darena edit load $name")
|
||||||
|
} catch (e: ArenaCreationException) {
|
||||||
|
sender.sendMessage("${ChatColor.RED}[!] ${e.message}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("edit")
|
||||||
|
inner class EditCommand: BaseCommand() {
|
||||||
|
|
||||||
|
@Subcommand("load")
|
||||||
|
@Description("Load and teleport you to the world of an arena for editing.")
|
||||||
|
@CommandPermission("darena.edit.load")
|
||||||
|
fun onLoad(player: Player, arena: Arena) {
|
||||||
|
Logger.trace(EditCommand::class, "onLoad(player: $player, arena: ${arena.config.name})")
|
||||||
|
try {
|
||||||
|
editManager.load(arena)
|
||||||
|
editManager.tp(player)
|
||||||
|
} catch (e: ArenaEditException) {
|
||||||
|
player.sendMessage("${ChatColor.RED}[!] ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("save")
|
||||||
|
@Description("Save the world currently being edited.")
|
||||||
|
@CommandPermission("darena.edit.load")
|
||||||
|
fun onSave(sender: CommandSender) {
|
||||||
|
Logger.trace(EditCommand::class, "onSave(sender: $sender)")
|
||||||
|
try {
|
||||||
|
editManager.save()
|
||||||
|
} catch (e: ArenaEditException) {
|
||||||
|
sender.sendMessage("${ChatColor.RED}[!] ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("tp")
|
||||||
|
@Description("Teleport into the world currently being edited.")
|
||||||
|
@CommandPermission("darena.edit.tp")
|
||||||
|
fun onTp(player: Player) {
|
||||||
|
Logger.trace(EditCommand::class, "onTp(player: $player)")
|
||||||
|
try {
|
||||||
|
editManager.tp(player)
|
||||||
|
} catch (e: ArenaEditException) {
|
||||||
|
player.sendMessage("${ChatColor.RED}[!] ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("finish")
|
||||||
|
@Description("Save and stop editing the world currently being edited.")
|
||||||
|
@CommandPermission("darena.edit.finish")
|
||||||
|
fun onFinish(sender: CommandSender) {
|
||||||
|
Logger.trace(EditCommand::class, "onFinish(sender: $sender)")
|
||||||
|
try {
|
||||||
|
editManager.finish()
|
||||||
|
} catch (e: ArenaEditException) {
|
||||||
|
sender.sendMessage("${ChatColor.RED}[!] ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("config")
|
||||||
|
@Description("Load and teleport you to the world of an arena for editing.")
|
||||||
|
@CommandPermission("darena.edit.load")
|
||||||
|
fun onConfig(player: Player, path: String) {
|
||||||
|
Logger.trace(EditCommand::class, "onConfig(player: $player, path: $path)")
|
||||||
|
player.sendMessage("This feature is currently disabled.")
|
||||||
|
// try {
|
||||||
|
// val info = editManager.getConfigValue(path)
|
||||||
|
// player.sendMessage("------------------")
|
||||||
|
// player.sendMessage("Name: ${info.name}")
|
||||||
|
// player.sendMessage("Value: ${info.value}")
|
||||||
|
// player.sendMessage("Description: ${info.description}")
|
||||||
|
// player.sendMessage("------------------")
|
||||||
|
// } catch (e: ArenaEditException) {
|
||||||
|
// player.sendMessage("${ChatColor.RED}[!] ${e.message}")
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("visualize")
|
||||||
|
inner class VisualizeCommand: BaseCommand() {
|
||||||
|
@Subcommand("spawns")
|
||||||
|
@Description("Toggle the visibility of all spawns")
|
||||||
|
@CommandPermission("darena.edit.visualize.spawns")
|
||||||
|
fun onSpawns(player: Player) {
|
||||||
|
editManager.toggleSpawnVisibility()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("checkpoints")
|
||||||
|
@Description("Toggle the visibility of all checkpoints")
|
||||||
|
@CommandPermission("darena.edit.visualize.checkpoints")
|
||||||
|
fun onCheckpoints(player: Player) {
|
||||||
|
editManager.toggleCheckpointVisibility()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("toggle")
|
||||||
|
inner class ToggleCommand: BaseCommand() {
|
||||||
|
@Subcommand("spawnplacing")
|
||||||
|
@Description("Toogle placing spawns with armor stands")
|
||||||
|
@CommandPermission("darena.edit.toggle.spawnplacing")
|
||||||
|
fun onSpawnPlacing(player: Player) {
|
||||||
|
val newValue = editManager.toggleFeature(player, EditSetting.SPAWN_PLACING)
|
||||||
|
player.sendMessage("Spawn placing is now: $newValue")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("team")
|
||||||
|
inner class TeamCommand: BaseCommand() {
|
||||||
|
|
||||||
|
@Subcommand("create")
|
||||||
|
@Description("Create a team.")
|
||||||
|
@CommandPermission("darena.team.create")
|
||||||
|
fun onCreate(sender: CommandSender, teamName: String) {
|
||||||
|
Logger.trace(TeamCommand::class, "onCreate(sender: $sender, teamName: $teamName)")
|
||||||
|
try {
|
||||||
|
teamManager.createTeam(teamName)
|
||||||
|
sender.sendMessage("${ChatColor.YELLOW}[?] Team $teamName was successfully created.")
|
||||||
|
} catch (e: TeamExistsException) {
|
||||||
|
sender.sendMessage("${ChatColor.RED}[!] ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("join|add-player")
|
||||||
|
@Description("Add a player to a team.")
|
||||||
|
@CommandPermission("darena.team.join")
|
||||||
|
@CommandCompletion("@team @players")
|
||||||
|
fun onAddPlayer(sender: CommandSender, team: Team, player: Player) {
|
||||||
|
Logger.trace(TeamCommand::class, "onAddPlayer(sender: $sender, team: $team, player: $player)")
|
||||||
|
if (teamManager.getTeamForPlayer(player.uniqueId) != null) {
|
||||||
|
sender.sendMessage("${ChatColor.RED}[!] Player is already in a team!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
teamManager.addPlayerToTeam(team, player.uniqueId)
|
||||||
|
sender.sendMessage("${ChatColor.YELLOW}[?] Player was successfully added to team.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("leave|remove-player")
|
||||||
|
@Description("Remove a player from a team.")
|
||||||
|
@CommandPermission("darena.team.leave")
|
||||||
|
@CommandCompletion("@players")
|
||||||
|
fun onRemovePlayer(sender: CommandSender, player: Player) {
|
||||||
|
Logger.trace(TeamCommand::class, "onRemovePlayer(sender: $sender, player: $player)")
|
||||||
|
if (teamManager.getTeamForPlayer(player.uniqueId) == null) {
|
||||||
|
sender.sendMessage("${ChatColor.RED}[!] Player is not in a team!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
teamManager.removePlayerFromTeam(player.uniqueId)
|
||||||
|
sender.sendMessage("${ChatColor.YELLOW}[?] Player was successfully removed from their team.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("remove")
|
||||||
|
@Description("Remove a team.")
|
||||||
|
@CommandPermission("darena.team.remove")
|
||||||
|
fun onRemove(sender: CommandSender, team: Team) {
|
||||||
|
Logger.trace(TeamCommand::class, "onRemove(sender: $sender, team: $team)")
|
||||||
|
teamManager.removeTeam(team)
|
||||||
|
sender.sendMessage("${ChatColor.YELLOW}[?] Team ${team.name} was successfully removed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("remove-all")
|
||||||
|
@Description("Remove all teams.")
|
||||||
|
@CommandPermission("darena.team.remove-all")
|
||||||
|
fun onRemoveAll(sender: CommandSender) {
|
||||||
|
Logger.trace(TeamCommand::class, "onRemoveAll(sender: $sender)")
|
||||||
|
val count = teamManager.removeAllTeams()
|
||||||
|
pointsManager.clear()
|
||||||
|
sender.sendMessage("${ChatColor.YELLOW}[?] Removed $count teams.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("auto")
|
||||||
|
@Description("Remove all current teams and enable/disable automatic team mode.")
|
||||||
|
@CommandPermission("darena.team.auto")
|
||||||
|
fun onAuto(sender: CommandSender, enable: Boolean) {
|
||||||
|
Logger.trace(TeamCommand::class, "onAuto(sender: $sender, enable: $enable)")
|
||||||
|
teamManager.autoTeams = enable
|
||||||
|
teamManager.removeAllTeams()
|
||||||
|
if (enable)
|
||||||
|
teamManager.autoTeam()
|
||||||
|
|
||||||
|
sender.sendMessage("${ChatColor.YELLOW}[?] Automatic team creation is now ${if (enable) ("${ChatColor.GREEN}enabled") else "${ChatColor.RED}disabled"}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("option")
|
||||||
|
@Description("Change team options.")
|
||||||
|
@CommandPermission("darena.team.option")
|
||||||
|
fun onOption(sender: CommandSender, team: Team, @Values("@teamOptions") option: String, value: String) {
|
||||||
|
Logger.trace(TeamCommand::class, "onOption(sender: $sender, option: $option, value: $value)")
|
||||||
|
try {
|
||||||
|
when (option.lowercase()) {
|
||||||
|
"color" -> team.color = TextColor.fromCSSHexString(value)!!
|
||||||
|
"prefix" -> team.prefix = value.replace("\"", "")
|
||||||
|
else -> sender.sendMessage("${ChatColor.RED}[!] Option $option does not exist!")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
sender.sendMessage("${ChatColor.RED}[!] Something went wrong while trying to update the value of $option!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("list")
|
||||||
|
@Description("Get a list of all teams and their members.")
|
||||||
|
@CommandPermission("darena.team.list")
|
||||||
|
fun onList(sender: CommandSender) {
|
||||||
|
Logger.trace(TeamCommand::class, "onList(sender: $sender)")
|
||||||
|
//TODO: Make these configurable
|
||||||
|
sender.sendMessage("============[ Teams ]============")
|
||||||
|
for (teamName in teamManager.getAllTeamNames()) {
|
||||||
|
val team = teamManager.getTeamByName(teamName)
|
||||||
|
|
||||||
|
sender.sendMessage(teamManager.getTeamComponent(team))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("points")
|
||||||
|
inner class PointsCommand: BaseCommand() {
|
||||||
|
|
||||||
|
@Subcommand("add")
|
||||||
|
@Description("Add points to a team.")
|
||||||
|
@CommandPermission("darena.points.add")
|
||||||
|
fun onAdd(sender: CommandSender, team: Team, amount: Int) {
|
||||||
|
Logger.trace(PointsCommand::class, "onAdd(sender: $sender, team: ${team.name}, amount: $amount)")
|
||||||
|
pointsManager.givePointsToTeam(team, amount)
|
||||||
|
sender.sendMessage("${ChatColor.YELLOW}[?] Added $amount points to the score of ${team.name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("remove")
|
||||||
|
@Description("Remove points from a team.")
|
||||||
|
@CommandPermission("darena.points.remove")
|
||||||
|
fun onRemove(sender: CommandSender, team: Team, amount: Int) {
|
||||||
|
Logger.trace(PointsCommand::class, "onRemove(sender: $sender, team: ${team.name}, amount: $amount)")
|
||||||
|
pointsManager.givePointsToTeam(team, -amount)
|
||||||
|
sender.sendMessage("${ChatColor.YELLOW}[?] Removed $amount points from the score of ${team.name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcommand("reset")
|
||||||
|
@Description("Reset everyones score.")
|
||||||
|
@CommandPermission("darena.points.reset")
|
||||||
|
fun onReset(sender: CommandSender) {
|
||||||
|
Logger.trace(PointsCommand::class, "onReset(sender: $sender)")
|
||||||
|
pointsManager.clear()
|
||||||
|
sender.sendMessage("${ChatColor.YELLOW}[?] Reset everyone's score")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package nl.kallestruik.darena.events
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.arenas.Arena
|
||||||
|
import nl.kallestruik.darena.types.arena.ProcessedArenaCheckpoint
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.HandlerList
|
||||||
|
import org.bukkit.event.player.PlayerEvent
|
||||||
|
|
||||||
|
class CheckpointReachedEvent(
|
||||||
|
player: Player,
|
||||||
|
val arena: Arena,
|
||||||
|
val checkpoint: ProcessedArenaCheckpoint
|
||||||
|
): PlayerEvent(player) {
|
||||||
|
override fun getHandlers(): HandlerList {
|
||||||
|
return HANDLERS_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val HANDLERS_LIST = HandlerList()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getHandlerList(): HandlerList {
|
||||||
|
return HANDLERS_LIST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package nl.kallestruik.darena.exceptions
|
||||||
|
|
||||||
|
class ArenaCreationException(
|
||||||
|
message: String? = null,
|
||||||
|
cause: Throwable? = null
|
||||||
|
): Exception(message, cause)
|
|
@ -0,0 +1,6 @@
|
||||||
|
package nl.kallestruik.darena.exceptions
|
||||||
|
|
||||||
|
class ArenaEditException(
|
||||||
|
message: String? = null,
|
||||||
|
cause: Throwable? = null
|
||||||
|
): Exception(message, cause)
|
|
@ -0,0 +1,6 @@
|
||||||
|
package nl.kallestruik.darena.exceptions
|
||||||
|
|
||||||
|
class ArenaEndAbortedException(
|
||||||
|
message: String? = null,
|
||||||
|
cause: Throwable? = null
|
||||||
|
): Exception(message, cause)
|
|
@ -0,0 +1,6 @@
|
||||||
|
package nl.kallestruik.darena.exceptions
|
||||||
|
|
||||||
|
class ArenaStartAbortedException(
|
||||||
|
message: String? = null,
|
||||||
|
cause: Throwable? = null
|
||||||
|
): Exception(message, cause)
|
|
@ -0,0 +1,6 @@
|
||||||
|
package nl.kallestruik.darena.exceptions
|
||||||
|
|
||||||
|
class PotionEffectNotFoundException(
|
||||||
|
message: String? = null,
|
||||||
|
cause: Throwable? = null
|
||||||
|
): Exception(message, cause)
|
|
@ -0,0 +1,21 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.block.BlockBreakEvent
|
||||||
|
|
||||||
|
class BlockBreakListener(
|
||||||
|
val arenaManager: ArenaManager
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onBlockBreak(event: BlockBreakEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.NO_TILE_DROPS, event.player)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.player.PlayerMoveEvent
|
||||||
|
|
||||||
|
class BorderListener(
|
||||||
|
private val arenaManager: ArenaManager
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onMove(event: PlayerMoveEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.HORIZONTAL_BORDERS, event.player))
|
||||||
|
return
|
||||||
|
|
||||||
|
if (event.to.y < arenaManager.currentArena!!.session!!.borderBottom.height)
|
||||||
|
event.player.health = 0.0
|
||||||
|
|
||||||
|
if (event.to.y > arenaManager.currentArena!!.session!!.borderTop.height)
|
||||||
|
event.player.health = 0.0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.block.BlockBreakEvent
|
||||||
|
import org.bukkit.event.block.BlockPlaceEvent
|
||||||
|
|
||||||
|
class BuildRestrictionListener(
|
||||||
|
private val arenaManager: ArenaManager,
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onBlockPlace(event: BlockPlaceEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.RESTRICT_BUILDING, event.player))
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!arenaManager.currentArena!!.config.placeAllowed.isAllowed(event.blockPlaced.type)) {
|
||||||
|
event.isCancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onBlockBreak(event: BlockBreakEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.RESTRICT_BUILDING, event.player))
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!arenaManager.currentArena!!.config.breakAllowed.isAllowed(event.block.type)) {
|
||||||
|
event.isCancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.events.CheckpointReachedEvent
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import nl.kallestruik.darena.util.ArenaUtil
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.player.PlayerMoveEvent
|
||||||
|
|
||||||
|
class CheckpointListener(
|
||||||
|
private val arenaManager: ArenaManager,
|
||||||
|
): Listener {
|
||||||
|
@EventHandler
|
||||||
|
fun onMove(event: PlayerMoveEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.CHECKPOINTS, event.player))
|
||||||
|
return
|
||||||
|
|
||||||
|
val arena = arenaManager.currentArena!!
|
||||||
|
val uuid = event.player.uniqueId
|
||||||
|
val reachedCheckpoints = arena.session!!.reachedCheckpoints[uuid] ?: mutableSetOf()
|
||||||
|
|
||||||
|
for (checkpoint in arena.session!!.checkpoints) {
|
||||||
|
if (reachedCheckpoints.contains(checkpoint.label))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (!checkpoint.isInside(event.to))
|
||||||
|
continue
|
||||||
|
|
||||||
|
reachedCheckpoints.add(checkpoint.label)
|
||||||
|
arena.session!!.lastCheckpoint[uuid] = checkpoint
|
||||||
|
arena.session!!.reachedCheckpoints[uuid] = reachedCheckpoints
|
||||||
|
|
||||||
|
arena.pointsSession!!.giveCheckpointPoints(event.player, checkpoint)
|
||||||
|
|
||||||
|
// Call event to let other handlers do further actions with it.
|
||||||
|
Bukkit.getPluginManager().callEvent(CheckpointReachedEvent(event.player, arena, checkpoint))
|
||||||
|
|
||||||
|
if (checkpoint.movePlayer) {
|
||||||
|
checkpoint.resolveSpawnRule()
|
||||||
|
if (checkpoint.spawnRule != null)
|
||||||
|
checkpoint.spawnRule!!.spawnPlayer(event.player, arena.world)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkpoint.isFinish)
|
||||||
|
ArenaUtil.finishPlayer(event.player, arena, arenaManager)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import nl.kallestruik.darena.events.CheckpointReachedEvent
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import nl.kallestruik.darena.util.ArenaUtil
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
|
||||||
|
class CheckpointMessageListener(
|
||||||
|
val arenaManager: ArenaManager
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onCheckpointReached(event: CheckpointReachedEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.CHECKPOINT_MESSAGE, event.player)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val timeSinceStart = System.currentTimeMillis() - event.arena.session!!.startTime
|
||||||
|
|
||||||
|
//TODO: Make this message configurable.
|
||||||
|
event.arena.sendMessage(Component.text("${event.player.name} has reached a checkpoint! [${ArenaUtil.formatTimeMillis(timeSinceStart)}]"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.events.CheckpointReachedEvent
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import org.bukkit.Sound
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
|
||||||
|
class CheckpointSoundListener(
|
||||||
|
val arenaManager: ArenaManager
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onCheckpointReached(event: CheckpointReachedEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.CHECKPOINT_SOUND, event.player)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event.player.let {
|
||||||
|
it.playSound(it.location, Sound.BLOCK_NOTE_BLOCK_BELL, 1.0F, 1.0F)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import nl.kallestruik.darena.managers.EditManager
|
||||||
|
import nl.kallestruik.darena.types.EditSetting
|
||||||
|
import nl.kallestruik.darena.types.arena.ArenaSpawn
|
||||||
|
import nl.kallestruik.dlib.gui.chestGUI
|
||||||
|
import nl.kallestruik.dlib.gui.textInputDialog
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.entity.ArmorStand
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.entity.EntityDamageByEntityEvent
|
||||||
|
import org.bukkit.event.entity.EntityPlaceEvent
|
||||||
|
import org.bukkit.event.player.PlayerInteractAtEntityEvent
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
|
||||||
|
class EditListener(
|
||||||
|
val editManager: EditManager,
|
||||||
|
val plugin: Plugin
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onEntityPlace(event: EntityPlaceEvent) {
|
||||||
|
if (!editManager.shouldProcessEvent(event.player, EditSetting.SPAWN_PLACING))
|
||||||
|
return
|
||||||
|
|
||||||
|
val entity = event.entity
|
||||||
|
|
||||||
|
if (entity !is ArmorStand)
|
||||||
|
return
|
||||||
|
|
||||||
|
val loc = entity.location
|
||||||
|
|
||||||
|
val spawn = ArenaSpawn(loc.x, loc.y, loc.z, loc.yaw, loc.pitch)
|
||||||
|
|
||||||
|
textInputDialog(event.player!!, plugin) {
|
||||||
|
prompt = Component.text("Spawn name?")
|
||||||
|
|
||||||
|
item {
|
||||||
|
material = Material.PAPER
|
||||||
|
amount = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
onFinish { name ->
|
||||||
|
if (editManager.addSpawn(name, spawn)) {
|
||||||
|
event.player?.sendMessage("Added spawn $name")
|
||||||
|
} else {
|
||||||
|
event.player?.sendMessage("Adding spawn failed. Is the name already in use?")
|
||||||
|
}
|
||||||
|
event.entity.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel {
|
||||||
|
event.player?.sendMessage("Canceled adding spawn")
|
||||||
|
event.entity.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onEntityDamage(event: EntityDamageByEntityEvent) {
|
||||||
|
if (event.damager !is Player)
|
||||||
|
return
|
||||||
|
|
||||||
|
val player = event.damager as Player
|
||||||
|
|
||||||
|
if (!editManager.shouldProcessEvent(player, EditSetting.ALWAYS)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.entity is ArmorStand) {
|
||||||
|
entityDamageArmorStand(player, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun entityDamageArmorStand(player: Player, event: EntityDamageByEntityEvent) {
|
||||||
|
if (!event.entity.hasMetadata("ArenaSpawn"))
|
||||||
|
return
|
||||||
|
|
||||||
|
// This cast should be safe since we should be the only ones editing this.
|
||||||
|
val spawnName = event.entity.getMetadata("ArenaSpawn")[0].value() as String
|
||||||
|
|
||||||
|
event.isCancelled = true
|
||||||
|
|
||||||
|
editManager.removeSpawnGUI(player, spawnName)
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onPlayerInteractAtEntity(event: PlayerInteractAtEntityEvent) {
|
||||||
|
if (!editManager.shouldProcessEvent(event.player, EditSetting.ALWAYS)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.rightClicked is ArmorStand) {
|
||||||
|
playerInteractArmorstand(event.player, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun playerInteractArmorstand(player: Player, event: PlayerInteractAtEntityEvent) {
|
||||||
|
if (!event.rightClicked.hasMetadata("ArenaSpawn"))
|
||||||
|
return
|
||||||
|
|
||||||
|
// This cast should be safe since we should be the only ones editing this.
|
||||||
|
val spawnName = event.rightClicked.getMetadata("ArenaSpawn")[0].value() as String
|
||||||
|
|
||||||
|
val spawn = editManager.getSpawn(spawnName)
|
||||||
|
|
||||||
|
event.isCancelled = true
|
||||||
|
|
||||||
|
chestGUI(player, plugin) {
|
||||||
|
title = Component.text("Options for: $spawnName")
|
||||||
|
rows = 3
|
||||||
|
|
||||||
|
//TODO: Any more actions to add to a spawn?
|
||||||
|
|
||||||
|
// 0 1 2 3 4 5 6 7 8
|
||||||
|
// 0 b b b b I b b b b
|
||||||
|
// 1 b x R x x x D x b
|
||||||
|
// 2 b x x x x x x x b
|
||||||
|
// 3 b b b b b b b b b
|
||||||
|
|
||||||
|
border {
|
||||||
|
material = Material.LIGHT_BLUE_STAINED_GLASS_PANE
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
x = 4
|
||||||
|
y = 0
|
||||||
|
|
||||||
|
name = Component.text("Information")
|
||||||
|
material = Material.PAPER
|
||||||
|
|
||||||
|
lore(Component.empty())
|
||||||
|
lore(Component.text("x: ${spawn.x}"))
|
||||||
|
lore(Component.text("y: ${spawn.y}"))
|
||||||
|
lore(Component.text("z: ${spawn.z}"))
|
||||||
|
lore(Component.text("yaw: ${spawn.yaw}"))
|
||||||
|
lore(Component.text("pitch: ${spawn.pitch}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
x = 2
|
||||||
|
y = 1
|
||||||
|
|
||||||
|
name = Component.text("Add to rule")
|
||||||
|
material = Material.LIME_DYE
|
||||||
|
|
||||||
|
onLeftClick {
|
||||||
|
editManager.addSpawnToRuleGUI(player, spawnName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
x = 6
|
||||||
|
y = 1
|
||||||
|
|
||||||
|
name = Component.text("Delete")
|
||||||
|
material = Material.BARRIER
|
||||||
|
|
||||||
|
onLeftClick {
|
||||||
|
editManager.removeSpawnGUI(player, spawnName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.entity.EntityDamageEvent
|
||||||
|
|
||||||
|
class FallListener(
|
||||||
|
private val arenaManager: ArenaManager
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onDamage(event: EntityDamageEvent) {
|
||||||
|
if (event.entity !is Player)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.NO_FALL, event.entity as Player))
|
||||||
|
return
|
||||||
|
|
||||||
|
if (event.cause == EntityDamageEvent.DamageCause.FALL
|
||||||
|
|| event.cause == EntityDamageEvent.DamageCause.FLY_INTO_WALL
|
||||||
|
)
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.managers.ConfigManager
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.entity.Fireball
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.block.Action
|
||||||
|
import org.bukkit.event.entity.EntityDamageByEntityEvent
|
||||||
|
import org.bukkit.event.entity.EntityExplodeEvent
|
||||||
|
import org.bukkit.event.player.PlayerInteractEvent
|
||||||
|
|
||||||
|
class FireballListener(
|
||||||
|
private val arenaManager: ArenaManager,
|
||||||
|
private val configManager: ConfigManager,
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onUse(event: PlayerInteractEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.ITEM_FIREBALL, event.player))
|
||||||
|
return
|
||||||
|
|
||||||
|
if (event.action != Action.RIGHT_CLICK_AIR && event.action != Action.RIGHT_CLICK_BLOCK)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!event.hasItem() || event.item!!.type != Material.FIRE_CHARGE)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (event.player.getCooldown(Material.FIRE_CHARGE) > 0) return
|
||||||
|
|
||||||
|
val fireball = event.player.launchProjectile(Fireball::class.java)
|
||||||
|
fireball.velocity = fireball.velocity.multiply(3)
|
||||||
|
fireball.setIsIncendiary(false)
|
||||||
|
fireball.yield = 4f
|
||||||
|
|
||||||
|
|
||||||
|
event.player.setCooldown(Material.FIRE_CHARGE, configManager.cooldownFireball)
|
||||||
|
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onEntityExplode(event: EntityExplodeEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.ITEM_FIREBALL, null))
|
||||||
|
return
|
||||||
|
|
||||||
|
for (block in event.blockList()) {
|
||||||
|
block.type = Material.AIR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onDamage(event: EntityDamageByEntityEvent) {
|
||||||
|
if (event.entity !is Player)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.ITEM_FIREBALL, event.entity as Player))
|
||||||
|
return
|
||||||
|
|
||||||
|
if (event.damager !is Fireball)
|
||||||
|
return
|
||||||
|
|
||||||
|
event.damage = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onAttack(event: EntityDamageByEntityEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.ITEM_FIREBALL, null))
|
||||||
|
return
|
||||||
|
|
||||||
|
if (event.entity !is Fireball)
|
||||||
|
return
|
||||||
|
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import org.bukkit.Location
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.player.PlayerFishEvent
|
||||||
|
|
||||||
|
class GrapplingHookListener(
|
||||||
|
val arenaManager: ArenaManager,
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onFish(event: PlayerFishEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.ITEM_GRAPPLING_HOOK, event.player)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNearBlock(event.hook.location))
|
||||||
|
return
|
||||||
|
|
||||||
|
val direction = event.hook.location.toVector().subtract(event.player.eyeLocation.toVector()).normalize()
|
||||||
|
|
||||||
|
event.player.velocity = event.player.velocity.add(direction.multiply(3))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isNearBlock(location: Location): Boolean {
|
||||||
|
if (location.subtract(0.0, 1.0, 0.0).block.type != Material.AIR)
|
||||||
|
return true
|
||||||
|
|
||||||
|
if (location.subtract(1.0, 0.0, 0.0).block.type != Material.AIR)
|
||||||
|
return true
|
||||||
|
|
||||||
|
if (location.subtract(0.0, 0.0, 1.0).block.type != Material.AIR)
|
||||||
|
return true
|
||||||
|
|
||||||
|
if (location.add(0.0, 1.0, 0.0).block.type != Material.AIR)
|
||||||
|
return true
|
||||||
|
|
||||||
|
if (location.add(1.0, 0.0, 0.0).block.type != Material.AIR)
|
||||||
|
return true
|
||||||
|
|
||||||
|
if (location.add(0.0, 0.0, 1.0).block.type != Material.AIR)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import nl.kallestruik.darena.util.ArenaUtil
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.attribute.Attribute
|
||||||
|
import org.bukkit.entity.Entity
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.Cancellable
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.entity.EntityDamageByEntityEvent
|
||||||
|
import org.bukkit.event.entity.EntityDamageEvent
|
||||||
|
import org.bukkit.event.entity.PlayerDeathEvent
|
||||||
|
import org.bukkit.event.entity.ProjectileHitEvent
|
||||||
|
|
||||||
|
class KillListener(
|
||||||
|
private val arenaManager: ArenaManager
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onDeath(event: PlayerDeathEvent) {
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.ALWAYS, event.entity))
|
||||||
|
return
|
||||||
|
|
||||||
|
event.isCancelled = true
|
||||||
|
event.entity.health = event.entity.getAttribute(Attribute.GENERIC_MAX_HEALTH)?.value ?: 20.0
|
||||||
|
|
||||||
|
arenaManager.currentArena!!.session!!.deaths.add(event.entity.uniqueId)
|
||||||
|
|
||||||
|
val killerUUID = arenaManager.currentArena!!.session!!.lastDamage[event.entity.uniqueId]
|
||||||
|
val killer = if (killerUUID != null)
|
||||||
|
Bukkit.getPlayer(killerUUID)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
|
if (killer != null)
|
||||||
|
arenaManager.currentArena!!.pointsSession!!.giveKillPoints(killer)
|
||||||
|
|
||||||
|
if (arenaManager.shouldProcessEvent(ArenaFeature.DEATH_MESSAGE, event.entity)) {
|
||||||
|
if (killer == null)
|
||||||
|
arenaManager.currentArena!!.sendMessage(
|
||||||
|
Component.text("${event.entity.name} died")
|
||||||
|
)
|
||||||
|
else
|
||||||
|
arenaManager.currentArena!!.sendMessage(
|
||||||
|
Component.text("${event.entity.name} was killed by ${killer.name}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arenaManager.shouldProcessEvent(ArenaFeature.RESPAWN, event.entity)) {
|
||||||
|
arenaManager.currentArena!!.respawn(event.entity)
|
||||||
|
} else {
|
||||||
|
if (arenaManager.shouldProcessEvent(ArenaFeature.ALWAYS, event.entity))
|
||||||
|
arenaManager.currentArena!!.makeSpectator(event.entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arenaManager.currentArena!!.session!!.participants.size <= arenaManager.currentArena!!.config.endConditions.playersLeft) {
|
||||||
|
arenaManager.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onDamage(event: EntityDamageByEntityEvent) {
|
||||||
|
checkAndUpdateDamager(event.entity, event.damager, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onProjectileHit(event: ProjectileHitEvent) {
|
||||||
|
checkAndUpdateDamager(event.hitEntity, event.entity, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkAndUpdateDamager(hit: Entity?, hitter: Entity, event: Cancellable) {
|
||||||
|
if (hit !is Player)
|
||||||
|
return
|
||||||
|
|
||||||
|
val damager = ArenaUtil.resolveDamager(hitter) ?: return
|
||||||
|
|
||||||
|
if (!arenaManager.shouldProcessEvent(ArenaFeature.PVP, hit)) {
|
||||||
|
if (arenaManager.shouldProcessEvent(ArenaFeature.ALWAYS, hit)) {
|
||||||
|
if (event is EntityDamageByEntityEvent)
|
||||||
|
if (event.cause != EntityDamageEvent.DamageCause.ENTITY_EXPLOSION
|
||||||
|
&& event.cause != EntityDamageEvent.DamageCause.BLOCK_EXPLOSION
|
||||||
|
) {
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hit.uniqueId == damager.uniqueId)
|
||||||
|
return
|
||||||
|
|
||||||
|
arenaManager.currentArena!!.session!!.lastDamage[hit.uniqueId] = damager.uniqueId
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.events.CheckpointReachedEvent
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
|
import nl.kallestruik.darena.util.ArenaUtil
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
|
||||||
|
class SectionListener(
|
||||||
|
val arenaManager: ArenaManager,
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onCheckpointReached(event: CheckpointReachedEvent) {
|
||||||
|
val checkpoint = event.checkpoint
|
||||||
|
val arena = event.arena
|
||||||
|
val uuid = event.player.uniqueId
|
||||||
|
|
||||||
|
// Increment finished section counter for player.
|
||||||
|
arena.session!!.finishedSectionsByPlayer[uuid] = arena.session!!.finishedSectionsByPlayer.getOrDefault(uuid, 0) + 1
|
||||||
|
|
||||||
|
if (checkpoint.spawnPool != null) {
|
||||||
|
val finishedSectionsByPlayer = arena.session!!.finishedSectionsByPlayer[uuid]!!
|
||||||
|
arena.pointsSession!!.givePoolPoints(event.player, checkpoint.spawnPool, finishedSectionsByPlayer)
|
||||||
|
|
||||||
|
Logger.debug(CheckpointListener::class, "finished count: $finishedSectionsByPlayer")
|
||||||
|
Logger.debug(CheckpointListener::class, "Times: ${checkpoint.spawnPool.times}")
|
||||||
|
if (finishedSectionsByPlayer >= checkpoint.spawnPool.times) {
|
||||||
|
ArenaUtil.finishPlayer(event.player, arena, arenaManager)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package nl.kallestruik.darena.listeners
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.managers.TeamManager
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent
|
||||||
|
|
||||||
|
class TeamListener(
|
||||||
|
private val teamManager: TeamManager
|
||||||
|
): Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onPlayerJoined(event: PlayerJoinEvent) {
|
||||||
|
var team = teamManager.getTeamForPlayer(event.player.uniqueId)
|
||||||
|
if (team != null) {
|
||||||
|
team.mcTeam.addEntry(event.player.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!teamManager.autoTeams)
|
||||||
|
return
|
||||||
|
|
||||||
|
Logger.info(TeamListener::class, "Creating automatic team for player: ${event.player.name}")
|
||||||
|
team = teamManager.createTeam(event.player.name)
|
||||||
|
teamManager.addPlayerToTeam(team, event.player.uniqueId)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +1,144 @@
|
||||||
package nl.kallestruik.darena.managers
|
package nl.kallestruik.darena.managers
|
||||||
|
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import nl.kallestruik.darena.arenas.Arena
|
import nl.kallestruik.darena.arenas.Arena
|
||||||
import nl.kallestruik.darena.arenas.ArenaConfig
|
import nl.kallestruik.darena.arenas.ArenaConfig
|
||||||
import nl.kallestruik.darena.util.ArenaUtil
|
import nl.kallestruik.darena.exceptions.ArenaCreationException
|
||||||
|
import nl.kallestruik.darena.exceptions.ArenaEndAbortedException
|
||||||
|
import nl.kallestruik.darena.exceptions.ArenaStartAbortedException
|
||||||
|
import nl.kallestruik.darena.types.ArenaFeature
|
||||||
|
import nl.kallestruik.darena.types.Reloadable
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import nl.kallestruik.dlib.config.ConfigHelper.toConfigSchema
|
||||||
|
import org.bukkit.entity.Player
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
||||||
class ArenaManager(
|
class ArenaManager(
|
||||||
private val arenaUtil: ArenaUtil,
|
private val teamManager: TeamManager,
|
||||||
|
private val pointsManager: PointsManager,
|
||||||
|
private val configManager: ConfigManager,
|
||||||
private val plugin: JavaPlugin,
|
private val plugin: JavaPlugin,
|
||||||
) {
|
private val arenaFolder: File,
|
||||||
private val arenas: MutableList<Arena> = ArrayList()
|
schemaFile: File,
|
||||||
|
): Reloadable {
|
||||||
|
lateinit var editManager: EditManager;
|
||||||
|
private val arenas: MutableMap<String, Arena> = mutableMapOf()
|
||||||
|
var currentArena: Arena? = null
|
||||||
|
|
||||||
fun loadArenas(arenaFolder: File) {
|
override fun reload() {
|
||||||
|
arenas.clear()
|
||||||
|
loadArenas()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
schemaFile.parentFile.mkdirs()
|
||||||
|
schemaFile.writeText(Json.encodeToString(ArenaConfig.serializer().descriptor.toConfigSchema(listOf())))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadArenas() {
|
||||||
|
Logger.trace(ArenaManager::class, "loadArenas()")
|
||||||
|
arenaFolder.mkdirs()
|
||||||
Files.walk(arenaFolder.toPath()).use { walk ->
|
Files.walk(arenaFolder.toPath()).use { walk ->
|
||||||
walk.forEach { path ->
|
walk.forEach { path ->
|
||||||
arenas.add(Arena(
|
if (Files.isDirectory(path))
|
||||||
ArenaConfig.load(path.toFile()),
|
return@forEach
|
||||||
arenaUtil,
|
|
||||||
plugin)
|
if (path.toString().endsWith(".yml")) {
|
||||||
|
Logger.info(ArenaManager::class, "Skipping legacy config at $path")
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.info(ArenaManager::class, "Loading arena from: $path")
|
||||||
|
val config = ArenaConfig.load(path.toFile())
|
||||||
|
|
||||||
|
arenas[config.name] = Arena(
|
||||||
|
config,
|
||||||
|
plugin,
|
||||||
|
configManager,
|
||||||
|
this,
|
||||||
|
teamManager,
|
||||||
|
pointsManager,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(ArenaStartAbortedException::class)
|
||||||
|
fun start(arena: Arena, loadingTimeOverride: Int, spectatorTimeOverride: Int, beforeStartTimeOverride: Int) {
|
||||||
|
Logger.trace(ArenaManager::class, "start(arena: ${arena.config.name}, loadingTimeOverride: $loadingTimeOverride, spectatorTimeOverride: $spectatorTimeOverride, beforeStartTimeOverride: $beforeStartTimeOverride)")
|
||||||
|
|
||||||
|
if (currentArena != null)
|
||||||
|
throw ArenaStartAbortedException("There is currently another arena in progress. Please end that one first.")
|
||||||
|
|
||||||
|
if (editManager.isEditing())
|
||||||
|
throw ArenaStartAbortedException("There is currently an arena being edited. Please finish that first.")
|
||||||
|
|
||||||
|
// Try to start the arena. If something goes wrong clean it up and throw the exception again so the user can be informed.
|
||||||
|
arena.start(loadingTimeOverride, spectatorTimeOverride, beforeStartTimeOverride)
|
||||||
|
|
||||||
|
currentArena = arena
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(ArenaEndAbortedException::class)
|
||||||
|
fun end() {
|
||||||
|
Logger.trace(ArenaManager::class, "end()")
|
||||||
|
if (currentArena == null)
|
||||||
|
throw ArenaEndAbortedException("There is currently no arena in progress.")
|
||||||
|
|
||||||
|
currentArena!!.end()
|
||||||
|
currentArena = null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(ArenaCreationException::class)
|
||||||
|
fun createArena(name: String) {
|
||||||
|
if (arenas.contains(name))
|
||||||
|
throw ArenaCreationException("Arena with name $name already exists!")
|
||||||
|
|
||||||
|
val config = ArenaConfig(name = name)
|
||||||
|
config.file = File(arenaFolder, "$name.json")
|
||||||
|
|
||||||
|
arenas[config.name] = Arena(
|
||||||
|
config,
|
||||||
|
plugin,
|
||||||
|
configManager,
|
||||||
|
this,
|
||||||
|
teamManager,
|
||||||
|
pointsManager,
|
||||||
|
)
|
||||||
|
|
||||||
|
config.save(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveConfig(arena: Arena) {
|
||||||
|
arena.config.save(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getArena(name: String): Arena? {
|
||||||
|
Logger.trace(ArenaManager::class, "getArena(name: $name)")
|
||||||
|
return arenas[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllArenaNames(): Collection<String> {
|
||||||
|
Logger.trace(ArenaManager::class, "getAllArenaNames()")
|
||||||
|
return arenas.keys
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shouldProcessEvent(feature: ArenaFeature, player: Player?): Boolean {
|
||||||
|
if (currentArena == null)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (!currentArena!!.session!!.isInProgress)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (feature != ArenaFeature.ALWAYS && !currentArena!!.hasFeature(feature))
|
||||||
|
return false
|
||||||
|
|
||||||
|
if ((player != null && !currentArena!!.session!!.participants.contains(player)))
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,32 @@
|
||||||
package nl.kallestruik.darena.managers
|
package nl.kallestruik.darena.managers
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.types.Reloadable
|
||||||
|
import nl.kallestruik.darena.types.arena.ArenaLocation
|
||||||
|
import nl.kallestruik.darena.util.ConfigHelper
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class ConfigManager(
|
class ConfigManager(
|
||||||
val configFile: File
|
val configFile: File,
|
||||||
) {
|
): Reloadable {
|
||||||
|
var spawnPos: ArenaLocation = ArenaLocation(0, 100, 0)
|
||||||
|
var spawnWorldName = "world"
|
||||||
|
var cooldownFireball = 30
|
||||||
|
|
||||||
|
|
||||||
|
override fun reload() {
|
||||||
|
load()
|
||||||
|
}
|
||||||
|
|
||||||
fun load() {
|
fun load() {
|
||||||
//TODO: Load config
|
val config = ConfigHelper.getOrCreateConfig(configFile, "template/config.yml")
|
||||||
|
|
||||||
|
if (config.isConfigurationSection("spawn-pos"))
|
||||||
|
spawnPos = ArenaLocation.load(config.getConfigurationSection("spawn-pos")!!)
|
||||||
|
|
||||||
|
if (config.contains("spawn-world"))
|
||||||
|
spawnWorldName = config.getString("spawn-world")!!
|
||||||
|
|
||||||
|
if (config.contains("cooldown-fireball"))
|
||||||
|
cooldownFireball = config.getInt("cooldown-fireball")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,311 @@
|
||||||
|
package nl.kallestruik.darena.managers
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor
|
||||||
|
import nl.kallestruik.darena.arenas.Arena
|
||||||
|
import nl.kallestruik.darena.arenas.world.ArenaWorld
|
||||||
|
import nl.kallestruik.darena.exceptions.ArenaEditException
|
||||||
|
import nl.kallestruik.darena.types.EditSetting
|
||||||
|
import nl.kallestruik.darena.types.EditVisualization
|
||||||
|
import nl.kallestruik.darena.types.arena.ArenaSpawn
|
||||||
|
import nl.kallestruik.darena.types.arena.ArenaSpawnRule
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import nl.kallestruik.darena.util.RenderUtil
|
||||||
|
import nl.kallestruik.dlib.config.ConfigEntryInfo
|
||||||
|
import nl.kallestruik.dlib.config.ConfigHelper
|
||||||
|
import nl.kallestruik.dlib.gui.confirmationDialog
|
||||||
|
import nl.kallestruik.dlib.gui.textInputDialog
|
||||||
|
import org.bukkit.Color
|
||||||
|
import org.bukkit.GameMode
|
||||||
|
import org.bukkit.Location
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.entity.EntityType
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.metadata.FixedMetadataValue
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable
|
||||||
|
import org.bukkit.scheduler.BukkitTask
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class EditManager(
|
||||||
|
private val arenaManager: ArenaManager,
|
||||||
|
private val plugin: Plugin
|
||||||
|
) {
|
||||||
|
private var currentArena: Arena? = null
|
||||||
|
private var currentWorld: ArenaWorld? = null
|
||||||
|
private val currentSettings: MutableMap<UUID, MutableMap<EditSetting, Boolean>> = mutableMapOf()
|
||||||
|
private val currentVisualizations: MutableMap<EditVisualization, Boolean> = mutableMapOf()
|
||||||
|
private var checkpointVisualizationTask: BukkitTask? = null
|
||||||
|
|
||||||
|
@Throws(ArenaEditException::class)
|
||||||
|
fun load(arena: Arena) {
|
||||||
|
Logger.trace(EditManager::class, "load(arena: ${arena.config.name})")
|
||||||
|
|
||||||
|
if (isEditing())
|
||||||
|
throw ArenaEditException("Cant start editing a world when another one is already loaded!")
|
||||||
|
|
||||||
|
if (arenaManager.currentArena != null)
|
||||||
|
throw ArenaEditException("Cant start editing a world when there is an arena in progress!")
|
||||||
|
|
||||||
|
arena.world.reset()
|
||||||
|
currentArena = arena
|
||||||
|
currentWorld = arena.world
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(ArenaEditException::class)
|
||||||
|
fun save() {
|
||||||
|
Logger.trace(EditManager::class, "save()")
|
||||||
|
|
||||||
|
if (!isEditing())
|
||||||
|
throw ArenaEditException("Cant save a world if you aren't editing one!")
|
||||||
|
|
||||||
|
if (isVisualizationEnabled(EditVisualization.SPAWNS)) {
|
||||||
|
removeSpawnVisualizers()
|
||||||
|
}
|
||||||
|
|
||||||
|
currentWorld!!.save()
|
||||||
|
saveConfig()
|
||||||
|
|
||||||
|
if (isVisualizationEnabled(EditVisualization.SPAWNS)) {
|
||||||
|
addSpawnVisualizers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(ArenaEditException::class)
|
||||||
|
fun tp(player: Player) {
|
||||||
|
Logger.trace(EditManager::class, "tp(player: ${player.name})")
|
||||||
|
|
||||||
|
if (!isEditing())
|
||||||
|
throw ArenaEditException("Cant teleport to a world if none is being edited!")
|
||||||
|
|
||||||
|
player.teleport(Location(currentWorld!!.world, 0.5, 100.0, 0.5))
|
||||||
|
player.gameMode = GameMode.CREATIVE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun finish() {
|
||||||
|
Logger.trace(EditManager::class, "finish()")
|
||||||
|
|
||||||
|
if (!isEditing())
|
||||||
|
throw ArenaEditException("Cant stop editing what is not being edited!")
|
||||||
|
|
||||||
|
save()
|
||||||
|
currentWorld!!.empty()
|
||||||
|
currentArena = null
|
||||||
|
currentWorld = null
|
||||||
|
currentSettings.clear()
|
||||||
|
checkpointVisualizationTask?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isEditing(): Boolean {
|
||||||
|
return currentWorld != null
|
||||||
|
}
|
||||||
|
|
||||||
|
// fun getConfigValue(path: String): ConfigEntryInfo {
|
||||||
|
// Logger.trace(EditManager::class, "getConfigValue(path: $path)")
|
||||||
|
//
|
||||||
|
// if (!isEditing())
|
||||||
|
// throw ArenaEditException("You arent editing anything!")
|
||||||
|
//
|
||||||
|
// return ConfigHelper.getInfoByPath(path, currentArena!!.config)
|
||||||
|
// }
|
||||||
|
|
||||||
|
fun toggleFeature(player: Player, setting: EditSetting): Boolean {
|
||||||
|
val settingsMap = currentSettings.getOrPut(player.uniqueId, ::mutableMapOf)
|
||||||
|
val newValue = !(settingsMap[setting] ?: false)
|
||||||
|
settingsMap[setting] = newValue
|
||||||
|
return newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveConfig() {
|
||||||
|
if (!isEditing()) {
|
||||||
|
throw ArenaEditException("You cant save something that has not been changed!")
|
||||||
|
}
|
||||||
|
|
||||||
|
arenaManager.saveConfig(currentArena!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shouldProcessEvent(player: Player?, setting: EditSetting): Boolean {
|
||||||
|
if (currentArena == null)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (!isEditing())
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (player == null)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (setting != EditSetting.ALWAYS && !playerHasFeatureEnabled(player, setting))
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun playerHasFeatureEnabled(player: Player, setting: EditSetting): Boolean {
|
||||||
|
return currentSettings[player.uniqueId]?.get(setting) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addSpawn(name: String, spawn: ArenaSpawn): Boolean {
|
||||||
|
Logger.trace(EditManager::class, "finishSpawn(name: $name, spawn: $spawn)")
|
||||||
|
|
||||||
|
if (!isEditing())
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (name in currentArena!!.config.spawns)
|
||||||
|
return false
|
||||||
|
|
||||||
|
currentArena!!.config.spawns[name] = spawn
|
||||||
|
refreshSpawnVisualizer()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isVisualizationEnabled(visualization: EditVisualization): Boolean {
|
||||||
|
return currentVisualizations[visualization] ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleSpawnVisibility() {
|
||||||
|
if (!isEditing()) {
|
||||||
|
throw ArenaEditException("You cant visualize what isn't there!")
|
||||||
|
}
|
||||||
|
|
||||||
|
val newValue = !isVisualizationEnabled(EditVisualization.SPAWNS)
|
||||||
|
currentVisualizations[EditVisualization.SPAWNS] = newValue
|
||||||
|
|
||||||
|
if (newValue) {
|
||||||
|
addSpawnVisualizers()
|
||||||
|
} else {
|
||||||
|
removeSpawnVisualizers()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addSpawnVisualizers() {
|
||||||
|
val world = currentWorld!!.world
|
||||||
|
for (entry in currentArena!!.config.spawns) {
|
||||||
|
val entity = world.spawnEntity(entry.value.toLocation(world), EntityType.ARMOR_STAND)
|
||||||
|
entity.setGravity(false)
|
||||||
|
entity.isCustomNameVisible = true
|
||||||
|
entity.customName(Component.text("SPAWN: ${entry.key}"))
|
||||||
|
entity.setMetadata("ArenaSpawn", FixedMetadataValue(plugin, entry.key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeSpawnVisualizers() {
|
||||||
|
for (entity in currentWorld!!.world.entities) {
|
||||||
|
if (!entity.hasMetadata("ArenaSpawn"))
|
||||||
|
continue
|
||||||
|
|
||||||
|
entity.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refreshSpawnVisualizer() {
|
||||||
|
if (!isEditing())
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!isVisualizationEnabled(EditVisualization.SPAWNS))
|
||||||
|
return
|
||||||
|
|
||||||
|
removeSpawnVisualizers()
|
||||||
|
addSpawnVisualizers()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeSpawn(spawnName: String) {
|
||||||
|
if (!isEditing()) {
|
||||||
|
throw ArenaEditException("Cant remove a spawn if you arent editing an arena!")
|
||||||
|
}
|
||||||
|
|
||||||
|
currentArena!!.config.spawns.remove(spawnName)
|
||||||
|
refreshSpawnVisualizer()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleCheckpointVisibility() {
|
||||||
|
if (!isEditing()) {
|
||||||
|
throw ArenaEditException("You cant visualize what isn't there!")
|
||||||
|
}
|
||||||
|
|
||||||
|
val newValue = !isVisualizationEnabled(EditVisualization.CHECKPOINTS)
|
||||||
|
currentVisualizations[EditVisualization.CHECKPOINTS] = newValue
|
||||||
|
if (newValue) {
|
||||||
|
checkpointVisualizationTask = CheckpointVisualizationRunnable(currentArena!!).runTaskTimer(plugin, 0, 5)
|
||||||
|
} else {
|
||||||
|
checkpointVisualizationTask?.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addSpawnToRuleGUI(player: Player, spawnName: String) {
|
||||||
|
if (!isEditing())
|
||||||
|
throw ArenaEditException("You arent editing an arena!")
|
||||||
|
|
||||||
|
if (spawnName !in currentArena!!.config.spawns)
|
||||||
|
throw ArenaEditException("That spawn does not exist!")
|
||||||
|
|
||||||
|
textInputDialog(player, plugin) {
|
||||||
|
prompt = Component.text("Rule name")
|
||||||
|
item {
|
||||||
|
material = Material.LIME_DYE
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel {
|
||||||
|
player.sendMessage(Component.text("Canceled adding $spawnName to a rule."))
|
||||||
|
}
|
||||||
|
|
||||||
|
onFinish { name ->
|
||||||
|
val rule: ArenaSpawnRule
|
||||||
|
if (name in currentArena!!.config.spawnRules) {
|
||||||
|
rule = currentArena!!.config.spawnRules[name]!!
|
||||||
|
|
||||||
|
player.sendMessage(Component.text("Added $spawnName to rule $name."))
|
||||||
|
} else {
|
||||||
|
rule = ArenaSpawnRule()
|
||||||
|
|
||||||
|
player.sendMessage(Component.text("Created new rule $name and added $spawnName to it."))
|
||||||
|
}
|
||||||
|
|
||||||
|
rule.spawns.add(spawnName)
|
||||||
|
currentArena!!.config.spawnRules[name] = rule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSpawn(spawnName: String): ArenaSpawn {
|
||||||
|
if (!isEditing())
|
||||||
|
throw ArenaEditException("You cant get a spawn when you arent editing an arena!")
|
||||||
|
|
||||||
|
return currentArena!!.config.spawns[spawnName]
|
||||||
|
?: throw ArenaEditException("That spawn does not exist!")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeSpawnGUI(player: Player, spawnName: String) {
|
||||||
|
confirmationDialog(player, plugin) {
|
||||||
|
title = Component.text("Remove spawn?")
|
||||||
|
description = Component
|
||||||
|
.text("Are you sure that you want to remove this spawn?")
|
||||||
|
.color(NamedTextColor.RED)
|
||||||
|
|
||||||
|
onConfirm {
|
||||||
|
removeSpawn(spawnName)
|
||||||
|
refreshSpawnVisualizer()
|
||||||
|
player.sendMessage(Component.text("Spawn $spawnName removed!"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CheckpointVisualizationRunnable(
|
||||||
|
val arena: Arena,
|
||||||
|
): BukkitRunnable() {
|
||||||
|
override fun run() {
|
||||||
|
for (checkpoint in arena.config.checkpoints) {
|
||||||
|
val lower = checkpoint.lower
|
||||||
|
val upper = checkpoint.upper
|
||||||
|
|
||||||
|
RenderUtil.drawAABB(
|
||||||
|
arena.world.world,
|
||||||
|
lower.x, lower.y, lower.z,
|
||||||
|
upper.x, upper.y, upper.z,
|
||||||
|
Color.AQUA
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package nl.kallestruik.darena.managers
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.types.Game
|
||||||
|
import nl.kallestruik.darena.types.Reloadable
|
||||||
|
import nl.kallestruik.darena.util.ConfigHelper
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class GameManager(
|
||||||
|
val arenaManager: ArenaManager,
|
||||||
|
val gameFile: File,
|
||||||
|
): Reloadable {
|
||||||
|
val games = mutableMapOf<String,Game>()
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
val config = ConfigHelper.getOrCreateConfig(gameFile, "template/games.yml")
|
||||||
|
|
||||||
|
for (gameName in config.getKeys(false)) {
|
||||||
|
val game = Game(gameName)
|
||||||
|
for (arenaName in config.getStringList("$gameName.arenas")) {
|
||||||
|
val arena = arenaManager.getArena(arenaName)
|
||||||
|
if (arena == null) {
|
||||||
|
Logger.warn(GameManager::class, "Arena with name $arenaName is used in games.yml but not defined!")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
game.arenas.add(arena)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.contains("$gameName.description"))
|
||||||
|
game.description = config.getString("$gameName.description")!!
|
||||||
|
|
||||||
|
games[gameName] = game
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reload() {
|
||||||
|
games.clear()
|
||||||
|
load()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,16 @@
|
||||||
package nl.kallestruik.darena.managers
|
package nl.kallestruik.darena.managers
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor
|
||||||
import nl.kallestruik.darena.exceptions.TeamNotFoundException
|
import nl.kallestruik.darena.exceptions.TeamNotFoundException
|
||||||
import nl.kallestruik.darena.types.Team
|
import nl.kallestruik.darena.types.Team
|
||||||
|
import nl.kallestruik.darena.util.ConfigHelper
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.HashMap
|
|
||||||
|
|
||||||
class PointsManager(
|
class PointsManager(
|
||||||
val teamManager: TeamManager,
|
val teamManager: TeamManager,
|
||||||
|
@ -13,24 +19,59 @@ class PointsManager(
|
||||||
private val points: MutableMap<Team, Int> = HashMap()
|
private val points: MutableMap<Team, Int> = HashMap()
|
||||||
|
|
||||||
fun load() {
|
fun load() {
|
||||||
//TODO: Load points from file.
|
val config = ConfigHelper.getOrCreateConfig(pointsFile, "template/points.yml")
|
||||||
|
if (config.isConfigurationSection("teams")) {
|
||||||
|
for (teamName in config.getConfigurationSection("teams")!!.getKeys(false)) {
|
||||||
|
try {
|
||||||
|
val team = teamManager.getTeamByName(teamName)
|
||||||
|
points[team] = config.getInt("teams.$teamName")
|
||||||
|
} catch (e: TeamNotFoundException) {
|
||||||
|
Logger.warn(PointsManager::class, "Team $teamName is present in points.yml but does not exist ignoring.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateTeamSuffixForAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save() {
|
||||||
|
val config = YamlConfiguration()
|
||||||
|
val teamsSection = config.createSection("teams")
|
||||||
|
for (entry in points) {
|
||||||
|
teamsSection.set(entry.key.name, entry.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.save(pointsFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(TeamNotFoundException::class)
|
@Throws(TeamNotFoundException::class)
|
||||||
fun givePointsToPlayer(uuid: UUID, amount: Int, reason: String) {
|
fun givePointsToPlayer(uuid: UUID, amount: Int) {
|
||||||
val team = teamManager.getTeamForPlayer(uuid)
|
val team = teamManager.getTeamForPlayer(uuid) ?: throw TeamNotFoundException()
|
||||||
|
|
||||||
givePointsToTeam(team, amount, reason)
|
givePointsToTeam(team, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun givePointsToTeam(team: Team, amount: Int, reason: String) {
|
fun givePointsToTeam(team: Team, amount: Int) {
|
||||||
points[team] = (points[team]?: 0) + amount
|
points[team] = (points[team] ?: 0) + amount
|
||||||
//TODO: Send message with reason to team members (and everyone else?)
|
updateTeamSuffix(team)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateTeamSuffix(team: Team) {
|
||||||
|
team.mcTeam.suffix(
|
||||||
|
Component
|
||||||
|
.text(" [${getPointsForTeam(team)}]")
|
||||||
|
.color(NamedTextColor.AQUA)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateTeamSuffixForAll() {
|
||||||
|
for (team in teamManager.getAllTeams()) {
|
||||||
|
updateTeamSuffix(team)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(TeamNotFoundException::class)
|
@Throws(TeamNotFoundException::class)
|
||||||
fun getPointsForPlayer(uuid: UUID): Int {
|
fun getPointsForPlayer(uuid: UUID): Int {
|
||||||
val team = teamManager.getTeamForPlayer(uuid)
|
val team = teamManager.getTeamForPlayer(uuid) ?: throw TeamNotFoundException()
|
||||||
|
|
||||||
return getPointsForTeam(team)
|
return getPointsForTeam(team)
|
||||||
}
|
}
|
||||||
|
@ -39,5 +80,31 @@ class PointsManager(
|
||||||
return points[team] ?: 0
|
return points[team] ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendLeaderboard(sender: CommandSender) {
|
||||||
|
val sortedMap = points.toList().sortedByDescending { (_, v) -> v }.toMap()
|
||||||
|
|
||||||
|
//TODO: Make these message configurable
|
||||||
|
sender.sendMessage(Component.text("============[ Leaderboard ]============"))
|
||||||
|
var i = 1
|
||||||
|
for (entry in sortedMap) {
|
||||||
|
var teamMembers: Component = Component.empty()
|
||||||
|
for (uuid in teamManager.getTeamMembers(entry.key)) {
|
||||||
|
val player = Bukkit.getPlayer(uuid)
|
||||||
|
teamMembers = teamMembers.append(Component.text("${player?.name}\n"))
|
||||||
|
}
|
||||||
|
sender.sendMessage(
|
||||||
|
Component.empty()
|
||||||
|
.append(Component.text("$i. "))
|
||||||
|
.append(teamManager.getTeamComponent(entry.key))
|
||||||
|
.append(Component.text(": "))
|
||||||
|
.append(Component.text(entry.value))
|
||||||
|
)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
points.clear()
|
||||||
|
updateTeamSuffixForAll()
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,16 @@
|
||||||
package nl.kallestruik.darena.managers
|
package nl.kallestruik.darena.managers
|
||||||
|
|
||||||
import net.kyori.adventure.text.format.TextColor
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.event.HoverEvent
|
||||||
import nl.kallestruik.darena.exceptions.TeamExistsException
|
import nl.kallestruik.darena.exceptions.TeamExistsException
|
||||||
import nl.kallestruik.darena.exceptions.TeamNotFoundException
|
import nl.kallestruik.darena.exceptions.TeamNotFoundException
|
||||||
import nl.kallestruik.darena.types.Team
|
import nl.kallestruik.darena.types.Team
|
||||||
|
import nl.kallestruik.darena.util.ConfigHelper
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
import kotlin.collections.HashMap
|
|
||||||
|
|
||||||
class TeamManager(
|
class TeamManager(
|
||||||
val teamsFile: File
|
val teamsFile: File
|
||||||
|
@ -15,37 +18,91 @@ class TeamManager(
|
||||||
private val teams: MutableMap<String, Team> = HashMap()
|
private val teams: MutableMap<String, Team> = HashMap()
|
||||||
private val teamMembers: MutableMap<Team, MutableList<UUID>> = HashMap()
|
private val teamMembers: MutableMap<Team, MutableList<UUID>> = HashMap()
|
||||||
private val teamForPlayer: MutableMap<UUID, Team> = HashMap()
|
private val teamForPlayer: MutableMap<UUID, Team> = HashMap()
|
||||||
|
var autoTeams: Boolean = true
|
||||||
|
|
||||||
fun load() {
|
fun load() {
|
||||||
//TODO: Load teams from file
|
// Make sure all lists are empty so we don't have leftovers.
|
||||||
|
teams.clear()
|
||||||
|
teamMembers.clear()
|
||||||
|
teamForPlayer.clear()
|
||||||
|
|
||||||
|
val config = ConfigHelper.getOrCreateConfig(teamsFile, "template/teams.yml")
|
||||||
|
autoTeams = config.getBoolean("auto-teams")
|
||||||
|
if (config.isConfigurationSection("teams")) {
|
||||||
|
for (teamName in config.getConfigurationSection("teams")!!.getKeys(false)) {
|
||||||
|
Logger.debug(TeamManager::class, "Loading team $teamName")
|
||||||
|
val team = Team.fromConfig(config.getConfigurationSection("teams.$teamName")!!)
|
||||||
|
teams[teamName] = team
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.contains("members")) {
|
||||||
|
for (memberString in config.getStringList("members")) {
|
||||||
|
val parts = memberString.split(":")
|
||||||
|
val uuid = UUID.fromString(parts[0])
|
||||||
|
val teamName = parts[1]
|
||||||
|
addPlayerToTeam(teamName, uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save() {
|
||||||
|
val config = YamlConfiguration()
|
||||||
|
config.set("auto-teams", autoTeams)
|
||||||
|
|
||||||
|
val teamSection = config.createSection("teams")
|
||||||
|
for (team in teams.values) {
|
||||||
|
team.save(teamSection)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.set("members", teamForPlayer.map {
|
||||||
|
"${it.key}:${it.value.name}"
|
||||||
|
})
|
||||||
|
|
||||||
|
config.save(teamsFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(TeamExistsException::class)
|
@Throws(TeamExistsException::class)
|
||||||
fun createTeam(teamName: String) {
|
fun createTeam(teamName: String): Team {
|
||||||
if (teams.containsKey(teamName))
|
if (teams.containsKey(teamName))
|
||||||
throw TeamExistsException("The team $teamName already exists!")
|
throw TeamExistsException("The team $teamName already exists!")
|
||||||
|
|
||||||
teams[teamName] = Team(teamName)
|
val team = Team(teamName)
|
||||||
|
teams[teamName] = team
|
||||||
|
return team
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(TeamNotFoundException::class)
|
fun removeTeam(team: Team) {
|
||||||
fun setTeamColor(teamName: String, color: TextColor) {
|
teams.remove(team.name)
|
||||||
val team = getTeamByName(teamName)
|
for (entry in teamForPlayer) {
|
||||||
|
if (entry.value == team) {
|
||||||
team.color = color
|
removePlayerFromTeam(entry.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
team.mcTeam.unregister()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(TeamNotFoundException::class)
|
@Throws(TeamNotFoundException::class)
|
||||||
fun addPlayerToTeam(teamName: String, uuid: UUID) {
|
fun addPlayerToTeam(teamName: String, uuid: UUID) {
|
||||||
|
Logger.trace(TeamManager::class, "addPlayerToTeam(teamName: $teamName, uuid: $uuid)")
|
||||||
val team = getTeamByName(teamName)
|
val team = getTeamByName(teamName)
|
||||||
|
|
||||||
|
addPlayerToTeam(team, uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addPlayerToTeam(team: Team, uuid: UUID) {
|
||||||
|
Logger.trace(TeamManager::class, "addPlayerToTeam(team: ${team.name}, uuid: $uuid)")
|
||||||
teamMembers.getOrPut(team) { ArrayList() }.add(uuid)
|
teamMembers.getOrPut(team) { ArrayList() }.add(uuid)
|
||||||
teamForPlayer[uuid] = team
|
teamForPlayer[uuid] = team
|
||||||
|
|
||||||
|
val player = Bukkit.getPlayer(uuid)
|
||||||
|
if (player != null)
|
||||||
|
team.mcTeam.addEntry(player.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(TeamNotFoundException::class)
|
@Throws(TeamNotFoundException::class)
|
||||||
fun getTeamForPlayer(uuid: UUID): Team {
|
fun getTeamForPlayer(uuid: UUID): Team? {
|
||||||
return teamForPlayer[uuid] ?: throw TeamNotFoundException("Player with uuid $uuid is not part of a team!")
|
return teamForPlayer[uuid]
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(TeamNotFoundException::class)
|
@Throws(TeamNotFoundException::class)
|
||||||
|
@ -56,6 +113,51 @@ class TeamManager(
|
||||||
@Throws(TeamNotFoundException::class)
|
@Throws(TeamNotFoundException::class)
|
||||||
fun getTeamMembers(teamName: String): List<UUID> {
|
fun getTeamMembers(teamName: String): List<UUID> {
|
||||||
val team = getTeamByName(teamName)
|
val team = getTeamByName(teamName)
|
||||||
|
return getTeamMembers(team)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTeamMembers(team: Team): List<UUID> {
|
||||||
return teamMembers[team] ?: return emptyList()
|
return teamMembers[team] ?: return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun removeAllTeams(): Int {
|
||||||
|
val count = teams.size
|
||||||
|
for (team in getAllTeams()) {
|
||||||
|
removeTeam(team)
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
fun autoTeam() {
|
||||||
|
for (player in Bukkit.getOnlinePlayers()) {
|
||||||
|
val team = createTeam(player.name)
|
||||||
|
addPlayerToTeam(team, player.uniqueId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removePlayerFromTeam(uuid: UUID) {
|
||||||
|
val team = getTeamForPlayer(uuid)
|
||||||
|
teamMembers[team]?.remove(uuid)
|
||||||
|
teamForPlayer.remove(uuid)
|
||||||
|
team?.mcTeam?.removeEntry(Bukkit.getPlayer(uuid)!!.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllTeamNames() = teams.keys
|
||||||
|
|
||||||
|
fun getAllTeams() = teams.values
|
||||||
|
|
||||||
|
fun getMemberTooltip(team: Team): HoverEvent<Component> {
|
||||||
|
var hoverComponent = Component.text("Members:")
|
||||||
|
getTeamMembers(team).forEach {
|
||||||
|
hoverComponent = hoverComponent.append(Component.text("\n${Bukkit.getOfflinePlayer(it).name}"))
|
||||||
|
}
|
||||||
|
return hoverComponent.asHoverEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTeamComponent(team: Team): Component {
|
||||||
|
return Component
|
||||||
|
.text(team.name)
|
||||||
|
.color(team.color)
|
||||||
|
.hoverEvent(getMemberTooltip(team))
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package nl.kallestruik.darena.types
|
||||||
|
|
||||||
|
enum class ArenaFeature {
|
||||||
|
ALWAYS,
|
||||||
|
RESPAWN,
|
||||||
|
DEATH_MESSAGE,
|
||||||
|
CHECKPOINTS,
|
||||||
|
CHECKPOINT_SOUND,
|
||||||
|
CHECKPOINT_MESSAGE,
|
||||||
|
PVP,
|
||||||
|
NO_FALL,
|
||||||
|
SHOW_TIMER,
|
||||||
|
RESTRICT_BUILDING,
|
||||||
|
GAMEMODE_ADVENTURE,
|
||||||
|
HORIZONTAL_BORDERS,
|
||||||
|
NO_TILE_DROPS,
|
||||||
|
ITEM_FIREBALL,
|
||||||
|
ITEM_GRAPPLING_HOOK;
|
||||||
|
}
|
|
@ -1,4 +0,0 @@
|
||||||
package nl.kallestruik.darena.types
|
|
||||||
|
|
||||||
interface ConfigListLoadable<T> {
|
|
||||||
fun loadFromList(key: String, value: Any): T
|
|
|
@ -1,7 +0,0 @@
|
||||||
package nl.kallestruik.darena.types
|
|
||||||
|
|
||||||
interface ConfigListSaveable {
|
|
||||||
fun getKey(): String
|
|
||||||
|
|
||||||
fun getValue(): Any
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package nl.kallestruik.darena.types
|
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
|
||||||
|
|
||||||
interface ConfigLoadable<T> {
|
|
||||||
fun load(section: ConfigurationSection): T
|
|
|
@ -1,7 +0,0 @@
|
||||||
package nl.kallestruik.darena.types
|
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
|
||||||
|
|
||||||
interface ConfigSaveable {
|
|
||||||
fun save(section: ConfigurationSection)
|
|
||||||
}
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package nl.kallestruik.darena.types
|
||||||
|
|
||||||
|
enum class EditSetting {
|
||||||
|
ALWAYS,
|
||||||
|
SPAWN_PLACING
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package nl.kallestruik.darena.types
|
||||||
|
|
||||||
|
enum class EditVisualization {
|
||||||
|
SPAWNS,
|
||||||
|
CHECKPOINTS
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package nl.kallestruik.darena.types
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.arenas.Arena
|
||||||
|
|
||||||
|
data class Game(
|
||||||
|
var name: String,
|
||||||
|
val arenas: MutableList<Arena> = mutableListOf(),
|
||||||
|
var description: String = "",
|
||||||
|
)
|
|
@ -0,0 +1,6 @@
|
||||||
|
package nl.kallestruik.darena.types
|
||||||
|
|
||||||
|
interface Reloadable {
|
||||||
|
|
||||||
|
fun reload()
|
||||||
|
}
|
|
@ -1,8 +1,47 @@
|
||||||
package nl.kallestruik.darena.types
|
package nl.kallestruik.darena.types
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
import net.kyori.adventure.text.format.TextColor
|
import net.kyori.adventure.text.format.TextColor
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.configuration.ConfigurationSection
|
||||||
|
|
||||||
data class Team(
|
data class Team(
|
||||||
val name: String,
|
val name: String
|
||||||
|
) {
|
||||||
|
val mcTeam: org.bukkit.scoreboard.Team = Bukkit.getScoreboardManager().mainScoreboard.getTeam(name)
|
||||||
|
?: Bukkit.getScoreboardManager().mainScoreboard.registerNewTeam(name)
|
||||||
|
|
||||||
|
init {
|
||||||
|
mcTeam.setOption(org.bukkit.scoreboard.Team.Option.COLLISION_RULE, org.bukkit.scoreboard.Team.OptionStatus.NEVER)
|
||||||
|
}
|
||||||
|
|
||||||
var color: TextColor = TextColor.color(0xFFFFFF)
|
var color: TextColor = TextColor.color(0xFFFFFF)
|
||||||
)
|
set(value) {
|
||||||
|
field = value
|
||||||
|
mcTeam.prefix(mcTeam.prefix().color(value))
|
||||||
|
}
|
||||||
|
var prefix: String = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
mcTeam.prefix(Component.text("$value ").color(color))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save(section: ConfigurationSection) {
|
||||||
|
val teamSection = section.createSection(name)
|
||||||
|
teamSection.set("color", color.asHexString())
|
||||||
|
teamSection.set("prefix", prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromConfig(section: ConfigurationSection): Team {
|
||||||
|
val team = Team(
|
||||||
|
section.name
|
||||||
|
)
|
||||||
|
team.color = TextColor.fromCSSHexString(section.getString("color")!!)!!
|
||||||
|
team.prefix = section.getString("prefix")!!
|
||||||
|
|
||||||
|
return team
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
|
import org.bukkit.Material
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ArenaAllowList(
|
||||||
|
@Description([
|
||||||
|
"The list of rules. These rules support full regular expression. For example .*_wool will match all colors of wool."])
|
||||||
|
var ruleList: MutableList<ArenaAllowRule> = mutableListOf(),
|
||||||
|
) {
|
||||||
|
fun isAllowed(material: Material): Boolean {
|
||||||
|
Logger.trace(ArenaAllowList::class, "isAllowed(material: $material)")
|
||||||
|
for (rule in ruleList) {
|
||||||
|
Logger.debug(ArenaAllowList::class, "Checking rule: $rule")
|
||||||
|
return when (rule.match(material.name.lowercase())) {
|
||||||
|
AllowRuleResult.NO_MATCH -> continue
|
||||||
|
AllowRuleResult.ALLOWED -> true
|
||||||
|
AllowRuleResult.BLOCKED -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import nl.kallestruik.dlib.config.annotations.DefaultString
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ArenaAllowRule(
|
||||||
|
@Serializable(RegexSerializer::class)
|
||||||
|
var rule: Regex,
|
||||||
|
@DefaultString("BLOCK")
|
||||||
|
var type: Type,
|
||||||
|
) {
|
||||||
|
fun match(toMatch: String): AllowRuleResult {
|
||||||
|
Logger.trace(ArenaAllowRule::class, "match(toMatch: $toMatch)")
|
||||||
|
if (rule.matches(toMatch)) {
|
||||||
|
Logger.debug(ArenaAllowRule::class, "Rule matches")
|
||||||
|
return when (type) {
|
||||||
|
Type.ALLOW -> AllowRuleResult.ALLOWED
|
||||||
|
Type.BLOCK -> AllowRuleResult.BLOCKED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AllowRuleResult.NO_MATCH
|
||||||
|
}
|
||||||
|
|
||||||
|
object RegexSerializer: KSerializer<Regex> {
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Regex", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: Regex) {
|
||||||
|
encoder.encodeString(value.pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): Regex {
|
||||||
|
return Regex(decoder.decodeString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
ALLOW,
|
||||||
|
BLOCK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class AllowRuleResult {
|
||||||
|
NO_MATCH,
|
||||||
|
ALLOWED,
|
||||||
|
BLOCKED,
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.kallestruik.darena.arenas.world.ArenaWorld
|
||||||
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
|
import org.bukkit.Material
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ArenaBlockRemoveZone(
|
||||||
|
@Description(["The lower corner of the area to remove."])
|
||||||
|
val lower: ArenaLocation,
|
||||||
|
@Description(["The upper corner of the area to remove."])
|
||||||
|
val upper: ArenaLocation,
|
||||||
|
) {
|
||||||
|
fun removeBlocks(world: ArenaWorld) {
|
||||||
|
for (x in lower.x.until(upper.x + 1)) {
|
||||||
|
for (y in lower.y.until(upper.y + 1)) {
|
||||||
|
for (z in lower.z.until(upper.z + 1)) {
|
||||||
|
world.world.getBlockAt(x, y, z).type = Material.AIR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.kallestruik.darena.types.border.AbstractBorderNode
|
||||||
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ArenaBorders(
|
||||||
|
@Description(["The vertical border of the world. This is the normal minecraft border."])
|
||||||
|
val vertical: List<AbstractBorderNode> = listOf(),
|
||||||
|
@Description(["A horizontal border that kills everyone that is above it. This border is indicated using particle effects."])
|
||||||
|
val top: List<AbstractBorderNode> = listOf(),
|
||||||
|
@Description(["A horizontal border that kills everyone that is below it. This border is indicated using particle effects."])
|
||||||
|
val bottom: List<AbstractBorderNode> = listOf(),
|
||||||
|
)
|
|
@ -1,35 +1,30 @@
|
||||||
package nl.kallestruik.darena.types.arena
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
import kotlinx.serialization.SerialName
|
||||||
import javax.naming.ConfigurationException
|
import kotlinx.serialization.Serializable
|
||||||
import nl.kallestruik.darena.types.ConfigSaveable
|
import nl.kallestruik.dlib.config.annotations.DefaultBoolean
|
||||||
import nl.kallestruik.darena.types.ConfigLoadable
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
import nl.kallestruik.darena.util.ConfigHelper
|
|
||||||
import nl.kallestruik.darena.util.Logger
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ArenaCheckpoint(
|
data class ArenaCheckpoint(
|
||||||
|
@Description(["The name of the checkpoint."])
|
||||||
val label: String,
|
val label: String,
|
||||||
|
@Description(["The lower corner of the checkpoint area."])
|
||||||
val lower: ArenaLocation,
|
val lower: ArenaLocation,
|
||||||
|
@Description(["The upper corner of the checkpoint area."])
|
||||||
val upper: ArenaLocation,
|
val upper: ArenaLocation,
|
||||||
val spawn: String,
|
@SerialName("spawn-rule")
|
||||||
): ConfigSaveable {
|
@Description(["The spawn rule to use for this checkpoint when respawning or moving players."])
|
||||||
override fun save(section: ConfigurationSection) {
|
var spawnRule: String = "",
|
||||||
Logger.trace(ArenaCheckpoint::class, "save(section: ${section.currentPath})")
|
@SerialName("spawn-pool")
|
||||||
section.set("label", label)
|
@Description(["The spawn pool to use for this checkpoint when respawning or moving players."])
|
||||||
lower.save(ConfigHelper.getOrCreateSection(section, "lower"))
|
val spawnPool: String = "",
|
||||||
upper.save(ConfigHelper.getOrCreateSection(section, "upper"))
|
@SerialName("move-player")
|
||||||
section.set("spawn", spawn)
|
@Description(["Whether or not to teleport players to the spawn-rule/spawn-pool when they reach the checkpoint."])
|
||||||
}
|
@DefaultBoolean(false)
|
||||||
|
val movePlayer: Boolean = false,
|
||||||
companion object: ConfigLoadable<ArenaCheckpoint> {
|
@SerialName("is-finish")
|
||||||
override fun load(section: ConfigurationSection): ArenaCheckpoint {
|
@Description(["Whether or not this checkpoint counts as a finish. If it does players will be placed into spectator mode upon reaching it."])
|
||||||
Logger.trace(ArenaCheckpoint::class, "load(section: ${section.currentPath})")
|
@DefaultBoolean(false)
|
||||||
return ArenaCheckpoint(
|
val isFinish: Boolean = false,
|
||||||
section.name,
|
)
|
||||||
ArenaLocation.load(section.getConfigurationSection("lower")!!),
|
|
||||||
ArenaLocation.load(section.getConfigurationSection("upper")!!),
|
|
||||||
section.getString("spawn") ?: throw ConfigurationException("Could not find required field spawn in '${section.currentPath}'")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.kallestruik.dlib.config.annotations.DefaultInt
|
||||||
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
|
import nl.kallestruik.dlib.config.annotations.MinInt
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ArenaCountdown(
|
||||||
|
@SerialName("loading")
|
||||||
|
@Description(["The time to count down before teleport players into the map for spectating."])
|
||||||
|
@MinInt(0)
|
||||||
|
@DefaultInt(10)
|
||||||
|
var loadingTime: Int = 10,
|
||||||
|
@SerialName("spectator")
|
||||||
|
@Description(["The time players have in spectator mode before being moved to the staging area."])
|
||||||
|
@MinInt(0)
|
||||||
|
@DefaultInt(10)
|
||||||
|
var spectatorTime: Int = 10,
|
||||||
|
@SerialName("before-start")
|
||||||
|
@Description(["The time to count down in the staging area before removing the area defined in the block-remove-zone section."])
|
||||||
|
@MinInt(0)
|
||||||
|
@DefaultInt(10)
|
||||||
|
var beforeStartTime: Int = 10,
|
||||||
|
)
|
|
@ -1,32 +1,28 @@
|
||||||
package nl.kallestruik.darena.types.arena
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
import kotlinx.serialization.Serializable
|
||||||
import org.bukkit.configuration.InvalidConfigurationException
|
|
||||||
import nl.kallestruik.darena.types.ConfigListSaveable
|
|
||||||
import nl.kallestruik.darena.types.ConfigListLoadable
|
|
||||||
import nl.kallestruik.darena.util.Logger
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import nl.kallestruik.dlib.config.annotations.DefaultInt
|
||||||
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
|
import nl.kallestruik.dlib.config.annotations.MinInt
|
||||||
|
import org.bukkit.NamespacedKey
|
||||||
|
import org.bukkit.enchantments.Enchantment
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ArenaEnchantment(
|
data class ArenaEnchantment(
|
||||||
|
@Description(["The name of the enchantment.",
|
||||||
|
"", "Valid values: The id of the enchantment as shown in the /enchant command without the minecraft:",
|
||||||
|
"For example: sharpness, knockback, power"])
|
||||||
val name: String,
|
val name: String,
|
||||||
|
@Description(["The level of the enchantment. Allows over enchanting."])
|
||||||
|
@MinInt(1)
|
||||||
|
@DefaultInt(1)
|
||||||
val level: Int,
|
val level: Int,
|
||||||
): ConfigListSaveable {
|
) {
|
||||||
override fun getKey(): String {
|
fun addToItem(stack: ItemStack) {
|
||||||
Logger.trace(ArenaEnchantment::class, "getKey()")
|
Logger.trace(ArenaEnchantment::class, "addToItem(stack: $stack)")
|
||||||
return name
|
stack.addUnsafeEnchantment(Enchantment.getByKey(NamespacedKey.minecraft(name.lowercase()))!!, level)
|
||||||
}
|
|
||||||
|
|
||||||
override fun getValue(): Int {
|
|
||||||
Logger.trace(ArenaEnchantment::class, "getValue()")
|
|
||||||
return level
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object: ConfigListLoadable<ArenaEnchantment> {
|
|
||||||
override fun loadFromList(key: String, value: Any): ArenaEnchantment {
|
|
||||||
Logger.trace(ArenaEnchantment::class, "loadFromList(key: $key, value: $value)")
|
|
||||||
return ArenaEnchantment(
|
|
||||||
key,
|
|
||||||
value as Int
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.kallestruik.dlib.config.annotations.DefaultInt
|
||||||
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
|
import nl.kallestruik.dlib.config.annotations.MinInt
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ArenaEndConditions(
|
||||||
|
@SerialName("players-left")
|
||||||
|
@Description(["End the game when there are this many players left alive. Set to -1 to disable."])
|
||||||
|
@MinInt(-1)
|
||||||
|
@DefaultInt(-1)
|
||||||
|
val playersLeft: Int = -1,
|
||||||
|
@Description(["End the game after this amount of seconds. Set to -1 to disable."])
|
||||||
|
@MinInt(-1)
|
||||||
|
@DefaultInt(-1)
|
||||||
|
val time: Int = -1,
|
||||||
|
@SerialName("players-finished")
|
||||||
|
@Description(["End the game after this amount of players reach a finish checkpoint. Set to -1 to disable."])
|
||||||
|
@MinInt(-1)
|
||||||
|
@DefaultInt(-1)
|
||||||
|
val playersFinished: Int = -1,
|
||||||
|
)
|
|
@ -1,36 +1,70 @@
|
||||||
package nl.kallestruik.darena.types.arena
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
import nl.kallestruik.darena.util.ConfigHelper
|
import com.destroystokyo.paper.Namespaced
|
||||||
import nl.kallestruik.darena.types.ConfigSaveable
|
import kotlinx.serialization.Serializable
|
||||||
import nl.kallestruik.darena.types.ConfigLoadable
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import nl.kallestruik.dlib.config.annotations.*
|
||||||
import org.bukkit.Material
|
import org.bukkit.Material
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
import org.bukkit.NamespacedKey
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ArenaItem(
|
data class ArenaItem(
|
||||||
|
@Description(["The item type."])
|
||||||
|
@DefaultString("AIR")
|
||||||
val material: Material = Material.AIR,
|
val material: Material = Material.AIR,
|
||||||
|
@Description(["The amount."])
|
||||||
|
@MinInt(1)
|
||||||
|
@DefaultInt(1)
|
||||||
val amount: Int = 1,
|
val amount: Int = 1,
|
||||||
|
@Description(["The enchantments that will be applied to the item."])
|
||||||
val enchantments: List<ArenaEnchantment> = listOf(),
|
val enchantments: List<ArenaEnchantment> = listOf(),
|
||||||
|
@Description(["Whether the item takes durability damage or not."])
|
||||||
|
@DefaultBoolean(false)
|
||||||
val unbreakable: Boolean = false,
|
val unbreakable: Boolean = false,
|
||||||
): ConfigSaveable {
|
@Description(["A list of blocks that this block can be placed on. Requires the feature GAMEMODE_ADVENTURE to work.",
|
||||||
override fun save(section: ConfigurationSection) {
|
"", "Valid values: all minecraft block ids as seen in game with F3+h on without the minecraft:",
|
||||||
section.set("material", material.toString())
|
"For example: stone, granite, andesite, diamond_block"])
|
||||||
section.set("amount", amount)
|
val placeOn: Set<String> = mutableSetOf(),
|
||||||
ConfigHelper.saveList(ConfigHelper.getOrCreateSection(section, "enchantments"), enchantments, ArenaEnchantment)
|
@Description(["A list of blocks that this item can be break. Requires the feature GAMEMODE_ADVENTURE to work.",
|
||||||
section.set("unbreakable", unbreakable)
|
"", "Valid values: all minecraft block ids as seen in game with F3+h on without the minecraft:",
|
||||||
}
|
"For example: stone, granite, andesite, diamond_block"])
|
||||||
|
val canBreak: Set<String> = mutableSetOf(),
|
||||||
companion object: ConfigLoadable<ArenaItem> {
|
) {
|
||||||
override fun load(section: ConfigurationSection): ArenaItem {
|
fun asItemStack(): ItemStack {
|
||||||
val enchantments = section.getConfigurationSection("enchantments")?.let {
|
Logger.trace(ArenaItem::class, "asItemStack()")
|
||||||
ArenaEnchantment.loadList(it)
|
val stack = ItemStack(material, amount)
|
||||||
} ?: listOf()
|
for (enchantment in enchantments) {
|
||||||
|
enchantment.addToItem(stack)
|
||||||
return ArenaItem(
|
|
||||||
ConfigHelper.matchMaterial(section.getString("material")!!),
|
|
||||||
section.getInt("amount", 1),
|
|
||||||
enchantments,
|
|
||||||
section.getBoolean("unbreakable", false)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val meta = stack.itemMeta
|
||||||
|
|
||||||
|
if (placeOn.isNotEmpty()) {
|
||||||
|
val placeableKeys = mutableSetOf<Namespaced>()
|
||||||
|
for (block in placeOn) {
|
||||||
|
//TODO: Make this give a nicer error if it fails.
|
||||||
|
placeableKeys.add(NamespacedKey.minecraft(block))
|
||||||
|
}
|
||||||
|
|
||||||
|
meta.setPlaceableKeys(placeableKeys)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canBreak.isNotEmpty()) {
|
||||||
|
val destroyableKeys = mutableSetOf<Namespaced>()
|
||||||
|
for (block in canBreak) {
|
||||||
|
destroyableKeys.add(NamespacedKey.minecraft(block))
|
||||||
|
}
|
||||||
|
|
||||||
|
meta.setDestroyableKeys(destroyableKeys)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unbreakable) {
|
||||||
|
meta.isUnbreakable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.itemMeta = meta
|
||||||
|
|
||||||
|
return stack
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,48 @@
|
||||||
package nl.kallestruik.darena.types.arena
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
import kotlinx.serialization.Serializable
|
||||||
import nl.kallestruik.darena.util.ConfigHelper
|
import nl.kallestruik.dlib.config.annotations.AllowedKeys
|
||||||
import nl.kallestruik.darena.types.ConfigSaveable
|
import nl.kallestruik.dlib.config.annotations.AllowedKeysExclusive
|
||||||
import nl.kallestruik.darena.types.ConfigLoadable
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.inventory.EquipmentSlot
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ArenaLoadout(
|
data class ArenaLoadout(
|
||||||
val name: String,
|
@Description(["The items that will be in the players hotbar."])
|
||||||
val hotbar: List<ArenaItem>,
|
@AllowedKeys(["0", "1", "2", "3", "4", "5", "6", "7", "8"])
|
||||||
val armor: List<ArenaItem>,
|
val hotbar: MutableMap<String, ArenaItem> = mutableMapOf(),
|
||||||
val offhand: ArenaItem,
|
@Description(["The items that will be in the players armor slots."])
|
||||||
): ConfigSaveable {
|
@AllowedKeysExclusive(["helmet", "head"])
|
||||||
override fun save(section: ConfigurationSection) {
|
@AllowedKeysExclusive(["chestplate", "chest", "elytra"])
|
||||||
ConfigHelper.saveList(ConfigHelper.getOrCreateSection(section, "hotbar"), hotbar)
|
@AllowedKeysExclusive(["leggings", "legs", "pants"])
|
||||||
ConfigHelper.saveList(ConfigHelper.getOrCreateSection(section, "armor"), armor)
|
@AllowedKeysExclusive(["boots", "feet"])
|
||||||
offhand.save(ConfigHelper.getOrCreateSection(section, "offhand"))
|
val armor: MutableMap<String, ArenaItem> = mutableMapOf(),
|
||||||
}
|
@Description(["The item that will be in the players offhand."])
|
||||||
|
val offhand: ArenaItem? = null,
|
||||||
|
@Description(["The potion effects that will be applied to players upon spawning."])
|
||||||
|
val effects: MutableList<ArenaPotionEffect> = mutableListOf(),
|
||||||
|
) {
|
||||||
|
fun equip(player: Player) {
|
||||||
|
for (entry in hotbar) {
|
||||||
|
val slot = entry.key.toInt()
|
||||||
|
player.inventory.setItem(slot, entry.value.asItemStack())
|
||||||
|
}
|
||||||
|
|
||||||
companion object : ConfigLoadable<ArenaLoadout> {
|
for (entry in armor) {
|
||||||
override fun load(section: ConfigurationSection): ArenaLoadout {
|
when (entry.key) {
|
||||||
|
"helmet", "head" -> player.inventory.setItem(EquipmentSlot.HEAD, entry.value.asItemStack())
|
||||||
|
"chestplate", "chest", "elytra" -> player.inventory.setItem(EquipmentSlot.CHEST, entry.value.asItemStack())
|
||||||
|
"leggings", "legs", "pants" -> player.inventory.setItem(EquipmentSlot.LEGS, entry.value.asItemStack())
|
||||||
|
"boots", "feet" -> player.inventory.setItem(EquipmentSlot.FEET, entry.value.asItemStack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val hotbar = section.getConfigurationSection("hotbar")?.let {
|
if (offhand != null)
|
||||||
ArenaItem.loadList(it)
|
player.inventory.setItemInOffHand(offhand.asItemStack())
|
||||||
}?: listOf()
|
|
||||||
|
|
||||||
val armor = section.getConfigurationSection("armor")?.let {
|
for (effect in effects) {
|
||||||
ArenaItem.loadList(it)
|
effect.apply(player)
|
||||||
}?: listOf()
|
|
||||||
|
|
||||||
val offhand = section.getConfigurationSection("offhand")?.let {
|
|
||||||
ArenaItem.load(it)
|
|
||||||
}?: ArenaItem()
|
|
||||||
|
|
||||||
return ArenaLoadout(
|
|
||||||
section.name,
|
|
||||||
hotbar,
|
|
||||||
armor,
|
|
||||||
offhand,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
package nl.kallestruik.darena.types.arena
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.kallestruik.dlib.config.annotations.DefaultInt
|
||||||
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
import org.bukkit.configuration.ConfigurationSection
|
||||||
import nl.kallestruik.darena.types.ConfigSaveable
|
|
||||||
import nl.kallestruik.darena.types.ConfigLoadable
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ArenaLocation(
|
data class ArenaLocation(
|
||||||
|
@Description(["The x coordinate."])
|
||||||
|
@DefaultInt(0)
|
||||||
val x: Int,
|
val x: Int,
|
||||||
|
@Description(["The y coordinate."])
|
||||||
|
@DefaultInt(0)
|
||||||
val y: Int,
|
val y: Int,
|
||||||
|
@Description(["The z coordinate."])
|
||||||
|
@DefaultInt(0)
|
||||||
val z: Int
|
val z: Int
|
||||||
): ConfigSaveable {
|
) {
|
||||||
override fun save(section: ConfigurationSection) {
|
companion object {
|
||||||
section.set("x", x)
|
fun load(section: ConfigurationSection): ArenaLocation {
|
||||||
section.set("y", y)
|
|
||||||
section.set("z", z)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object: ConfigLoadable<ArenaLocation> {
|
|
||||||
override fun load(section: ConfigurationSection): ArenaLocation {
|
|
||||||
return ArenaLocation(
|
return ArenaLocation(
|
||||||
section.getInt("x", 0),
|
section.getInt("x", 0),
|
||||||
section.getInt("y", 0),
|
section.getInt("y", 0),
|
||||||
|
|
|
@ -1,31 +1,23 @@
|
||||||
package nl.kallestruik.darena.types.arena
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
import kotlinx.serialization.SerialName
|
||||||
import nl.kallestruik.darena.util.ConfigHelper
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ArenaPoints(
|
data class ArenaPoints(
|
||||||
|
@Description(["The points given to a player when they get a kill."])
|
||||||
val kill: List<Int> = listOf(0),
|
val kill: List<Int> = listOf(0),
|
||||||
|
@SerialName("last-man-standing")
|
||||||
|
@Description(["The points given to a player for staying alive the longest."])
|
||||||
val lastManStanding: List<Int> = listOf(0),
|
val lastManStanding: List<Int> = listOf(0),
|
||||||
val checkpoints: Map<String, List<Int>> = mapOf()
|
@Description(["The points given to a player for reaching a checkpoint.",
|
||||||
) {
|
"","Valid keys: the label of a checkpoint defined in the checkpoints section."])
|
||||||
fun save(section: ConfigurationSection) {
|
val checkpoints: Map<String, List<Int>> = mapOf(),
|
||||||
section.set("kill", ConfigHelper.intListToString(kill))
|
@Description(["The points given to a player for reaching a checkpoint that is part of a spawn pool.",
|
||||||
section.set("lastManStanding", ConfigHelper.intListToString(lastManStanding))
|
"The first list represents the first checkpoint, the second the second checkpoint, etc",
|
||||||
|
"",
|
||||||
val checkpointSection = ConfigHelper.getOrCreateSection(section, "checkpoints")
|
"Valid keys: the names of spawn pools as defined in the spawn-pools section.",
|
||||||
for (entry in checkpoints) {
|
])
|
||||||
checkpointSection.set(entry.key, ConfigHelper.intListToString(entry.value))
|
val pools: Map<String, List<List<Int>>> = mapOf(),
|
||||||
}
|
)
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun load(section: ConfigurationSection): ArenaPoints {
|
|
||||||
return ArenaPoints(
|
|
||||||
ConfigHelper.parseIntList(section.getString("kill")!!),
|
|
||||||
ConfigHelper.parseIntList(section.getString("lastManStanding")!!),
|
|
||||||
ConfigHelper.parseStringIntListMap(section.getConfigurationSection("checkpoints")!!)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,28 +1,30 @@
|
||||||
package nl.kallestruik.darena.types.arena
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
import nl.kallestruik.darena.types.ConfigSaveable
|
import kotlinx.serialization.Serializable
|
||||||
import nl.kallestruik.darena.types.ConfigLoadable
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
import nl.kallestruik.dlib.config.annotations.MaxInt
|
||||||
|
import nl.kallestruik.dlib.config.annotations.MinInt
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.potion.PotionEffect
|
||||||
|
import org.bukkit.potion.PotionEffectType
|
||||||
|
|
||||||
class ArenaPotionEffect(
|
@Serializable
|
||||||
|
data class ArenaPotionEffect(
|
||||||
|
@Description(["The name of the potion effect.",
|
||||||
|
"", "Valid values: the name of the potion effect as seen in the /effect command without minecraft:",
|
||||||
|
"For example: invisibility, strength, haste"])
|
||||||
val name: String,
|
val name: String,
|
||||||
|
@Description(["The strength of the potion effect."])
|
||||||
|
@MinInt(0)
|
||||||
|
@MaxInt(255)
|
||||||
val strength: Int,
|
val strength: Int,
|
||||||
|
@Description(["The duration of the potion effect in ticks. (20 ticks = 1 second)"])
|
||||||
|
@MinInt(0)
|
||||||
val duration: Int,
|
val duration: Int,
|
||||||
|
@Description(["Whether the potion effect should be hidden from the player. This hides the particles and the icon."])
|
||||||
val hidden: Boolean
|
val hidden: Boolean
|
||||||
): ConfigSaveable {
|
) {
|
||||||
override fun save(section: ConfigurationSection) {
|
fun apply(player: Player) {
|
||||||
section.set("strength", strength)
|
player.addPotionEffect(PotionEffect(PotionEffectType.getByName(name)!!, duration, strength, !hidden, !hidden, !hidden))
|
||||||
section.set("duration", duration)
|
|
||||||
section.set("hidden", hidden)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object: ConfigLoadable<ArenaPotionEffect> {
|
|
||||||
override fun load(section: ConfigurationSection): ArenaPotionEffect {
|
|
||||||
return ArenaPotionEffect(
|
|
||||||
section.name,
|
|
||||||
section.getInt("strength"),
|
|
||||||
section.getInt("duration"),
|
|
||||||
section.getBoolean("hidden"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +1,44 @@
|
||||||
package nl.kallestruik.darena.types.arena
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import nl.kallestruik.darena.arenas.world.ArenaWorld
|
import nl.kallestruik.darena.arenas.world.ArenaWorld
|
||||||
import nl.kallestruik.darena.util.ConfigHelper
|
import nl.kallestruik.darena.util.Logger
|
||||||
import nl.kallestruik.darena.types.ConfigSaveable
|
import nl.kallestruik.dlib.config.annotations.DefaultFloat
|
||||||
import nl.kallestruik.darena.types.ConfigLoadable
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
|
import nl.kallestruik.dlib.config.annotations.MaxFloat
|
||||||
|
import nl.kallestruik.dlib.config.annotations.MinFloat
|
||||||
import org.bukkit.Location
|
import org.bukkit.Location
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
import org.bukkit.World
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class ArenaSpawn(
|
data class ArenaSpawn(
|
||||||
val label: String,
|
@Description(["The x coordinate of the spawn point."])
|
||||||
|
@DefaultFloat(0.0)
|
||||||
val x: Double,
|
val x: Double,
|
||||||
|
@Description(["The y coordinate of the spawn point."])
|
||||||
|
@DefaultFloat(0.0)
|
||||||
val y: Double,
|
val y: Double,
|
||||||
|
@Description(["The z coordinate of the spawn point."])
|
||||||
|
@DefaultFloat(0.0)
|
||||||
val z: Double,
|
val z: Double,
|
||||||
|
@Description(["The yaw of the spawn point."])
|
||||||
|
@MinFloat(-180.0)
|
||||||
|
@MaxFloat(180.0)
|
||||||
|
@DefaultFloat(0.0)
|
||||||
val yaw: Float,
|
val yaw: Float,
|
||||||
val pitch: Float,
|
@Description(["The pitch of the spawn point."])
|
||||||
val loadout: String
|
@MinFloat(-90.0)
|
||||||
): ConfigSaveable {
|
@MaxFloat(90.0)
|
||||||
|
@DefaultFloat(0.0)
|
||||||
|
val pitch: Float
|
||||||
|
) {
|
||||||
fun spawn(world: ArenaWorld, player: Player) {
|
fun spawn(world: ArenaWorld, player: Player) {
|
||||||
player.teleport(Location(world.world, x, y, z, yaw, pitch))
|
Logger.trace(ArenaSpawn::class, "spawn(world: ${world.name}, player: ${player.name})")
|
||||||
|
player.teleport(toLocation(world.world))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun save(section: ConfigurationSection) {
|
fun toLocation(world: World): Location {
|
||||||
section.set("x", x)
|
return Location(world, x, y, z, yaw, pitch)
|
||||||
section.set("y", y)
|
|
||||||
section.set("z", z)
|
|
||||||
section.set("yaw", yaw.toDouble())
|
|
||||||
section.set("pitch", pitch.toDouble())
|
|
||||||
section.set("loadout", loadout)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object: ConfigLoadable<ArenaSpawn> {
|
|
||||||
override fun load(section: ConfigurationSection): ArenaSpawn {
|
|
||||||
return ArenaSpawn(
|
|
||||||
section.name,
|
|
||||||
section.getDouble("x"),
|
|
||||||
section.getDouble("y"),
|
|
||||||
section.getDouble("z"),
|
|
||||||
section.getDouble("yaw").toFloat(),
|
|
||||||
section.getDouble("pitch").toFloat(),
|
|
||||||
section.getString("loadout")!!
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.kallestruik.dlib.config.annotations.DefaultInt
|
||||||
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
|
import nl.kallestruik.dlib.config.annotations.MinInt
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ArenaSpawnPool(
|
||||||
|
@Description(["The name of the spawn pool."])
|
||||||
|
val label: String,
|
||||||
|
@Description(["The amount of spawn rules that will be chosen before a player is considered finished."])
|
||||||
|
@MinInt(1)
|
||||||
|
@DefaultInt(-1)
|
||||||
|
val times: Int,
|
||||||
|
@Description(["The list of spawn rules that the pool can choose from.",
|
||||||
|
"", "Valid values: any spawn rule defined in the spawn-rules section."])
|
||||||
|
val rules: List<String>
|
||||||
|
)
|
|
@ -1,27 +1,27 @@
|
||||||
package nl.kallestruik.darena.types.arena
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
import nl.kallestruik.darena.types.ConfigLoadable
|
import kotlinx.serialization.SerialName
|
||||||
import nl.kallestruik.darena.types.ConfigSaveable
|
import kotlinx.serialization.Serializable
|
||||||
import nl.kallestruik.darena.util.ConfigHelper
|
import nl.kallestruik.dlib.config.annotations.DefaultBoolean
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
import nl.kallestruik.dlib.config.annotations.DefaultString
|
||||||
|
import nl.kallestruik.dlib.config.annotations.Description
|
||||||
|
|
||||||
|
@Serializable
|
||||||
class ArenaSpawnRule(
|
class ArenaSpawnRule(
|
||||||
val reuseSpawns: Boolean,
|
@SerialName("reuse-spawns")
|
||||||
val effects: Map<String, ArenaPotionEffect>,
|
@Description(["Whether or not to reuse the spawns for different players."])
|
||||||
val spawns: List<String>
|
@DefaultBoolean(true)
|
||||||
): ConfigSaveable {
|
val reuseSpawns: Boolean = true,
|
||||||
override fun save(section: ConfigurationSection) {
|
@SerialName("reuse-individual")
|
||||||
section.set("reuseSpawns", reuseSpawns)
|
@Description(["Whether or not to reuse the spawns for individual players.",
|
||||||
ConfigHelper.saveMap(ConfigHelper.getOrCreateSection(section, "effects"), effects)
|
"When this is set a player will always use the same spawn after spawning there once."])
|
||||||
section.set("spawns", spawns)
|
@DefaultBoolean(false)
|
||||||
}
|
val reuseIndividual: Boolean = false,
|
||||||
|
@Description(["The list of spawns that this spawn rule can pick from.",
|
||||||
companion object: ConfigLoadable<ArenaSpawnRule> {
|
"", "Valid values: any spawn name defined in the spawns section."])
|
||||||
override fun load(section: ConfigurationSection): ArenaSpawnRule {
|
val spawns: MutableList<String> = mutableListOf(),
|
||||||
return ArenaSpawnRule(
|
@Description(["The loadout to use for players that are spawned using this spawn rule. Use none to not give them any items.",
|
||||||
section.getBoolean("reuseSpawn", true),
|
"", "Valid values: any loadout defined in the loadouts section or none."])
|
||||||
ConfigHelper.loadMap(section.getConfigurationSection("effects")!!, ArenaPotionEffect),
|
@DefaultString("none")
|
||||||
section.getStringList("spawns"))
|
val loadout: String = "none"
|
||||||
}
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
import org.bukkit.Location
|
||||||
|
|
||||||
|
data class ProcessedArenaCheckpoint(
|
||||||
|
val label: String,
|
||||||
|
val lower: ArenaLocation,
|
||||||
|
val upper: ArenaLocation,
|
||||||
|
var spawnRule: ProcessedArenaSpawnRule?,
|
||||||
|
val spawnPool: ProcessedArenaSpawnPool?,
|
||||||
|
val movePlayer: Boolean,
|
||||||
|
val isFinish: Boolean,
|
||||||
|
) {
|
||||||
|
constructor(checkpoint: ArenaCheckpoint, spawnRule: ProcessedArenaSpawnRule?, spawnPool: ProcessedArenaSpawnPool?)
|
||||||
|
: this(checkpoint.label, checkpoint.lower, checkpoint.upper, spawnRule, spawnPool, checkpoint.movePlayer, checkpoint.isFinish)
|
||||||
|
|
||||||
|
fun isInside(location: Location): Boolean {
|
||||||
|
return location.blockX <= upper.x && location.blockX >= lower.x
|
||||||
|
&& location.blockY <= upper.y && location.blockY >= lower.y
|
||||||
|
&& location.blockZ <= upper.z && location.blockZ >= lower.z
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolveSpawnRule() {
|
||||||
|
if (spawnRule != null)
|
||||||
|
return
|
||||||
|
|
||||||
|
spawnRule = spawnPool!!.nextRule()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
data class ProcessedArenaSpawnPool(
|
||||||
|
val label: String,
|
||||||
|
val times: Int,
|
||||||
|
val rules: MutableList<ProcessedArenaSpawnRule>
|
||||||
|
) {
|
||||||
|
fun nextRule(): ProcessedArenaSpawnRule? {
|
||||||
|
return rules.removeFirstOrNull()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package nl.kallestruik.darena.types.arena
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.arenas.world.ArenaWorld
|
||||||
|
import nl.kallestruik.darena.util.ArenaUtil
|
||||||
|
import nl.kallestruik.darena.util.Logger
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
data class ProcessedArenaSpawnRule(
|
||||||
|
val reuseSpawns: Boolean,
|
||||||
|
val reuseIndividual: Boolean,
|
||||||
|
val spawns: List<ArenaSpawn>,
|
||||||
|
val loadout: ArenaLoadout?,
|
||||||
|
) {
|
||||||
|
var lastSpawnIndex = -1
|
||||||
|
private val lastForPlayer = mutableMapOf<UUID, Int>()
|
||||||
|
|
||||||
|
constructor(spawnRule: ArenaSpawnRule, spawns: List<ArenaSpawn>, loadout: ArenaLoadout?): this(spawnRule.reuseSpawns, spawnRule.reuseIndividual, spawns, loadout)
|
||||||
|
|
||||||
|
fun spawnPlayers(players: Collection<Player>, world: ArenaWorld) {
|
||||||
|
Logger.trace(ProcessedArenaSpawnRule::class, "spawnPlayers(players: $players, world: ${world.name})")
|
||||||
|
players.forEach() {player ->
|
||||||
|
spawnPlayer(player, world)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun spawnPlayer(player: Player, world: ArenaWorld) {
|
||||||
|
if (player.health <= 0) {
|
||||||
|
player.spigot().respawn()
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.trace(ProcessedArenaSpawnRule::class, "spawnPlayer(player: ${player.name}, world: ${world.name})")
|
||||||
|
if (player.uniqueId in lastForPlayer) {
|
||||||
|
spawnAndEquip(player, world, spawns[lastForPlayer[player.uniqueId]!!])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reuseSpawns && lastSpawnIndex + 1 >= spawns.size) {
|
||||||
|
Logger.warn(ProcessedArenaSpawnRule::class, "Cant spawn player \"${player.name}\" because there are no spots left and reuse-spawns is set to false.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val nextSpawnIndex = (lastSpawnIndex + 1) % spawns.size
|
||||||
|
spawnAndEquip(player, world, spawns[nextSpawnIndex])
|
||||||
|
lastForPlayer[player.uniqueId] = nextSpawnIndex
|
||||||
|
|
||||||
|
lastSpawnIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun spawnAndEquip(player: Player, world: ArenaWorld, spawn: ArenaSpawn) {
|
||||||
|
ArenaUtil.clearPlayer(player)
|
||||||
|
spawn.spawn(world, player)
|
||||||
|
loadout?.equip(player)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package nl.kallestruik.darena.types.border
|
||||||
|
|
||||||
|
abstract class AbstractBorder {
|
||||||
|
|
||||||
|
abstract fun size(size: Double, time: Long = 0)
|
||||||
|
|
||||||
|
abstract fun center(x: Double, y: Double)
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package nl.kallestruik.darena.types.border
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.SerializationException
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
|
@Serializable(AbstractBorderNode.BorderScriptSerializer::class)
|
||||||
|
abstract class AbstractBorderNode {
|
||||||
|
@Transient
|
||||||
|
var next: AbstractBorderNode? = null
|
||||||
|
|
||||||
|
abstract fun execute(border: AbstractBorder, plugin: JavaPlugin)
|
||||||
|
|
||||||
|
abstract fun toCommand(): String
|
||||||
|
|
||||||
|
protected fun runNext(border: AbstractBorder, plugin: JavaPlugin) {
|
||||||
|
next?.execute(border, plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun cancel() {
|
||||||
|
next?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
object BorderScriptSerializer: KSerializer<AbstractBorderNode> {
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BorderScriptNode", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: AbstractBorderNode) {
|
||||||
|
encoder.encodeString(value.toCommand())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): AbstractBorderNode {
|
||||||
|
return createNode(decoder.decodeString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNode(command: String): AbstractBorderNode {
|
||||||
|
val parts = command.split(" ", limit = 2)
|
||||||
|
return when (parts[0].uppercase()) {
|
||||||
|
"CENTER" -> BorderNodeCenter.fromParameters(parts[1])
|
||||||
|
"SET" -> BorderNodeSet.fromParameters(parts[1])
|
||||||
|
"TRANSITION" -> BorderNodeTransition.fromParameters(parts[1])
|
||||||
|
"WAIT" -> BorderNodeWait.fromParameters(parts[1])
|
||||||
|
else -> throw SerializationException("${parts[0]} is not a valid border script keyword.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package nl.kallestruik.darena.types.border
|
||||||
|
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
|
class BorderNodeCenter(
|
||||||
|
private val x: Double,
|
||||||
|
private val y: Double,
|
||||||
|
): AbstractBorderNode() {
|
||||||
|
|
||||||
|
override fun execute(border: AbstractBorder, plugin: JavaPlugin) {
|
||||||
|
border.center(x, y)
|
||||||
|
runNext(border, plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toCommand(): String {
|
||||||
|
return "CENTER $x $y"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromParameters(params: String): BorderNodeCenter {
|
||||||
|
val parts = params.split(" ")
|
||||||
|
return BorderNodeCenter(parts[0].toDouble(), parts[1].toDouble())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package nl.kallestruik.darena.types.border
|
||||||
|
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
|
class BorderNodeSet(
|
||||||
|
private val size: Double
|
||||||
|
): AbstractBorderNode() {
|
||||||
|
|
||||||
|
override fun execute(border: AbstractBorder, plugin: JavaPlugin) {
|
||||||
|
border.size(size)
|
||||||
|
runNext(border, plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toCommand(): String {
|
||||||
|
return "SET $size"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromParameters(params: String): BorderNodeSet {
|
||||||
|
return BorderNodeSet(params.toDouble())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package nl.kallestruik.darena.types.border
|
||||||
|
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
|
class BorderNodeTransition(
|
||||||
|
private val size: Double,
|
||||||
|
private val time: Long
|
||||||
|
): AbstractBorderNode() {
|
||||||
|
|
||||||
|
override fun execute(border: AbstractBorder, plugin: JavaPlugin) {
|
||||||
|
border.size(size, time)
|
||||||
|
runNext(border, plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toCommand(): String {
|
||||||
|
return "TRANSITION $size $time"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromParameters(params: String): BorderNodeTransition {
|
||||||
|
val parts = params.split(" ")
|
||||||
|
return BorderNodeTransition(parts[0].toDouble(), parts[1].toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package nl.kallestruik.darena.types.border
|
||||||
|
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable
|
||||||
|
|
||||||
|
class BorderNodeWait(
|
||||||
|
private val time: Long
|
||||||
|
): AbstractBorderNode() {
|
||||||
|
private var runnable: BukkitRunnable? = null
|
||||||
|
|
||||||
|
override fun execute(border: AbstractBorder, plugin: JavaPlugin) {
|
||||||
|
object : BukkitRunnable() {
|
||||||
|
override fun run() {
|
||||||
|
runNext(border, plugin)
|
||||||
|
}
|
||||||
|
}.runTaskLater(plugin, time * 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toCommand(): String {
|
||||||
|
return "WAIT $time"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cancel() {
|
||||||
|
super.cancel()
|
||||||
|
runnable?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromParameters(params: String): BorderNodeWait {
|
||||||
|
return BorderNodeWait(params.toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package nl.kallestruik.darena.types.border
|
||||||
|
|
||||||
|
class HorizontalBorder: AbstractBorder() {
|
||||||
|
var height = 0
|
||||||
|
//TODO: Implement visible border in the form of a block being placed.
|
||||||
|
//TODO: Implement visible border in the form of particles if a player is close.
|
||||||
|
|
||||||
|
override fun size(size: Double, time: Long) {
|
||||||
|
height = size.toInt()
|
||||||
|
//TODO: Implement transition over time.
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun center(x: Double, y: Double) {
|
||||||
|
//TODO: Maybe throw exception or something
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package nl.kallestruik.darena.types.border
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.arenas.world.ArenaWorld
|
||||||
|
|
||||||
|
class MCBorder(
|
||||||
|
private val world: ArenaWorld
|
||||||
|
): AbstractBorder() {
|
||||||
|
override fun size(size: Double, time: Long) {
|
||||||
|
world.world.worldBorder.setSize(size, time)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun center(x: Double, y: Double) {
|
||||||
|
world.world.worldBorder.setCenter(x, y)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package nl.kallestruik.darena.types.countdown
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.util.CountdownHelper
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable
|
||||||
|
import org.bukkit.scheduler.BukkitTask
|
||||||
|
|
||||||
|
|
||||||
|
open class AsyncCountdown(
|
||||||
|
val plugin: JavaPlugin,
|
||||||
|
val time: Int,
|
||||||
|
val subtitle: String,
|
||||||
|
val alwaysAsTitle: Boolean = false,
|
||||||
|
val alwaysAsActionBar: Boolean = false,
|
||||||
|
val after: Runnable,
|
||||||
|
): Countdown {
|
||||||
|
var task: BukkitTask? = null
|
||||||
|
|
||||||
|
override fun start(players: Collection<Player>): Countdown {
|
||||||
|
var currentTime = time
|
||||||
|
task = object : BukkitRunnable() {
|
||||||
|
override fun run() {
|
||||||
|
if (currentTime > 0) {
|
||||||
|
if (!alwaysAsActionBar && (alwaysAsTitle || currentTime <= 5 || currentTime % 10 == 0)) {
|
||||||
|
CountdownHelper.sendTitleToAll(players, currentTime, subtitle)
|
||||||
|
CountdownHelper.sendActionBarToAll(players, currentTime, subtitle)
|
||||||
|
} else {
|
||||||
|
CountdownHelper.sendActionBarToAll(players, currentTime, subtitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTime--
|
||||||
|
} else {
|
||||||
|
CountdownHelper.clearTitleForAll(players)
|
||||||
|
CountdownHelper.clearActionBarForAll(players)
|
||||||
|
Bukkit.getScheduler().runTask(plugin, after)
|
||||||
|
this@AsyncCountdown.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.runTaskTimerAsynchronously(plugin, 0, 20)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cancel() {
|
||||||
|
task?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package nl.kallestruik.darena.types.countdown
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
|
class AsyncCountdownWithSyncTask(
|
||||||
|
plugin: JavaPlugin,
|
||||||
|
time: Int,
|
||||||
|
subtitle: String,
|
||||||
|
private val during: Runnable,
|
||||||
|
after: Runnable,
|
||||||
|
alwaysAsTitle: Boolean = false,
|
||||||
|
alwaysAsActionBar: Boolean = false,
|
||||||
|
): AsyncCountdown(plugin, time, subtitle, alwaysAsTitle, alwaysAsActionBar, after) {
|
||||||
|
override fun start(players: Collection<Player>): Countdown {
|
||||||
|
super.start(players)
|
||||||
|
Bukkit.getScheduler().runTask(plugin, during)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package nl.kallestruik.darena.types.countdown
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
interface Countdown {
|
||||||
|
fun start(players: Collection<Player>): Countdown
|
||||||
|
|
||||||
|
fun cancel()
|
||||||
|
}
|
|
@ -1,10 +1,14 @@
|
||||||
package nl.kallestruik.darena.util
|
package nl.kallestruik.darena.util
|
||||||
|
|
||||||
|
import nl.kallestruik.darena.arenas.Arena
|
||||||
|
import nl.kallestruik.darena.managers.ArenaManager
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.GameMode
|
import org.bukkit.GameMode
|
||||||
|
import org.bukkit.entity.Entity
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.entity.Projectile
|
||||||
|
|
||||||
class ArenaUtil {
|
object ArenaUtil {
|
||||||
|
|
||||||
fun getPossibleParticipants(): List<Player> {
|
fun getPossibleParticipants(): List<Player> {
|
||||||
Logger.trace(ArenaUtil::class, "getPossibleParticipants()")
|
Logger.trace(ArenaUtil::class, "getPossibleParticipants()")
|
||||||
|
@ -15,4 +19,60 @@ class ArenaUtil {
|
||||||
Logger.trace(ArenaUtil::class, "getPossibleSpectators()")
|
Logger.trace(ArenaUtil::class, "getPossibleSpectators()")
|
||||||
return Bukkit.getOnlinePlayers().filter { it.gameMode == GameMode.SPECTATOR }
|
return Bukkit.getOnlinePlayers().filter { it.gameMode == GameMode.SPECTATOR }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clearPlayer(player: Player) {
|
||||||
|
player.inventory.close()
|
||||||
|
player.inventory.clear()
|
||||||
|
|
||||||
|
player.exp = 0F
|
||||||
|
player.level = 0
|
||||||
|
player.health = 20.0
|
||||||
|
player.saturation = 0F
|
||||||
|
player.foodLevel = 20
|
||||||
|
player.fireTicks = 0
|
||||||
|
player.fallDistance = 0F
|
||||||
|
for (effect in player.activePotionEffects) {
|
||||||
|
player.removePotionEffect(effect.type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolveDamager(damager: Entity): Player? {
|
||||||
|
if (damager is Player)
|
||||||
|
return damager
|
||||||
|
|
||||||
|
if (damager is Projectile && damager.shooter is Player)
|
||||||
|
return damager.shooter as Player
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun formatTime(timeLeft: Int): String {
|
||||||
|
val minutes = (timeLeft / 60).toString().padStart(2, '0')
|
||||||
|
val seconds = (timeLeft % 60).toString().padStart(2, '0')
|
||||||
|
return "$minutes:$seconds"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun formatTimeMillis(timeLeft: Long): String {
|
||||||
|
val minutes = (timeLeft / 1000 / 60).toString().padStart(2, '0')
|
||||||
|
val seconds = ((timeLeft / 1000) % 60).toString().padStart(2, '0')
|
||||||
|
val millis = (timeLeft % 1000).toString().padStart(3, '0')
|
||||||
|
return "$minutes:$seconds.$millis"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun finishPlayer(player: Player, arena: Arena, arenaManager: ArenaManager) {
|
||||||
|
Logger.trace(ArenaManager::class, "finishPlayer(player: $player, arena: ${arena.config.name}, arenaManager: $arenaManager)")
|
||||||
|
arena.makeSpectator(player)
|
||||||
|
arena.session!!.playersFinished++
|
||||||
|
if (arena.config.endConditions.playersFinished != -1
|
||||||
|
&& arena.session!!.playersFinished >= arena.config.endConditions.playersFinished
|
||||||
|
) {
|
||||||
|
arenaManager.end()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arenaManager.currentArena!!.session!!.participants.size <= arenaManager.currentArena!!.config.endConditions.playersLeft) {
|
||||||
|
arenaManager.end()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
package nl.kallestruik.darena.util
|
package nl.kallestruik.darena.util
|
||||||
|
|
||||||
import nl.kallestruik.darena.exceptions.MaterialNotFoundException
|
import nl.kallestruik.darena.exceptions.MaterialNotFoundException
|
||||||
import nl.kallestruik.darena.types.ConfigSaveable
|
|
||||||
import nl.kallestruik.darena.types.ConfigLoadable
|
|
||||||
import nl.kallestruik.darena.types.ConfigListSaveable
|
|
||||||
import nl.kallestruik.darena.types.ConfigListLoadable
|
|
||||||
import org.bukkit.Material
|
import org.bukkit.Material
|
||||||
import org.bukkit.configuration.file.YamlConfiguration
|
|
||||||
import org.bukkit.configuration.ConfigurationSection
|
import org.bukkit.configuration.ConfigurationSection
|
||||||
import java.io.*
|
import org.bukkit.configuration.file.YamlConfiguration
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
object ConfigHelper {
|
object ConfigHelper {
|
||||||
|
|
||||||
|
@ -54,40 +52,6 @@ object ConfigHelper {
|
||||||
return section.createSection(key)
|
return section.createSection(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T: ConfigSaveable> saveMap(section: ConfigurationSection, map: Map<String, T>) {
|
|
||||||
Logger.trace(ConfigHelper::class, "saveMap(section: ${section.currentPath}, map: ${map.toString()})")
|
|
||||||
for (entry in map.entries) {
|
|
||||||
entry.value.save(ConfigHelper.getOrCreateSection(section, entry.key))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T: ConfigListSaveable> saveList(section: ConfigurationSection, list: List<T>) {
|
|
||||||
Logger.trace(ConfigHelper::class, "saveList(section: ${section.currentPath}, list: ${list.toString()})")
|
|
||||||
for (item in list) {
|
|
||||||
section.set(item.getKey(), item.getValue())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <R, T: ConfigLoadable<R>> loadMap(section: ConfigurationSection, loader: T): MutableMap<String, R> {
|
|
||||||
Logger.trace(ConfigHelper::class, "loadMap(section: ${section.currentPath}, loader: ${loader::class.qualifiedName})")
|
|
||||||
val map = mutableMapOf<String, R>()
|
|
||||||
for (key in section.getKeys(false)) {
|
|
||||||
map.put(key, loader.load(section.getConfigurationSection(key)!!))
|
|
||||||
}
|
|
||||||
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <R, T: ConfigListLoadable<R>> loadList(section: ConfigurationSection, loader: T): MutableList<R> {
|
|
||||||
Logger.trace(ConfigHelper::class, "loadList(section: ${section.currentPath}, loader: ${loader::class.qualifiedName})")
|
|
||||||
val list = mutableListOf<R>()
|
|
||||||
for (key in section.getKeys(false)) {
|
|
||||||
loader.loadFromList(key, section.get(key)!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(MaterialNotFoundException::class)
|
@Throws(MaterialNotFoundException::class)
|
||||||
fun matchMaterial(material: String): Material {
|
fun matchMaterial(material: String): Material {
|
||||||
Logger.trace(ConfigHelper::class, "matchMaterial(material: $material)")
|
Logger.trace(ConfigHelper::class, "matchMaterial(material: $material)")
|
||||||
|
@ -97,6 +61,7 @@ object ConfigHelper {
|
||||||
fun parseIntList(string: String): List<Int> {
|
fun parseIntList(string: String): List<Int> {
|
||||||
Logger.trace(ConfigHelper::class, "parseIntList(string: $string)")
|
Logger.trace(ConfigHelper::class, "parseIntList(string: $string)")
|
||||||
val list = mutableListOf<Int>()
|
val list = mutableListOf<Int>()
|
||||||
|
|
||||||
for (key in string.split(" ")) {
|
for (key in string.split(" ")) {
|
||||||
list.add(key.toInt())
|
list.add(key.toInt())
|
||||||
}
|
}
|
||||||
|
@ -113,14 +78,4 @@ object ConfigHelper {
|
||||||
|
|
||||||
return sb.toString().trim()
|
return sb.toString().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseStringIntListMap(section: ConfigurationSection): Map<String, List<Int>> {
|
|
||||||
Logger.trace(ConfigHelper::class, "parseStringIntListMap(section: ${section.currentPath})")
|
|
||||||
val map = mutableMapOf<String, List<Int>>()
|
|
||||||
for (key in section.getKeys(true)) {
|
|
||||||
map[key] = parseIntList(section.getString(key)!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package nl.kallestruik.darena.util
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.format.TextColor
|
||||||
|
import net.kyori.adventure.title.Title
|
||||||
|
import org.bukkit.Sound
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
|
||||||
|
object CountdownHelper {
|
||||||
|
|
||||||
|
fun sendTitleToAll(players: Collection<Player>, timeLeft: Int, subtitle: String) {
|
||||||
|
for (player in players) {
|
||||||
|
player.playSound(player.location, Sound.BLOCK_NOTE_BLOCK_BIT, 1.0F, 0.5F)
|
||||||
|
player.showTitle(
|
||||||
|
Title.title(
|
||||||
|
Component.text(timeLeft)
|
||||||
|
.color(TextColor.color(0xFFAA00)),
|
||||||
|
Component.text("$subtitle..."),
|
||||||
|
Title.Times.of(Duration.ZERO, Duration.ofSeconds(3), Duration.ofSeconds(2))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendActionBarToAll(players: Collection<Player>, timeLeft: Int, prefix: String) {
|
||||||
|
for (player in players) {
|
||||||
|
player.sendActionBar(
|
||||||
|
Component.text("$prefix: ")
|
||||||
|
.append(
|
||||||
|
Component
|
||||||
|
.text(timeLeft)
|
||||||
|
.color(TextColor.color(0xFFAA00))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearTitleForAll(players: Collection<Player>) {
|
||||||
|
for (player in players) {
|
||||||
|
player.clearTitle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearActionBarForAll(players: Collection<Player>) {
|
||||||
|
for (player in players) {
|
||||||
|
player.sendActionBar(Component.empty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,7 @@ package nl.kallestruik.darena.util
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
object Logger {
|
object Logger {
|
||||||
var level: LogLevel = LogLevel.INFO
|
var level: LogLevel = LogLevel.TRACE
|
||||||
|
|
||||||
|
|
||||||
fun trace(source: KClass<*>, message: String) {
|
fun trace(source: KClass<*>, message: String) {
|
||||||
if (level.shouldLog(LogLevel.TRACE))
|
if (level.shouldLog(LogLevel.TRACE))
|
||||||
|
@ -36,7 +35,7 @@ object Logger {
|
||||||
println("[CRITICAL] [${source.qualifiedName}] $message")
|
println("[CRITICAL] [${source.qualifiedName}] $message")
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LogLevel(
|
enum class LogLevel(
|
||||||
val weight: Int
|
val weight: Int
|
||||||
) {
|
) {
|
||||||
TRACE(100),
|
TRACE(100),
|
||||||
|
@ -44,7 +43,7 @@ object Logger {
|
||||||
INFO(60),
|
INFO(60),
|
||||||
WARN(40),
|
WARN(40),
|
||||||
ERROR(20),
|
ERROR(20),
|
||||||
CRITICAL(0)
|
CRITICAL(0);
|
||||||
|
|
||||||
fun shouldLog(level: LogLevel): Boolean {
|
fun shouldLog(level: LogLevel): Boolean {
|
||||||
return level.weight <= weight
|
return level.weight <= weight
|
||||||
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
package nl.kallestruik.darena.util
|
||||||
|
|
||||||
|
import org.bukkit.Color
|
||||||
|
import org.bukkit.Particle
|
||||||
|
import org.bukkit.World
|
||||||
|
|
||||||
|
object RenderUtil {
|
||||||
|
private const val AABB_PARTICLES_PER_BLOCK = 2
|
||||||
|
private const val PLANE_PARTICLES_PER_BLOCK = 1
|
||||||
|
|
||||||
|
fun drawXZPlane(world: World,
|
||||||
|
lowerX: Int, lowerZ: Int,
|
||||||
|
upperX: Int, upperZ: Int,
|
||||||
|
y: Int, color: Color) {
|
||||||
|
|
||||||
|
for (x in upperX downTo lowerX) {
|
||||||
|
for (z in upperZ downTo lowerZ) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
x + 0.5,
|
||||||
|
y + 0.5,
|
||||||
|
z + 0.5,
|
||||||
|
PLANE_PARTICLES_PER_BLOCK,
|
||||||
|
0.5,
|
||||||
|
0.0,
|
||||||
|
0.5,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun drawAABB(world: World,
|
||||||
|
lowerX: Int, lowerY: Int, lowerZ: Int,
|
||||||
|
upperX: Int, upperY: Int, upperZ: Int,
|
||||||
|
color: Color) {
|
||||||
|
// h--g
|
||||||
|
// /| /|
|
||||||
|
// e--f | y z
|
||||||
|
// | d|-c | /
|
||||||
|
// |/ |/ | /
|
||||||
|
// a--b 0 ---> x
|
||||||
|
//
|
||||||
|
// a = lower
|
||||||
|
// g = upper
|
||||||
|
|
||||||
|
|
||||||
|
// AB
|
||||||
|
for (x in upperX downTo lowerX) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
x + 0.5,
|
||||||
|
lowerY.toDouble(),
|
||||||
|
lowerZ.toDouble(),
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.5,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AD
|
||||||
|
for (z in upperZ downTo lowerZ) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
lowerX.toDouble(),
|
||||||
|
lowerY.toDouble(),
|
||||||
|
z + 0.5,
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.5,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AE
|
||||||
|
for (y in upperY downTo lowerY) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
lowerX.toDouble(),
|
||||||
|
y + 0.5,
|
||||||
|
lowerZ.toDouble(),
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.0,
|
||||||
|
0.5,
|
||||||
|
0.0,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GH
|
||||||
|
for (x in upperX downTo lowerX) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
x + 0.5,
|
||||||
|
upperY + 1.0,
|
||||||
|
upperZ + 1.0,
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.5,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FG
|
||||||
|
for (z in upperZ downTo lowerZ) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
upperX + 1.0,
|
||||||
|
upperY + 1.0,
|
||||||
|
z + 0.5,
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.5,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CG
|
||||||
|
for (y in upperY downTo lowerY) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
upperX + 1.0,
|
||||||
|
y + 0.5,
|
||||||
|
upperZ + 1.0,
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.0,
|
||||||
|
0.5,
|
||||||
|
0.0,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BF
|
||||||
|
for (y in upperY downTo lowerY) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
upperX + 1.0,
|
||||||
|
y + 0.5,
|
||||||
|
lowerZ.toDouble(),
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.0,
|
||||||
|
0.5,
|
||||||
|
0.0,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DH
|
||||||
|
for (y in upperY downTo lowerY) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
lowerX.toDouble(),
|
||||||
|
y + 0.5,
|
||||||
|
upperZ + 1.0,
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.0,
|
||||||
|
0.5,
|
||||||
|
0.0,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AF
|
||||||
|
for (x in upperX downTo lowerX) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
x + 0.5,
|
||||||
|
upperY + 1.0,
|
||||||
|
lowerZ.toDouble(),
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.5,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AH
|
||||||
|
for (z in upperZ downTo lowerZ) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
lowerX.toDouble(),
|
||||||
|
upperY + 1.0,
|
||||||
|
z + 0.5,
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.5,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BC
|
||||||
|
for (z in upperZ downTo lowerZ) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
upperX + 1.0,
|
||||||
|
lowerY.toDouble(),
|
||||||
|
z + 0.5,
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.5,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CD
|
||||||
|
for (x in upperX downTo lowerX) {
|
||||||
|
world.spawnParticle(
|
||||||
|
Particle.REDSTONE,
|
||||||
|
x + 0.5,
|
||||||
|
lowerY.toDouble(),
|
||||||
|
upperZ + 1.0,
|
||||||
|
AABB_PARTICLES_PER_BLOCK,
|
||||||
|
0.5,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Particle.DustOptions(color, 1.0F)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,4 +4,57 @@ main: nl.kallestruik.darena.DArena
|
||||||
api-version: 1.16
|
api-version: 1.16
|
||||||
depend:
|
depend:
|
||||||
- "DLib"
|
- "DLib"
|
||||||
commands: []
|
commands:
|
||||||
|
darena:
|
||||||
|
leaderboard:
|
||||||
|
permissions:
|
||||||
|
darena.debug:
|
||||||
|
default: op
|
||||||
|
darena.start:
|
||||||
|
default: op
|
||||||
|
darena.end:
|
||||||
|
default: op
|
||||||
|
darena.list:
|
||||||
|
default: op
|
||||||
|
darena.leaderboard:
|
||||||
|
default: true
|
||||||
|
darena.reload:
|
||||||
|
default: op
|
||||||
|
darena.new:
|
||||||
|
default: op
|
||||||
|
darena.edit.load:
|
||||||
|
default: op
|
||||||
|
darena.edit.save:
|
||||||
|
default: op
|
||||||
|
darena.edit.tp:
|
||||||
|
default: op
|
||||||
|
darena.edit.finish:
|
||||||
|
default: op
|
||||||
|
darena.team.create:
|
||||||
|
default: op
|
||||||
|
darena.team.join:
|
||||||
|
default: op
|
||||||
|
darena.team.leave:
|
||||||
|
default: op
|
||||||
|
darena.team.remove:
|
||||||
|
default: op
|
||||||
|
darena.team.remove-all:
|
||||||
|
default: op
|
||||||
|
darena.team.auto:
|
||||||
|
default: op
|
||||||
|
darena.team.option:
|
||||||
|
default: op
|
||||||
|
darena.team.list:
|
||||||
|
default: op
|
||||||
|
darena.points.add:
|
||||||
|
default: op
|
||||||
|
darena.points.remove:
|
||||||
|
default: op
|
||||||
|
darena.points.reset:
|
||||||
|
default: op
|
||||||
|
darena.edit.toggle.spawnplacing:
|
||||||
|
default: op
|
||||||
|
darena.edit.visualize.spawns:
|
||||||
|
default: op
|
||||||
|
darena.edit.visualize.checkpoints:
|
||||||
|
default: op
|
|
@ -35,14 +35,14 @@
|
||||||
#loadouts:
|
#loadouts:
|
||||||
# default:
|
# default:
|
||||||
# hotbar:
|
# hotbar:
|
||||||
# weapon:
|
# 0:
|
||||||
# material: iron_axe
|
# material: iron_axe
|
||||||
# amount: 1
|
# amount: 1
|
||||||
# enchantments:
|
# enchantments:
|
||||||
# name: sharpness
|
# name: sharpness
|
||||||
# level: 5
|
# level: 5
|
||||||
# unbreakable: true
|
# unbreakable: true
|
||||||
# food:
|
# 1:
|
||||||
# material: cooked_beef
|
# material: cooked_beef
|
||||||
# amount: 10
|
# amount: 10
|
||||||
# armor:
|
# armor:
|
||||||
|
@ -72,6 +72,7 @@
|
||||||
# y: 10
|
# y: 10
|
||||||
# z: 10
|
# z: 10
|
||||||
# spawnRule: "label1"
|
# spawnRule: "label1"
|
||||||
|
# move-player: false
|
||||||
# checkpoint2:
|
# checkpoint2:
|
||||||
# lower:
|
# lower:
|
||||||
# x: 20
|
# x: 20
|
||||||
|
@ -82,6 +83,7 @@
|
||||||
# y: 10
|
# y: 10
|
||||||
# z: 30
|
# z: 30
|
||||||
# spawnRule: "label2"
|
# spawnRule: "label2"
|
||||||
|
# move-player: false
|
||||||
# finish:
|
# finish:
|
||||||
# lower:
|
# lower:
|
||||||
# x: 40
|
# x: 40
|
||||||
|
@ -92,6 +94,7 @@
|
||||||
# y: 10
|
# y: 10
|
||||||
# z: 50
|
# z: 50
|
||||||
# spawnRule: "spectator"
|
# spawnRule: "spectator"
|
||||||
|
# move-player: true
|
||||||
#points:
|
#points:
|
||||||
# kill: 10 5
|
# kill: 10 5
|
||||||
# last-man-standing: 10 5 3 0
|
# last-man-standing: 10 5 3 0
|
||||||
|
@ -99,6 +102,11 @@
|
||||||
# checkpoint1: 10
|
# checkpoint1: 10
|
||||||
# checkpoint2: 5
|
# checkpoint2: 5
|
||||||
# finish: 20 10 5
|
# finish: 20 10 5
|
||||||
|
# pools:
|
||||||
|
# maps:
|
||||||
|
# - 10 5
|
||||||
|
# - 12 6
|
||||||
|
# - 14 7
|
||||||
#spawnRules:
|
#spawnRules:
|
||||||
# initial:
|
# initial:
|
||||||
# reuse-spawns: true
|
# reuse-spawns: true
|
||||||
|
@ -107,7 +115,7 @@
|
||||||
# strength: 1
|
# strength: 1
|
||||||
# duration: 10
|
# duration: 10
|
||||||
# hidden: false
|
# hidden: false
|
||||||
# invisability:
|
# invisibility:
|
||||||
# strength: 1
|
# strength: 1
|
||||||
# duration: 100000000
|
# duration: 100000000
|
||||||
# hidden: true
|
# hidden: true
|
||||||
|
@ -118,3 +126,62 @@
|
||||||
# reuse-spawns: true
|
# reuse-spawns: true
|
||||||
# spawns:
|
# spawns:
|
||||||
# - "spectatorSpawn"
|
# - "spectatorSpawn"
|
||||||
|
# map1:
|
||||||
|
# reuse-spawns: false
|
||||||
|
# reuse-individual: true
|
||||||
|
# spawns:
|
||||||
|
# - "exampleSpawn1"
|
||||||
|
# - "exampleSpawn2"
|
||||||
|
# - "exampleSpawn3"
|
||||||
|
# map2:
|
||||||
|
# reuse-spawns: false
|
||||||
|
# reuse-individual: true
|
||||||
|
# spawns:
|
||||||
|
# - "exampleSpawn1"
|
||||||
|
# - "exampleSpawn2"
|
||||||
|
# - "exampleSpawn3"
|
||||||
|
# map3:
|
||||||
|
# reuse-spawns: false
|
||||||
|
# reuse-individual: true
|
||||||
|
# spawns:
|
||||||
|
# - "exampleSpawn1"
|
||||||
|
# - "exampleSpawn2"
|
||||||
|
# - "exampleSpawn3"
|
||||||
|
# map4:
|
||||||
|
# reuse-spawns: false
|
||||||
|
# reuse-individual: true
|
||||||
|
# spawns:
|
||||||
|
# - "exampleSpawn1"
|
||||||
|
# - "exampleSpawn2"
|
||||||
|
# - "exampleSpawn3"
|
||||||
|
# map5:
|
||||||
|
# reuse-spawns: false
|
||||||
|
# reuse-individual: true
|
||||||
|
# spawns:
|
||||||
|
# - "exampleSpawn1"
|
||||||
|
# - "exampleSpawn2"
|
||||||
|
# - "exampleSpawn3"
|
||||||
|
#spawn-pools:
|
||||||
|
# maps:
|
||||||
|
# times: 3
|
||||||
|
# rules:
|
||||||
|
# - map1
|
||||||
|
# - map2
|
||||||
|
# - map3
|
||||||
|
# - map4
|
||||||
|
# - map5
|
||||||
|
#borders:
|
||||||
|
# vertical:
|
||||||
|
# - CENTER 0 0
|
||||||
|
# - SET 100.0
|
||||||
|
# - WAIT 400
|
||||||
|
# - TRANSITION 80.5 200
|
||||||
|
# top:
|
||||||
|
# - SET 256.0
|
||||||
|
# bottom:
|
||||||
|
# - SET 80.0
|
||||||
|
#end-conditions:
|
||||||
|
# players-left: 1
|
||||||
|
# time: 60
|
||||||
|
# players-finished: 5
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
spawn-pos:
|
||||||
|
x: 0
|
||||||
|
y: 100
|
||||||
|
z: 0
|
||||||
|
spawn-world: world
|
||||||
|
|
||||||
|
cooldown-fireball: 30
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
auto-teams: true
|
Loading…
Reference in New Issue