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.LeaderboardCommand
|
||||
import club.mcscrims.speedhg.command.TimerCommand
|
||||
import club.mcscrims.speedhg.config.LanguageManager
|
||||
import club.mcscrims.speedhg.database.DatabaseManager
|
||||
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.webhook.DiscordWebhookManager
|
||||
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
|
||||
|
||||
class SpeedHG : JavaPlugin() {
|
||||
@@ -81,6 +86,7 @@ class SpeedHG : JavaPlugin() {
|
||||
registerKits()
|
||||
registerCommands()
|
||||
registerListener()
|
||||
registerRecipes()
|
||||
|
||||
logger.info("SpeedHG wurde geladen!")
|
||||
}
|
||||
@@ -105,8 +111,16 @@ class SpeedHG : JavaPlugin() {
|
||||
private fun registerCommands()
|
||||
{
|
||||
val kitCommand = KitCommand()
|
||||
getCommand( "kit" )?.setExecutor( kitCommand )
|
||||
getCommand( "kit" )?.tabCompleter = kitCommand
|
||||
getCommand( "kit" )?.apply {
|
||||
setExecutor( kitCommand )
|
||||
tabCompleter = kitCommand
|
||||
}
|
||||
|
||||
val timerCommand = TimerCommand( this )
|
||||
getCommand( "timer" )?.apply {
|
||||
setExecutor( timerCommand )
|
||||
tabCompleter = timerCommand
|
||||
}
|
||||
|
||||
getCommand( "leaderboard" )?.setExecutor( LeaderboardCommand() )
|
||||
}
|
||||
@@ -123,4 +137,20 @@ class SpeedHG : JavaPlugin() {
|
||||
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.game.modules.FeastManager
|
||||
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.trans
|
||||
import net.kyori.adventure.title.Title
|
||||
@@ -45,6 +46,7 @@ class GameManager(
|
||||
|
||||
val feastManager = FeastManager( plugin )
|
||||
val pitManager = PitManager( plugin )
|
||||
val recraftManager = RecraftManager( plugin )
|
||||
|
||||
init {
|
||||
plugin.server.pluginManager.registerEvents( this, plugin )
|
||||
@@ -52,6 +54,8 @@ class GameManager(
|
||||
gameTask = Bukkit.getScheduler().runTaskTimer( plugin, { ->
|
||||
gameLoop()
|
||||
}, 20L, 20L )
|
||||
|
||||
recraftManager.startRunnable()
|
||||
}
|
||||
|
||||
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.minimessage.MiniMessage
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
|
||||
import org.bukkit.command.CommandSender
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
private val langManager get() = SpeedHG.instance.languageManager
|
||||
@@ -31,6 +32,14 @@ fun Player.sendMsg(
|
||||
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(
|
||||
key: String,
|
||||
vararg placeholders: Pair<String, String>
|
||||
@@ -71,3 +80,20 @@ fun Component.toLegacyString(): String
|
||||
|
||||
val Player.getDisplayName: String
|
||||
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-cave-surface-mix: true # Ignorieren, wenn einer Sonne hat und der andere nicht
|
||||
|
||||
recraftNerf:
|
||||
enabled: false
|
||||
beforeFeast: true
|
||||
maxAmount: 64
|
||||
|
||||
discord:
|
||||
enabled: false
|
||||
webhook-url: "DEINE_WEBHOOK_URL_HIER"
|
||||
|
||||
@@ -69,6 +69,11 @@ commands:
|
||||
empty: '<red>There are currently no stats</red>'
|
||||
line: '#<rank> - <green><name></green> - <aqua><score></aqua>'
|
||||
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:
|
||||
title: '<gradient:red:gold><bold>SpeedHG</bold></gradient>'
|
||||
|
||||
@@ -10,6 +10,9 @@ permissions:
|
||||
speedhg.bypass:
|
||||
description: 'Allows joining the server while its running'
|
||||
default: false
|
||||
speedhg.admin.timer:
|
||||
description: 'Change the current game time'
|
||||
default: false
|
||||
|
||||
commands:
|
||||
kit:
|
||||
@@ -18,3 +21,7 @@ commands:
|
||||
leaderboard:
|
||||
description: 'View the top 10 players'
|
||||
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