Add timer command, recraft module & recipes
Introduce a /timer admin command with tab completion (speedhg.admin.timer) and a parseTimeToSeconds utility; add CommandSender.sendMsg extension and language strings. Add RecraftManager module that enforces a configurable recraft-nerf (reduces extra recraft points for alive players) and starts from GameManager; wire the new module into GameManager. Register mushroom-stew shapeless recipes for cocoa and cactus in plugin startup and tidy command registration. Update plugin.yml with permission/command entries and adjust imports as needed.
This commit is contained in:
@@ -2,6 +2,7 @@ package club.mcscrims.speedhg
|
|||||||
|
|
||||||
import club.mcscrims.speedhg.command.KitCommand
|
import club.mcscrims.speedhg.command.KitCommand
|
||||||
import club.mcscrims.speedhg.command.LeaderboardCommand
|
import club.mcscrims.speedhg.command.LeaderboardCommand
|
||||||
|
import club.mcscrims.speedhg.command.TimerCommand
|
||||||
import club.mcscrims.speedhg.config.LanguageManager
|
import club.mcscrims.speedhg.config.LanguageManager
|
||||||
import club.mcscrims.speedhg.database.DatabaseManager
|
import club.mcscrims.speedhg.database.DatabaseManager
|
||||||
import club.mcscrims.speedhg.database.StatsManager
|
import club.mcscrims.speedhg.database.StatsManager
|
||||||
@@ -18,6 +19,10 @@ import club.mcscrims.speedhg.listener.StatsListener
|
|||||||
import club.mcscrims.speedhg.scoreboard.ScoreboardManager
|
import club.mcscrims.speedhg.scoreboard.ScoreboardManager
|
||||||
import club.mcscrims.speedhg.webhook.DiscordWebhookManager
|
import club.mcscrims.speedhg.webhook.DiscordWebhookManager
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.NamespacedKey
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import org.bukkit.inventory.ShapelessRecipe
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
class SpeedHG : JavaPlugin() {
|
class SpeedHG : JavaPlugin() {
|
||||||
@@ -81,6 +86,7 @@ class SpeedHG : JavaPlugin() {
|
|||||||
registerKits()
|
registerKits()
|
||||||
registerCommands()
|
registerCommands()
|
||||||
registerListener()
|
registerListener()
|
||||||
|
registerRecipes()
|
||||||
|
|
||||||
logger.info("SpeedHG wurde geladen!")
|
logger.info("SpeedHG wurde geladen!")
|
||||||
}
|
}
|
||||||
@@ -105,8 +111,16 @@ class SpeedHG : JavaPlugin() {
|
|||||||
private fun registerCommands()
|
private fun registerCommands()
|
||||||
{
|
{
|
||||||
val kitCommand = KitCommand()
|
val kitCommand = KitCommand()
|
||||||
getCommand( "kit" )?.setExecutor( kitCommand )
|
getCommand( "kit" )?.apply {
|
||||||
getCommand( "kit" )?.tabCompleter = kitCommand
|
setExecutor( kitCommand )
|
||||||
|
tabCompleter = kitCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
val timerCommand = TimerCommand( this )
|
||||||
|
getCommand( "timer" )?.apply {
|
||||||
|
setExecutor( timerCommand )
|
||||||
|
tabCompleter = timerCommand
|
||||||
|
}
|
||||||
|
|
||||||
getCommand( "leaderboard" )?.setExecutor( LeaderboardCommand() )
|
getCommand( "leaderboard" )?.setExecutor( LeaderboardCommand() )
|
||||||
}
|
}
|
||||||
@@ -123,4 +137,20 @@ class SpeedHG : JavaPlugin() {
|
|||||||
pm.registerEvents( MenuListener(), this )
|
pm.registerEvents( MenuListener(), this )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun registerRecipes()
|
||||||
|
{
|
||||||
|
val soup = ItemStack( Material.MUSHROOM_STEW )
|
||||||
|
|
||||||
|
val cocoRecipe = ShapelessRecipe(NamespacedKey.minecraft( "cocoa_soup" ), soup )
|
||||||
|
cocoRecipe.addIngredient( Material.COCOA_BEANS )
|
||||||
|
cocoRecipe.addIngredient( Material.BOWL )
|
||||||
|
|
||||||
|
val cactiRecipe = ShapelessRecipe(NamespacedKey.minecraft( "cacti_soup" ), soup )
|
||||||
|
cactiRecipe.addIngredient( Material.CACTUS )
|
||||||
|
cactiRecipe.addIngredient( Material.BOWL )
|
||||||
|
|
||||||
|
Bukkit.addRecipe( cocoRecipe )
|
||||||
|
Bukkit.addRecipe( cactiRecipe )
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package club.mcscrims.speedhg.command
|
||||||
|
|
||||||
|
import club.mcscrims.speedhg.SpeedHG
|
||||||
|
import club.mcscrims.speedhg.game.GameState
|
||||||
|
import club.mcscrims.speedhg.util.parseTimeToSeconds
|
||||||
|
import club.mcscrims.speedhg.util.sendMsg
|
||||||
|
import org.bukkit.command.Command
|
||||||
|
import org.bukkit.command.CommandExecutor
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
import org.bukkit.command.TabCompleter
|
||||||
|
|
||||||
|
class TimerCommand(
|
||||||
|
private val plugin: SpeedHG
|
||||||
|
) : CommandExecutor, TabCompleter {
|
||||||
|
|
||||||
|
override fun onCommand(
|
||||||
|
sender: CommandSender,
|
||||||
|
command: Command,
|
||||||
|
label: String,
|
||||||
|
args: Array<out String>
|
||||||
|
): Boolean
|
||||||
|
{
|
||||||
|
if (!sender.hasPermission( "speedhg.admin.timer" ))
|
||||||
|
{
|
||||||
|
sender.sendMsg( "default.no_permission" )
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( args.isEmpty() )
|
||||||
|
{
|
||||||
|
sender.sendMsg( "commands.timer.usage" )
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val newTime = args[0].parseTimeToSeconds()
|
||||||
|
|
||||||
|
if ( newTime == null || newTime < 0 )
|
||||||
|
{
|
||||||
|
sender.sendMsg( "commands.timer.positiveNumber" )
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( plugin.gameManager.currentState != GameState.INGAME )
|
||||||
|
{
|
||||||
|
sender.sendMsg( "commands.timer.onlyIngame" )
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.gameManager.timer = newTime
|
||||||
|
|
||||||
|
val minutes = newTime / 60
|
||||||
|
val seconds = newTime % 60
|
||||||
|
val formattedTime = if ( minutes > 0 ) "${minutes}m ${seconds}s" else "${seconds}s"
|
||||||
|
|
||||||
|
sender.sendMsg( "commands.timer.set", "time" to formattedTime )
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTabComplete(
|
||||||
|
sender: CommandSender,
|
||||||
|
command: Command,
|
||||||
|
label: String,
|
||||||
|
args: Array<out String>
|
||||||
|
): List<String?>
|
||||||
|
{
|
||||||
|
if ( args.size == 1 && sender.hasPermission( "speedhg.admin.timer" ))
|
||||||
|
{
|
||||||
|
val suggestions = listOf("10m", "30m", "1h", "60s", "600", "1800")
|
||||||
|
return suggestions.filter { it.startsWith( args[0], true ) }.toMutableList()
|
||||||
|
}
|
||||||
|
return mutableListOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package club.mcscrims.speedhg.game
|
|||||||
import club.mcscrims.speedhg.SpeedHG
|
import club.mcscrims.speedhg.SpeedHG
|
||||||
import club.mcscrims.speedhg.game.modules.FeastManager
|
import club.mcscrims.speedhg.game.modules.FeastManager
|
||||||
import club.mcscrims.speedhg.game.modules.PitManager
|
import club.mcscrims.speedhg.game.modules.PitManager
|
||||||
|
import club.mcscrims.speedhg.game.modules.RecraftManager
|
||||||
import club.mcscrims.speedhg.util.sendMsg
|
import club.mcscrims.speedhg.util.sendMsg
|
||||||
import club.mcscrims.speedhg.util.trans
|
import club.mcscrims.speedhg.util.trans
|
||||||
import net.kyori.adventure.title.Title
|
import net.kyori.adventure.title.Title
|
||||||
@@ -45,6 +46,7 @@ class GameManager(
|
|||||||
|
|
||||||
val feastManager = FeastManager( plugin )
|
val feastManager = FeastManager( plugin )
|
||||||
val pitManager = PitManager( plugin )
|
val pitManager = PitManager( plugin )
|
||||||
|
val recraftManager = RecraftManager( plugin )
|
||||||
|
|
||||||
init {
|
init {
|
||||||
plugin.server.pluginManager.registerEvents( this, plugin )
|
plugin.server.pluginManager.registerEvents( this, plugin )
|
||||||
@@ -52,6 +54,8 @@ class GameManager(
|
|||||||
gameTask = Bukkit.getScheduler().runTaskTimer( plugin, { ->
|
gameTask = Bukkit.getScheduler().runTaskTimer( plugin, { ->
|
||||||
gameLoop()
|
gameLoop()
|
||||||
}, 20L, 20L )
|
}, 20L, 20L )
|
||||||
|
|
||||||
|
recraftManager.startRunnable()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var lobbyIdleCount: Int = 0
|
private var lobbyIdleCount: Int = 0
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
package club.mcscrims.speedhg.game.modules
|
||||||
|
|
||||||
|
import club.mcscrims.speedhg.SpeedHG
|
||||||
|
import club.mcscrims.speedhg.util.sendMsg
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable
|
||||||
|
|
||||||
|
class RecraftManager(
|
||||||
|
private val plugin: SpeedHG
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val beforeFeast = plugin.config.getBoolean( "game.recraftNerf.beforeFeast", true )
|
||||||
|
private val recraftNerfEnabled = plugin.config.getBoolean( "game.recraftNerf.enabled", false )
|
||||||
|
private val maxRecraftAmount = plugin.config.getInt( "game.recraftNerf.maxAmount", 64 )
|
||||||
|
|
||||||
|
fun startRunnable()
|
||||||
|
{
|
||||||
|
if ( !recraftNerfEnabled )
|
||||||
|
return
|
||||||
|
|
||||||
|
object : BukkitRunnable() {
|
||||||
|
|
||||||
|
override fun run()
|
||||||
|
{
|
||||||
|
if ( beforeFeast &&
|
||||||
|
plugin.gameManager.feastManager.hasSpawned )
|
||||||
|
{
|
||||||
|
this.cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.getOnlinePlayers().stream()
|
||||||
|
.filter { plugin.gameManager.alivePlayers.contains( it.uniqueId ) }
|
||||||
|
.forEach {
|
||||||
|
val recraft = Recraft()
|
||||||
|
recraft.calcRecraft( *it.inventory.contents )
|
||||||
|
|
||||||
|
if ( recraft.getRecraftPoints() > maxRecraftAmount )
|
||||||
|
{
|
||||||
|
it.sendMsg( "recraftNerf.tooMuch" )
|
||||||
|
|
||||||
|
while ( recraft.getRecraftPoints() > maxRecraftAmount )
|
||||||
|
recraft.decrease( it, 1 )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}.runTaskTimer( plugin, 10L, 10L )
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Recraft {
|
||||||
|
|
||||||
|
private val recraftMaterials = listOf(
|
||||||
|
RecraftMaterial( 1, arrayOf( Material.RED_MUSHROOM, Material.BROWN_MUSHROOM )),
|
||||||
|
RecraftMaterial( 1, arrayOf( Material.COCOA_BEANS )),
|
||||||
|
RecraftMaterial( 1, arrayOf( Material.CACTUS ))
|
||||||
|
)
|
||||||
|
|
||||||
|
fun calcRecraft(
|
||||||
|
vararg items: ItemStack?
|
||||||
|
) {
|
||||||
|
recraftMaterials.forEach( RecraftMaterial::reset )
|
||||||
|
|
||||||
|
for ( item in items )
|
||||||
|
{
|
||||||
|
if ( item == null )
|
||||||
|
continue
|
||||||
|
|
||||||
|
for ( recraftMaterial in recraftMaterials )
|
||||||
|
{
|
||||||
|
val type = item.type
|
||||||
|
if (recraftMaterial.containsKey( type ))
|
||||||
|
recraftMaterial[ type ] = recraftMaterial.getOrDefault( type, 0 ) + item.amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decrease(
|
||||||
|
player: Player,
|
||||||
|
amount: Int
|
||||||
|
) {
|
||||||
|
val lowestMaterials = mutableListOf<Material>()
|
||||||
|
|
||||||
|
for ( recraftMaterial in recraftMaterials )
|
||||||
|
if ( recraftMaterial.getLowestMaterial() != null )
|
||||||
|
lowestMaterials.add( recraftMaterial.getLowestMaterial()!! )
|
||||||
|
|
||||||
|
var highestMaterial: Material? = null
|
||||||
|
var i = 0f
|
||||||
|
|
||||||
|
for ( lowestMaterial in lowestMaterials )
|
||||||
|
{
|
||||||
|
val recraftMaterial = byMaterial( lowestMaterial )
|
||||||
|
?: continue
|
||||||
|
|
||||||
|
if (recraftMaterial[ lowestMaterial ]!! * recraftMaterial.getMaterialValue() > i )
|
||||||
|
{
|
||||||
|
i = recraftMaterial[ lowestMaterial ]!! * recraftMaterial.getMaterialValue()
|
||||||
|
highestMaterial = lowestMaterial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val recraftMaterial = byMaterial( highestMaterial!! )
|
||||||
|
recraftMaterial?.decrease( highestMaterial, amount )
|
||||||
|
|
||||||
|
for ( item in player.inventory.contents )
|
||||||
|
{
|
||||||
|
if ( item == null )
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ( item.type == highestMaterial )
|
||||||
|
{
|
||||||
|
item.amount -= amount
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRecraftPoints(): Float
|
||||||
|
{
|
||||||
|
var points = 0f
|
||||||
|
|
||||||
|
for ( recraftMaterial in recraftMaterials )
|
||||||
|
points += recraftMaterial.getPoints()
|
||||||
|
|
||||||
|
return points
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun byMaterial(
|
||||||
|
material: Material
|
||||||
|
): RecraftMaterial?
|
||||||
|
{
|
||||||
|
return recraftMaterials.stream()
|
||||||
|
.filter { it.containsKey( material ) }
|
||||||
|
.findFirst().orElse( null )
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RecraftMaterial(
|
||||||
|
val maxSoupAmount: Int,
|
||||||
|
val materials: Array<Material>
|
||||||
|
) : HashMap<Material, Int>() {
|
||||||
|
|
||||||
|
fun getPoints(): Float
|
||||||
|
{
|
||||||
|
return getOrDefault( getLowestMaterial(), 0 ).toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decrease(
|
||||||
|
material: Material,
|
||||||
|
amount: Int
|
||||||
|
) {
|
||||||
|
put( material, get( material )!! - amount )
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLowestMaterial(): Material?
|
||||||
|
{
|
||||||
|
if ( size > 1 )
|
||||||
|
{
|
||||||
|
if (values.stream().anyMatch { int -> int == 0 })
|
||||||
|
return null
|
||||||
|
|
||||||
|
val materialIntegerEntry = entries.stream().min(Comparator.comparingInt { it.value })
|
||||||
|
return materialIntegerEntry.map { it.key }.orElse( null )
|
||||||
|
}
|
||||||
|
else return keys.stream().findFirst().orElse( null )
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMaterialValue() = ( maxSoupAmount / size ).toFloat()
|
||||||
|
|
||||||
|
fun reset() = replaceAll { _, _ -> 0 }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import club.mcscrims.speedhg.SpeedHG
|
|||||||
import net.kyori.adventure.text.Component
|
import net.kyori.adventure.text.Component
|
||||||
import net.kyori.adventure.text.minimessage.MiniMessage
|
import net.kyori.adventure.text.minimessage.MiniMessage
|
||||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
|
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
private val langManager get() = SpeedHG.instance.languageManager
|
private val langManager get() = SpeedHG.instance.languageManager
|
||||||
@@ -31,6 +32,14 @@ fun Player.sendMsg(
|
|||||||
this.sendMessage( component )
|
this.sendMessage( component )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun CommandSender.sendMsg(
|
||||||
|
key: String,
|
||||||
|
vararg placeholders: Pair<String, String>
|
||||||
|
) {
|
||||||
|
val component = langManager.getDefaultComponent( key, placeholders.toMap() )
|
||||||
|
this.sendMessage( component )
|
||||||
|
}
|
||||||
|
|
||||||
fun Player.trans(
|
fun Player.trans(
|
||||||
key: String,
|
key: String,
|
||||||
vararg placeholders: Pair<String, String>
|
vararg placeholders: Pair<String, String>
|
||||||
@@ -70,4 +79,21 @@ fun Component.toLegacyString(): String
|
|||||||
}
|
}
|
||||||
|
|
||||||
val Player.getDisplayName: String
|
val Player.getDisplayName: String
|
||||||
get() = legacySerializer.serialize( this.displayName() )
|
get() = legacySerializer.serialize( this.displayName() )
|
||||||
|
|
||||||
|
fun String.parseTimeToSeconds(): Int?
|
||||||
|
{
|
||||||
|
val regex = Regex( "^(\\d+)([smh]?)$", RegexOption.IGNORE_CASE )
|
||||||
|
val match = regex.find( this.trim() ) ?: return null
|
||||||
|
|
||||||
|
val value = match.groupValues[ 1 ].toIntOrNull() ?: return null
|
||||||
|
val unit = match.groupValues[ 2 ].lowercase()
|
||||||
|
|
||||||
|
return when( unit )
|
||||||
|
{
|
||||||
|
"h" -> value * 3600
|
||||||
|
"m" -> value * 60
|
||||||
|
"s", "" -> value
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,11 @@ anti-runner:
|
|||||||
ignore-vertical-distance: 15.0 # Wenn Höhenunterschied > 15, Timer ignorieren
|
ignore-vertical-distance: 15.0 # Wenn Höhenunterschied > 15, Timer ignorieren
|
||||||
ignore-cave-surface-mix: true # Ignorieren, wenn einer Sonne hat und der andere nicht
|
ignore-cave-surface-mix: true # Ignorieren, wenn einer Sonne hat und der andere nicht
|
||||||
|
|
||||||
|
recraftNerf:
|
||||||
|
enabled: false
|
||||||
|
beforeFeast: true
|
||||||
|
maxAmount: 64
|
||||||
|
|
||||||
discord:
|
discord:
|
||||||
enabled: false
|
enabled: false
|
||||||
webhook-url: "DEINE_WEBHOOK_URL_HIER"
|
webhook-url: "DEINE_WEBHOOK_URL_HIER"
|
||||||
|
|||||||
@@ -69,6 +69,11 @@ commands:
|
|||||||
empty: '<red>There are currently no stats</red>'
|
empty: '<red>There are currently no stats</red>'
|
||||||
line: '#<rank> - <green><name></green> - <aqua><score></aqua>'
|
line: '#<rank> - <green><name></green> - <aqua><score></aqua>'
|
||||||
footer: '<gray>====== <gold>Leaderboard</gold> ======</gray>'
|
footer: '<gray>====== <gold>Leaderboard</gold> ======</gray>'
|
||||||
|
timer:
|
||||||
|
usage: '<red>Usage: /timer <seconds></red>'
|
||||||
|
positiveNumber: '<red>Invalid time format! Use, for example, 10m, 30s, or 600.</red>'
|
||||||
|
onlyIngame: '<red>Timer can only be set in game.</red>'
|
||||||
|
set: '<green>The game timer has been set to <time>!</green>'
|
||||||
|
|
||||||
scoreboard:
|
scoreboard:
|
||||||
title: '<gradient:red:gold><bold>SpeedHG</bold></gradient>'
|
title: '<gradient:red:gold><bold>SpeedHG</bold></gradient>'
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ permissions:
|
|||||||
speedhg.bypass:
|
speedhg.bypass:
|
||||||
description: 'Allows joining the server while its running'
|
description: 'Allows joining the server while its running'
|
||||||
default: false
|
default: false
|
||||||
|
speedhg.admin.timer:
|
||||||
|
description: 'Change the current game time'
|
||||||
|
default: false
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
kit:
|
kit:
|
||||||
@@ -17,4 +20,8 @@ commands:
|
|||||||
usage: '/kit <kitName> <playstyle>'
|
usage: '/kit <kitName> <playstyle>'
|
||||||
leaderboard:
|
leaderboard:
|
||||||
description: 'View the top 10 players'
|
description: 'View the top 10 players'
|
||||||
usage: '/leaderboard'
|
usage: '/leaderboard'
|
||||||
|
timer:
|
||||||
|
description: 'Change the current game time (Admin Command)'
|
||||||
|
usage: '/timer <seconds>'
|
||||||
|
permission: speedhg.admin.timer
|
||||||
Reference in New Issue
Block a user