Lots of things
parent
cbf1e64db9
commit
95391de3b7
|
@ -1,2 +1,3 @@
|
|||
/build/
|
||||
/.gradle/
|
||||
/.idea/
|
||||
|
|
|
@ -31,5 +31,10 @@
|
|||
<option name="name" value="maven2" />
|
||||
<option name="url" value="https://repo.aikar.co/content/groups/aikar/" />
|
||||
</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>
|
||||
</project>
|
|
@ -14,6 +14,9 @@
|
|||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</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">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.5.31"
|
||||
kotlin("jvm") version "1.6.10"
|
||||
id("com.github.johnrengelman.shadow") version "7.1.0"
|
||||
kotlin("plugin.serialization") version "1.6.10"
|
||||
}
|
||||
|
||||
group = "nl.kallestruik"
|
||||
|
@ -11,6 +12,7 @@ version = "2.0"
|
|||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
|
||||
maven("https://papermc.io/repo/repository/maven-public/")
|
||||
maven("https://repo.aikar.co/content/groups/aikar/")
|
||||
|
@ -19,7 +21,9 @@ repositories {
|
|||
dependencies {
|
||||
implementation("co.aikar:acf-paper:0.5.0-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("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
|
||||
}
|
||||
|
||||
tasks.compileJava {
|
||||
|
|
|
@ -1,89 +1,89 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
|
|
@ -1,25 +1,174 @@
|
|||
package nl.kallestruik.darena
|
||||
|
||||
import nl.kallestruik.darena.managers.ConfigManager
|
||||
import nl.kallestruik.darena.managers.PointsManager
|
||||
import nl.kallestruik.darena.managers.TeamManager
|
||||
import co.aikar.commands.BukkitCommandExecutionContext
|
||||
import co.aikar.commands.InvalidCommandArgument
|
||||
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 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 pointsManager: PointsManager
|
||||
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() {
|
||||
commandManager = PaperCommandManager(this)
|
||||
commandManager.enableUnstableAPI("help")
|
||||
commandManager.enableUnstableAPI("brigadier")
|
||||
|
||||
|
||||
|
||||
configManager = ConfigManager(File(dataFolder, "config.yml"))
|
||||
configManager.load()
|
||||
|
||||
teamManager = TeamManager(File(dataFolder, "teams.yml"))
|
||||
teamManager.load()
|
||||
|
||||
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() {
|
||||
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
|
||||
|
||||
import net.kyori.adventure.text.Component
|
||||
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.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.potion.PotionEffect
|
||||
import org.bukkit.potion.PotionEffectType
|
||||
import org.bukkit.scheduler.BukkitRunnable
|
||||
|
||||
class Arena(
|
||||
private val config: ArenaConfig,
|
||||
private val arenaUtil: ArenaUtil,
|
||||
val config: ArenaConfig,
|
||||
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
|
||||
// Simple stuff done: 0.001474s
|
||||
// createArena start: 0.0001026s
|
||||
// Loaded schematic: 0.000162s
|
||||
// Created new world: 3.122034s
|
||||
// Loaded world: 0.0075989s
|
||||
// Placed arena: 2.3205267s
|
||||
// Fixed light: 4.2093585s
|
||||
// createArena done: 0.0002953
|
||||
// Countdown done: 5.0126158
|
||||
// Fully done: 0.0982557
|
||||
var session: ArenaSession? = null
|
||||
var pointsSession: PointsSession? = null
|
||||
|
||||
fun start(loadingTimeOverride: Int, spectatorTimeOverride: Int, beforeStartTimeOverride: Int) {
|
||||
val loadingTime = if (loadingTimeOverride != -1) loadingTimeOverride else config.countdown.loadingTime
|
||||
val spectatorTime = if (spectatorTimeOverride != -1) spectatorTimeOverride else config.countdown.spectatorTime
|
||||
val beforeStartTime = if (beforeStartTimeOverride != -1) beforeStartTimeOverride else config.countdown.beforeStartTime
|
||||
|
||||
fun start() {
|
||||
//TODO: Redo everything in here.
|
||||
// Create a new session
|
||||
session = ArenaSession()
|
||||
pointsSession = PointsSession(teamManager, pointsManager, this)
|
||||
// Add all participants and spectators
|
||||
session.participants.addAll(arenaUtil.getPossibleParticipants());
|
||||
session.spectators.addAll(arenaUtil.getPossibleSpectators());
|
||||
// Reset the world
|
||||
world.reset()
|
||||
session!!.originalParticipants.addAll(ArenaUtil.getPossibleParticipants())
|
||||
session!!.participants.addAll(session!!.originalParticipants)
|
||||
session!!.spectators.addAll(ArenaUtil.getPossibleSpectators())
|
||||
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
|
||||
session.spectators.forEach {
|
||||
// config.spectatorSpawn.spawn(world, it)
|
||||
}
|
||||
// Spawn everyone as a spectator so they can explore the map.
|
||||
for (player in session!!.allPlayers) {
|
||||
player.gameMode = GameMode.SPECTATOR
|
||||
}
|
||||
|
||||
// Randomize spawns
|
||||
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()
|
||||
val spectatorSpawnRule = session!!.spawnRules["spectator"] ?: throw ArenaStartAbortedException("Missing spawn rule \"spectator\". This rule is required.")
|
||||
|
||||
// Spawn all the players
|
||||
session.participants.forEachIndexed { index, player ->
|
||||
// TODO: Freeze players in place (for in arena countdown) (if countdown is 0 dont freeze them)
|
||||
config.spawns[session.initialSpawns[index % session.initialSpawns.size]]!!.spawn(world, player)
|
||||
}
|
||||
// TODO:
|
||||
spectatorSpawnRule.spawnPlayers(session!!.allPlayers, world)
|
||||
|
||||
session!!.currentCountdown = AsyncCountdown(plugin, spectatorTime, "Starting in", alwaysAsActionBar = true) { //TODO: Make the subtitle configurable
|
||||
for (participant in session!!.participants) {
|
||||
if (config.features.contains(ArenaFeature.GAMEMODE_ADVENTURE))
|
||||
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() {
|
||||
world.reset()
|
||||
fun startTimers() {
|
||||
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
|
||||
|
||||
import nl.kallestruik.darena.types.arena.ArenaLoadout
|
||||
import nl.kallestruik.darena.types.arena.ArenaCheckpoint
|
||||
import nl.kallestruik.darena.types.arena.ArenaPoints
|
||||
import nl.kallestruik.darena.types.arena.ArenaSpawn
|
||||
import nl.kallestruik.darena.types.arena.ArenaSpawnRule
|
||||
import nl.kallestruik.darena.util.ConfigHelper
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import nl.kallestruik.darena.types.ArenaFeature
|
||||
import nl.kallestruik.darena.types.arena.*
|
||||
import nl.kallestruik.darena.util.Logger
|
||||
import nl.kallestruik.dlib.config.annotations.Description
|
||||
import java.io.File
|
||||
import org.bukkit.configuration.InvalidConfigurationException
|
||||
import org.bukkit.configuration.file.YamlConfiguration
|
||||
import kotlin.collections.mutableMapOf
|
||||
|
||||
@Serializable
|
||||
data class ArenaConfig(
|
||||
@Description(["The name of the arena."])
|
||||
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(),
|
||||
@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 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 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? {
|
||||
val config = YamlConfiguration()
|
||||
config.set("name", name)
|
||||
@Transient
|
||||
lateinit var file: File
|
||||
|
||||
ConfigHelper.saveMap(ConfigHelper.getOrCreateSection(config, "spawns"), spawns)
|
||||
ConfigHelper.saveMap(ConfigHelper.getOrCreateSection(config, "loadouts"), loadouts)
|
||||
ConfigHelper.saveMap(ConfigHelper.getOrCreateSection(config, "checkpoints"), checkpoints)
|
||||
points.save(ConfigHelper.getOrCreateSection(config, "points"))
|
||||
ConfigHelper.saveMap(ConfigHelper.getOrCreateSection(config, "spawnRules"), spawnRules)
|
||||
fun save(toString: Boolean = false): String? {
|
||||
val json = Json {
|
||||
prettyPrint = true
|
||||
}.encodeToString(this)
|
||||
|
||||
if (toString) {
|
||||
return config.saveToString()
|
||||
return json
|
||||
}
|
||||
|
||||
config.save(file)
|
||||
file.writeText(json, Charsets.UTF_8)
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Throws(InvalidConfigurationException::class)
|
||||
fun load(file: File): ArenaConfig {
|
||||
val config = ConfigHelper.getOrCreateConfig(file, "template/arena.yml")
|
||||
if (!config.contains("name")) {
|
||||
throw InvalidConfigurationException("The arena configuration file '${file.name}' does not contain the required attribute 'name'")
|
||||
}
|
||||
Logger.trace(ArenaConfig::class, "load(file: ${file.path})")
|
||||
|
||||
val arenaConfig = ArenaConfig(config.getString("name")!!, file)
|
||||
val config = Json.decodeFromString<ArenaConfig>(file.readText(Charsets.UTF_8))
|
||||
config.file = file
|
||||
|
||||
if (config.contains("spawns")) {
|
||||
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
|
||||
return config
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,81 @@
|
|||
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 java.util.UUID
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
import java.util.*
|
||||
|
||||
data class ArenaSession(
|
||||
val participants: MutableList<Player> = ArrayList(),
|
||||
val spectators: MutableList<Player> = ArrayList(),
|
||||
val completedObjective: MutableList<UUID> = ArrayList(),
|
||||
val deaths: MutableList<UUID> = ArrayList(),
|
||||
val initialSpawns: MutableList<String> = ArrayList(),
|
||||
)
|
||||
val originalParticipants: MutableList<Player> = mutableListOf(),
|
||||
val participants: MutableList<Player> = mutableListOf(),
|
||||
val spectators: MutableList<Player> = mutableListOf(),
|
||||
val allPlayers: MutableList<Player> = mutableListOf(),
|
||||
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.ArenaWorldLoadException
|
||||
import nl.kallestruik.darena.exceptions.ArenaWorldSaveException
|
||||
import nl.kallestruik.darena.managers.ConfigManager
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.World
|
||||
import org.bukkit.WorldCreator
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.exists
|
||||
|
||||
|
||||
class ArenaWorld(
|
||||
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 {
|
||||
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\".")
|
||||
}
|
||||
|
||||
@Throws(ArenaWorldLoadException::class)
|
||||
fun reset() {
|
||||
//TODO: Teleport everyone out.
|
||||
empty()
|
||||
Bukkit.unloadWorld(world, false)
|
||||
load()
|
||||
createWorld()
|
||||
}
|
||||
|
||||
@Throws(ArenaWorldSaveException::class)
|
||||
fun save() {
|
||||
world.save()
|
||||
try {
|
||||
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 ->
|
||||
val destination = Path.of(
|
||||
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)
|
||||
}
|
||||
|
@ -56,7 +78,10 @@ class ArenaWorld(
|
|||
private fun load() {
|
||||
try {
|
||||
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 ->
|
||||
Files.delete(path)
|
||||
}
|
||||
|
@ -65,9 +90,12 @@ class ArenaWorld(
|
|||
Files.walk(loadPath).use { walk ->
|
||||
walk.forEach { source ->
|
||||
val destination = Path.of(
|
||||
world.worldFolder.path, source.toString()
|
||||
worldFolder.path, source.toString()
|
||||
.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)
|
||||
}
|
||||
|
@ -76,4 +104,11 @@ class ArenaWorld(
|
|||
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
|
||||
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import nl.kallestruik.darena.arenas.Arena
|
||||
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 java.io.File
|
||||
import java.nio.file.Files
|
||||
|
||||
class ArenaManager(
|
||||
private val arenaUtil: ArenaUtil,
|
||||
private val teamManager: TeamManager,
|
||||
private val pointsManager: PointsManager,
|
||||
private val configManager: ConfigManager,
|
||||
private val plugin: JavaPlugin,
|
||||
) {
|
||||
private val arenas: MutableList<Arena> = ArrayList()
|
||||
private val arenaFolder: File,
|
||||
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 ->
|
||||
walk.forEach { path ->
|
||||
arenas.add(Arena(
|
||||
ArenaConfig.load(path.toFile()),
|
||||
arenaUtil,
|
||||
plugin)
|
||||
if (Files.isDirectory(path))
|
||||
return@forEach
|
||||
|
||||
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
|
||||
|
||||
import nl.kallestruik.darena.types.Reloadable
|
||||
import nl.kallestruik.darena.types.arena.ArenaLocation
|
||||
import nl.kallestruik.darena.util.ConfigHelper
|
||||
import java.io.File
|
||||
|
||||
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() {
|
||||
//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
|
||||
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.format.NamedTextColor
|
||||
import nl.kallestruik.darena.exceptions.TeamNotFoundException
|
||||
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.util.*
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
class PointsManager(
|
||||
val teamManager: TeamManager,
|
||||
|
@ -13,24 +19,59 @@ class PointsManager(
|
|||
private val points: MutableMap<Team, Int> = HashMap()
|
||||
|
||||
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)
|
||||
fun givePointsToPlayer(uuid: UUID, amount: Int, reason: String) {
|
||||
val team = teamManager.getTeamForPlayer(uuid)
|
||||
fun givePointsToPlayer(uuid: UUID, amount: Int) {
|
||||
val team = teamManager.getTeamForPlayer(uuid) ?: throw TeamNotFoundException()
|
||||
|
||||
givePointsToTeam(team, amount, reason)
|
||||
givePointsToTeam(team, amount)
|
||||
}
|
||||
|
||||
fun givePointsToTeam(team: Team, amount: Int, reason: String) {
|
||||
points[team] = (points[team]?: 0) + amount
|
||||
//TODO: Send message with reason to team members (and everyone else?)
|
||||
fun givePointsToTeam(team: Team, amount: Int) {
|
||||
points[team] = (points[team] ?: 0) + amount
|
||||
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)
|
||||
fun getPointsForPlayer(uuid: UUID): Int {
|
||||
val team = teamManager.getTeamForPlayer(uuid)
|
||||
val team = teamManager.getTeamForPlayer(uuid) ?: throw TeamNotFoundException()
|
||||
|
||||
return getPointsForTeam(team)
|
||||
}
|
||||
|
@ -39,5 +80,31 @@ class PointsManager(
|
|||
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
|
||||
|
||||
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.TeamNotFoundException
|
||||
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.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
class TeamManager(
|
||||
val teamsFile: File
|
||||
|
@ -15,37 +18,91 @@ class TeamManager(
|
|||
private val teams: MutableMap<String, Team> = HashMap()
|
||||
private val teamMembers: MutableMap<Team, MutableList<UUID>> = HashMap()
|
||||
private val teamForPlayer: MutableMap<UUID, Team> = HashMap()
|
||||
var autoTeams: Boolean = true
|
||||
|
||||
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)
|
||||
fun createTeam(teamName: String) {
|
||||
fun createTeam(teamName: String): Team {
|
||||
if (teams.containsKey(teamName))
|
||||
throw TeamExistsException("The team $teamName already exists!")
|
||||
|
||||
teams[teamName] = Team(teamName)
|
||||
val team = Team(teamName)
|
||||
teams[teamName] = team
|
||||
return team
|
||||
}
|
||||
|
||||
@Throws(TeamNotFoundException::class)
|
||||
fun setTeamColor(teamName: String, color: TextColor) {
|
||||
val team = getTeamByName(teamName)
|
||||
|
||||
team.color = color
|
||||
fun removeTeam(team: Team) {
|
||||
teams.remove(team.name)
|
||||
for (entry in teamForPlayer) {
|
||||
if (entry.value == team) {
|
||||
removePlayerFromTeam(entry.key)
|
||||
}
|
||||
}
|
||||
team.mcTeam.unregister()
|
||||
}
|
||||
|
||||
@Throws(TeamNotFoundException::class)
|
||||
fun addPlayerToTeam(teamName: String, uuid: UUID) {
|
||||
Logger.trace(TeamManager::class, "addPlayerToTeam(teamName: $teamName, uuid: $uuid)")
|
||||
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)
|
||||
teamForPlayer[uuid] = team
|
||||
|
||||
val player = Bukkit.getPlayer(uuid)
|
||||
if (player != null)
|
||||
team.mcTeam.addEntry(player.name)
|
||||
}
|
||||
|
||||
@Throws(TeamNotFoundException::class)
|
||||
fun getTeamForPlayer(uuid: UUID): Team {
|
||||
return teamForPlayer[uuid] ?: throw TeamNotFoundException("Player with uuid $uuid is not part of a team!")
|
||||
fun getTeamForPlayer(uuid: UUID): Team? {
|
||||
return teamForPlayer[uuid]
|
||||
}
|
||||
|
||||
@Throws(TeamNotFoundException::class)
|
||||
|
@ -56,6 +113,51 @@ class TeamManager(
|
|||
@Throws(TeamNotFoundException::class)
|
||||
fun getTeamMembers(teamName: String): List<UUID> {
|
||||
val team = getTeamByName(teamName)
|
||||
return getTeamMembers(team)
|
||||
}
|
||||
|
||||
fun getTeamMembers(team: Team): List<UUID> {
|
||||
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
|
||||
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.format.TextColor
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
|
||||
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)
|
||||
)
|
||||
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
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
import javax.naming.ConfigurationException
|
||||
import nl.kallestruik.darena.types.ConfigSaveable
|
||||
import nl.kallestruik.darena.types.ConfigLoadable
|
||||
import nl.kallestruik.darena.util.ConfigHelper
|
||||
import nl.kallestruik.darena.util.Logger
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import nl.kallestruik.dlib.config.annotations.DefaultBoolean
|
||||
import nl.kallestruik.dlib.config.annotations.Description
|
||||
|
||||
@Serializable
|
||||
data class ArenaCheckpoint(
|
||||
@Description(["The name of the checkpoint."])
|
||||
val label: String,
|
||||
@Description(["The lower corner of the checkpoint area."])
|
||||
val lower: ArenaLocation,
|
||||
@Description(["The upper corner of the checkpoint area."])
|
||||
val upper: ArenaLocation,
|
||||
val spawn: String,
|
||||
): ConfigSaveable {
|
||||
override fun save(section: ConfigurationSection) {
|
||||
Logger.trace(ArenaCheckpoint::class, "save(section: ${section.currentPath})")
|
||||
section.set("label", label)
|
||||
lower.save(ConfigHelper.getOrCreateSection(section, "lower"))
|
||||
upper.save(ConfigHelper.getOrCreateSection(section, "upper"))
|
||||
section.set("spawn", spawn)
|
||||
}
|
||||
|
||||
companion object: ConfigLoadable<ArenaCheckpoint> {
|
||||
override fun load(section: ConfigurationSection): ArenaCheckpoint {
|
||||
Logger.trace(ArenaCheckpoint::class, "load(section: ${section.currentPath})")
|
||||
return ArenaCheckpoint(
|
||||
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}'")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@SerialName("spawn-rule")
|
||||
@Description(["The spawn rule to use for this checkpoint when respawning or moving players."])
|
||||
var spawnRule: String = "",
|
||||
@SerialName("spawn-pool")
|
||||
@Description(["The spawn pool to use for this checkpoint when respawning or moving players."])
|
||||
val spawnPool: String = "",
|
||||
@SerialName("move-player")
|
||||
@Description(["Whether or not to teleport players to the spawn-rule/spawn-pool when they reach the checkpoint."])
|
||||
@DefaultBoolean(false)
|
||||
val movePlayer: Boolean = false,
|
||||
@SerialName("is-finish")
|
||||
@Description(["Whether or not this checkpoint counts as a finish. If it does players will be placed into spectator mode upon reaching it."])
|
||||
@DefaultBoolean(false)
|
||||
val isFinish: Boolean = false,
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
import org.bukkit.configuration.InvalidConfigurationException
|
||||
import nl.kallestruik.darena.types.ConfigListSaveable
|
||||
import nl.kallestruik.darena.types.ConfigListLoadable
|
||||
import kotlinx.serialization.Serializable
|
||||
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(
|
||||
@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,
|
||||
@Description(["The level of the enchantment. Allows over enchanting."])
|
||||
@MinInt(1)
|
||||
@DefaultInt(1)
|
||||
val level: Int,
|
||||
): ConfigListSaveable {
|
||||
override fun getKey(): String {
|
||||
Logger.trace(ArenaEnchantment::class, "getKey()")
|
||||
return name
|
||||
}
|
||||
) {
|
||||
fun addToItem(stack: ItemStack) {
|
||||
Logger.trace(ArenaEnchantment::class, "addToItem(stack: $stack)")
|
||||
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
|
||||
|
||||
import nl.kallestruik.darena.util.ConfigHelper
|
||||
import nl.kallestruik.darena.types.ConfigSaveable
|
||||
import nl.kallestruik.darena.types.ConfigLoadable
|
||||
import com.destroystokyo.paper.Namespaced
|
||||
import kotlinx.serialization.Serializable
|
||||
import nl.kallestruik.darena.util.Logger
|
||||
import nl.kallestruik.dlib.config.annotations.*
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
import org.bukkit.NamespacedKey
|
||||
import org.bukkit.inventory.ItemStack
|
||||
|
||||
@Serializable
|
||||
data class ArenaItem(
|
||||
@Description(["The item type."])
|
||||
@DefaultString("AIR")
|
||||
val material: Material = Material.AIR,
|
||||
@Description(["The amount."])
|
||||
@MinInt(1)
|
||||
@DefaultInt(1)
|
||||
val amount: Int = 1,
|
||||
@Description(["The enchantments that will be applied to the item."])
|
||||
val enchantments: List<ArenaEnchantment> = listOf(),
|
||||
@Description(["Whether the item takes durability damage or not."])
|
||||
@DefaultBoolean(false)
|
||||
val unbreakable: Boolean = false,
|
||||
): ConfigSaveable {
|
||||
override fun save(section: ConfigurationSection) {
|
||||
section.set("material", material.toString())
|
||||
section.set("amount", amount)
|
||||
ConfigHelper.saveList(ConfigHelper.getOrCreateSection(section, "enchantments"), enchantments, ArenaEnchantment)
|
||||
section.set("unbreakable", unbreakable)
|
||||
}
|
||||
|
||||
companion object: ConfigLoadable<ArenaItem> {
|
||||
override fun load(section: ConfigurationSection): ArenaItem {
|
||||
val enchantments = section.getConfigurationSection("enchantments")?.let {
|
||||
ArenaEnchantment.loadList(it)
|
||||
} ?: listOf()
|
||||
|
||||
return ArenaItem(
|
||||
ConfigHelper.matchMaterial(section.getString("material")!!),
|
||||
section.getInt("amount", 1),
|
||||
enchantments,
|
||||
section.getBoolean("unbreakable", false)
|
||||
)
|
||||
@Description(["A list of blocks that this block can be placed on. Requires the feature GAMEMODE_ADVENTURE to work.",
|
||||
"", "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 placeOn: Set<String> = mutableSetOf(),
|
||||
@Description(["A list of blocks that this item can be break. Requires the feature GAMEMODE_ADVENTURE to work.",
|
||||
"", "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(),
|
||||
) {
|
||||
fun asItemStack(): ItemStack {
|
||||
Logger.trace(ArenaItem::class, "asItemStack()")
|
||||
val stack = ItemStack(material, amount)
|
||||
for (enchantment in enchantments) {
|
||||
enchantment.addToItem(stack)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
import nl.kallestruik.darena.util.ConfigHelper
|
||||
import nl.kallestruik.darena.types.ConfigSaveable
|
||||
import nl.kallestruik.darena.types.ConfigLoadable
|
||||
import kotlinx.serialization.Serializable
|
||||
import nl.kallestruik.dlib.config.annotations.AllowedKeys
|
||||
import nl.kallestruik.dlib.config.annotations.AllowedKeysExclusive
|
||||
import nl.kallestruik.dlib.config.annotations.Description
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.EquipmentSlot
|
||||
|
||||
@Serializable
|
||||
data class ArenaLoadout(
|
||||
val name: String,
|
||||
val hotbar: List<ArenaItem>,
|
||||
val armor: List<ArenaItem>,
|
||||
val offhand: ArenaItem,
|
||||
): ConfigSaveable {
|
||||
override fun save(section: ConfigurationSection) {
|
||||
ConfigHelper.saveList(ConfigHelper.getOrCreateSection(section, "hotbar"), hotbar)
|
||||
ConfigHelper.saveList(ConfigHelper.getOrCreateSection(section, "armor"), armor)
|
||||
offhand.save(ConfigHelper.getOrCreateSection(section, "offhand"))
|
||||
}
|
||||
@Description(["The items that will be in the players hotbar."])
|
||||
@AllowedKeys(["0", "1", "2", "3", "4", "5", "6", "7", "8"])
|
||||
val hotbar: MutableMap<String, ArenaItem> = mutableMapOf(),
|
||||
@Description(["The items that will be in the players armor slots."])
|
||||
@AllowedKeysExclusive(["helmet", "head"])
|
||||
@AllowedKeysExclusive(["chestplate", "chest", "elytra"])
|
||||
@AllowedKeysExclusive(["leggings", "legs", "pants"])
|
||||
@AllowedKeysExclusive(["boots", "feet"])
|
||||
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> {
|
||||
override fun load(section: ConfigurationSection): ArenaLoadout {
|
||||
for (entry in armor) {
|
||||
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 {
|
||||
ArenaItem.loadList(it)
|
||||
}?: listOf()
|
||||
if (offhand != null)
|
||||
player.inventory.setItemInOffHand(offhand.asItemStack())
|
||||
|
||||
val armor = section.getConfigurationSection("armor")?.let {
|
||||
ArenaItem.loadList(it)
|
||||
}?: listOf()
|
||||
|
||||
val offhand = section.getConfigurationSection("offhand")?.let {
|
||||
ArenaItem.load(it)
|
||||
}?: ArenaItem()
|
||||
|
||||
return ArenaLoadout(
|
||||
section.name,
|
||||
hotbar,
|
||||
armor,
|
||||
offhand,
|
||||
)
|
||||
for (effect in effects) {
|
||||
effect.apply(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
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 nl.kallestruik.darena.types.ConfigSaveable
|
||||
import nl.kallestruik.darena.types.ConfigLoadable
|
||||
|
||||
@Serializable
|
||||
data class ArenaLocation(
|
||||
@Description(["The x coordinate."])
|
||||
@DefaultInt(0)
|
||||
val x: Int,
|
||||
@Description(["The y coordinate."])
|
||||
@DefaultInt(0)
|
||||
val y: Int,
|
||||
@Description(["The z coordinate."])
|
||||
@DefaultInt(0)
|
||||
val z: Int
|
||||
): ConfigSaveable {
|
||||
override fun save(section: ConfigurationSection) {
|
||||
section.set("x", x)
|
||||
section.set("y", y)
|
||||
section.set("z", z)
|
||||
}
|
||||
|
||||
companion object: ConfigLoadable<ArenaLocation> {
|
||||
override fun load(section: ConfigurationSection): ArenaLocation {
|
||||
) {
|
||||
companion object {
|
||||
fun load(section: ConfigurationSection): ArenaLocation {
|
||||
return ArenaLocation(
|
||||
section.getInt("x", 0),
|
||||
section.getInt("y", 0),
|
||||
|
|
|
@ -1,31 +1,23 @@
|
|||
package nl.kallestruik.darena.types.arena
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
import nl.kallestruik.darena.util.ConfigHelper
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import nl.kallestruik.dlib.config.annotations.Description
|
||||
|
||||
@Serializable
|
||||
data class ArenaPoints(
|
||||
@Description(["The points given to a player when they get a kill."])
|
||||
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 checkpoints: Map<String, List<Int>> = mapOf()
|
||||
) {
|
||||
fun save(section: ConfigurationSection) {
|
||||
section.set("kill", ConfigHelper.intListToString(kill))
|
||||
section.set("lastManStanding", ConfigHelper.intListToString(lastManStanding))
|
||||
|
||||
val checkpointSection = ConfigHelper.getOrCreateSection(section, "checkpoints")
|
||||
for (entry in checkpoints) {
|
||||
checkpointSection.set(entry.key, ConfigHelper.intListToString(entry.value))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun load(section: ConfigurationSection): ArenaPoints {
|
||||
return ArenaPoints(
|
||||
ConfigHelper.parseIntList(section.getString("kill")!!),
|
||||
ConfigHelper.parseIntList(section.getString("lastManStanding")!!),
|
||||
ConfigHelper.parseStringIntListMap(section.getConfigurationSection("checkpoints")!!)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@Description(["The points given to a player for reaching a checkpoint.",
|
||||
"","Valid keys: the label of a checkpoint defined in the checkpoints section."])
|
||||
val checkpoints: Map<String, List<Int>> = mapOf(),
|
||||
@Description(["The points given to a player for reaching a checkpoint that is part of a spawn pool.",
|
||||
"The first list represents the first checkpoint, the second the second checkpoint, etc",
|
||||
"",
|
||||
"Valid keys: the names of spawn pools as defined in the spawn-pools section.",
|
||||
])
|
||||
val pools: Map<String, List<List<Int>>> = mapOf(),
|
||||
)
|
||||
|
|
|
@ -1,28 +1,30 @@
|
|||
package nl.kallestruik.darena.types.arena
|
||||
|
||||
import nl.kallestruik.darena.types.ConfigSaveable
|
||||
import nl.kallestruik.darena.types.ConfigLoadable
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
import kotlinx.serialization.Serializable
|
||||
import nl.kallestruik.dlib.config.annotations.Description
|
||||
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,
|
||||
@Description(["The strength of the potion effect."])
|
||||
@MinInt(0)
|
||||
@MaxInt(255)
|
||||
val strength: Int,
|
||||
@Description(["The duration of the potion effect in ticks. (20 ticks = 1 second)"])
|
||||
@MinInt(0)
|
||||
val duration: Int,
|
||||
@Description(["Whether the potion effect should be hidden from the player. This hides the particles and the icon."])
|
||||
val hidden: Boolean
|
||||
): ConfigSaveable {
|
||||
override fun save(section: ConfigurationSection) {
|
||||
section.set("strength", strength)
|
||||
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"))
|
||||
}
|
||||
) {
|
||||
fun apply(player: Player) {
|
||||
player.addPotionEffect(PotionEffect(PotionEffectType.getByName(name)!!, duration, strength, !hidden, !hidden, !hidden))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,46 +1,44 @@
|
|||
package nl.kallestruik.darena.types.arena
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import nl.kallestruik.darena.arenas.world.ArenaWorld
|
||||
import nl.kallestruik.darena.util.ConfigHelper
|
||||
import nl.kallestruik.darena.types.ConfigSaveable
|
||||
import nl.kallestruik.darena.types.ConfigLoadable
|
||||
import nl.kallestruik.darena.util.Logger
|
||||
import nl.kallestruik.dlib.config.annotations.DefaultFloat
|
||||
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.configuration.ConfigurationSection
|
||||
import org.bukkit.World
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
@Serializable
|
||||
data class ArenaSpawn(
|
||||
val label: String,
|
||||
@Description(["The x coordinate of the spawn point."])
|
||||
@DefaultFloat(0.0)
|
||||
val x: Double,
|
||||
@Description(["The y coordinate of the spawn point."])
|
||||
@DefaultFloat(0.0)
|
||||
val y: Double,
|
||||
@Description(["The z coordinate of the spawn point."])
|
||||
@DefaultFloat(0.0)
|
||||
val z: Double,
|
||||
@Description(["The yaw of the spawn point."])
|
||||
@MinFloat(-180.0)
|
||||
@MaxFloat(180.0)
|
||||
@DefaultFloat(0.0)
|
||||
val yaw: Float,
|
||||
val pitch: Float,
|
||||
val loadout: String
|
||||
): ConfigSaveable {
|
||||
@Description(["The pitch of the spawn point."])
|
||||
@MinFloat(-90.0)
|
||||
@MaxFloat(90.0)
|
||||
@DefaultFloat(0.0)
|
||||
val pitch: Float
|
||||
) {
|
||||
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) {
|
||||
section.set("x", x)
|
||||
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")!!
|
||||
)
|
||||
}
|
||||
fun toLocation(world: World): Location {
|
||||
return Location(world, x, y, z, yaw, pitch)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
import nl.kallestruik.darena.types.ConfigLoadable
|
||||
import nl.kallestruik.darena.types.ConfigSaveable
|
||||
import nl.kallestruik.darena.util.ConfigHelper
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import nl.kallestruik.dlib.config.annotations.DefaultBoolean
|
||||
import nl.kallestruik.dlib.config.annotations.DefaultString
|
||||
import nl.kallestruik.dlib.config.annotations.Description
|
||||
|
||||
@Serializable
|
||||
class ArenaSpawnRule(
|
||||
val reuseSpawns: Boolean,
|
||||
val effects: Map<String, ArenaPotionEffect>,
|
||||
val spawns: List<String>
|
||||
): ConfigSaveable {
|
||||
override fun save(section: ConfigurationSection) {
|
||||
section.set("reuseSpawns", reuseSpawns)
|
||||
ConfigHelper.saveMap(ConfigHelper.getOrCreateSection(section, "effects"), effects)
|
||||
section.set("spawns", spawns)
|
||||
}
|
||||
|
||||
companion object: ConfigLoadable<ArenaSpawnRule> {
|
||||
override fun load(section: ConfigurationSection): ArenaSpawnRule {
|
||||
return ArenaSpawnRule(
|
||||
section.getBoolean("reuseSpawn", true),
|
||||
ConfigHelper.loadMap(section.getConfigurationSection("effects")!!, ArenaPotionEffect),
|
||||
section.getStringList("spawns"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@SerialName("reuse-spawns")
|
||||
@Description(["Whether or not to reuse the spawns for different players."])
|
||||
@DefaultBoolean(true)
|
||||
val reuseSpawns: Boolean = true,
|
||||
@SerialName("reuse-individual")
|
||||
@Description(["Whether or not to reuse the spawns for individual players.",
|
||||
"When this is set a player will always use the same spawn after spawning there once."])
|
||||
@DefaultBoolean(false)
|
||||
val reuseIndividual: Boolean = false,
|
||||
@Description(["The list of spawns that this spawn rule can pick from.",
|
||||
"", "Valid values: any spawn name defined in the spawns section."])
|
||||
val spawns: MutableList<String> = mutableListOf(),
|
||||
@Description(["The loadout to use for players that are spawned using this spawn rule. Use none to not give them any items.",
|
||||
"", "Valid values: any loadout defined in the loadouts section or none."])
|
||||
@DefaultString("none")
|
||||
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
|
||||
|
||||
import nl.kallestruik.darena.arenas.Arena
|
||||
import nl.kallestruik.darena.managers.ArenaManager
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.GameMode
|
||||
import org.bukkit.entity.Entity
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.entity.Projectile
|
||||
|
||||
class ArenaUtil {
|
||||
object ArenaUtil {
|
||||
|
||||
fun getPossibleParticipants(): List<Player> {
|
||||
Logger.trace(ArenaUtil::class, "getPossibleParticipants()")
|
||||
|
@ -15,4 +19,60 @@ class ArenaUtil {
|
|||
Logger.trace(ArenaUtil::class, "getPossibleSpectators()")
|
||||
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
|
||||
|
||||
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.configuration.file.YamlConfiguration
|
||||
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 {
|
||||
|
||||
|
@ -54,40 +52,6 @@ object ConfigHelper {
|
|||
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)
|
||||
fun matchMaterial(material: String): Material {
|
||||
Logger.trace(ConfigHelper::class, "matchMaterial(material: $material)")
|
||||
|
@ -97,6 +61,7 @@ object ConfigHelper {
|
|||
fun parseIntList(string: String): List<Int> {
|
||||
Logger.trace(ConfigHelper::class, "parseIntList(string: $string)")
|
||||
val list = mutableListOf<Int>()
|
||||
|
||||
for (key in string.split(" ")) {
|
||||
list.add(key.toInt())
|
||||
}
|
||||
|
@ -113,14 +78,4 @@ object ConfigHelper {
|
|||
|
||||
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
|
||||
|
||||
object Logger {
|
||||
var level: LogLevel = LogLevel.INFO
|
||||
|
||||
var level: LogLevel = LogLevel.TRACE
|
||||
|
||||
fun trace(source: KClass<*>, message: String) {
|
||||
if (level.shouldLog(LogLevel.TRACE))
|
||||
|
@ -36,7 +35,7 @@ object Logger {
|
|||
println("[CRITICAL] [${source.qualifiedName}] $message")
|
||||
}
|
||||
|
||||
enum LogLevel(
|
||||
enum class LogLevel(
|
||||
val weight: Int
|
||||
) {
|
||||
TRACE(100),
|
||||
|
@ -44,7 +43,7 @@ object Logger {
|
|||
INFO(60),
|
||||
WARN(40),
|
||||
ERROR(20),
|
||||
CRITICAL(0)
|
||||
CRITICAL(0);
|
||||
|
||||
fun shouldLog(level: LogLevel): Boolean {
|
||||
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
|
||||
depend:
|
||||
- "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:
|
||||
# default:
|
||||
# hotbar:
|
||||
# weapon:
|
||||
# 0:
|
||||
# material: iron_axe
|
||||
# amount: 1
|
||||
# enchantments:
|
||||
# name: sharpness
|
||||
# level: 5
|
||||
# unbreakable: true
|
||||
# food:
|
||||
# 1:
|
||||
# material: cooked_beef
|
||||
# amount: 10
|
||||
# armor:
|
||||
|
@ -72,6 +72,7 @@
|
|||
# y: 10
|
||||
# z: 10
|
||||
# spawnRule: "label1"
|
||||
# move-player: false
|
||||
# checkpoint2:
|
||||
# lower:
|
||||
# x: 20
|
||||
|
@ -82,6 +83,7 @@
|
|||
# y: 10
|
||||
# z: 30
|
||||
# spawnRule: "label2"
|
||||
# move-player: false
|
||||
# finish:
|
||||
# lower:
|
||||
# x: 40
|
||||
|
@ -92,6 +94,7 @@
|
|||
# y: 10
|
||||
# z: 50
|
||||
# spawnRule: "spectator"
|
||||
# move-player: true
|
||||
#points:
|
||||
# kill: 10 5
|
||||
# last-man-standing: 10 5 3 0
|
||||
|
@ -99,6 +102,11 @@
|
|||
# checkpoint1: 10
|
||||
# checkpoint2: 5
|
||||
# finish: 20 10 5
|
||||
# pools:
|
||||
# maps:
|
||||
# - 10 5
|
||||
# - 12 6
|
||||
# - 14 7
|
||||
#spawnRules:
|
||||
# initial:
|
||||
# reuse-spawns: true
|
||||
|
@ -107,7 +115,7 @@
|
|||
# strength: 1
|
||||
# duration: 10
|
||||
# hidden: false
|
||||
# invisability:
|
||||
# invisibility:
|
||||
# strength: 1
|
||||
# duration: 100000000
|
||||
# hidden: true
|
||||
|
@ -118,3 +126,62 @@
|
|||
# reuse-spawns: true
|
||||
# spawns:
|
||||
# - "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