diff --git a/build.gradle.kts b/build.gradle.kts index 579b578..0481fb6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,6 @@ plugins { id("java") id("maven-publish") id("com.github.johnrengelman.shadow") version "8.1.1" - id("io.papermc.paperweight.userdev") version "1.7.1" kotlin("jvm") version libs.versions.kotlin kotlin("kapt") version libs.versions.kotlin } @@ -18,36 +17,11 @@ repositories { maven("https://libraries.minecraft.net/") maven("https://repo.codemc.io/repository/maven-public/") maven("https://repo.lunarclient.dev") - - maven { - url = uri("https://maven.pkg.github.com/McScrims-Network/Main-CoreSystem") - credentials { - username = System.getenv("GITHUB_ACTOR") - password = System.getenv("GITHUB_TOKEN") - } - } } dependencies { - implementation("org.mongodb:mongodb-driver-kotlin-coroutine:4.11.1") - implementation("org.mongodb:bson-kotlinx:4.11.1") - - implementation("net.kyori:adventure-api:4.14.0") - implementation("net.kyori:adventure-text-minimessage:4.14.0") - implementation("net.kyori:adventure-platform-bukkit:4.3.2") - - compileOnly("net.luckperms:api:5.4") - - compileOnly("com.lunarclient:apollo-api:1.2.0") - compileOnly("com.lunarclient:apollo-extra-adventure4:1.2.0") - - compileOnly("org.popcraft:chunky-common:1.3.38") - - implementation("club.mcscrims:core:1.4.3.2") - implementation("club.mcscrims:spigot:1.4.3.2") - + implementation("fr.mrmicky:fastboard:2.1.3") compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT") - paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT") } tasks { @@ -65,10 +39,6 @@ tasks { archiveVersion.set(project.version.toString()) } - assemble { - dependsOn( reobfJar ) - } - build { dependsOn( shadowJar ) } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a441313..9355b41 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/kotlin/club/mcscrims/speedhg/SpeedHG.kt b/src/main/kotlin/club/mcscrims/speedhg/SpeedHG.kt index 3c45d98..ec89680 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/SpeedHG.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/SpeedHG.kt @@ -1,291 +1,61 @@ package club.mcscrims.speedhg -import club.mcscrims.core.config.ConfigData -import club.mcscrims.core.config.ConfigFormat -import club.mcscrims.core.config.ConfigLoader -import club.mcscrims.core.database.DatabaseConfig -import club.mcscrims.core.database.mongodb.MongoManager -import club.mcscrims.speedhg.ability.AbilityContext -import club.mcscrims.speedhg.ability.AbilityHitListener -import club.mcscrims.speedhg.ability.CooldownManager -import club.mcscrims.speedhg.ability.HitCounterManager -import club.mcscrims.speedhg.config.KitConfig -import club.mcscrims.speedhg.config.MessageConfig -import club.mcscrims.speedhg.config.PluginConfig -import club.mcscrims.speedhg.database.StatsRepository +import club.mcscrims.speedhg.config.LanguageManager import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.command.KitsCommand -import club.mcscrims.speedhg.database.PlayerRepository -import club.mcscrims.speedhg.kit.KitInventoryListener -import club.mcscrims.speedhg.kit.KitInventoryManager -import club.mcscrims.speedhg.kit.KitListener -import club.mcscrims.speedhg.kit.KitManager -import club.mcscrims.speedhg.listener.GameStateListener -import club.mcscrims.speedhg.listener.LunarClientListener -import club.mcscrims.speedhg.recraft.RecraftInspector -import club.mcscrims.speedhg.recraft.RecraftUtils -import club.mcscrims.speedhg.world.WorldManager -import club.mcscrims.spigot.chat.ChatFormatter -import club.mcscrims.spigot.chat.ChatManager -import club.mcscrims.spigot.network.SpigotNetworkManager -import club.mcscrims.spigot.scheduler.SchedulerManager -import club.mcscrims.spigot.util.WorldEditUtils -import com.mongodb.client.model.Indexes -import kotlinx.coroutines.runBlocking -import net.kyori.adventure.text.Component -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer -import net.luckperms.api.LuckPerms +import club.mcscrims.speedhg.game.modules.AntiRunningManager +import club.mcscrims.speedhg.listener.ConnectListener +import club.mcscrims.speedhg.scoreboard.ScoreboardManager import org.bukkit.Bukkit -import org.bukkit.entity.Player import org.bukkit.plugin.java.JavaPlugin class SpeedHG : JavaPlugin() { companion object { - internal lateinit var instance: SpeedHG - - fun Component.content(): String - { - return LegacyComponentSerializer.legacySection().serialize( this ) - } + lateinit var instance: SpeedHG + private set } - private lateinit var configLoader: ConfigLoader - internal lateinit var pluginConfig: ConfigData - internal lateinit var messageConfig: ConfigData - internal lateinit var kitConfig: ConfigData - internal lateinit var databaseConfig: ConfigData + lateinit var languageManager: LanguageManager + private set - internal lateinit var chatManager: ChatManager - internal lateinit var chatFormatter: ChatFormatter + lateinit var gameManager: GameManager + private set - private lateinit var mongoManager: MongoManager - internal lateinit var statsRepository: StatsRepository - internal lateinit var playerRepository: PlayerRepository + lateinit var antiRunningManager: AntiRunningManager + private set - internal lateinit var networkManager: SpigotNetworkManager - internal lateinit var schedulerManager: SchedulerManager - - internal lateinit var gameManager: GameManager - internal lateinit var worldManager: WorldManager - - internal lateinit var abilityContext: AbilityContext - internal lateinit var kitManager: KitManager - internal lateinit var kitInventoryManager: KitInventoryManager - - internal lateinit var worldEditUtils: WorldEditUtils - - internal lateinit var luckPerms: LuckPerms - internal var isReady: Boolean = false - - override fun onLoad() - { - instance = this - - worldManager = WorldManager( this ) - worldManager.deleteWorld() - } + lateinit var scoreboardManager: ScoreboardManager + private set override fun onEnable() { - loadConfigurations() - setupDatabase() - networkManager = SpigotNetworkManager.getInstance()!! + instance = this - worldManager.setupWorld() - - chatFormatter = ChatFormatter.create( - plugin = this, - configClass = MessageConfig::class, - messageExtractor = { config -> config.getAllMessages() }, - listExtractor = { config -> config.getListMessages() } - ) - - chatManager = ChatManager.withCustomConfig( this, chatFormatter ) - chatManager.initialize() - - schedulerManager = SchedulerManager( this ) - - worldEditUtils = WorldEditUtils( this ) + saveDefaultConfig() + languageManager = LanguageManager( this ) gameManager = GameManager( this ) - gameManager.initialize() + antiRunningManager = AntiRunningManager( this ) - val cooldownManager = CooldownManager() - val hitCounterManager = HitCounterManager() - abilityContext = AbilityContext( cooldownManager, hitCounterManager ) + scoreboardManager = ScoreboardManager( this ) - kitManager = KitManager( this ) - kitManager.initialize() - - kitInventoryManager = KitInventoryManager( this, kitManager ) - - setupLuckPerms() registerListener() - registerCommands() - RecraftUtils.registerRecipes() - RecraftInspector( this ).startRunnable() + logger.info("SpeedHG wurde geladen!") } override fun onDisable() { - kitManager.clearAll() - closeDatabase() - networkManager.shutdown() + super.onDisable() } private fun registerListener() { - server.pluginManager.registerEvents(GameStateListener( this, gameManager ), this ) - server.pluginManager.registerEvents(KitListener( this, kitManager ), this ) - server.pluginManager.registerEvents(KitInventoryListener( this, kitManager, kitInventoryManager ), this ) - server.pluginManager.registerEvents(AbilityHitListener( this, abilityContext ), this ) - LunarClientListener( this ) + val pm = Bukkit.getPluginManager() + + pm.registerEvents( ConnectListener(), this ) } - private fun registerCommands() - { - getCommand("kits")?.setExecutor(KitsCommand( this, kitInventoryManager )) - } - - fun getAlivePlayers(): List - { - val alivePlayers = mutableListOf() - - runBlocking { - val players = playerRepository.findAlivePlayers( server.name ) - alivePlayers.addAll(players.map { Bukkit.getPlayer( it.uuid )!! }) - } - - return alivePlayers - } - - /* - * LUCKPERMS >> - */ - - private fun setupLuckPerms() - { - val provider = server.servicesManager.getRegistration( LuckPerms::class.java ) - - if ( provider != null ) - luckPerms = provider.provider - - if ( !::luckPerms.isInitialized ) - logger.warning( "LuckPerms could not be loaded." ) - else - logger.info( "LuckPerms has successfully been loaded." ) - } - - /* - * DATABASE >> - */ - - private fun setupDatabase() - { - // MongoManager initialisieren - mongoManager = MongoManager.getInstance( name, logger ) - val connection = mongoManager.createConnection( "speedhg", databaseConfig.data ) - - // Repositories initialisieren - statsRepository = StatsRepository( connection ) - playerRepository = PlayerRepository( connection ) - - // Indizes erstellen - runBlocking { - try - { - connection.createIndex( "player_stats", Indexes.ascending( "kills" )) - connection.createIndex( "player_stats", Indexes.descending( "deaths" )) - connection.createIndex( "player_stats", Indexes.ascending( "wins" )) - connection.createIndex( "player_stats", Indexes.ascending( "unathleticIndex" )) - connection.createIndex( "player_stats", Indexes.ascending( "ironFarmed" )) - connection.createIndex( "kit_players", Indexes.text( "server" )) - } - catch ( ex: Exception ) { - logger.warning( "Failed to create MongoDB indexes: ${ex.message}" ) - return@runBlocking - } - } - - logger.info( "Successfully enabled MongoDB" ) - } - - private fun closeDatabase() - { - // Alle Server von Kit-Spielern auf NULL setzen - runBlocking { - try - { - val players = Bukkit.getOnlinePlayers() - players.forEach { player -> - playerRepository.updateServer( player.uniqueId, "NULL" ) - } - logger.info( "Updated ${players.size} players server to NULL." ) - } - catch ( ex: Exception ) { - logger.warning( "Failed to update player server: ${ex.message}" ) - } - }.also { - // MongoManager beenden - mongoManager.shutdown() - - logger.info( "Successfully disabled MongoDB" ) - } - } - - /* - * CONFIG >> - */ - - private fun loadConfigurations() - { - configLoader = ConfigLoader.getInstance( name, logger, dataFolder ) - createConfigurations() - } - - private fun createConfigurations() - { - pluginConfig = configLoader.loadConfig( - fileName = "config.yml", - format = ConfigFormat.YAML, - autoReload = true - ) - - messageConfig = configLoader.loadConfig( - fileName = "messages.yml", - format = ConfigFormat.YAML, - autoReload = false - ) - - kitConfig = configLoader.loadConfig( - fileName = "kits.yml", - format = ConfigFormat.YAML, - autoReload = false - ) - - databaseConfig = configLoader.loadConfig( - fileName = "database.json", - format = ConfigFormat.JSON, - autoReload = false - ) - } - - fun reloadConfigurations() - { - try - { - pluginConfig.reload() - messageConfig.reload() - kitConfig.reload() - databaseConfig.reload() - } - catch ( ex: Exception ) { - logger.severe( "Failed to reload configurations: ${ex.message}" ) - } - } - -} +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/ability/AbilityContext.kt b/src/main/kotlin/club/mcscrims/speedhg/ability/AbilityContext.kt deleted file mode 100644 index 12d333e..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/ability/AbilityContext.kt +++ /dev/null @@ -1,75 +0,0 @@ -package club.mcscrims.speedhg.ability - -import org.bukkit.entity.Player - -class AbilityContext( - val cooldownManager: CooldownManager, - val hitCounterManager: HitCounterManager -) { - - fun canUseAbility( - player: Player, - key: String, - requiredHits: Int? = null, - cooldownSeconds: Int? = null - ): AbilityResult - { - if ( cooldownSeconds != null && cooldownManager.isOnCooldown( player, key )) - { - val remaining = cooldownManager.getRemainingSeconds( player, key ) - return AbilityResult.onCooldown( remaining ) - } - - if ( requiredHits != null && !hitCounterManager.hasReachedThreshold( player, key, requiredHits )) - { - val missing = hitCounterManager.getRemainingHits( player, key, requiredHits ) - return AbilityResult.insufficientHits( missing ) - } - - if ( requiredHits != null ) - hitCounterManager.resetHits( player, key ) - - if ( cooldownSeconds != null ) - cooldownManager.startCooldown( player, key, cooldownSeconds ) - - return AbilityResult.success() - } - - fun incrementHit( - player: Player, - key: String - ): Int - { - return hitCounterManager.incrementHit( player, key ) - } - - fun getHits( - player: Player, - key: String - ): Int - { - return hitCounterManager.getHits( player, key ) - } - - fun getRemainingCooldown( - player: Player, - key: String - ): Double - { - return cooldownManager.getRemainingSeconds( player, key ) - } - - fun clearPlayerData( - player: Player - ) { - cooldownManager.clearAllCooldowns( player ) - hitCounterManager.clearAllHits( player ) - } - - fun clearAll() - { - cooldownManager.clearAll() - hitCounterManager.clearAll() - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/ability/AbilityHitListener.kt b/src/main/kotlin/club/mcscrims/speedhg/ability/AbilityHitListener.kt deleted file mode 100644 index 5495f1a..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/ability/AbilityHitListener.kt +++ /dev/null @@ -1,47 +0,0 @@ -package club.mcscrims.speedhg.ability - -import club.mcscrims.speedhg.SpeedHG -import org.bukkit.entity.Player -import org.bukkit.event.EventHandler -import org.bukkit.event.Listener -import org.bukkit.event.entity.EntityDamageByEntityEvent - -class AbilityHitListener( - private val plugin: SpeedHG, - private val abilityContext: AbilityContext -) : Listener { - - @EventHandler - fun onEntityDamage( - event: EntityDamageByEntityEvent - ) { - val damager = event.damager - val victim = event.entity - - if ( damager !is Player || victim !is Player ) - return - - val hitCount = abilityContext.incrementHit( damager, "melee_hit" ) - - val result = abilityContext.canUseAbility( - player = damager, - key = "example_ability", - requiredHits = 3, - cooldownSeconds = 10 - ) - - if ( result.success ) - { - plugin.chatManager.sendMessage( damager, "ability.activated", "{player}" to victim.name ) - } - else if ( result.missingHits > 0 ) - { - plugin.chatManager.sendMessage( damager, "ability.more_hits", "{current}" to hitCount.toString(), "{needed}" to result.missingHits.toString() ) - } - else if ( result.remainingCooldown > 0 ) - { - plugin.chatManager.sendMessage( damager, "ability.cooldown", "{cooldown}" to result.remainingCooldown.toString() ) - } - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/ability/AbilityResult.kt b/src/main/kotlin/club/mcscrims/speedhg/ability/AbilityResult.kt deleted file mode 100644 index fd821be..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/ability/AbilityResult.kt +++ /dev/null @@ -1,21 +0,0 @@ -package club.mcscrims.speedhg.ability - -data class AbilityResult( - val success: Boolean, - val reason: String?, - val remainingCooldown: Double = 0.0, - val missingHits: Int = 0 -) { - companion object - { - - fun success(): AbilityResult = AbilityResult( true, null ) - - fun onCooldown( remaining: Double ): AbilityResult = - AbilityResult( false, "Ability is on cooldown", remainingCooldown = remaining ) - - fun insufficientHits( missing: Int ): AbilityResult = - AbilityResult( false, "Not enough hits", missingHits = missing ) - - } -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/ability/CooldownManager.kt b/src/main/kotlin/club/mcscrims/speedhg/ability/CooldownManager.kt deleted file mode 100644 index 36268e9..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/ability/CooldownManager.kt +++ /dev/null @@ -1,68 +0,0 @@ -package club.mcscrims.speedhg.ability - -import org.bukkit.entity.Player -import java.util.UUID -import java.util.concurrent.ConcurrentHashMap - -class CooldownManager { - - private val cooldowns = ConcurrentHashMap>() - - fun startCooldown( - player: Player, - key: String, - seconds: Int - ) { - val expiryTime = System.currentTimeMillis() + ( seconds * 1000L ) - cooldowns.computeIfAbsent( player.uniqueId ) { ConcurrentHashMap() }[ key ] = expiryTime - } - - fun isOnCooldown( - player: Player, - key: String - ): Boolean - { - val playerCooldowns = cooldowns[player.uniqueId] ?: return false - val expiryTime = playerCooldowns[key] ?: return false - - if ( System.currentTimeMillis() >= expiryTime ) - { - playerCooldowns.remove( key ) - if ( playerCooldowns.isEmpty() ) cooldowns.remove( player.uniqueId ) - return false - } - - return true - } - - fun getRemainingSeconds( - player: Player, - key: String - ): Double - { - val playerCooldowns = cooldowns[player.uniqueId] ?: return 0.0 - val expiryTime = playerCooldowns[key] ?: return 0.0 - - val remaining = ( expiryTime - System.currentTimeMillis() ) / 1000.0 - return if ( remaining > 0 ) remaining else 0.0 - } - - fun clearCooldown( - player: Player, - key: String - ) { - cooldowns[player.uniqueId]?.remove( key ) - } - - fun clearAllCooldowns( - player: Player - ) { - cooldowns.remove( player.uniqueId ) - } - - fun clearAll() - { - cooldowns.clear() - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/ability/HitCounterManager.kt b/src/main/kotlin/club/mcscrims/speedhg/ability/HitCounterManager.kt deleted file mode 100644 index 34bb0fe..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/ability/HitCounterManager.kt +++ /dev/null @@ -1,66 +0,0 @@ -package club.mcscrims.speedhg.ability - -import org.bukkit.entity.Player -import java.util.UUID -import java.util.concurrent.ConcurrentHashMap - -class HitCounterManager { - - private val hitCounts = ConcurrentHashMap>() - - fun incrementHit( - player: Player, - key: String - ): Int - { - val playerHits = hitCounts.computeIfAbsent( player.uniqueId ) { ConcurrentHashMap() } - val newCount = playerHits.compute( key ) { _, current -> ( current ?: 0 ) + 1 } ?: 1 - return newCount - } - - fun getHits( - player: Player, - key: String - ): Int - { - return hitCounts[player.uniqueId]?.get( key ) ?: 0 - } - - fun resetHits( - player: Player, - key: String - ) { - hitCounts[player.uniqueId]?.remove( key ) - } - - fun hasReachedThreshold( - player: Player, - key: String, - requiredHits: Int - ): Boolean - { - return getHits( player, key ) >= requiredHits - } - - fun getRemainingHits( - player: Player, - key: String, - requiredHits: Int - ): Int - { - val current = getHits( player, key ) - return maxOf( 0, requiredHits - current ) - } - - fun clearAllHits( - player: Player - ) { - hitCounts.remove( player.uniqueId ) - } - - fun clearAll() - { - hitCounts.clear() - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/command/KitsCommand.kt b/src/main/kotlin/club/mcscrims/speedhg/command/KitsCommand.kt deleted file mode 100644 index db33fa2..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/command/KitsCommand.kt +++ /dev/null @@ -1,31 +0,0 @@ -package club.mcscrims.speedhg.command - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.kit.KitInventoryManager -import org.bukkit.command.Command -import org.bukkit.command.CommandExecutor -import org.bukkit.command.CommandSender -import org.bukkit.entity.Player - -class KitsCommand( - private val plugin: SpeedHG, - private val kitInventoryManager: KitInventoryManager -) : CommandExecutor { - - override fun onCommand( - sender: CommandSender, - command: Command, - label: String, - args: Array - ): Boolean { - if ( sender !is Player ) - { - plugin.chatManager.sendSenderMessage( sender, "default.only_players" ) - return true - } - - kitInventoryManager.openKitInventory( sender, 1 ) - return true - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/config/KitConfig.kt b/src/main/kotlin/club/mcscrims/speedhg/config/KitConfig.kt deleted file mode 100644 index dad59be..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/config/KitConfig.kt +++ /dev/null @@ -1,152 +0,0 @@ -package club.mcscrims.speedhg.config - -import club.mcscrims.core.config.annotations.ConfigClass -import club.mcscrims.core.config.annotations.ConfigField - -@ConfigClass( - name = "kits", - description = "Kit configuration", - version = "1.0" -) -data class KitConfig( - - @ConfigField(name = "anchor", description = "Anchor configurations") - val anchor: Map = getAnchorConfigurations(), - - @ConfigField(name = "armorer", description = "Armorer configurations") - val armorer: Map = getArmorerConfigurations(), - - @ConfigField(name = "blackpanther", description = "BlackPanther configurations") - val blackPanther: Map = getBlackPantherConfigurations(), - - @ConfigField(name = "blitzcrank", description = "Blitzcrank configurations") - val blitzcrank: Map = getBlitzcrankConfigurations(), - - @ConfigField(name = "gladiator", description = "Gladiator configurations") - val gladiator: Map = getGladiatorConfigurations(), - - @ConfigField(name = "goblin", description = "Goblin configurations") - val goblin: Map = getGoblinConfigurations(), - - @ConfigField(name = "icemage", description = "IceMage configurations") - val iceMage: Map = getIceMageConfigurations(), - - @ConfigField(name = "poseidon", description = "Poseidon configurations") - val poseidon: Map = getPoseidonConfigurations(), - - @ConfigField(name = "rattlesnake", description = "Rattlesnake configurations") - val rattlesnake: Map = getRattlesnakeConfigurations(), - - @ConfigField(name = "tesla", description = "Tesla configurations") - val tesla: Map = getTeslaConfigurations(), - - @ConfigField(name = "voodoo", description = "Voodoo configurations") - val voodoo: Map = getVoodooConfigurations(), - - @ConfigField(name = "perks", description = "Perk configuration") - val perks: Map> = getPerkConfiguration() -) { - - fun getAllKitConfigurations(): Map - { - return anchor + armorer + blackPanther + gladiator + goblin + iceMage + poseidon + - rattlesnake + tesla + voodoo - } - - fun getConfigForKit(id: String): Map - { - return when (id.lowercase()) { - "anchor" -> anchor - "armorer" -> armorer - "blackpanther" -> blackPanther - "gladiator" -> gladiator - "goblin" -> goblin - "icemage" -> iceMage - "poseidon" -> poseidon - "rattlesnake" -> rattlesnake - "tesla" -> tesla - "voodoo" -> voodoo - else -> emptyMap() - } - } - -} - -private fun getAnchorConfigurations() = mapOf( - "offensive extra damage" to 1.0 -) - -private fun getArmorerConfigurations() = mapOf( - "kills until new armor" to 2.0 -) - -private fun getBlackPantherConfigurations() = mapOf( - "enderpearl hit damage" to 3.0, - "extra damage on top" to 0.5, - "default hit radius" to 3.0, - "explosion multiplier" to 3.0 -) - -private fun getBlitzcrankConfigurations() = mapOf( - "ultimate damage" to 10.0, - "ultimate radius" to 5.0, - "ultimate stun duration" to 0.5, - "hook range" to 8.0, - "stun height" to 4.0, - "stun radius" to 3.0, - "stun slow duration" to 5.0 -) - -private fun getGladiatorConfigurations() = mapOf( - "cage radius" to 23.0, - "cage height" to 10.0, - "wither effect after x seconds" to 180.0 -) - -private fun getGoblinConfigurations() = mapOf( - "bunker radius" to 10.0, - "bunker time until disappear" to 15.0, - "knockback and pullin radius" to 7.0, - "kit steal time" to 60.0, - "soup steal chance" to 20.0 -) - -private fun getIceMageConfigurations() = mapOf( - "chance for slowness" to 2.0 -) - -private fun getPoseidonConfigurations() = mapOf( - "default hit radius" to 3.0, - "lightning hit damage" to 4.0 -) - -private fun getRattlesnakeConfigurations() = mapOf( - "maximum jump distance" to 10.0, - "speed duration" to 10.0, - "poison duration" to 8.0, - "maximum negative effect duration" to 16.0, - "default jump radius" to 10.0 -) - -private fun getTeslaConfigurations() = mapOf( - "disable push at height" to 50.0, - "default thunder radius" to 5.0, - "push strength" to 1.0, - "fire tick duration" to 5.0, - "time until thunder disables" to 7.0, - "thunder damage" to 1.5 -) - -private fun getVoodooConfigurations() = mapOf( - "default curse radius" to 3.0, - "maximum effect duration" to 15.0, - "clicked players minimum health" to 10.0, - "voodoo hold duration" to 5.0, - "chance for wither effect" to 5.0 -) - -private fun getPerkConfiguration() = mapOf( - "knockback" to Pair( "knockback strength", 1.5 ), - "pullin" to Pair( "pullin strength", 0.5 ), - "radiusincrease" to Pair( "new radius", 5.0 ) -) \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/config/LanguageManager.kt b/src/main/kotlin/club/mcscrims/speedhg/config/LanguageManager.kt new file mode 100644 index 0000000..6d22a9e --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/config/LanguageManager.kt @@ -0,0 +1,96 @@ +package club.mcscrims.speedhg.config + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.minimessage.MiniMessage +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder +import org.bukkit.configuration.file.YamlConfiguration +import org.bukkit.entity.Player +import org.bukkit.plugin.java.JavaPlugin +import java.io.File +import java.util.concurrent.ConcurrentHashMap + +class LanguageManager( + private val plugin: JavaPlugin +) { + + // Map: Sprachcode -> (Key -> Nachricht) + private val languages = ConcurrentHashMap>() + private val miniMessage = MiniMessage.miniMessage() + private val defaultLanguage = plugin.config.getString("default.language", "en_US")!! + + init { + loadLanguages() + } + + fun loadLanguages() + { + languages.clear() + val folder = File( plugin.dataFolder, "languages" ) + + if ( !folder.exists() ) { + folder.mkdirs() + createDefault("de_DE") + createDefault("en_US") + } + + folder.walk().filter { it.extension == "yml" }.forEach { file -> + val langCode = file.nameWithoutExtension + val config = YamlConfiguration.loadConfiguration( file ) + + val messages = config.getKeys( true ) + .filter { config.isString( it ) } + .associateWith { config.getString( it ) } + + languages[ langCode ] = messages as Map + plugin.logger.info("Sprache geladen: $langCode (${messages.size} Nachrichten)") + } + } + + private fun createDefault( + locale: String + ) { + val fileName = "languages/$locale.yml" + if (plugin.getResource( fileName ) != null) { + plugin.saveResource( fileName, false ) + } + } + + fun getRawMessage( + player: Player, + key: String + ): String + { + val locale = player.locale().toString() + val langMap = languages[ locale ] ?: languages[ defaultLanguage ] + return langMap?.get( key ) ?: "Missing Key: $key" + } + + fun getRawMessageList( + player: Player, + key: String + ): List + { + var locale = player.locale().toString() + val langMap = languages[ locale ] + + if ( langMap == null ) { + locale = defaultLanguage + } + + val file = File( plugin.dataFolder, "languages/$locale.yml" ) + val config = YamlConfiguration.loadConfiguration( file ) + return if (config.contains( key )) config.getStringList( key ) else listOf("Missing List: $key") + } + + fun getComponent( + player: Player, + key: String, + placeholders: Map + ): Component + { + val raw = getRawMessage( player, key ) + val tags = placeholders.map { (k, v) -> Placeholder.parsed( k, v ) } + return miniMessage.deserialize( raw, *tags.toTypedArray() ) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/config/MessageConfig.kt b/src/main/kotlin/club/mcscrims/speedhg/config/MessageConfig.kt deleted file mode 100644 index 17c312a..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/config/MessageConfig.kt +++ /dev/null @@ -1,194 +0,0 @@ -package club.mcscrims.speedhg.config - -import club.mcscrims.core.config.annotations.ConfigClass -import club.mcscrims.core.config.annotations.ConfigField - -@ConfigClass( - name = "messages", - description = "Messages configuration", - version = "1.0" -) -data class MessageConfig( - @ConfigField(name = "default", description = "Default messages") - val defaultMessages: Map = getDefaultMessages(), - - @ConfigField(name = "commands", description = "Command messages") - val commandMessages: Map = getCommandMessages(), - - @ConfigField(name = "death", description = "Death messages") - val deathMessages: Map> = getDeathMessages(), - - @ConfigField(name = "kits", description = "Kit messages") - val kitMessages: Map = getKitMessages(), - - @ConfigField(name = "kits-default", description = "Default kit messages") - val defaultKitMessages: Map = getDefaultKitMessages() -) { - - data class KitConfig( - val name: String = "", - val description: List, - val items: List, - val messages: Map - ) - - data class KitItemConfig( - val itemName: String, - val playStyleNames: Map - ) - - fun getAllMessages(): Map - { - return defaultMessages + commandMessages + defaultKitMessages + kitMessages.flatMap { it.value.messages.entries }.associate { it.key to it.value } - } - - fun getListMessages(): Map> - { - return deathMessages - } - - fun getKitItemNames( - kit: String, - itemKey: String - ): Map? - { - return kitMessages[ kit ]?.items?.find { it.itemName == itemKey }?.playStyleNames - } - -} - -private fun getDefaultMessages(): Map = mapOf( - "default.prefix" to "McScrims ", - "default.no_permission" to "%prefix% You don't have permission to do that!", - "default.player_not_found" to "%prefix% This player could not be found!", - "default.command_cooldown" to "%prefix% Please wait {time} seconds before using this command again!", - "default.reload" to "%prefix% Successfully reloaded the plugin.", - "default.only_players" to "%prefix% Only players can execute this command." -) - -private fun getCommandMessages(): Map = mapOf( - "commands.unknown" to "%prefix% Unknown subcommand: {unknown} \n%prefix% Use /{command} for an overview." -) - -private fun getDeathMessages(): Map> = mapOf( - "player" to listOf( - " {player} was killed by {killer}!" - ), - "entity" to listOf( - " {player} was killed by {entity}!" - ), - "world" to listOf( - " {player} has died!" - ) -) - -private fun getDefaultKitMessages(): Map = mapOf( - "kits.missingHits" to "" -) - -private fun getKitMessages(): Map = mapOf( - "anchor" to MessageConfig.KitConfig( - name = "Anchor", - description = listOf( - "Create an anchor where", - "you are lookin and gain no", - "knockback in your radius." - ), - items = listOf(MessageConfig.KitItemConfig( - itemName = "anvil", - playStyleNames = mapOf( - "offensive" to "Anchor (right-click)", - "defensive" to "Anchor (right-click)" - ) - )), - messages = mapOf( - "tooFarAway" to "%prefix% You can only place your anchor in a {radius} block radius!", - "alreadyActivated" to "%prefix% You have already placed an anchor!", - "broken" to "%prefix% " - ) - ), - "armorer" to MessageConfig.KitConfig( - name = "Armorer", - description = listOf( - "Gain a stronger armor the", - "more kills you gain. Every 2", - "kills your armor gets better.", - "All the way up to iron." - ), - items = emptyList(), - messages = mapOf( - "upgrade.normal" to "%prefix% Your armor has been upgraded to {armorType}.", - "upgrade.enchanted" to "%prefix% Your armor has been enchanted." - ) - ), - "blackpanther" to MessageConfig.KitConfig( - name = "Black Panther", - description = listOf( - "Use your abilities to either", - "do more damage and push enemies", - "away or jump on enemies and", - "give them instant damage." - ), - items = listOf( - MessageConfig.KitItemConfig( - itemName = "blackDye", - playStyleNames = mapOf( - "offensive" to "Push (right-click)", - "defensive" to "Wakanda Forever! (right-click)" - ) - ), - MessageConfig.KitItemConfig( - itemName = "blazePowder", - playStyleNames = mapOf( - "offensive" to "Extra Damage (right-click)", - "defensive" to "Extra Damage (right-click)" - ) - ) - ), - messages = mapOf( - "wakandaForever.hit" to "%prefix% You have hit {hit} players with WAKANDA FOREVER!", - "extraDamage.activated" to "%prefix% Your extra damage is now activated.", - "extraDamage.deactivated" to "%prefix% Your extra damage is now deactivated." - ) - ), - "blitzcrank" to MessageConfig.KitConfig( - name = "Blitzcrank", - description = listOf( - "Use your abilities to", - "slow down enemies and", - "either stun or hook them." - ), - items = listOf( - MessageConfig.KitItemConfig( - itemName = "hots", - playStyleNames = mapOf( - "defensive" to "Slow (right-click)", - "offensive" to "Slow (right-click)" - ) - ), - MessageConfig.KitItemConfig( - itemName = "fishingrod", - playStyleNames = mapOf( - "defensive" to "Hook (right-click)", - "offensive" to "Hook (right-click)" - ) - ), - MessageConfig.KitItemConfig( - itemName = "pufferfish", - playStyleNames = mapOf( - "defensive" to "Stun (right-click)", - "offensive" to "Stun (right-click)" - ) - ) - ), - messages = mapOf( - "ultimate.target" to "%prefix% You have been slowed by {player}!", - "ultimate.player" to "%prefix% You have slowed {nearby} players.", - "no_player_in_sight" to "%prefix% There is no player in your radius and/or sight to hook!", - "hook.player" to "%prefix% You have hooked {player}!", - "hook.target" to "%prefix% You have been hooked to a Blitzcrank!", - "stun.target" to "%prefix% You have been stunned!", - "stun.player" to "%prefix% You have stunned {nearby} nearby players." - ) - ) -) \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/config/PluginConfig.kt b/src/main/kotlin/club/mcscrims/speedhg/config/PluginConfig.kt deleted file mode 100644 index be91010..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/config/PluginConfig.kt +++ /dev/null @@ -1,147 +0,0 @@ -package club.mcscrims.speedhg.config - -import club.mcscrims.core.config.DurationEntry -import club.mcscrims.core.config.DurationType -import club.mcscrims.core.config.annotations.ConfigClass -import club.mcscrims.core.config.annotations.ConfigField -import club.mcscrims.core.config.annotations.DefaultValue -import club.mcscrims.core.network.NetworkConfig -import club.mcscrims.speedhg.SpeedHG - -@ConfigClass( - name = "config", - description = "Main plugin configuration", - version = "1.0" -) -data class PluginConfig( - @ConfigField(name = "debug", description = "Enable debug mode") - @DefaultValue("false") - val debug: Boolean = false, - - @ConfigField(name = "language", description = "Default player language") - @DefaultValue("en_US") - val language: String = "en_US", - - @ConfigField(name = "restart_time", description = "Time after server restarts if playerList is empty") - @DefaultValue("3") - val restartTime: Int = 3, - - @ConfigField(name = "network", description = "Network configuration for server communication") - val network: NetworkConfig = NetworkConfig(), - - @ConfigField(name = "cooldown", description = "Default cooldown for kit items") - @DefaultValue("30") - val cooldown: Int = 30, - - @ConfigField(name = "needed_hits", description = "Default needed hits for kit items") - @DefaultValue("15") - val neededHits: Int = 15, - - @ConfigField(name = "world", description = "World configuration") - val world: WorldConfig = WorldConfig(), - - @ConfigField(name = "announcement", description = "Announcement configuration") - val announcement: Map = getAnnouncementConfiguration(), - - @ConfigField(name = "game", description = "Game configuration") - val game: GameConfig = GameConfig() -) { - - data class WorldConfig( - val name: String = "Default", - val border: Map = getBorderConfiguration() - ) - - data class GameConfig( - val name: String = "SpeedHG", - val variantName: String = "Solo - Single Kit", - val minimumPlayers: Int = 2, - val competitiveGame: Boolean = false, - val competitiveCommands: List = emptyList(), - val playerStates: Map = getPlayerStates(), - val recraftNerf: Map = getRecraftNerf(), - val teams: Map = getTeams(), - val blockedKits: List = emptyList(), - val blockedPerks: List = emptyList(), - val perks: Map = getPerks() - ) - - fun isDurationIncreasing( - entry: DurationEntry - ): Boolean - { - return entry.type == DurationType.INCREASING - } - - fun getDuration( - playerState: String - ): DurationEntry - { - return (getPlayerStates()[ playerState ]?.duration as DurationEntry ) - } - - data class StateConfig( - val duration: Any, - val scoreboard: String - ) - -} - -private fun getPlayerStates() = mapOf( - "waiting" to PluginConfig.StateConfig( - DurationEntry( DurationType.FIXED, -1 ), - "Waiting - %time%" - ), - "pre_start" to PluginConfig.StateConfig( - DurationEntry( DurationType.FIXED, 300 ), - "Waiting - %time%" - ), - "immunity" to PluginConfig.StateConfig( - DurationEntry( DurationType.FIXED, 90 ), - "Playing - %time%" - ), - "battle" to PluginConfig.StateConfig( - DurationEntry( DurationType.INCREASING ), - "Playing - %time%" - ), - "feast" to PluginConfig.StateConfig( - DurationEntry( DurationType.FIXED, 300 ), - "Playing - %time%" - ), - "deathmatch" to PluginConfig.StateConfig( - DurationEntry( DurationType.INCREASING ), - "Playing - %time%" - ), - "end" to PluginConfig.StateConfig( - DurationEntry( DurationType.FIXED, 60 ), - "Ending - %time%" - ), -) - -private fun getRecraftNerf() = mapOf( - "enabled" to false, - "max_amount" to 64, - "before_state" to "FEAST" -) - -private fun getTeams() = mapOf( - "enabled" to false, - "maximum_players" to 2 -) - -private fun getPerks() = mapOf( - "maximum_amount" to 2 -) - -private fun getBorderConfiguration() = mapOf( - "size" to 1000.0, - "warning_distance" to 5.0, - "damage" to 5.0, - "decrease" to 100.0 -) - -private fun getAnnouncementConfiguration() = mapOf( - "enabled" to true, - "after" to 10, - "minimumPlayers" to 6 -) \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/database/PlayerRepository.kt b/src/main/kotlin/club/mcscrims/speedhg/database/PlayerRepository.kt deleted file mode 100644 index 9151d87..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/database/PlayerRepository.kt +++ /dev/null @@ -1,125 +0,0 @@ -package club.mcscrims.speedhg.database - -import club.mcscrims.core.database.mongodb.MongoConnection -import club.mcscrims.core.database.mongodb.MongoRepository -import com.mongodb.client.model.Filters -import com.mongodb.client.model.Updates -import org.bson.codecs.pojo.annotations.BsonId -import org.bson.codecs.pojo.annotations.BsonProperty -import org.bson.conversions.Bson -import org.bson.types.ObjectId -import org.bukkit.Bukkit -import java.util.UUID - -/** - * MongoDB-Entity für einen Spieler - */ -data class KitPlayer( - @BsonId - val id: ObjectId = ObjectId(), - - @BsonProperty - val uuid: String, - - @BsonProperty - val isAlive: Boolean, - - @BsonProperty - val unlockedKits: List, - - @BsonProperty - val server: String -) - -/** - * MongoDB-Repository für Spieler-Daten - */ -class PlayerRepository( - connection: MongoConnection -): MongoRepository( connection, "kit_players", KitPlayer::class.java ) { - - override fun getId( - entity: KitPlayer - ): ObjectId - { - return entity.id - } - - override fun setId( - entity: KitPlayer, - id: ObjectId - ): KitPlayer - { - return entity.copy( id = id ) - } - - override fun entityToUpdateDocument( - entity: KitPlayer - ): Bson = Updates.combine( - Updates.set("isAlive", entity.isAlive), - Updates.set("unlockedKits", entity.unlockedKits), - Updates.set("server", entity.server) - ) - - /** - * Findet einen Spieler anhand seiner UUID - */ - suspend fun findByUuid( - uuid: UUID - ): KitPlayer? - { - val filter = Filters.eq("uuid", uuid.toString()) - return findFirst(filter) - } - - /** - * Findet alle lebenden Spieler für einen Server - */ - suspend fun findAlivePlayers( - server: String - ): List - { - val filter = Filters.and( - Filters.eq("server", server), - Filters.eq("isAlive", true) - ) - return find(filter, Bukkit.getMaxPlayers()) - } - - /** - * Aktualisiert den Alive-Status eines Spielers - */ - suspend fun updateAliveStatus( - uuid: UUID, - isAlive: Boolean - ) { - val filter = Filters.eq("uuid", uuid.toString()) - val update = Updates.set("isAlive", isAlive) - connection.updateOne(collectionName, filter, update) - } - - /** - * Aktualisiert den Server eines Spielers - */ - suspend fun updateServer( - uuid: UUID, - server: String - ) { - val filter = Filters.eq("uuid", uuid.toString()) - val update = Updates.set("server", server) - connection.updateOne(collectionName, filter, update) - } - - /** - * Aktualisiert die freigeschalteten Kits eines Spielers - */ - suspend fun updateUnlockedKits( - uuid: UUID, - unlockedKits: List - ) { - val filter = Filters.eq("uuid", uuid.toString()) - val update = Updates.set("unlockedKits", unlockedKits) - connection.updateOne(collectionName, filter, update) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/database/StatsRepository.kt b/src/main/kotlin/club/mcscrims/speedhg/database/StatsRepository.kt deleted file mode 100644 index d773d77..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/database/StatsRepository.kt +++ /dev/null @@ -1,200 +0,0 @@ -package club.mcscrims.speedhg.database - -import club.mcscrims.core.database.mongodb.MongoConnection -import club.mcscrims.core.database.mongodb.MongoRepository -import com.mongodb.client.model.Aggregates -import com.mongodb.client.model.Filters -import com.mongodb.client.model.Sorts -import com.mongodb.client.model.Updates -import kotlinx.coroutines.flow.toList -import org.bson.codecs.pojo.annotations.BsonId -import org.bson.codecs.pojo.annotations.BsonProperty -import org.bson.conversions.Bson -import org.bson.types.ObjectId -import java.util.UUID - -/** - * MongoDB-Entity für die Stats von einem Spieler - */ -data class PlayerStats( - @BsonId - val id: ObjectId? = null, - - @BsonProperty("uuid") - val uuid: String, - - @BsonProperty("kills") - val kills: Int, - - @BsonProperty("deaths") - val deaths: Int, - - @BsonProperty("wins") - val wins: Int, - - @BsonProperty("gamesPlayed") - val gamesPlayed: Int, - - @BsonProperty("unathleticIndex") - val unathleticIndex: Double, - - @BsonProperty("ironFarmed") - val ironFarmed: Double -) - -/** - * MongoDB-Repository für Stats-Daten - */ -class StatsRepository( - connection: MongoConnection -): MongoRepository( connection, "player_stats", PlayerStats::class.java ) { - - override fun getId( - entity: PlayerStats - ): ObjectId? - { - return entity.id - } - - override fun setId( - entity: PlayerStats, - id: ObjectId - ): PlayerStats - { - return entity.copy( id = id ) - } - - override fun entityToUpdateDocument( - entity: PlayerStats - ): Bson = Updates.combine( - Updates.set( "kills", entity.kills ), - Updates.set( "deaths", entity.deaths ), - Updates.set( "wins", entity.wins ), - Updates.set( "gamesPlayed", entity.gamesPlayed ), - Updates.set( "unathleticIndex", entity.unathleticIndex ), - Updates.set( "ironFarmed", entity.ironFarmed ) - ) - - /** - * Findet einen Spieler anhand seiner UUID - */ - suspend fun findByUuid( - uuid: UUID - ): PlayerStats? - { - val filter = Filters.eq( "uuid", uuid.toString() ) - return findFirst( filter ) - } - - /** - * Findet Top-Spieler nach Kills - */ - suspend fun findTopPlayersByKills( - limit: Int = 10 - ): List - { - return connection.getCollection( collectionName ).aggregate(listOf( - Aggregates.sort(Sorts.descending( "kills" )), - Aggregates.limit( limit ) - ), PlayerStats::class.java ).toList() - } - - /** - * Findet Top-Spieler nach Deaths - */ - suspend fun findTopPlayersByDeaths( - limit: Int = 10 - ): List - { - return connection.getCollection( collectionName ).aggregate(listOf( - Aggregates.sort(Sorts.ascending( "deaths" )), - Aggregates.limit( limit ) - ), PlayerStats::class.java ).toList() - } - - /** - * Findet Top-Spieler nach Wins - */ - suspend fun findTopPlayersByWins( - limit: Int = 10 - ): List - { - return connection.getCollection( collectionName ).aggregate(listOf( - Aggregates.sort(Sorts.descending( "wins" )), - Aggregates.limit( limit ) - ), PlayerStats::class.java ).toList() - } - - /** - * Fügt Kills zu einem Spieler hinzu - */ - suspend fun addKills( - uuid: UUID, - kills: Int - ) { - val filter = Filters.eq( "uuid", uuid.toString() ) - val update = Updates.inc( "kills", kills ) - connection.updateOne( collectionName, filter, update ) - } - - /** - * Fügt Deaths zu einem Spieler hinzu - */ - suspend fun addDeaths( - uuid: UUID, - deaths: Int - ) { - val filter = Filters.eq( "uuid", uuid.toString() ) - val update = Updates.inc( "deaths", deaths ) - connection.updateOne( collectionName, filter, update ) - } - - /** - * Fügt Wins zu einem Spieler hinzu - */ - suspend fun addWins( - uuid: UUID, - wins: Int - ) { - val filter = Filters.eq( "uuid", uuid.toString() ) - val update = Updates.inc( "wins", wins ) - connection.updateOne( collectionName, filter, update ) - } - - /** - * Fügt Spiele zu einem Spieler hinzu - */ - suspend fun addGames( - uuid: UUID, - gamesPlayed: Int - ) { - val filter = Filters.eq( "uuid", uuid.toString() ) - val update = Updates.inc( "gamesPlayed", gamesPlayed ) - connection.updateOne( collectionName, filter, update ) - } - - /** - * Fügt Unathletic-Index zu einem Spieler hinzu - */ - suspend fun addUnathleticIndex( - uuid: UUID, - unathleticIndex: Double - ) { - val filter = Filters.eq( "uuid", uuid.toString() ) - val update = Updates.inc( "unathleticIndex", unathleticIndex ) - connection.updateOne( collectionName, filter, update ) - } - - /** - * Fügt Eisen zu einem Spieler hinzu - */ - suspend fun addIronFarmed( - uuid: UUID, - ironFarmed: Double - ) { - val filter = Filters.eq( "uuid", uuid.toString() ) - val update = Updates.inc( "ironFarmed", ironFarmed ) - connection.updateOne( collectionName, filter, update ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt b/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt index 8e99497..8080bb5 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt @@ -1,138 +1,370 @@ package club.mcscrims.speedhg.game import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.game.impl.BattleState -import club.mcscrims.speedhg.game.impl.DeathmatchState -import club.mcscrims.speedhg.game.impl.EndState -import club.mcscrims.speedhg.game.impl.FeastState -import club.mcscrims.speedhg.game.impl.ImmunityState -import club.mcscrims.speedhg.game.impl.PreStartState -import club.mcscrims.speedhg.game.impl.WaitingState -import org.bukkit.Location +import club.mcscrims.speedhg.util.sendMsg +import club.mcscrims.speedhg.util.trans +import net.kyori.adventure.title.Title +import org.bukkit.* +import org.bukkit.attribute.Attribute import org.bukkit.entity.Player -import org.bukkit.util.BoundingBox -import java.util.concurrent.ConcurrentHashMap +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.player.PlayerQuitEvent +import org.bukkit.inventory.ItemStack +import org.bukkit.potion.PotionEffect +import org.bukkit.potion.PotionEffectType +import org.bukkit.scheduler.BukkitTask +import java.util.* +import kotlin.random.Random class GameManager( private val plugin: SpeedHG -) { +): Listener { - private var currentState: GameState? = null - internal val gameStateTypes = ConcurrentHashMap() + var currentState: GameState = GameState.LOBBY + private set - private val winners = ArrayList() + var timer = 0 - internal lateinit var feastLocation: Location - internal lateinit var feastBox: BoundingBox - internal var feastHeight: Int = 1 + val alivePlayers = mutableSetOf() - fun initialize() + private var gameTask: BukkitTask? = null + + // Einstellungen aus Config (gecached für Performance) + private val minPlayers = plugin.config.getInt("game.min-players", 2) + private val lobbyTime = plugin.config.getInt("game.lobby-time", 60) + private val invincibilityTime = plugin.config.getInt("game.invincibility-time", 60) + private val startBorder = plugin.config.getDouble("game.border-start", 300.0) + private val endBorder = plugin.config.getDouble("game.border-end", 20.0) + // Zeit in Sekunden, bis Border komplett klein ist (z.B. 10 Min) + private val borderShrinkTime = plugin.config.getLong("game.border-shrink-time", 600) + + init { + plugin.server.pluginManager.registerEvents( this, plugin ) + + gameTask = Bukkit.getScheduler().runTaskTimer( plugin, { -> + gameLoop() + }, 20L, 20L ) + } + + private fun gameLoop() { - currentState = WaitingState( - this, - plugin, - plugin.schedulerManager, - plugin.pluginConfig.data.getDuration( "waiting" ).seconds - ) + when( currentState ) + { + GameState.LOBBY -> + { + if ( Bukkit.getOnlinePlayers().size >= minPlayers ) + { + setGameState( GameState.STARTING ) + timer = lobbyTime + } + } - gameStateTypes[ GameStateTypes.WAITING ] = currentState!! - gameStateTypes[ GameStateTypes.PRE_START ] = PreStartState( this, plugin, plugin.schedulerManager, plugin.pluginConfig.data.getDuration( "pre_start" ).seconds ) - gameStateTypes[ GameStateTypes.IMMUNITY ] = ImmunityState( this, plugin, plugin.schedulerManager, plugin.pluginConfig.data.getDuration( "immunity" ).seconds ) - gameStateTypes[ GameStateTypes.BATTLE ] = BattleState( this, plugin, plugin.schedulerManager, plugin.pluginConfig.data.getDuration( "battle" ).seconds ) - gameStateTypes[ GameStateTypes.FEAST ] = FeastState( this, plugin, plugin.schedulerManager, plugin.pluginConfig.data.getDuration( "feast" ).seconds ) - gameStateTypes[ GameStateTypes.DEATHMATCH ] = DeathmatchState( this, plugin, plugin.schedulerManager, plugin.pluginConfig.data.getDuration( "deathmatch" ).seconds ) - gameStateTypes[ GameStateTypes.END ] = EndState( this, plugin, plugin.schedulerManager, plugin.pluginConfig.data.getDuration( "end" ).seconds ) + GameState.STARTING -> + { + if ( Bukkit.getOnlinePlayers().size < minPlayers ) + { + setGameState( GameState.LOBBY ) + Bukkit.getOnlinePlayers().forEach { player -> + player.sendMsg( "game.start-aborted" ) + } + return + } - try { - currentState?.onEnter( null ) - } catch ( e: Exception ) { - plugin.logger.severe("Error during onEnter for state ${currentState?.name}: ${e.message}") - e.printStackTrace() + timer-- + + if (timer in listOf( 60, 30, 10, 5, 4, 3, 2, 1 )) + { + Bukkit.getOnlinePlayers().forEach { p -> + p.sendMsg( "timer.lobby", "time" to timer.toString() ) + p.playSound( p.location, Sound.BLOCK_NOTE_BLOCK_HAT, 1f, 1f ) + } + } + + if ( timer <= 0 ) + startGame() + } + + GameState.INVINCIBILITY -> + { + timer-- + + if ( timer <= 0 ) + startFighting() + else + { + Bukkit.getOnlinePlayers().forEach { p -> + p.sendActionBar(p.trans( "timer.actionbar-invincibility", "time" to timer.toString() )) + } + } + } + + GameState.INGAME -> + { + timer++ + updateCompass() + checkWin() + } + + GameState.ENDING -> + { + timer-- + + if ( timer <= 0 ) + Bukkit.shutdown() + } } } - fun transitionTo( - stateType: GameStateTypes + fun setGameState( + newState: GameState ) { - val previousState = currentState - val nextState = gameStateTypes[ stateType ]!! + this.currentState = newState + } - try { - currentState?.onExit( nextState ) - } catch ( e: Exception ) { - plugin.logger.severe("Error during onExit for state ${currentState?.name}: ${e.message}") - e.printStackTrace() - } + private fun startGame() + { + setGameState( GameState.INVINCIBILITY ) + timer = invincibilityTime - if ( nextState is FeastState ) - { - feastLocation = nextState.feastLocation - feastBox = nextState.feastBox - feastHeight = nextState.feastHeight - } + val world = Bukkit.getWorld( "world" ) ?: return + world.time = 0 + world.setStorm( false ) - currentState = nextState + val border = world.worldBorder + border.center = Location( world, 0.0, 0.0, 0.0 ) + border.size = startBorder + border.damageBuffer = 0.0 + border.damageAmount = 1.0 - try { - nextState.onEnter( previousState ) - } catch ( e: Exception ) { - plugin.logger.severe("Error during onEnter for state ${nextState.name}: ${e.message}") - e.printStackTrace() - } - } + val speedEffect = PotionEffect( + PotionEffectType.SPEED, + timer, + 0, + false, + false, + true + ) - fun addWinners( - vararg winners: Player + val hasteEffect = PotionEffect( + PotionEffectType.HASTE, + timer, + 0, + false, + false, + true + ) + + alivePlayers.clear() + Bukkit.getOnlinePlayers().forEach { player -> + alivePlayers.add( player.uniqueId ) + + player.gameMode = GameMode.SURVIVAL + player.health = player.getAttribute( Attribute.GENERIC_MAX_HEALTH )?.value ?: 20.0 + player.foodLevel = 20 + player.inventory.clear() + player.activePotionEffects.forEach { player.removePotionEffect( it.type ) } + player.addPotionEffects(listOf( speedEffect, hasteEffect )) + + teleportRandomly( player, world, startBorder / 2 ) + + // TODO: Kit Items geben + // plugin.kitManager.giveKit( player ) + + player.inventory.addItem(ItemStack( Material.COMPASS )) + player.sendMsg( "game.started" ) + } + + Bukkit.getOnlinePlayers().forEach { player -> + player.sendMsg( "game.invincibility-start" ) + } + } + + private fun startFighting() + { + setGameState( GameState.INGAME ) + timer = 0 // Reset Timer für "Ingame" + + val world = Bukkit.getWorld( "world" ) ?: return + world.worldBorder.setSize( endBorder, borderShrinkTime ) + + plugin.antiRunningManager.resetTimers() + + Bukkit.getOnlinePlayers().forEach { p -> + p.sendMsg( "game.fighting-started" ) + p.playSound( p.location, Sound.ENTITY_ENDER_DRAGON_GROWL, 1f, 1f ) + + p.showTitle(Title.title( + p.trans( "title.fight-main" ), + p.trans( "title.fight-sub" ) + )) + } + } + + fun onPlayerEliminated( + player: Player, + killer: Player? ) { - winners.forEach { this.winners.add( it ) } + if (!alivePlayers.contains( player.uniqueId )) return + + alivePlayers.remove( player.uniqueId ) + player.gameMode = GameMode.SPECTATOR + + player.inventory.contents.filterNotNull().forEach { + player.world.dropItemNaturally( player.location, it ) + } + + val msgKey = if ( killer != null ) "game.death-killed" else "game.death-pve" + val killerName = killer?.name ?: "Environment" + + Bukkit.getOnlinePlayers().forEach { p -> + p.sendMsg( msgKey, "player" to player.name, "killer" to killerName, "left" to alivePlayers.size.toString() ) + } + + checkWin() } - fun getWinners(): List = winners.toList() - - fun getCurrentState(): GameState? = currentState - - fun getCurrentStateType(): GameStateTypes? = gameStateTypes.filter { it.value.name == currentState?.name }.keys.firstOrNull() - - fun isRunning(): Boolean + private fun checkWin() { - return getCurrentStateType() == GameStateTypes.IMMUNITY || - getCurrentStateType() == GameStateTypes.BATTLE || - getCurrentStateType() == GameStateTypes.FEAST || - getCurrentStateType() == GameStateTypes.DEATHMATCH + if ( currentState != GameState.INGAME && currentState != GameState.INVINCIBILITY ) return + + if ( alivePlayers.size <= 1 ) + { + val winnerUUID = alivePlayers.firstOrNull() + val winnerName = if ( winnerUUID != null ) Bukkit.getPlayer( winnerUUID )?.name ?: "N/A" else "N/A" + endGame( winnerName ) + } } - fun isBeforeFeast(): Boolean - { - return getCurrentStateType() == GameStateTypes.WAITING || - getCurrentStateType() == GameStateTypes.PRE_START || - getCurrentStateType() == GameStateTypes.IMMUNITY || - (getCurrentStateType() == GameStateTypes.BATTLE && - !( currentState as BattleState ).afterFeast ) + private fun endGame( + winnerName: String + ) { + setGameState( GameState.ENDING ) + timer = 15 + + Bukkit.getOnlinePlayers().forEach { p -> + p.showTitle(Title.title( + p.trans( "title.win-main", "winner" to winnerName ), + p.trans( "title.win-sub" ) + )) + p.sendMsg( "game.win-chat", "winner" to winnerName ) + } } - fun isBefore( - stateType: GameStateTypes - ): Boolean - { - return getCurrentStateType()?.points!! < stateType.points + // --- Helfer Methoden --- + + private fun teleportRandomly( + player: Player, + world: World, + maxRadius: Double + ) { + var loc: Location + var safe = false + var attempts = 0 + + do { + val x = Random.nextDouble( -maxRadius, maxRadius ) + val z = Random.nextDouble( -maxRadius, maxRadius ) + val y = world.getHighestBlockYAt( x.toInt(), z.toInt() ) + 1.0 + loc = Location( world, x, y, z ) + + val block = loc.block.type + val below = loc.subtract( 0.0, 1.0, 0.0 ).block.type + + if ( below.isSolid && below != Material.LAVA && below != Material.CACTUS && block == Material.AIR ) + safe = true + attempts++ + } while ( !safe && attempts < 20 ) + + player.teleport(loc.add( 0.0, 1.0, 0.0 )) } - fun shutdown() + private fun updateCompass() { - currentState?.onExit( null ) - currentState = null - } + val players = Bukkit.getOnlinePlayers().filter { alivePlayers.contains( it.uniqueId ) } -} + for ( p in players ) + { + var nearest: Player? = null + var minDistance = Double.MAX_VALUE -enum class GameStateTypes( - val points: Int -) { - WAITING( 0 ), - PRE_START( 1 ), - IMMUNITY( 2 ), - BATTLE( 3 ), - FEAST( 4 ), - DEATHMATCH( 5 ), - END( 6 ) -} + for ( target in players ) + { + if ( p == target ) continue + + val dist = p.location.distanceSquared( target.location ) + if ( dist < minDistance ) + { + minDistance = dist + nearest = target + } + } + + if ( nearest != null ) + p.compassTarget = nearest.location + else + p.compassTarget = p.world.spawnLocation + } + } + + // --- Event Listener Integration --- + + @EventHandler + fun onDeath( + event: PlayerDeathEvent + ) { + if ( currentState == GameState.INGAME || currentState == GameState.INVINCIBILITY ) + { + event.deathMessage( null ) + event.drops.clear() + + Bukkit.getScheduler().runTask( plugin ) { -> + event.entity.spigot().respawn() + } + + onPlayerEliminated( event.entity, event.entity.killer ) + } + } + + @EventHandler + fun onQuit( + event: PlayerQuitEvent + ) { + if (alivePlayers.contains( event.player.uniqueId )) + { + if ( currentState == GameState.INGAME || currentState == GameState.INVINCIBILITY ) + { + val lastDamageCause = event.player.lastDamageCause + + if ( lastDamageCause != null && lastDamageCause is EntityDamageByEntityEvent ) + { + val attacker = lastDamageCause.damager + + if ( attacker is Player ) + { + onPlayerEliminated( event.player, attacker ) + return + } + } + + onPlayerEliminated( event.player, null ) // PVE Tod + } + else alivePlayers.remove( event.player.uniqueId ) + } + } + + @EventHandler + fun onDamage( + event: EntityDamageEvent + ) { + if ( currentState == GameState.INVINCIBILITY && event.entity is Player ) + event.isCancelled = true + + if ( currentState == GameState.LOBBY || currentState == GameState.STARTING || currentState == GameState.ENDING ) + event.isCancelled = true // Nie Schaden in Lobby/Ende + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/GameState.kt b/src/main/kotlin/club/mcscrims/speedhg/game/GameState.kt index 2e977e3..515b6b8 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/game/GameState.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/game/GameState.kt @@ -1,109 +1,9 @@ package club.mcscrims.speedhg.game -import club.mcscrims.core.config.DurationType -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.spigot.scheduler.SchedulerManager -import club.mcscrims.spigot.scheduler.TaskRegistration -import org.bukkit.Bukkit -import org.bukkit.entity.Player -import org.bukkit.scheduler.BukkitTask - -abstract class GameState( - val name: String, - protected val gameManager: GameManager, - protected val plugin: SpeedHG, - protected val schedulerManager: SchedulerManager, - protected val durationSeconds: Int? = null -) { - - private var tickTask: BukkitTask? = null - private var remainingSeconds: Int = durationSeconds ?: 0 - private var isActive: Boolean = false - - open fun onEnter( - previous: GameState? - ) { - isActive = true - remainingSeconds = durationSeconds ?: 0 - - if ( durationSeconds != null ) - startTicking() - } - - abstract fun onTick() - - abstract fun onEndOfDuration() - - open fun onExit( - next: GameState? - ) { - isActive = false - stopTicking() - } - - private fun startTicking() - { - tickTask = Bukkit.getScheduler().runTaskTimer( plugin, { -> - if ( !isActive ) - { - stopTicking() - return@runTaskTimer - } - - try { - onTick() - } catch ( e: Exception ) { - plugin.logger.severe("Error during onTick for state $name: ${e.message}") - e.printStackTrace() - } - - if ( durationSeconds != null && remainingSeconds > 0 ) - { - if (plugin.pluginConfig.data.getDuration( name ).type == DurationType.INCREASING ) - { - remainingSeconds++ - return@runTaskTimer - } - - remainingSeconds-- - - if ( remainingSeconds == 0) - try { - onEndOfDuration() - } catch ( e: Exception ) { - plugin.logger.severe("Error during onEndOfDuration for state $name: ${e.message}") - e.printStackTrace() - } - } - }, 20L, 20L ) - } - - private fun stopTicking() { - tickTask?.cancel() - tickTask = null - } - - protected fun broadcast( - messageKey: String, - vararg placeholders: Pair - ) { - plugin.chatManager.broadcast( messageKey, *placeholders ) - } - - protected fun forAlivePlayers( - action: (Player) -> Unit - ) { - Bukkit.getOnlinePlayers() - .filter { !it.isDead } - .forEach { player -> - try { - action( player ) - } catch ( e: Exception ) { - plugin.logger.warning("Error executing action for player ${player.name}: ${e.message}") - } - } - } - - fun getRemainingSeconds(): Int = remainingSeconds - -} +enum class GameState { + LOBBY, // Warten auf Spieler + STARTING, // Countdown läuft + INVINCIBILITY, // Schutzzeit (Spieler verteilt, kein Schaden) + INGAME, // Kampfphase (Schaden an, Border schrumpft) + ENDING // Gewinner steht fest, Server startet neu +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/impl/BattleState.kt b/src/main/kotlin/club/mcscrims/speedhg/game/impl/BattleState.kt deleted file mode 100644 index 41c092b..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/game/impl/BattleState.kt +++ /dev/null @@ -1,65 +0,0 @@ -package club.mcscrims.speedhg.game.impl - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.game.GameState -import club.mcscrims.speedhg.game.GameStateTypes -import club.mcscrims.spigot.scheduler.SchedulerManager -import org.bukkit.Bukkit -import org.bukkit.entity.Player - -class BattleState( - gameManager: GameManager, - plugin: SpeedHG, - schedulerManager: SchedulerManager, - durationSeconds: Int? = null -) : GameState( "battle", gameManager, plugin, schedulerManager, durationSeconds ) { - - var afterFeast: Boolean = false - - override fun onEnter( - previous: GameState? - ) { - super.onEnter( previous ) - Bukkit.getOnlinePlayers().forEach( Player::clearActivePotionEffects ) - } - - override fun onTick() - { - val win = checkForWinners() - - when( getRemainingSeconds() ) - { - 300 -> - { - gameManager.gameStateTypes[ GameStateTypes.BATTLE ] = this - gameManager.transitionTo( GameStateTypes.FEAST ) - afterFeast = true - } - - 1800 -> - { - if ( !win ) - { - gameManager.transitionTo( GameStateTypes.DEATHMATCH ) - return - } - } - } - } - - override fun onEndOfDuration() {} - - private fun checkForWinners(): Boolean - { - val players = Bukkit.getOnlinePlayers().filter { !it.isDead } - - if ( players.size > 1 ) - return false - - gameManager.addWinners( players.first() ) - gameManager.transitionTo( GameStateTypes.END ) - return true - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/impl/DeathmatchState.kt b/src/main/kotlin/club/mcscrims/speedhg/game/impl/DeathmatchState.kt deleted file mode 100644 index dafba99..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/game/impl/DeathmatchState.kt +++ /dev/null @@ -1,113 +0,0 @@ -package club.mcscrims.speedhg.game.impl - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.game.GameState -import club.mcscrims.speedhg.game.GameStateTypes -import club.mcscrims.speedhg.game.impl.PreStartState.AnnouncementType -import club.mcscrims.spigot.scheduler.SchedulerManager -import org.bukkit.Bukkit -import org.bukkit.Location -import org.bukkit.Material -import org.bukkit.Statistic -import org.bukkit.potion.PotionEffect -import org.bukkit.potion.PotionEffectType - -class DeathmatchState( - gameManager: GameManager, - plugin: SpeedHG, - schedulerManager: SchedulerManager, - durationSeconds: Int? = null -) : GameState( "deathmatch", gameManager, plugin, schedulerManager, durationSeconds ) { - - private val world = plugin.worldManager.getWorld() - private val pitLocation = Location( world, 0.0, 1.0, 0.0 ) - - override fun onTick() - { - checkForWinners() - - when( getRemainingSeconds() ) - { - 0 -> - { - plugin.chatManager.broadcast( "gameStates.deathmatch.started" ) - circle() - schedulerManager.runLater( 10L ) { randomTeleport() } - } - - 600 -> announce( AnnouncementType.MINUTES, 5 ) - 660 -> announce( AnnouncementType.MINUTES, 4 ) - 720 -> announce( AnnouncementType.MINUTES, 3 ) - 780 -> announce( AnnouncementType.MINUTES, 2 ) - 840 -> announce( AnnouncementType.MINUTES, 1 ) - 870 -> announce( AnnouncementType.SECONDS, 30 ) - 885 -> announce( AnnouncementType.SECONDS, 15 ) - 890 -> announce( AnnouncementType.SECONDS, 10 ) - 895 -> announce( AnnouncementType.SECONDS, 5 ) - 896 -> announce( AnnouncementType.SECONDS, 4 ) - 897 -> announce( AnnouncementType.SECONDS, 3 ) - 898 -> announce( AnnouncementType.SECONDS, 2 ) - 899 -> announce( AnnouncementType.SECONDS, 1 ) - - 900 -> try { - onEndOfDuration() - } catch ( e: Exception ) { - plugin.logger.severe("Error during onEndOfDuration for state $name: ${e.message}") - e.printStackTrace() - } - } - } - - override fun onEndOfDuration() - { - val winner = Bukkit.getOnlinePlayers().stream() - .filter { !it.isDead } - .map { it.getStatistic( Statistic.PLAYER_KILLS ) to it } - .max(compareBy { it.first }) - .get().second - - gameManager.addWinners( winner ) - gameManager.transitionTo( GameStateTypes.END ) - } - - private fun checkForWinners(): Boolean - { - val players = Bukkit.getOnlinePlayers().filter { !it.isDead } - - if ( players.size > 1 ) - return false - - gameManager.addWinners( players.first() ) - gameManager.transitionTo( GameStateTypes.END ) - return true - } - - private fun announce( - type: AnnouncementType, - time: Int - ) { - val arg = if ( type == AnnouncementType.MINUTES ) "M" else "S" - broadcast( "gameStates.deathmatch.ending$arg", "{time}" to time.toString() ) - } - - private fun randomTeleport() - { - forAlivePlayers { player -> - player.addPotionEffect(PotionEffect( PotionEffectType.RESISTANCE, 40, 20 )) - - val loc = pitLocation.clone() - loc.x = (( pitLocation.blockX - 15 )..( pitLocation.blockX + 15 )).random().toDouble() - loc.z = (( pitLocation.blockZ - 15 )..( pitLocation.blockZ + 15 )).random().toDouble() - loc.y = world!!.getHighestBlockYAt( loc.blockX, loc.blockZ ).toDouble() - player.teleport( loc ) - } - } - - private fun circle() - { - val highestLocation = plugin.worldManager.highestLocationWithRadius( pitLocation, 30 ) - plugin.worldEditUtils.createCylinder( world!!, pitLocation, 30.0, highestLocation.blockY, Material.AIR, true ) - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/impl/EndState.kt b/src/main/kotlin/club/mcscrims/speedhg/game/impl/EndState.kt deleted file mode 100644 index 7b33945..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/game/impl/EndState.kt +++ /dev/null @@ -1,43 +0,0 @@ -package club.mcscrims.speedhg.game.impl - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.game.GameState -import club.mcscrims.spigot.chat.getDisplayName -import club.mcscrims.spigot.scheduler.SchedulerManager -import kotlinx.coroutines.runBlocking -import org.bukkit.Bukkit - -class EndState( - gameManager: GameManager, - plugin: SpeedHG, - schedulerManager: SchedulerManager, - durationSeconds: Int? = null -) : GameState( "end", gameManager, plugin, schedulerManager, durationSeconds ) { - - override fun onEnter( - previous: GameState? - ) { - super.onEnter( previous ) - val winners = gameManager.getWinners() - - if ( winners.size > 1 ) - plugin.chatManager.broadcast( "gameStates.end.multipleWinners", "{winners}" to winners.joinToString { it.getDisplayName } ) - else if ( winners.size == 1 ) - plugin.chatManager.broadcast( "gameStates.end.oneWinner", "{winner}" to winners.first().getDisplayName ) - else - plugin.chatManager.broadcast( "gameStates.end.noWinners" ) - - runBlocking { winners.forEach { - plugin.statsRepository.addWins( it.uniqueId, 1 ) - } } - } - - override fun onTick() {} - - override fun onEndOfDuration() - { - Bukkit.shutdown() - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/impl/FeastState.kt b/src/main/kotlin/club/mcscrims/speedhg/game/impl/FeastState.kt deleted file mode 100644 index 0781e31..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/game/impl/FeastState.kt +++ /dev/null @@ -1,209 +0,0 @@ -package club.mcscrims.speedhg.game.impl - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.game.GameState -import club.mcscrims.speedhg.game.GameStateTypes -import club.mcscrims.speedhg.game.impl.PreStartState.AnnouncementType -import club.mcscrims.speedhg.util.RandomCollection -import club.mcscrims.spigot.item.ItemBuilder -import club.mcscrims.spigot.scheduler.SchedulerManager -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer -import org.bukkit.Location -import org.bukkit.Material -import org.bukkit.block.Chest -import org.bukkit.inventory.ItemStack -import org.bukkit.inventory.meta.PotionMeta -import org.bukkit.potion.PotionEffect -import org.bukkit.potion.PotionEffectType -import org.bukkit.util.BoundingBox -import java.util.* - -class FeastState( - gameManager: GameManager, - plugin: SpeedHG, - schedulerManager: SchedulerManager, - durationSeconds: Int? = null -) : GameState( "feast", gameManager, plugin, schedulerManager, durationSeconds ) { - - private val world = plugin.worldManager.getWorld() - private val random = Random() - - internal var feastLocation: Location - internal var feastBox: BoundingBox - internal var feastHeight: Int = 1 - - init - { - this.feastLocation = getLocation() - this.feastHeight = plugin.worldManager.highestLocationWithRadius( feastLocation, 11 ).blockY + 3 - this.feastBox = BoundingBox.of( feastLocation, 11.0, feastHeight.toDouble(), 11.0 ) - } - - override fun onEnter( - previous: GameState? - ) { - super.onEnter( previous ) - circle() - } - - override fun onTick() - { - when( getRemainingSeconds() ) - { - 300 -> announce( AnnouncementType.MINUTES, 5 ) - 240 -> announce( AnnouncementType.MINUTES, 4 ) - 180 -> announce( AnnouncementType.MINUTES, 3 ) - 120 -> announce( AnnouncementType.MINUTES, 2 ) - 60 -> announce( AnnouncementType.MINUTES, 1 ) - 30 -> announce( AnnouncementType.SECONDS, 30 ) - 15 -> announce( AnnouncementType.SECONDS, 15 ) - 10 -> announce( AnnouncementType.SECONDS, 10 ) - 5 -> announce( AnnouncementType.SECONDS, 5 ) - 4 -> announce( AnnouncementType.SECONDS, 4 ) - 3 -> announce( AnnouncementType.SECONDS, 3 ) - 2 -> announce( AnnouncementType.SECONDS, 2 ) - 1 -> announce( AnnouncementType.SECONDS, 1 ) - } - } - - override fun onEndOfDuration() - { - plugin.chatManager.broadcast( "feast.started", - "{x}" to feastLocation.blockX.toString(), - "{y}" to feastLocation.blockY.toString(), - "{z}" to feastLocation.blockZ.toString() - ) - - chests() - gameManager.transitionTo( GameStateTypes.BATTLE ) - } - - private fun chests() - { - feastLocation.clone().block.type = Material.ENCHANTING_TABLE - - val chestLocations = arrayOfNulls( 12 ) - - chestLocations[0] = feastLocation.clone().add( 1.0, 0.0, 1.0 ) - chestLocations[1] = feastLocation.clone().add( -1.0, 0.0, 1.0 ) - chestLocations[2] = feastLocation.clone().add( -1.0, 0.0, -1.0 ) - chestLocations[3] = feastLocation.clone().add( 1.0, 0.0, -1.0 ) - chestLocations[4] = feastLocation.clone().add( 2.0, 0.0, 2.0 ) - chestLocations[5] = feastLocation.clone().add( 0.0, 0.0, 2.0 ) - chestLocations[6] = feastLocation.clone().add( -2.0, 0.0, 2.0 ) - chestLocations[7] = feastLocation.clone().add( 2.0, 0.0, 0.0 ) - chestLocations[8] = feastLocation.clone().add( -2.0, 0.0, 0.0 ) - chestLocations[9] = feastLocation.clone().add( 2.0, 0.0, -2.0 ) - chestLocations[10] = feastLocation.clone().add( 0.0, 0.0, -2.0 ) - chestLocations[11] = feastLocation.clone().add( -2.0, 0.0, -2.0 ) - - Arrays.stream( chestLocations ).forEach { it!!.block.type = Material.CHEST } - - val diamondItems = RandomCollection() - diamondItems.add( 1.0, ItemStack( Material.DIAMOND_BOOTS )) - diamondItems.add( 1.0, ItemStack( Material.DIAMOND_CHESTPLATE )) - diamondItems.add( 1.0, ItemStack( Material.DIAMOND_SWORD )) - - val ironItems = RandomCollection() - ironItems.add( 1.0, ItemStack( Material.IRON_BOOTS )) - ironItems.add( 1.0, ItemStack( Material.IRON_LEGGINGS )) - ironItems.add( 1.0, ItemStack( Material.IRON_CHESTPLATE )) - ironItems.add( 1.0, ItemStack( Material.IRON_HELMET )) - ironItems.add( 1.0, ItemStack( Material.IRON_SWORD )) - - val netheriteItems = RandomCollection() - netheriteItems.add( 1.0, ItemStack( Material.NETHERITE_INGOT )) - netheriteItems.add( 1.0, ItemStack( Material.SMITHING_TABLE )) - - val sizeableItems = RandomCollection() - sizeableItems.add( 1.0, ItemStack( Material.RED_MUSHROOM )) - sizeableItems.add( 1.0, ItemStack( Material.BROWN_MUSHROOM )) - sizeableItems.add( 1.0, ItemStack( Material.BOWL )) - sizeableItems.add( 1.0, ItemStack( Material.CACTUS )) - sizeableItems.add( 1.0, ItemStack( Material.COCOA_BEANS )) - sizeableItems.add( 1.0, ItemStack( Material.MUSHROOM_STEW )) - sizeableItems.add( 1.0, ItemStack( Material.LAPIS_LAZULI )) - sizeableItems.add( 1.0, ItemStack( Material.ARROW )) - sizeableItems.add( 1.0, ItemStack( Material.COOKED_BEEF )) - sizeableItems.add( 1.0, ItemStack( Material.COOKED_PORKCHOP )) - sizeableItems.add( 1.0, ItemStack( Material.COOKED_CHICKEN )) - - val singleItems = RandomCollection() - singleItems.add( 1.0, ItemStack( Material.BOW )) - singleItems.add( 1.0, ItemStack( Material.COBWEB )) - singleItems.add( 1.0, ItemStack( Material.FLINT_AND_STEEL )) - singleItems.add( 1.0, ItemStack( Material.TNT )) - singleItems.add( 1.0, ItemStack( Material.ENDER_PEARL )) - singleItems.add( 1.0, ItemStack( Material.LAVA_BUCKET )) - singleItems.add( 1.0, ItemStack( Material.WATER_BUCKET )) - - val strengthPotion = ItemStack( Material.SPLASH_POTION ) - val effect = PotionEffect( PotionEffectType.STRENGTH, (60..240).random(), 0 ) - strengthPotion.editMeta { ( it as PotionMeta ).addCustomEffect( effect, true ) } - - singleItems.add( 1.0, strengthPotion ) - - val cleanser = ItemBuilder( plugin, Material.SUGAR ) - .name(LegacyComponentSerializer.legacySection().serialize(plugin.chatFormatter.format( "feast.cleanser.name" ))) - .unbreakable( true ) - .hideAttributes() - .build() - - singleItems.add( 1.0, cleanser ) - - val lootPool = RandomCollection>() - lootPool.add( 17.5, ironItems ) - lootPool.add( 6.8, diamondItems ) - lootPool.add( 33.0, sizeableItems ) - lootPool.add( 33.0, singleItems ) - lootPool.add( 1.5, netheriteItems ) - - for ( location in chestLocations ) - { - val chest = location!!.block.state as Chest - - for ( i in 0..( 6..12 ).random() ) - { - val randomItemCollection = lootPool.getRandom() - val itemList = randomItemCollection.random().getRandom() - - for ( item in itemList ) - { - if ( randomItemCollection == sizeableItems ) - item.amount = ( 1..16 ).random() - - chest.inventory.setItem(random.nextInt( 26 - 1 ) + 1, item) - } - } - } - } - - private fun announce( - type: AnnouncementType, - time: Int - ) { - val arg = if ( type == AnnouncementType.MINUTES ) "M" else "S" - broadcast( "gameStates.feast.starting$arg", "{time}" to time.toString() ) - } - - private fun circle() - { - plugin.worldEditUtils.createCylinder( world!!, feastLocation, 11.0, feastHeight, Material.GRASS_BLOCK, false ) - plugin.worldEditUtils.createCylinder( world, feastLocation.add( 0.0, 1.0, 0.0 ), 11.0, feastHeight, Material.AIR, false) - } - - private fun getLocation(): Location - { - val x = ( -100..100 ).random() - val z = ( -100..100 ).random() - - val highestY = world!!.getHighestBlockYAt( x, z ) - - if ( highestY >= 70 ) - return getLocation() - - return Location( world, x.toDouble(), highestY.toDouble() + 5.0, z.toDouble() ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/impl/ImmunityState.kt b/src/main/kotlin/club/mcscrims/speedhg/game/impl/ImmunityState.kt deleted file mode 100644 index e96dc4c..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/game/impl/ImmunityState.kt +++ /dev/null @@ -1,81 +0,0 @@ -package club.mcscrims.speedhg.game.impl - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.game.GameState -import club.mcscrims.speedhg.game.GameStateTypes -import club.mcscrims.speedhg.game.impl.PreStartState.AnnouncementType -import club.mcscrims.spigot.scheduler.SchedulerManager -import org.bukkit.Bukkit -import org.bukkit.Material -import org.bukkit.inventory.ItemStack -import org.bukkit.potion.PotionEffect -import org.bukkit.potion.PotionEffectType - -class ImmunityState( - gameManager: GameManager, - plugin: SpeedHG, - schedulerManager: SchedulerManager, - durationSeconds: Int? = null -) : GameState( "immunity", gameManager, plugin, schedulerManager, durationSeconds ) { - - override fun onEnter( - previous: GameState? - ) { - super.onEnter( previous ) - - val effects = listOf( - PotionEffect( PotionEffectType.HASTE, durationSeconds?.times( 20 ) ?: 0, 0 ), - PotionEffect( PotionEffectType.SPEED, durationSeconds?.times( 20 ) ?: 0, 0 ) - ) - - val players = Bukkit.getOnlinePlayers() - players.forEach { it.addPotionEffects( effects ) } - - for ( player in players ) - { - player.inventory.clear() - player.inventory.armorContents = emptyArray() - - player.inventory.setItem( 8, ItemStack( Material.COMPASS )) - - plugin.kitManager.startKitForPlayer( player ) - TODO( "Give perks" ) - } - - broadcast( "gameStates.immunity.warnings.butterfly" ) - } - - override fun onTick() - { - when( getRemainingSeconds() ) - { - 180 -> announce( AnnouncementType.MINUTES, 3 ) - 120 -> announce( AnnouncementType.MINUTES, 2 ) - 60 -> announce( AnnouncementType.MINUTES, 1 ) - 30 -> announce( AnnouncementType.SECONDS, 30 ) - 15 -> announce( AnnouncementType.SECONDS, 15 ) - 10 -> announce( AnnouncementType.SECONDS, 10 ) - 5 -> announce( AnnouncementType.SECONDS, 5 ) - 4 -> announce( AnnouncementType.SECONDS, 4 ) - 3 -> announce( AnnouncementType.SECONDS, 3 ) - 2 -> announce( AnnouncementType.SECONDS, 2 ) - 1 -> announce( AnnouncementType.SECONDS, 1 ) - } - } - - override fun onEndOfDuration() - { - broadcast( "gameStates.immunity.ended" ) - gameManager.transitionTo( GameStateTypes.BATTLE ) - } - - private fun announce( - type: AnnouncementType, - time: Int - ) { - val arg = if ( type == AnnouncementType.MINUTES ) "M" else "S" - broadcast( "gameStates.immunity.ending$arg", "{time}" to time.toString() ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/impl/PreStartState.kt b/src/main/kotlin/club/mcscrims/speedhg/game/impl/PreStartState.kt deleted file mode 100644 index 1d706f4..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/game/impl/PreStartState.kt +++ /dev/null @@ -1,102 +0,0 @@ -package club.mcscrims.speedhg.game.impl - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.game.GameState -import club.mcscrims.speedhg.game.GameStateTypes -import club.mcscrims.spigot.scheduler.SchedulerManager -import org.bukkit.Bukkit -import org.bukkit.Location - -class PreStartState( - gameManager: GameManager, - plugin: SpeedHG, - schedulerManager: SchedulerManager, - durationSeconds: Int? = null -) : GameState( "pre_start", gameManager, plugin, schedulerManager, durationSeconds ) { - - private lateinit var previous: GameState - - override fun onEnter( - previous: GameState? - ) { - super.onEnter( previous ) - - if ( previous != null ) - this.previous = previous - } - - var isStarting: Boolean = false - - override fun onTick() - { - val playerSize = Bukkit.getOnlinePlayers().size - - if ( playerSize < plugin.pluginConfig.data.game.minimumPlayers ) - { - isStarting = false - gameManager.transitionTo( GameStateTypes.WAITING ) - } - else isStarting = true - - if ( !isStarting ) - return - - if ( getRemainingSeconds() == 15 ) - teleport() - - when( getRemainingSeconds() ) - { - 300 -> announce( AnnouncementType.MINUTES, 5 ) - 240 -> announce( AnnouncementType.MINUTES, 4 ) - 180 -> announce( AnnouncementType.MINUTES, 3 ) - 120 -> announce( AnnouncementType.MINUTES, 2 ) - 60 -> announce( AnnouncementType.MINUTES, 1 ) - 30 -> announce( AnnouncementType.SECONDS, 30 ) - 15 -> announce( AnnouncementType.SECONDS, 15 ) - 10 -> announce( AnnouncementType.SECONDS, 10 ) - 5 -> announce( AnnouncementType.SECONDS, 5 ) - 4 -> announce( AnnouncementType.SECONDS, 4 ) - 3 -> announce( AnnouncementType.SECONDS, 3 ) - 2 -> announce( AnnouncementType.SECONDS, 2 ) - 1 -> announce( AnnouncementType.SECONDS, 1 ) - } - } - - override fun onEndOfDuration() - { - broadcast( "gameStates.preStart.started" ) - isStarting = false - gameManager.transitionTo( GameStateTypes.IMMUNITY ) - } - - override fun onExit( - next: GameState? - ) { - super.onExit( next ) - Bukkit.getOnlinePlayers().forEach { it.inventory.clear() } - } - - private fun announce( - type: AnnouncementType, - time: Int - ) { - val arg = if ( type == AnnouncementType.MINUTES ) "M" else "S" - broadcast( "gameStates.preStart.starting$arg", "{time}" to time.toString() ) - } - - private fun teleport() - { - for ( player in Bukkit.getOnlinePlayers() ) - { - val world = player.world - val loc = Location( world, 0.0, world.getHighestBlockYAt( 0, 0 ).toDouble(), 0.0 ) - player.teleport( loc ) - } - } - - internal enum class AnnouncementType { - MINUTES, SECONDS - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/impl/WaitingState.kt b/src/main/kotlin/club/mcscrims/speedhg/game/impl/WaitingState.kt deleted file mode 100644 index 4963479..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/game/impl/WaitingState.kt +++ /dev/null @@ -1,35 +0,0 @@ -package club.mcscrims.speedhg.game.impl - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.game.GameState -import club.mcscrims.speedhg.game.GameStateTypes -import club.mcscrims.spigot.scheduler.SchedulerManager -import org.bukkit.Bukkit - -class WaitingState( - gameManager: GameManager, - plugin: SpeedHG, - schedulerManager: SchedulerManager, - durationSeconds: Int? = null -) : GameState( "waiting", gameManager, plugin, schedulerManager, durationSeconds ) { - - private var secondsCounter: Int = 0 - - override fun onTick() - { - val playerSize = Bukkit.getOnlinePlayers().size - - if ( playerSize < plugin.pluginConfig.data.game.minimumPlayers ) - if ( secondsCounter == 15 ) - { - broadcast( "gameStates.waiting.awaiting_players", "{min_players}" to plugin.pluginConfig.data.game.minimumPlayers.toString() ) - secondsCounter = 0 - } - else secondsCounter++ - else gameManager.transitionTo( GameStateTypes.PRE_START ) - } - - override fun onEndOfDuration() {} - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/modules/AntiRunningManager.kt b/src/main/kotlin/club/mcscrims/speedhg/game/modules/AntiRunningManager.kt new file mode 100644 index 0000000..340f076 --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/game/modules/AntiRunningManager.kt @@ -0,0 +1,201 @@ +package club.mcscrims.speedhg.game.modules + +import club.mcscrims.speedhg.SpeedHG +import club.mcscrims.speedhg.game.GameState +import club.mcscrims.speedhg.util.sendMsg +import club.mcscrims.speedhg.util.trans +import net.kyori.adventure.bossbar.BossBar +import net.kyori.adventure.text.Component +import org.bukkit.Bukkit +import org.bukkit.GameMode +import org.bukkit.Sound +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.entity.EntityDamageByEntityEvent +import org.bukkit.potion.PotionEffect +import org.bukkit.potion.PotionEffectType +import java.util.UUID +import java.util.concurrent.ConcurrentHashMap +import kotlin.math.abs + +class AntiRunningManager( + private val plugin: SpeedHG +): Listener +{ + + private val lastCombatAction = ConcurrentHashMap() + + private val warningBar = BossBar.bossBar( + Component.text("FIGHT!"), + 1.0f, + BossBar.Color.RED, + BossBar.Overlay.PROGRESS + ) + + init { + Bukkit.getScheduler().runTaskTimer( plugin, { -> + checkPlayers() + }, 20L, 20L ) + + plugin.server.pluginManager.registerEvents( this, plugin ) + } + + @EventHandler + fun onDamage( + event: EntityDamageByEntityEvent + ) { + if (plugin.gameManager.currentState != GameState.INGAME) + return + + val victim = event.entity + val attacker = event.damager + + if ( victim is Player && attacker is Player) + { + val now = System.currentTimeMillis() + lastCombatAction[ victim.uniqueId ] = now + lastCombatAction[ attacker.uniqueId ] = now + + removePunishment( victim ) + removePunishment( attacker ) + } + } + + private fun checkPlayers() + { + if (plugin.gameManager.currentState != GameState.INGAME) + return + + val config = plugin.config.getConfigurationSection( "anti-runner" ) ?: return + if (!config.getBoolean( "enabled" )) return + + val radius = config.getDouble( "check-radius" ) + val maxYDiff = config.getDouble( "ignore-vertical-distance", 15.0 ) + val ignoreCaveMix = config.getBoolean( "ignore-cave-surface-mix", true ) + + val warnTime = config.getLong( "warn-time" ) * 1000 + val punishTime = config.getLong( "punish-time" ) * 1000 + + val players = Bukkit.getOnlinePlayers().filter { it.gameMode == GameMode.SURVIVAL } + + for ( player in players ) + { + val nearbyEnemy = players + .filter { other -> + other != player && + other.world == player.world && + other.location.distanceSquared( player.location ) <= ( radius * radius ) + } + .minByOrNull { it.location.distanceSquared( player.location ) } + + if ( nearbyEnemy == null ) + { + resetPlayerTimer( player ) + continue + } + + val yDiff = abs(player.location.y - nearbyEnemy.location.y) + + if ( yDiff > maxYDiff ) + { + resetPlayerTimer( player ) + continue + } + + if ( ignoreCaveMix && player.world.isDayTime ) + { + val lightA = player.location.block.lightFromSky + val lightB = nearbyEnemy.location.block.lightFromSky + + val isPlayerOutside = lightA > 12 + val isEnemyInCave = lightB < 4 + + val isPlayerInCave = lightA < 4 + val isEnemyOutside = lightB > 12 + + if (( isPlayerOutside && isEnemyInCave ) || ( isPlayerInCave && isEnemyOutside )) + { + resetPlayerTimer( player ) + continue + } + } + + val lastCombat = lastCombatAction.getOrDefault( player.uniqueId, System.currentTimeMillis() ) + val timeDiff = System.currentTimeMillis() - lastCombat + + if ( timeDiff > warnTime ) + player.showBossBar(warningBar.name(player.trans( "antirunner.warning" ))) + else + player.hideBossBar( warningBar ) + + if ( timeDiff > punishTime ) + applyPunishment( player ) + } + } + + private fun applyPunishment( + player: Player + ) { + player.addPotionEffect( + PotionEffect( + /* type = */ PotionEffectType.SLOWNESS, + /* duration = */ 60, + /* amplifier = */ 2, + /* ambient = */ false, + /* particles = */ false, + /* icon = */ true + ) + ) + + player.addPotionEffect( + PotionEffect( + /* type = */ PotionEffectType.GLOWING, + /* duration = */ 60, + /* amplifier = */ 0, + /* ambient = */ false, + /* particles = */ false, + /* icon = */ false + ) + ) + + if (!player.hasPotionEffect( PotionEffectType.GLOWING )) + { + player.sendMsg( "antirunner.punish" ) + player.playSound( player.location, Sound.ENTITY_ZOMBIE_VILLAGER_CURE, 1f, 0.5f ) + } + } + + private fun removePunishment( + player: Player + ) { + player.removePotionEffect( PotionEffectType.SLOWNESS ) + player.removePotionEffect( PotionEffectType.GLOWING ) + player.hideBossBar( warningBar ) + + lastCombatAction[ player.uniqueId ] = System.currentTimeMillis() + } + + private fun resetPlayerTimer( + player: Player + ) { + lastCombatAction[ player.uniqueId ] = System.currentTimeMillis() + player.hideBossBar( warningBar ) + + if (player.hasPotionEffect( PotionEffectType.GLOWING )) + removePunishment( player ) + } + + fun resetTimers() + { + val now = System.currentTimeMillis() + + Bukkit.getOnlinePlayers() + .filter { it.gameMode == GameMode.SURVIVAL } + .forEach { + lastCombatAction[ it.uniqueId ] = now + it.hideBossBar( warningBar ) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/AbstractKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/AbstractKit.kt deleted file mode 100644 index ed79da1..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/AbstractKit.kt +++ /dev/null @@ -1,116 +0,0 @@ -package club.mcscrims.speedhg.kit - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.ability.AbilityContext -import club.mcscrims.speedhg.ability.AbilityResult -import club.mcscrims.speedhg.game.GameManager -import net.kyori.adventure.text.Component -import org.bukkit.Material -import org.bukkit.entity.Player -import org.bukkit.event.entity.EntityDamageByEntityEvent -import org.bukkit.event.player.PlayerInteractEvent -import org.bukkit.event.player.PlayerMoveEvent -import org.bukkit.inventory.ItemStack - -abstract class AbstractKit( - val id: String, - val displayName: Component, - val description: List, - val icon: Material, - val playStyle: PlayStyle, - protected val plugin: SpeedHG, - protected val abilityContext: AbilityContext, - protected val gameManager: GameManager -) { - - lateinit var config: Map - val items = mutableListOf() - - abstract fun onSelect( player: Player ) - - abstract fun onStart( player: Player ) - - abstract fun onHit( attacker: Player, victim: Player, event: EntityDamageByEntityEvent ) - - abstract fun onDamaged( attacker: Player, victim: Player, event: EntityDamageByEntityEvent ) - - abstract fun onInteract( player: Player, event: PlayerInteractEvent ) - - abstract fun onMove( player: Player, event: PlayerMoveEvent ) - - open fun cleanup( - player: Player - ) { - abilityContext.clearPlayerData( player ) - } - - protected fun hasCooldown( - player: Player, - key: String - ): Boolean - { - return abilityContext.getRemainingCooldown( player, key ) > 0 - } - - protected fun startCooldown( - player: Player, - key: String, - seconds: Int - ) { - abilityContext.cooldownManager.startCooldown( player, key, seconds ) - } - - protected fun getRemainingCooldown( - player: Player, - key: String - ): Double - { - return abilityContext.getRemainingCooldown( player, key ) - } - - protected fun getHits( - player: Player, - key: String - ): Int - { - return abilityContext.getHits(player, key) - } - - protected fun resetHits( - player: Player, - key: String - ) { - abilityContext.hitCounterManager.resetHits( player, key ) - } - - protected fun abilityResult( - player: Player, - abilityKey: String, - requiredHits: Int? = null, - cooldownSeconds: Int? = null - ): AbilityResult { - return abilityContext.canUseAbility( player, abilityKey, requiredHits, cooldownSeconds ) - } - -} - -enum class PlayStyle { - OFFENSIVE, DEFENSIVE -} - -enum class KitMetaData { - IN_GLADIATOR, - GLADIATOR_BLOCK, - IS_ANVIL, - IS_BLACK_PANTHER, - BP_EXTRA_DAMAGE, - VOODOO_HOLD, - ICEMAGE_SNOWBALL, - ICEMAGE_SPEED; - - fun getKey(): String - { - return name - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/KitInventoryHolder.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/KitInventoryHolder.kt deleted file mode 100644 index 4091b59..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/KitInventoryHolder.kt +++ /dev/null @@ -1,20 +0,0 @@ -package club.mcscrims.speedhg.kit - -import org.bukkit.inventory.Inventory -import org.bukkit.inventory.InventoryHolder - -class KitInventoryHolder( - val page: Int -) : InventoryHolder { - - private lateinit var inventory: Inventory - - override fun getInventory(): Inventory = inventory - - fun setInventory( - inventory: Inventory - ) { - this.inventory = inventory - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/KitInventoryListener.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/KitInventoryListener.kt deleted file mode 100644 index 9cac587..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/KitInventoryListener.kt +++ /dev/null @@ -1,103 +0,0 @@ -package club.mcscrims.speedhg.kit - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.SpeedHG.Companion.content -import org.bukkit.Material -import org.bukkit.Sound -import org.bukkit.entity.Player -import org.bukkit.event.EventHandler -import org.bukkit.event.Listener -import org.bukkit.event.inventory.InventoryClickEvent -import org.bukkit.event.inventory.InventoryDragEvent - -class KitInventoryListener( - private val plugin: SpeedHG, - private val kitManager: KitManager, - private val kitInventoryManager: KitInventoryManager -) : Listener { - - @EventHandler - fun onInventoryClick( - event: InventoryClickEvent - ) { - val holder = event.inventory.holder - - if ( holder !is KitInventoryHolder ) - return - - event.isCancelled = true - - val player = event.whoClicked as? Player ?: return - val clickedItem = event.currentItem ?: return - - if ( clickedItem.type == Material.AIR ) - return - - when( event.rawSlot ) - { - 45 -> { - if ( holder.page > 1 ) - { - player.playSound( player.location, Sound.UI_BUTTON_CLICK, 1f, 1f ) - kitInventoryManager.openKitInventory( player, holder.page - 1 ) - } - } - 49 -> { - player.playSound( player.location, Sound.UI_BUTTON_CLICK, 1f, 0.8f ) - player.closeInventory() - } - 53 -> { - val totalKits = kitManager.getAllKits().size - val totalPages = ( totalKits + 27 ) / 28 - if ( holder.page < totalPages ) - { - player.playSound( player.location, Sound.UI_BUTTON_CLICK, 1f, 1f ) - kitInventoryManager.openKitInventory( player, holder.page + 1 ) - } - } - in 10..43 -> { - val allKits = kitManager.getAllKits().toList() - val startIndex = ( holder.page - 1 ) * 28 - val endIndex = ( startIndex + 28 ).coerceAtMost( allKits.size ) - val kitsOnPage = allKits.subList( startIndex, endIndex ) - - val kitSlots = listOf( - 10, 11, 12, 13, 14, 15, 16, - 19, 20, 21, 22, 23, 24, 25, - 28, 29, 30, 31, 32, 33, 34, - 37, 38, 39, 40, 41, 42, 43 - ) - - val slotIndex = kitSlots.indexOf( event.rawSlot ) - if ( slotIndex != -1 && slotIndex < kitsOnPage.size ) - { - val selectedKit = kitsOnPage[slotIndex] - - if (kitManager.selectKit( player, selectedKit.id )) - { - player.playSound( player.location, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1.2f ) - plugin.chatManager.sendMessage( - player, - "kits.selected", - "{kit}" to selectedKit.displayName.content() - ) - kitInventoryManager.openKitInventory( player, holder.page ) - } else { - player.playSound( player.location, Sound.ENTITY_VILLAGER_NO, 1f, 1f ) - } - } - } - } - } - - @EventHandler - fun onInventoryDrag( - event: InventoryDragEvent - ) { - val holder = event.inventory.holder - - if ( holder is KitInventoryHolder ) - event.isCancelled = true - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/KitInventoryManager.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/KitInventoryManager.kt deleted file mode 100644 index c1036e1..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/KitInventoryManager.kt +++ /dev/null @@ -1,223 +0,0 @@ -package club.mcscrims.speedhg.kit - -import club.mcscrims.speedhg.SpeedHG -import kotlinx.coroutines.runBlocking -import net.kyori.adventure.text.Component -import net.kyori.adventure.text.format.NamedTextColor -import net.kyori.adventure.text.format.TextDecoration -import org.bukkit.Bukkit -import org.bukkit.Material -import org.bukkit.enchantments.Enchantment -import org.bukkit.entity.Player -import org.bukkit.inventory.ItemFlag -import org.bukkit.inventory.ItemStack - -class KitInventoryManager( - private val plugin: SpeedHG, - private val kitManager: KitManager -) { - - private val kitsPerPage = 28 - private val rows = 6 - - fun openKitInventory( - player: Player, - page: Int = 1 - ) { - val allKits = kitManager.getAllKits().toList() - val totalPages = ( allKits.size + kitsPerPage - 1 ) / kitsPerPage - val validPage = page.coerceIn( 1, totalPages.coerceAtLeast( 1 )) - - val holder = KitInventoryHolder( validPage ) - val title = Component.text("Kits - Page $validPage/$totalPages") - .color( NamedTextColor.DARK_PURPLE ) - .decoration( TextDecoration.BOLD, true ) - - val inventory = Bukkit.createInventory( holder, rows * 9, title ) - holder.setInventory( inventory ) - - fillBorder( inventory ) - fillKits( inventory, allKits, validPage, player ) - fillNavigationItems( inventory, validPage, totalPages ) - - player.openInventory( inventory ) - } - - private fun fillBorder( - inventory: org.bukkit.inventory.Inventory - ) { - val borderItem = createBorderItem() - - for ( i in 0..8 ) - inventory.setItem( i, borderItem ) - - for ( i in 45..53 ) - inventory.setItem( i, borderItem ) - } - - private fun fillKits( - inventory: org.bukkit.inventory.Inventory, - allKits: List, - page: Int, - player: Player - ) { - val startIndex = ( page - 1 ) * kitsPerPage - val endIndex = ( startIndex + kitsPerPage ).coerceAtMost( allKits.size ) - val kitsOnPage = allKits.subList( startIndex, endIndex ) - - val selectedKit = kitManager.getSelectedKit( player ) - - val kitSlots = listOf( - 10, 11, 12, 13, 14, 15, 16, - 19, 20, 21, 22, 23, 24, 25, - 28, 29, 30, 31, 32, 33, 34, - 37, 38, 39, 40, 41, 42, 43 - ) - - kitsOnPage.forEachIndexed { index, kit -> - if ( index < kitSlots.size ) - { - val isSelected = selectedKit?.id == kit.id - val kitItem = createKitItem( kit, isSelected, player ) - inventory.setItem(kitSlots[ index ], kitItem ) - } - } - } - - private fun fillNavigationItems( - inventory: org.bukkit.inventory.Inventory, - page: Int, - totalPages: Int - ) { - if ( page > 1 ) - inventory.setItem( 45, createPreviousPageItem() ) - - inventory.setItem( 49, createCloseItem() ) - - if ( page < totalPages ) - inventory.setItem( 53, createNextPageItem() ) - } - - private fun createBorderItem(): ItemStack - { - val item = ItemStack( Material.GRAY_STAINED_GLASS_PANE ) - val meta = item.itemMeta - meta.displayName(Component.text( " " )) - item.itemMeta = meta - return item - } - - private fun createKitItem( - kit: AbstractKit, - isSelected: Boolean, - player: Player - ): ItemStack - { - val item = ItemStack( kit.icon ) - val meta = item.itemMeta - - meta.displayName(kit.displayName.decoration( TextDecoration.ITALIC, false )) - - val lore = mutableListOf() - - if ( isSelected ) { - lore.add( - Component.text("✔ Currently Selected") - .color( NamedTextColor.GREEN ) - .decoration( TextDecoration.ITALIC, false ) - ) - } else { - lore.add( - Component.text("Click to select") - .color( NamedTextColor.YELLOW ) - .decoration( TextDecoration.ITALIC, false ) - ) - } - - lore.add( Component.empty() ) - - kit.description.forEach { line -> - lore.add( - line.color( NamedTextColor.GRAY ) - .decoration( TextDecoration.ITALIC, false ) - ) - } - - if (canUseKit( player, kit )) { - if ( isSelected ) - { - meta.addEnchant( Enchantment.UNBREAKING, 1, true ) - meta.addItemFlags( ItemFlag.HIDE_ENCHANTS ) - } - } else { - lore.add( Component.empty() ) - lore.add( - Component.text("⚠ Locked") - .color( NamedTextColor.RED ) - .decoration( TextDecoration.ITALIC, false ) - .decoration( TextDecoration.BOLD, true ) - ) - } - - meta.lore( lore ) - meta.addItemFlags( ItemFlag.HIDE_ATTRIBUTES ) - item.itemMeta = meta - - return item - } - - private fun createPreviousPageItem(): ItemStack - { - val item = ItemStack( Material.ARROW ) - val meta = item.itemMeta - meta.displayName( - Component.text("← Previous Page") - .color( NamedTextColor.YELLOW ) - .decoration( TextDecoration.ITALIC, false ) - ) - item.itemMeta = meta - return item - } - - private fun createNextPageItem(): ItemStack - { - val item = ItemStack( Material.ARROW ) - val meta = item.itemMeta - meta.displayName( - Component.text("Next Page →") - .color( NamedTextColor.YELLOW ) - .decoration( TextDecoration.ITALIC, false ) - ) - item.itemMeta = meta - return item - } - - private fun createCloseItem(): ItemStack - { - val item = ItemStack( Material.BARRIER ) - val meta = item.itemMeta - meta.displayName( - Component.text("Close") - .color( NamedTextColor.RED ) - .decoration( TextDecoration.ITALIC, false ) - ) - item.itemMeta = meta - return item - } - - private fun canUseKit( - player: Player, - kit: AbstractKit - ): Boolean - { - val unlockedKits = mutableListOf() - - runBlocking { - val kitPlayer = plugin.playerRepository.findByUuid( player.uniqueId ) - if ( kitPlayer != null ) unlockedKits.addAll( kitPlayer.unlockedKits ) - } - - return unlockedKits.contains( kit.id ) - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/KitListener.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/KitListener.kt deleted file mode 100644 index 7f503c8..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/KitListener.kt +++ /dev/null @@ -1,75 +0,0 @@ -package club.mcscrims.speedhg.kit - -import club.mcscrims.speedhg.SpeedHG -import org.bukkit.Material -import org.bukkit.Sound -import org.bukkit.entity.Player -import org.bukkit.event.EventHandler -import org.bukkit.event.Listener -import org.bukkit.event.block.BlockBreakEvent -import org.bukkit.event.entity.EntityDamageByEntityEvent -import org.bukkit.event.player.PlayerInteractEvent -import org.bukkit.event.player.PlayerMoveEvent - -class KitListener( - private val plugin: SpeedHG, - private val kitManager: KitManager -) : Listener { - - @EventHandler - fun onDamage( - event: EntityDamageByEntityEvent - ) { - val attacker = event.damager as? Player ?: return - val victim = event.entity as? Player ?: return - - if (kitManager.getSelectedKit( attacker ) != null ) - kitManager.triggerHit( attacker, victim, event ) - - if (kitManager.getSelectedKit( victim ) != null ) - kitManager.triggerDamaged( victim, attacker, event ) - } - - @EventHandler - fun onPlayerInteract( - event: PlayerInteractEvent - ) { - val player = event.player - - if (kitManager.getSelectedKit( player ) != null ) - kitManager.triggerInteract( player, event ) - } - - @EventHandler - fun onPlayerMove( - event: PlayerMoveEvent - ) { - val player = event.player - - if (kitManager.getSelectedKit( player ) != null ) - kitManager.triggerMove( player, event ) - } - - @EventHandler - fun onAnvilBreak( - event: BlockBreakEvent - ) { - if ( !plugin.gameManager.isRunning() ) - return - - val block = event.block - - if ( block.type != Material.ANVIL || - !block.hasMetadata( KitMetaData.IS_ANVIL.getKey() )) - return - - event.isCancelled = true - - block.type = Material.AIR - block.removeMetadata( KitMetaData.IS_ANVIL.getKey(), plugin ) - - block.world.playSound( block.location, Sound.ENTITY_IRON_GOLEM_DEATH, 3f, 3f ) - plugin.chatManager.broadcast( "kits.anchor.messages.broken" ) - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/KitManager.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/KitManager.kt deleted file mode 100644 index fa3d38d..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/KitManager.kt +++ /dev/null @@ -1,224 +0,0 @@ -package club.mcscrims.speedhg.kit - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.ability.AbilityContext -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.kit.impl.AnchorKit -import club.mcscrims.speedhg.kit.impl.ArmorerKit -import club.mcscrims.speedhg.kit.impl.BlackPantherKit -import club.mcscrims.speedhg.kit.impl.BlitzcrankKit -import net.kyori.adventure.text.Component -import org.bukkit.Material -import org.bukkit.entity.Player -import org.bukkit.event.entity.EntityDamageByEntityEvent -import org.bukkit.event.player.PlayerInteractEvent -import org.bukkit.event.player.PlayerMoveEvent -import java.util.UUID -import java.util.concurrent.ConcurrentHashMap - -class KitManager( - private val plugin: SpeedHG -) { - - private val kits = ConcurrentHashMap() - private val selectedKits = ConcurrentHashMap() - - fun initialize() - { - registerKit( - kitClass = AnchorKit::class.java, - id = "anchor", - displayName = plugin.chatFormatter.format( "kits.anchor.displayName" ), - description = plugin.chatFormatter.formatList( "kits.anchor.lore" ), - icon = Material.ANVIL - ) - - registerKit( - kitClass = ArmorerKit::class.java, - id = "armorer", - displayName = plugin.chatFormatter.format( "kits.armorer.displayName" ), - description = plugin.chatFormatter.formatList( "kits.armorer.lore" ), - icon = Material.IRON_CHESTPLATE - ) - - registerKit( - kitClass = BlackPantherKit::class.java, - id = "blackpanther", - displayName = plugin.chatFormatter.format( "kits.blackpanther.displayName" ), - description = plugin.chatFormatter.formatList( "kits.blackpanther.lore" ), - icon = Material.DRAGON_EGG - ) - - registerKit( - kitClass = BlitzcrankKit::class.java, - id = "blitzcrank", - displayName = plugin.chatFormatter.format( "kits.blitzcrank.displayName" ), - description = plugin.chatFormatter.formatList( "kits.blitzcrank.lore" ), - icon = Material.FISHING_ROD - ) - } - - fun registerKit( - kitClass: Class, - id: String, - displayName: Component, - description: List, - icon: Material - ) { - val constructor = kitClass.getDeclaredConstructor( - String::class.java, - Component::class.java, - List::class.java, - Material::class.java, - PlayStyle::class.java, - SpeedHG::class.java, - AbilityContext::class.java, - GameManager::class.java - ) - - val kit = constructor.newInstance( - id, - displayName, - description, - icon, - PlayStyle.DEFENSIVE, - plugin, - plugin.abilityContext, - plugin.gameManager - ) - - kit.config = plugin.kitConfig.data.getConfigForKit( kit.id ) - kits[kit.id.lowercase()] = kit - plugin.logger.info("Registered kit: ${kit.displayName} (${kit.id})") - } - - fun getKit( - id: String - ): AbstractKit? - { - return kits[id.lowercase()] - } - - fun getAllKits(): Collection - { - return kits.values - } - - fun selectKit( - player: Player, - kitId: String - ): Boolean - { - val kit = getKit( kitId ) ?: return false - - val previousKit = selectedKits[player.uniqueId] - previousKit?.cleanup( player ) - - selectedKits[player.uniqueId] = kit - - try { - kit.onSelect( player ) - } catch (e: Exception) { - plugin.logger.severe("Error during onSelect for kit ${kit.id} and player ${player.name}: ${e.message}") - e.printStackTrace() - } - - return true - } - - fun getSelectedKit( - player: Player - ): AbstractKit? - { - return selectedKits[player.uniqueId] - } - - fun startKitForPlayer( - player: Player - ) { - val kit = selectedKits[player.uniqueId] ?: return - - try { - kit.onStart( player ) - } catch (e: Exception) { - plugin.logger.severe("Error during onStart for kit ${kit.id} and player ${player.name}: ${e.message}") - e.printStackTrace() - } - } - - fun triggerHit( - attacker: Player, - victim: Player, - event: EntityDamageByEntityEvent - ) { - val kit = selectedKits[attacker.uniqueId] ?: return - - try { - kit.onHit( attacker, victim, event ) - } catch (e: Exception) { - plugin.logger.severe("Error during onHit for kit ${kit.id} and player ${attacker.name}: ${e.message}") - e.printStackTrace() - } - } - - fun triggerDamaged( - attacker: Player, - victim: Player, - event: EntityDamageByEntityEvent - ) { - val kit = selectedKits[victim.uniqueId] ?: return - - try { - kit.onDamaged( attacker, victim, event ) - } catch (e: Exception) { - plugin.logger.severe("Error during onDamaged for kit ${kit.id} and player ${victim.name}: ${e.message}") - e.printStackTrace() - } - } - - fun triggerInteract( - player: Player, - event: PlayerInteractEvent - ) { - val kit = selectedKits[player.uniqueId] ?: return - - try { - kit.onInteract( player, event ) - } catch (e: Exception) { - plugin.logger.severe("Error during onInteract for kit ${kit.id} and player ${player.name}: ${e.message}") - e.printStackTrace() - } - } - - fun triggerMove( - player: Player, - event: PlayerMoveEvent - ) { - val kit = selectedKits[player.uniqueId] ?: return - - try { - kit.onMove( player, event ) - } catch (e: Exception) { - plugin.logger.severe("Error during onMove for kit ${kit.id} and player ${player.name}: ${e.message}") - e.printStackTrace() - } - } - - fun clearPlayerSelection( - player: Player - ) { - val kit = selectedKits.remove( player.uniqueId ) - kit?.cleanup( player ) - } - - fun clearAll() - { - selectedKits.values.forEach { kit -> - plugin.server.onlinePlayers.forEach { player -> - if ( selectedKits[player.uniqueId] == kit ) kit.cleanup( player ) - } - } - selectedKits.clear() - } - -} diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/AnchorKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/AnchorKit.kt deleted file mode 100644 index c21d7d2..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/AnchorKit.kt +++ /dev/null @@ -1,168 +0,0 @@ -package club.mcscrims.speedhg.kit.impl - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.SpeedHG.Companion.content -import club.mcscrims.speedhg.ability.AbilityContext -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.kit.AbstractKit -import club.mcscrims.speedhg.kit.KitMetaData -import club.mcscrims.speedhg.kit.PlayStyle -import club.mcscrims.spigot.item.ItemBuilder -import net.kyori.adventure.text.Component -import org.bukkit.Location -import org.bukkit.Material -import org.bukkit.Sound -import org.bukkit.entity.Player -import org.bukkit.event.block.Action -import org.bukkit.event.entity.EntityDamageByEntityEvent -import org.bukkit.event.player.PlayerInteractEvent -import org.bukkit.event.player.PlayerMoveEvent -import org.bukkit.inventory.ItemStack -import org.bukkit.metadata.FixedMetadataValue -import java.util.UUID -import java.util.concurrent.ConcurrentHashMap - -class AnchorKit( - id: String, - displayName: Component, - description: List, - icon: Material, - playStyle: PlayStyle, - plugin: SpeedHG, - abilityContext: AbilityContext, - gameManager: GameManager -) : AbstractKit( id, displayName, description, icon, playStyle, plugin, abilityContext, gameManager ) { - - private lateinit var anvilItem: ItemStack - - private val extraDamage: Double = plugin.kitConfig.data.anchor[ "offensive extra damage" ]!! - - private val radius = if ( playStyle == PlayStyle.DEFENSIVE ) 7.5 else 5.0 - private val anvilList = ConcurrentHashMap() - - override fun onSelect( player: Player ) {} - - override fun onStart( - player: Player - ) { - anvilItem = ItemBuilder( plugin, Material.ANVIL ) - .name(plugin.messageConfig.data.getKitItemNames( "anchor", "anvil" )!![ playStyle.name.lowercase() ]!!) - .unbreakable( true ) - .hideAttributes() - .build() - - items.add( anvilItem ) - player.inventory.setItem( 0, anvilItem ) - } - - override fun onHit( - attacker: Player, - victim: Player, - event: EntityDamageByEntityEvent - ) { - if ( !gameManager.isRunning() ) - return - - if (!anvilList.contains( attacker.uniqueId )) - return - - if ( playStyle != PlayStyle.OFFENSIVE ) - return - - event.damage += extraDamage - } - - override fun onDamaged( - attacker: Player, - victim: Player, - event: EntityDamageByEntityEvent - ) { - if ( !gameManager.isRunning() ) - return - - if (!anvilList.contains( attacker.uniqueId )) - return - - victim.velocity.setX( 0.0 ) - victim.velocity.setZ( 0.0 ) - victim.world.playSound( victim.location, Sound.BLOCK_ANVIL_HIT, 3f, 3f ) - } - - override fun onInteract( - player: Player, - event: PlayerInteractEvent - ) { - if ( !gameManager.isRunning() ) - return - - val action = event.action - - if ( action != Action.RIGHT_CLICK_AIR && - action != Action.RIGHT_CLICK_BLOCK ) - return - - val item = event.item ?: return - - if ( item != anvilItem ) - return - - event.isCancelled = true - - val eyeLocation = player.eyeLocation - - if (eyeLocation.distance( player.location ) > radius ) - { - plugin.chatManager.sendMessage( player, "kits.anchor.messages.tooFarAway" ) - return - } - - if (anvilList.contains( player.uniqueId )) - { - plugin.chatManager.sendMessage( player, "kits.anchor.messages.alreadyActivated" ) - return - } - - val result = abilityContext.canUseAbility( player, "anchor-anvil", 15 ) - - if ( result.missingHits > 0 ) - { - plugin.chatManager.sendMessage( player, "kits.missingHits", "{hits]" to result.missingHits.toString() ) - return - } - - val anvilLoc = eyeLocation.toBlockLocation() - anvilLoc.add( 0.0, 1.0, 0.0 ) - - anvilLoc.block.type = Material.ANVIL - anvilLoc.block.setMetadata( KitMetaData.IS_ANVIL.getKey(), FixedMetadataValue( plugin, true )) - - anvilList[ player.uniqueId ]= anvilLoc - - plugin.schedulerManager.runLater( 20 * 30L ) { - if (anvilList.contains( player.uniqueId )) - { - anvilList.remove( player.uniqueId ) - anvilLoc.block.type = Material.AIR - anvilLoc.block.removeMetadata( KitMetaData.IS_ANVIL.getKey(), plugin ) - } - } - } - - override fun onMove( - player: Player, - event: PlayerMoveEvent - ) { - if ( !gameManager.isRunning() ) - return - - val anvilLoc = anvilList[ player.uniqueId ] - ?: return - - if (player.location.distance( anvilLoc ) <= radius ) - return - - player.teleport( player.location ) - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/ArmorerKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/ArmorerKit.kt deleted file mode 100644 index 4d6a63c..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/ArmorerKit.kt +++ /dev/null @@ -1,114 +0,0 @@ -package club.mcscrims.speedhg.kit.impl - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.ability.AbilityContext -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.kit.AbstractKit -import club.mcscrims.speedhg.kit.PlayStyle -import net.kyori.adventure.text.Component -import org.bukkit.Material -import org.bukkit.Sound -import org.bukkit.Statistic -import org.bukkit.enchantments.Enchantment -import org.bukkit.entity.Player -import org.bukkit.event.entity.EntityDamageByEntityEvent -import org.bukkit.event.player.PlayerInteractEvent -import org.bukkit.event.player.PlayerMoveEvent -import org.bukkit.inventory.ItemStack -import kotlin.math.roundToInt - -class ArmorerKit( - id: String, - displayName: Component, - description: List, - icon: Material, - playStyle: PlayStyle, - plugin: SpeedHG, - abilityContext: AbilityContext, - gameManager: GameManager -) : AbstractKit( id, displayName, description, icon, playStyle, plugin, abilityContext, gameManager ) { - - private val killsUntilNew: Double = plugin.kitConfig.data.armorer[ "kills until new armor" ]!! - - override fun onSelect( player: Player ) {} - - override fun onStart( player: Player ) {} - - override fun onHit( - attacker: Player, - victim: Player, - event: EntityDamageByEntityEvent - ) { - if ( !gameManager.isRunning() ) - return - - if ( victim.health > 0.0 ) - return - - val kills = attacker.getStatistic( Statistic.PLAYER_KILLS ) - - if (( kills.toDouble() / killsUntilNew ) % 2 != 0.0 ) - return - - upgradeArmor( attacker, kills ) - attacker.playSound( attacker, Sound.BLOCK_NOTE_BLOCK_PLING, 1f, 1f ) - } - - override fun onDamaged( attacker: Player, victim: Player, event: EntityDamageByEntityEvent ) {} - - override fun onInteract( player: Player, event: PlayerInteractEvent ) {} - - override fun onMove( player: Player, event: PlayerMoveEvent ) {} - - private fun upgradeArmor( - player: Player, - killCount: Int - ) { - val kills = killsUntilNew.roundToInt() - - val armorType = when( killCount / kills ) - { - 1, 2 -> ArmorType.LEATHER - 3, 4 -> ArmorType.CHAINMAIL - 5, 6 -> ArmorType.GOLD - 7, 8 -> ArmorType.IRON - else -> return - } - - val enchanted = ( killCount / kills ) % 2 == 0 - - val armor = createArmor( armorType, enchanted ) - player.inventory.armorContents = arrayOf( null, armor[0], null, armor[1] ) - - if ( !enchanted ) - { - plugin.chatManager.sendMessage( player, "kits.armorer.messages.upgrade.normal", "{armorType}" to armorType.name ) - return - } - - plugin.chatManager.sendMessage( player, "kits.armorer.upgrade.enchanted" ) - } - - private fun createArmor( - type: ArmorType, - enchanted: Boolean - ) = listOf( - ItemStack( type.materialChestplate ).apply { - if ( enchanted ) addEnchantment( Enchantment.PROTECTION, 1 ) - }, - ItemStack( type.materialBoots ).apply { - if ( enchanted ) addEnchantment( Enchantment.PROTECTION, 1 ) - } - ) - - enum class ArmorType( - val materialChestplate: Material, - val materialBoots: Material - ) { - LEATHER( Material.LEATHER_CHESTPLATE, Material.LEATHER_BOOTS ), - CHAINMAIL( Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_BOOTS ), - GOLD( Material.GOLDEN_CHESTPLATE, Material.GOLDEN_BOOTS ), - IRON( Material.IRON_CHESTPLATE, Material.IRON_BOOTS ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlackPantherKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlackPantherKit.kt deleted file mode 100644 index 787456f..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlackPantherKit.kt +++ /dev/null @@ -1,276 +0,0 @@ -package club.mcscrims.speedhg.kit.impl - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.ability.AbilityContext -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.kit.AbstractKit -import club.mcscrims.speedhg.kit.KitMetaData -import club.mcscrims.speedhg.kit.PlayStyle -import club.mcscrims.spigot.item.ItemBuilder -import club.mcscrims.spigot.util.GroundDetector -import net.kyori.adventure.text.Component -import org.bukkit.Bukkit -import org.bukkit.GameMode -import org.bukkit.Material -import org.bukkit.Particle -import org.bukkit.entity.EnderPearl -import org.bukkit.entity.EntityType -import org.bukkit.entity.Player -import org.bukkit.event.block.Action -import org.bukkit.event.entity.EntityDamageByEntityEvent -import org.bukkit.event.player.PlayerInteractEvent -import org.bukkit.event.player.PlayerMoveEvent -import org.bukkit.inventory.ItemStack -import org.bukkit.metadata.FixedMetadataValue -import org.bukkit.potion.PotionEffect -import org.bukkit.potion.PotionEffectType -import org.bukkit.scheduler.BukkitRunnable -import org.bukkit.util.Vector -import kotlin.math.max -import kotlin.math.roundToInt - -class BlackPantherKit( - id: String, - displayName: Component, - description: List, - icon: Material, - playStyle: PlayStyle, - plugin: SpeedHG, - abilityContext: AbilityContext, - gameManager: GameManager -) : AbstractKit( id, displayName, description, icon, playStyle, plugin, abilityContext, gameManager ) { - - private lateinit var blackDye: ItemStack - private lateinit var blazePowder: ItemStack - - private val extraDamageAddition = plugin.kitConfig.data.blackPanther[ "extra damage on top" ]!! - private val defaultRadius = plugin.kitConfig.data.blackPanther[ "default hit radius" ]!! - private val explosionMultiplier = plugin.kitConfig.data.blackPanther[ "explosion multiplier" ]!! - - override fun onSelect( player: Player ) {} - - override fun onStart( - player: Player - ) { - when( playStyle ) - { - PlayStyle.DEFENSIVE -> - { - blackDye = ItemBuilder( plugin, Material.BLACK_DYE ) - .name(plugin.messageConfig.data.getKitItemNames( "blackpanther", "blackDye" )!![ "null" ]!!) - .unbreakable( true ) - .hideAttributes() - .build() - - items.add( blackDye ) - player.inventory.setItem( 0, blackDye ) - } - - PlayStyle.OFFENSIVE -> - { - blackDye = ItemBuilder( plugin, Material.BLACK_DYE ) - .name(plugin.messageConfig.data.getKitItemNames( "blackpanther", "blackDye" )!![ playStyle.name.lowercase() ]!!) - .unbreakable( true ) - .hideAttributes() - .build() - - blazePowder = ItemBuilder( plugin, Material.BLAZE_POWDER ) - .name(plugin.messageConfig.data.getKitItemNames( "blackpanther", "blazePowder" )!![ playStyle.name.lowercase() ]!!) - .unbreakable( true ) - .hideAttributes() - .build() - - items.addAll(listOf( blackDye, blazePowder )) - player.inventory.setItem( 0, blackDye ) - player.inventory.setItem( 1, blazePowder ) - } - } - } - - override fun onHit( - attacker: Player, - victim: Player, - event: EntityDamageByEntityEvent - ) { - if ( !gameManager.isRunning() ) - return - - if (!attacker.hasMetadata( KitMetaData.BP_EXTRA_DAMAGE.getKey() )) - return - - event.damage += extraDamageAddition - } - - override fun onDamaged( attacker: Player, victim: Player, event: EntityDamageByEntityEvent ) {} - - override fun onInteract( - player: Player, - event: PlayerInteractEvent - ) { - if ( !gameManager.isRunning() ) - return - - val action = event.action - - if ( action != Action.RIGHT_CLICK_AIR && - action != Action.RIGHT_CLICK_BLOCK ) - return - - val item = event.item ?: return - - if ( item != blackDye && - item != blazePowder ) - return - - event.isCancelled = true - - when( item.type ) - { - Material.BLACK_DYE -> - { - val result = abilityContext.canUseAbility( player, "blackDye-blackPanther", 15 ) - - if ( result.missingHits > 0 ) - { - plugin.chatManager.sendMessage( player, "kits.missingHits", "{hits]" to result.missingHits.toString() ) - return - } - - if ( playStyle == PlayStyle.DEFENSIVE ) - { - launchAndDash( player ) - return - } - - push( player ) - } - - Material.BLAZE_POWDER -> - { - val result = abilityContext.canUseAbility( player, "blazePowder-blackPanther", 15 ) - - if ( result.missingHits > 0 ) - { - plugin.chatManager.sendMessage( player, "kits.missingHits", "{hits]" to result.missingHits.toString() ) - return - } - - extraDamage( player ) - } - - else -> return - } - } - - override fun onMove( player: Player, event: PlayerMoveEvent ) {} - - private fun push( - player: Player - ) { - plugin.getAlivePlayers().stream() - .filter { it != player } - .filter { it.location.distance( player.location ) <= defaultRadius } - .forEach { nearby -> - val pushDirection = nearby.location.toVector().subtract( player.location.toVector() ).normalize() - pushDirection.multiply( 1.0 ) - pushDirection.setY( 0.5 ) - nearby.velocity = pushDirection - - val enderPearl = player.world.spawnEntity( player.location, EntityType.ENDER_PEARL ) as EnderPearl - enderPearl.velocity = pushDirection - enderPearl.setMetadata( KitMetaData.IS_BLACK_PANTHER.getKey(), FixedMetadataValue( plugin, true )) - - object : BukkitRunnable() - { - - override fun run() - { - if (GroundDetector.isOnGround( nearby )) - { - this.cancel() - return - } - - nearby.world.spawnParticle( Particle.END_ROD, nearby.location, 5, 0.2, 0.2, 0.2 ) - } - - }.runTaskTimer( plugin, 0L, 2L ) - } - } - - private fun launchAndDash( - player: Player, - upwardVelocity: Double = 2.2, - waitTicks: Long = 60L, - dashSpeed: Double = 2.8, - horizontalOnly: Boolean = true, - yBoost: Double = 0.2, - noFallDamageMillis: Double = 1.0 - ) { - if ( !player.isOnline ) return - if ( player.isInsideVehicle ) player.leaveVehicle() - if ( player.gameMode == GameMode.SPECTATOR ) return - - player.velocity = Vector( 0.0, upwardVelocity, 0.0 ) - player.fallDistance = 0f - - if ( noFallDamageMillis > 0 ) - player.addPotionEffect(PotionEffect( PotionEffectType.RESISTANCE, ( noFallDamageMillis * 20 ).toInt(), 999, false, false, false )) - - Bukkit.getScheduler().runTaskLater( plugin, { -> - val p = Bukkit.getPlayer( player.uniqueId ) ?: return@runTaskLater - if ( !p.isOnline ) return@runTaskLater - - var dir = p.eyeLocation.direction - if ( horizontalOnly ) dir = Vector( dir.x, 0.0, dir.z ) - dir = if ( dir.lengthSquared() < 1e-6 ) Vector( 0, 0, 0 ) else dir.normalize() - - val dash = dir.multiply( dashSpeed ).add(Vector( 0.0, yBoost, 0.0 )) - p.velocity = dash - }, max( 0L, waitTicks )) - - object : BukkitRunnable() - { - - override fun run() - { - if (GroundDetector.isOnGround( player )) - { - player.world.createExplosion( - player.location, - explosionMultiplier.roundToInt().toFloat(), - false, true - ) - - val alivePlayers = plugin.getAlivePlayers().stream() - .filter { it != player } - .filter { it.location.distance( player.location ) <= defaultRadius } - .toList() - - alivePlayers.forEach { nearby -> - nearby.damage( 6.0, player ) - } - - plugin.chatManager.sendMessage( player, "kits.blackPanther.messages.wakandaForever.hit", "{hit}" to alivePlayers.size.toString() ) - this.cancel() - } - } - - }.runTaskTimer( plugin, max( 0L, waitTicks ) + 5L, 5L ) - } - - private fun extraDamage( - player: Player - ) { - player.setMetadata( KitMetaData.BP_EXTRA_DAMAGE.getKey(), FixedMetadataValue( plugin, true )) - plugin.chatManager.sendMessage( player, "kits.blackPanther.messages.extraDamage.activated" ) - - plugin.schedulerManager.runLater( 12 * 30L ) { - - player.removeMetadata( KitMetaData.BP_EXTRA_DAMAGE.getKey(), plugin ) - plugin.chatManager.sendMessage( player, "kits.blackPanther.messages.extraDamage.deactivated" ) - - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlitzcrankKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlitzcrankKit.kt deleted file mode 100644 index 711b581..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlitzcrankKit.kt +++ /dev/null @@ -1,234 +0,0 @@ -package club.mcscrims.speedhg.kit.impl - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.SpeedHG.Companion.content -import club.mcscrims.speedhg.ability.AbilityContext -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.kit.AbstractKit -import club.mcscrims.speedhg.kit.PlayStyle -import club.mcscrims.speedhg.util.DirectionUtil -import club.mcscrims.spigot.chat.getDisplayName -import club.mcscrims.spigot.item.ItemBuilder -import net.kyori.adventure.text.Component -import org.bukkit.Material -import org.bukkit.entity.Player -import org.bukkit.event.block.Action -import org.bukkit.event.entity.EntityDamageByEntityEvent -import org.bukkit.event.player.PlayerInteractEvent -import org.bukkit.event.player.PlayerMoveEvent -import org.bukkit.inventory.ItemStack -import org.bukkit.potion.PotionEffect -import org.bukkit.potion.PotionEffectType -import org.bukkit.util.Vector - -class BlitzcrankKit( - id: String, - displayName: Component, - description: List, - icon: Material, - playStyle: PlayStyle, - plugin: SpeedHG, - abilityContext: AbilityContext, - gameManager: GameManager -) : AbstractKit( id, displayName, description, icon, playStyle, plugin, abilityContext, gameManager ) { - - override fun onSelect( player: Player ) {} - - private lateinit var hotsItem: ItemStack - private lateinit var fishingRodItem: ItemStack - private lateinit var pufferfishItem: ItemStack - - override fun onStart( - player: Player - ) { - hotsItem = ItemBuilder( plugin, Material.HEART_OF_THE_SEA ) - .name(plugin.messageConfig.data.getKitItemNames( "blitzcrank", "hots" )!![ playStyle.name.lowercase() ]!!) - .unbreakable( true ) - .hideAttributes() - .build() - - items.add( hotsItem ) - player.inventory.setItem( 0, hotsItem ) - - when( playStyle ) - { - PlayStyle.DEFENSIVE -> - { - fishingRodItem = ItemBuilder( plugin, Material.FISHING_ROD ) - .name(plugin.messageConfig.data.getKitItemNames( "blitzcrank", "fishingrod" )!![ playStyle.name.lowercase() ]!!) - .unbreakable( true ) - .hideAttributes() - .build() - - items.add( fishingRodItem ) - player.inventory.setItem( 1, fishingRodItem ) - } - - PlayStyle.OFFENSIVE -> - { - pufferfishItem = ItemBuilder( plugin, Material.PUFFERFISH ) - .name(plugin.messageConfig.data.getKitItemNames( "blitzcrank", "pufferfish" )!![ playStyle.name.lowercase() ]!!) - .unbreakable( true ) - .hideAttributes() - .build() - - items.add( pufferfishItem ) - player.inventory.setItem( 1, pufferfishItem ) - } - } - } - - override fun onHit( attacker: Player, victim: Player, event: EntityDamageByEntityEvent ) {} - - override fun onDamaged( attacker: Player, victim: Player, event: EntityDamageByEntityEvent ) {} - - // Ultimate - private val ultimateDamage = plugin.kitConfig.data.blitzcrank[ "ultimate damage" ]!! - private val ultimateRadius = plugin.kitConfig.data.blitzcrank[ "ultimate radius" ]!! - private val ultimateStunDuration = plugin.kitConfig.data.blitzcrank[ "ultimate stun duration" ]!! - - // Hook - private val hookRange = plugin.kitConfig.data.blitzcrank[ "hook range" ]!! - - // Stun - private val stunHeight = plugin.kitConfig.data.blitzcrank[ "stun height" ]!! - private val stunRadius = plugin.kitConfig.data.blitzcrank[ "stun radius" ]!! - private val stunSlowDuration = plugin.kitConfig.data.blitzcrank[ "stun slow duration" ]!! - - override fun onInteract( - player: Player, - event: PlayerInteractEvent - ) { - if ( !gameManager.isRunning() ) - return - - val action = event.action - - if ( action != Action.RIGHT_CLICK_AIR && - action != Action.RIGHT_CLICK_BLOCK ) - return - - val item = event.item ?: return - - if ( item != hotsItem && - item != fishingRodItem && - item != pufferfishItem ) - return - - when( item.type ) - { - Material.HEART_OF_THE_SEA -> - { - val result = abilityContext.canUseAbility( player, "hots-blitzcrank", 15 ) - - if ( result.missingHits > 0 ) - { - plugin.chatManager.sendMessage( player, "kits.missingHits", "{hits]" to result.missingHits.toString() ) - return - } - - ultimate( player, ultimateRadius ) - } - - Material.FISHING_ROD -> - { - val result = abilityContext.canUseAbility( player, "hook-blitzcrank", 15 ) - - if ( result.missingHits > 0 ) - { - plugin.chatManager.sendMessage( player, "kits.missingHits", "{hits]" to result.missingHits.toString() ) - return - } - - hookPlayer( player ) - } - - Material.PUFFERFISH -> - { - val result = abilityContext.canUseAbility( player, "stun-blitzcrank", 15 ) - - if ( result.missingHits > 0 ) - { - plugin.chatManager.sendMessage( player, "kits.missingHits", "{hits]" to result.missingHits.toString() ) - return - } - - stunNearby( player, stunRadius ) - } - - else -> return - } - } - - override fun onMove( player: Player, event: PlayerMoveEvent ) {} - - private fun ultimate( - player: Player, - radius: Double - ) { - val nearbyPlayers = player.world.getNearbyPlayers( player.location, radius ) - - nearbyPlayers.forEach { nearby -> - - nearby.damage( ultimateDamage, player ) - - nearby.addPotionEffect(PotionEffect( - PotionEffectType.SLOWNESS, - ultimateStunDuration.toInt() * 20, - 255, - false, - false, - false - )) - - plugin.chatManager.sendMessage( nearby, "kits.blitzcrank.messages.ultimate.target", "{player}" to player.getDisplayName ) - } - - plugin.chatManager.sendMessage( player, "kits.blitzcrank.messages.ultimate.player", "{nearby}" to nearbyPlayers.size.toString() ) - } - - private fun hookPlayer( - player: Player - ) { - val target = DirectionUtil.getTargetPlayerInLineOfSight( player, hookRange, 0.5 ) - - if ( target == null ) - { - plugin.chatManager.sendMessage( player, "kits.blitzcrank.messages.no_player_in_sight" ) - return - } - - val hookDirection = target.location.toVector().add( player.location.toVector() ).normalize() - hookDirection.multiply( 1.0 ) - hookDirection.setY( 0.5 ) - target.velocity = hookDirection - - plugin.chatManager.sendMessage( player, "kits.blitzcrank.messages.hook.player", "{player}" to target.getDisplayName ) - plugin.chatManager.sendMessage( target, "kits.blitzcrank.messages.hook.target" ) - } - - private fun stunNearby( - player: Player, - radius: Double - ) { - val nearbyPlayers = player.world.getNearbyPlayers( player.location, radius ) - - nearbyPlayers.forEach { nearby -> - - val velocity = nearby.velocity - velocity.add(Vector( 0.0, stunHeight, 0.0 )) - nearby.velocity = velocity - - nearby.addPotionEffect(PotionEffect( - PotionEffectType.SLOWNESS, - 20 * stunSlowDuration.toInt(), - 4, false, false, false - )) - - plugin.chatManager.sendMessage( nearby, "kits.blitzcrank.messages.stun.target" ) - } - - plugin.chatManager.sendMessage( player, "kits.blitzcrank.messages.stun.player", "{nearby}" to nearbyPlayers.size.toString() ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/listener/ConnectListener.kt b/src/main/kotlin/club/mcscrims/speedhg/listener/ConnectListener.kt new file mode 100644 index 0000000..9bc66ce --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/listener/ConnectListener.kt @@ -0,0 +1,57 @@ +package club.mcscrims.speedhg.listener + +import club.mcscrims.speedhg.SpeedHG +import club.mcscrims.speedhg.game.GameState +import club.mcscrims.speedhg.util.getDisplayName +import club.mcscrims.speedhg.util.sendMsg +import club.mcscrims.speedhg.util.trans +import net.kyori.adventure.text.Component +import net.kyori.adventure.title.Title +import org.bukkit.Bukkit +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.player.PlayerQuitEvent + +class ConnectListener : Listener { + + private val plugin = SpeedHG.instance + + @EventHandler + fun onJoin( + event: PlayerJoinEvent + ) { + val player = event.player + event.joinMessage( null ) + + if ( plugin.gameManager.currentState == GameState.INGAME || + plugin.gameManager.currentState == GameState.INVINCIBILITY ) + return + + Bukkit.getOnlinePlayers().forEach { p -> + p.sendMsg( "game.join", "" to player.name ) + } + + player.showTitle(Title.title( + player.trans( "player.welcome" ), + Component.empty() + )) + } + + @EventHandler + fun onQuit( + event: PlayerQuitEvent + ) { + val player = event.player + event.quitMessage( null ) + + if ( plugin.gameManager.currentState == GameState.INGAME || + plugin.gameManager.currentState == GameState.INVINCIBILITY ) + return + + Bukkit.getOnlinePlayers().forEach { p -> + p.sendMsg( "game.quit", "" to player.name ) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt b/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt index 96154bf..4061d53 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/listener/GameStateListener.kt @@ -1,62 +1,47 @@ package club.mcscrims.speedhg.listener import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.game.GameManager -import club.mcscrims.speedhg.game.GameStateTypes -import club.mcscrims.speedhg.util.DirectionUtil -import club.mcscrims.spigot.chat.getDisplayName -import kotlinx.coroutines.runBlocking -import org.bukkit.Bukkit +import club.mcscrims.speedhg.game.GameState +import club.mcscrims.speedhg.util.sendMsg import org.bukkit.Material import org.bukkit.Sound import org.bukkit.attribute.Attribute -import org.bukkit.entity.Entity import org.bukkit.entity.Player -import org.bukkit.entity.Projectile -import org.bukkit.entity.Tameable import org.bukkit.event.Event import org.bukkit.event.EventHandler import org.bukkit.event.Listener -import org.bukkit.event.block.Action import org.bukkit.event.block.BlockBreakEvent import org.bukkit.event.block.BlockPlaceEvent -import org.bukkit.event.block.BlockSpreadEvent import org.bukkit.event.block.LeavesDecayEvent import org.bukkit.event.enchantment.EnchantItemEvent import org.bukkit.event.entity.EntityDamageByEntityEvent -import org.bukkit.event.entity.EntityDamageEvent import org.bukkit.event.entity.FoodLevelChangeEvent -import org.bukkit.event.entity.PlayerDeathEvent import org.bukkit.event.inventory.ClickType import org.bukkit.event.inventory.CraftItemEvent import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryCloseEvent import org.bukkit.event.inventory.InventoryOpenEvent import org.bukkit.event.inventory.InventoryType -import org.bukkit.event.player.PlayerAttemptPickupItemEvent -import org.bukkit.event.player.PlayerBucketEmptyEvent import org.bukkit.event.player.PlayerDropItemEvent -import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.player.PlayerItemDamageEvent import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerQuitEvent import org.bukkit.inventory.EnchantingInventory -import org.bukkit.inventory.EquipmentSlot import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.Damageable import java.util.Random -import kotlin.math.min -class GameStateListener( - private val plugin: SpeedHG, - private val gameManager: GameManager -) : Listener { +class GameStateListener : Listener { + + private val plugin = SpeedHG.instance + private val gameManager = plugin.gameManager @EventHandler fun onLeavesDecay( event: LeavesDecayEvent ) { - if ( gameManager.isRunning() ) + if ( gameManager.currentState == GameState.INVINCIBILITY || + gameManager.currentState == GameState.INGAME ) return event.isCancelled = true @@ -68,40 +53,29 @@ class GameStateListener( ) { val player = event.player - if ( gameManager.getCurrentStateType() == GameStateTypes.FEAST ) - { - val block = event.block - - if (!gameManager.feastBox.contains( block.location.toVector() )) - return - - event.isCancelled = true - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) - return - } - - if ( gameManager.isRunning() ) + if ( gameManager.currentState == GameState.INVINCIBILITY || + gameManager.currentState == GameState.INGAME ) return event.isCancelled = true - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) + player.playSound( player.location, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) } - private val beforeInvisMaterials = setOf( - Material.OAK_LOG, - Material.DARK_OAK_LOG, - Material.BIRCH_LOG, - Material.ACACIA_LOG, - Material.JUNGLE_LOG, - Material.SPRUCE_LOG, - Material.STONE + private val beforeInvisMaterials = mapOf( + Material.OAK_LOG to Sound.BLOCK_WOOD_BREAK, + Material.DARK_OAK_LOG to Sound.BLOCK_WOOD_BREAK, + Material.BIRCH_LOG to Sound.BLOCK_WOOD_BREAK, + Material.ACACIA_LOG to Sound.BLOCK_WOOD_BREAK, + Material.JUNGLE_LOG to Sound.BLOCK_WOOD_BREAK, + Material.SPRUCE_LOG to Sound.BLOCK_WOOD_BREAK, + Material.STONE to Sound.BLOCK_STONE_BREAK ) - private val alwaysMaterials = setOf( - Material.RED_MUSHROOM, - Material.BROWN_MUSHROOM, - Material.COCOA_BEANS, - Material.CACTUS + private val alwaysMaterials = mapOf( + Material.RED_MUSHROOM to Sound.BLOCK_GRASS_BREAK, + Material.BROWN_MUSHROOM to Sound.BLOCK_GRASS_BREAK, + Material.COCOA_BEANS to Sound.BLOCK_WOOD_BREAK, + Material.CACTUS to Sound.BLOCK_WOOL_BREAK ) @EventHandler @@ -110,35 +84,26 @@ class GameStateListener( ) { val player = event.player - if ( !gameManager.isRunning() ) + if ( gameManager.currentState != GameState.INVINCIBILITY && + gameManager.currentState != GameState.INGAME ) { event.isCancelled = true - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) + player.playSound( player.location, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) return } val block = event.block - if ( gameManager.getCurrentStateType() == GameStateTypes.FEAST ) + if ( gameManager.currentState == GameState.INVINCIBILITY && + beforeInvisMaterials.containsKey( block.type )) { - if (!gameManager.feastBox.contains( block.location.toVector() )) - return - - event.isCancelled = true - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) + pickupBlock( event, player ) return } - if ( gameManager.getCurrentStateType() == GameStateTypes.IMMUNITY && - beforeInvisMaterials.contains( block.type )) + if (alwaysMaterials.containsKey( block.type )) { - pickup( event, player ) - return - } - - if (alwaysMaterials.contains( block.type )) - { - pickup( event, player ) + pickupBlock( event, player ) return } @@ -148,46 +113,51 @@ class GameStateListener( event.block.type = Material.AIR event.block.tick() - plugin.chatManager.sendMessage( player, "build.no_diamonds" ) - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) + player.sendMsg( "build.no_diamonds" ) + player.playSound( player.location, Sound.ENTITY_VILLAGER_NO, 1f, 1f ) return } - if ( block.type == Material.IRON_ORE && gameManager.isBeforeFeast() ) + // TODO: add feast check + if ( block.type == Material.IRON_ORE && TODO( "Add before feast check" )) { event.isCancelled = true - plugin.chatManager.sendMessage( player, "build.no_iron_before_feast" ) - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) + player.sendMsg( "build.no_iron_before_feast" ) + player.playSound( player.location, Sound.ENTITY_VILLAGER_NO, 1f, 1f ) } - else if ( block.type == Material.IRON_ORE && !gameManager.isBeforeFeast() ) + else if ( block.type == Material.IRON_ORE && TODO( "Add after feast check" )) { - runBlocking { plugin.statsRepository.addIronFarmed( player.uniqueId, 0.1 ) } + // TODO: add 0.1 to ironFarmed in database } } - private fun pickup( + private fun pickupBlock( event: BlockBreakEvent, player: Player ) { + val block = event.block + event.isCancelled = true + val sound = if (beforeInvisMaterials.containsKey( block.type )) + beforeInvisMaterials[ block.type ]!! + else alwaysMaterials[ block.type ]!! + if (!hasInventorySpace( player )) { - event.block.drops.forEach { player.world.dropItem( event.block.location, it ) } - event.block.type = Material.AIR + block.drops.forEach { player.world.dropItem( block.location, it ) } + player.playSound( player.location, sound, 1f, 1f ) + block.type = Material.AIR return } - event.block.drops.forEach { player.inventory.addItem( it ) } - event.block.type = Material.AIR + block.drops.forEach { player.inventory.addItem( it ) } + player.playSound( player.location, sound, 1f, 1f ) + block.type = Material.AIR } - private fun hasInventorySpace( - player: Player - ): Boolean - { - return player.inventory.any { it == null || it.type == Material.AIR } - } + private fun hasInventorySpace( player: Player ): Boolean + = player.inventory.any { it == null || it.type == Material.AIR } @EventHandler fun onDropItem( @@ -195,75 +165,15 @@ class GameStateListener( ) { val player = event.player - if ( !gameManager.isRunning() ) + if ( gameManager.currentState != GameState.INVINCIBILITY && + gameManager.currentState != GameState.INGAME ) { event.isCancelled = true - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) + player.playSound( player.location, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) return } - val kit = plugin.kitManager.getSelectedKit( player ) - ?: return - - val items = kit.items.ifEmpty { return } - - if (items.contains( event.itemDrop.itemStack )) - { - event.isCancelled = true - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) - } - } - - @EventHandler - fun onPickupItem( - event: PlayerAttemptPickupItemEvent - ) { - if ( gameManager.isRunning() ) - return - - event.isCancelled = true - } - - @EventHandler - fun onInteractCompass( - event: PlayerInteractEvent - ) { - if ( !gameManager.isRunning() ) - return - - val player = event.player - val action = event.action - - if ( action != Action.RIGHT_CLICK_AIR && - action != Action.RIGHT_CLICK_BLOCK ) - return - - val item = event.item - ?: return - - if ( item.type != Material.COMPASS ) - return - - val nearestPlayer = Bukkit.getOnlinePlayers().stream() - .filter { other -> other != player } - .map { other -> other.location.distance( player.location ) to other } - .filter { pair -> pair.first > 5.0 } - .toList() - .firstOrNull() - ?.second - - if ( nearestPlayer == null ) - { - plugin.chatManager.sendMessage( player, "compass.no_nearby_players" ) - return - } - - player.compassTarget = nearestPlayer.location - - val direction = DirectionUtil.getDirectionToPlayer( player, nearestPlayer ) - var actionBar = plugin.chatFormatter.format( "compass.actionBar" ) - actionBar = actionBar.replaceText { it.match( "" ).replacement( direction ).once() } - plugin.chatManager.getAudience( player ).sendActionBar( actionBar ) + // TODO: add kit item check } private val swordNerf = 0.5 @@ -277,11 +187,7 @@ class GameStateListener( fun onDamageEntity( event: EntityDamageByEntityEvent ) { - val damager = event.damager - - if ( damager !is Player ) - return - + val damager = event.damager as? Player ?: return val itemName = damager.inventory.itemInMainHand.type.name if (itemName.endsWith( "_SWORD" )) @@ -312,26 +218,16 @@ class GameStateListener( } @EventHandler - fun onJoin( - event: PlayerJoinEvent - ) { - disableCooldown( event.player ) - } + fun onJoin( event: PlayerJoinEvent ) { disableHitCooldown( event.player ) } @EventHandler - fun onQuit( - event: PlayerQuitEvent - ) { - disableCooldown( event.player ) - } + fun onQuit( event: PlayerQuitEvent ) { disableHitCooldown( event.player ) } - private fun disableCooldown( + private fun disableHitCooldown( player: Player ) { val attackSpeed = player.getAttribute( Attribute.GENERIC_ATTACK_SPEED ) - - if ( attackSpeed != null ) - attackSpeed.baseValue = 40.0 + if ( attackSpeed != null ) attackSpeed.baseValue = 40.0 } private val lapisLazuli = Material.LAPIS_LAZULI @@ -354,10 +250,9 @@ class GameStateListener( val item = event.currentItem ?: return - // prevent taking it out if ( item.type == lapisLazuli && event.rawSlot == 1 ) event.isCancelled = true - else if ( event.cursor.type == lapisLazuli && event.click == ClickType.DOUBLE_CLICK) + else if ( event.cursor.type == lapisLazuli && event.click == ClickType.DOUBLE_CLICK ) event.isCancelled = true } @@ -385,52 +280,12 @@ class GameStateListener( ( inventory as EnchantingInventory ).secondary = ItemStack( lapisLazuli, 64 ) } - @EventHandler - fun onInteractSoup( - event: PlayerInteractEvent - ) { - val player = event.player - val action = event.action - - if ( action != Action.RIGHT_CLICK_AIR && - action != Action.RIGHT_CLICK_BLOCK ) - return - - if ( !event.hasItem() || - event.material != Material.MUSHROOM_STEW ) - return - - if ( event.hand == EquipmentSlot.OFF_HAND ) - return - - if ( player.health < requireNotNull(player.getAttribute( Attribute.GENERIC_MAX_HEALTH )!!.defaultValue)) - { - player.health = min( player.health + 7, requireNotNull(player.getAttribute( Attribute.GENERIC_MAX_HEALTH )!!.defaultValue)) - player.inventory.setItemInMainHand(ItemStack( Material.BOWL )) - } - else if ( player.foodLevel < 20 ) - { - player.foodLevel += 6 - player.saturation += 7 - player.inventory.setItemInMainHand(ItemStack( Material.BOWL )) - } - } - - @EventHandler - fun onEntityDamage( - event: EntityDamageEvent - ) { - if ( gameManager.isRunning() ) - return - - event.isCancelled = true - } - @EventHandler fun onFoodLevelChange( event: FoodLevelChangeEvent ) { - if ( gameManager.isRunning() ) + if ( gameManager.currentState == GameState.INVINCIBILITY || + gameManager.currentState == GameState.INGAME ) return event.foodLevel = 20 @@ -441,244 +296,40 @@ class GameStateListener( fun onCraftItem( event: CraftItemEvent ) { - if ( !gameManager.isRunning() ) - return - - val player = event.whoClicked - - if ( player !is Player ) + if ( gameManager.currentState == GameState.INVINCIBILITY || + gameManager.currentState == GameState.INGAME ) return + val player = event.whoClicked as? Player ?: return val item = event.recipe.result + if ( event.isShiftClick ) + { + player.sendMsg( "craft.no_shift_click" ) + player.playSound( player.location, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) + return + } + if ( item.type == Material.SHIELD ) { - if ( event.isShiftClick ) - { - plugin.chatManager.sendMessage( player, "craft.no_shift_click" ) - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) - return - } - event.result = Event.Result.DENY - plugin.chatManager.sendMessage( player, "craft.no_shield" ) - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) + player.sendMsg( "craft.no_shield" ) + player.playSound( player.location, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) return } if (!item.type.name.contains( "iron", true )) return - if ( gameManager.isBeforeFeast() ) - { - event.result = Event.Result.DENY - plugin.chatManager.sendMessage( player, "craft.no_iron_before_feast" ) - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) - return - } + // TODO: add before feast check and deny item crafting if ( item.type.maxDurability > 0 ) { item.editMeta { meta -> ( meta as Damageable ).damage /= 2 } - plugin.chatManager.sendMessage( player, "craft.iron_nerf" ) + player.sendMsg( "craft.iron_nerf" ) } - runBlocking { plugin.statsRepository.addIronFarmed( player.uniqueId, 0.1 ) } - } - - @EventHandler - fun onInteractCleanse( - event: PlayerInteractEvent - ) { - if ( gameManager.isBeforeFeast() ) - return - - val player = event.player - val action = event.action - - val item = event.item ?: return - val block = event.clickedBlock - - if ( block != null && gameManager.feastBox.contains( block.location.toVector() )) - { - event.isCancelled = true - player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) - return - } - - if ( action != Action.RIGHT_CLICK_AIR && - action != Action.RIGHT_CLICK_BLOCK ) - return - - if ( item.type != Material.SUGAR || - !item.itemMeta.isUnbreakable ) - return - - event.isCancelled = true - - player.inventory.removeItemAnySlot( item ) - player.clearActivePotionEffects() - - plugin.chatManager.sendMessage( player, "feast.cleanser.cleaned" ) - } - - @EventHandler - fun onBucketEmpty( - event: PlayerBucketEmptyEvent - ) { - if ( gameManager.getCurrentStateType() != GameStateTypes.FEAST ) - return - - val location = event.blockClicked.location.toVector() - - if (!gameManager.feastBox.contains( location )) - return - - event.isCancelled = true - event.player.playSound( event.player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) - } - - @EventHandler - fun onLiquidSpread( - event: BlockSpreadEvent - ) { - if ( gameManager.getCurrentStateType() != GameStateTypes.FEAST ) - return - - val block = event.block - - if ( !block.isLiquid ) - return - - if (!gameManager.feastBox.contains( block.location.toVector() )) - return - - event.isCancelled = true - } - - enum class DeathType { - PLAYER, - ENTITY, - WORLD - } - - private val playerDeathMessages: Int = plugin.messageConfig.data.deathMessages["player"]!!.size - private val entityDeathMessages: Int = plugin.messageConfig.data.deathMessages["entity"]!!.size - private val worldDeathMessages: Int = plugin.messageConfig.data.deathMessages["world"]!!.size - - @EventHandler - fun onDeath( - event: PlayerDeathEvent - ) { - val player = event.entity - suppressEvent( event ) - - val lastDamageCause = player.lastDamageCause - var killer: Entity? = null - var deathType = DeathType.WORLD - - if ( lastDamageCause is EntityDamageByEntityEvent ) - { - val damager = lastDamageCause.damager - - if ( damager !is Player ) - { - if ( damager is Projectile ) - { - if ( damager.shooter is Player ) - { - deathType = DeathType.PLAYER - killer = damager.shooter as Player - } - else - { - deathType = DeathType.ENTITY - killer = damager.shooter as Entity - } - } - else if ( damager is Tameable && damager.isTamed && damager.owner is Player ) - { - deathType = DeathType.PLAYER - killer = damager.owner as Player - } - else - { - deathType = DeathType.ENTITY - killer = damager - } - } - else - { - deathType = DeathType.PLAYER - killer = damager - } - } - - if ( killer != null && killer is Player ) - { - killer.exp += 0.5f - runBlocking { plugin.statsRepository.addKills( killer.uniqueId, 1 ) } - } - - when( deathType ) - { - DeathType.PLAYER -> - { - val random = this.random.nextInt( 0, playerDeathMessages ) - - val message = plugin.chatFormatter.formatList( "death.player", - "{player}" to player.getDisplayName, "{killer}" to (killer as Player).getDisplayName - )[ random ] - - for ( p in Bukkit.getOnlinePlayers() ) - plugin.chatManager.sendInteractiveMessage( p, message ) - } - - DeathType.ENTITY -> - { - val random = this.random.nextInt( 0, entityDeathMessages ) - - val message = plugin.chatFormatter.formatList( "death.entity", - "{player}" to player.getDisplayName, "{entity}" to killer!!.type.name - )[ random ] - - for ( p in Bukkit.getOnlinePlayers() ) - plugin.chatManager.sendInteractiveMessage( p, message ) - } - - DeathType.WORLD -> - { - val random = this.random.nextInt( 0, worldDeathMessages ) - - val message = plugin.chatFormatter.formatList( "death.world", - "{player}" to player.getDisplayName - )[ random ] - - for ( p in Bukkit.getOnlinePlayers() ) - plugin.chatManager.sendInteractiveMessage( p, message ) - } - } - } - - private fun suppressEvent( - event: PlayerDeathEvent - ) { - val player = event.entity - - event.deathMessage( null ) - - event.setShouldDropExperience( false ) - - event.setShouldPlayDeathSound( false ) - player.world.strikeLightningEffect( player.location ) - - runBlocking { - plugin.statsRepository.addDeaths( player.uniqueId, 1 ) - plugin.playerRepository.updateAliveStatus( player.uniqueId, false ) - } - - if ( player.isOnline ) - player.kick(plugin.chatFormatter.format( "death.kick" )) + // TODO: add 0.1 to ironFarmed in database } } \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/listener/LunarClientListener.kt b/src/main/kotlin/club/mcscrims/speedhg/listener/LunarClientListener.kt deleted file mode 100644 index e3bd4c9..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/listener/LunarClientListener.kt +++ /dev/null @@ -1,105 +0,0 @@ -package club.mcscrims.speedhg.listener - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.game.GameStateTypes -import club.mcscrims.speedhg.util.LuckPermsUtils -import club.mcscrims.speedhg.util.TimeUtils -import com.lunarclient.apollo.Apollo -import com.lunarclient.apollo.event.ApolloListener -import com.lunarclient.apollo.event.Listen -import com.lunarclient.apollo.event.player.ApolloRegisterPlayerEvent -import com.lunarclient.apollo.mods.impl.ModFreelook -import com.lunarclient.apollo.mods.impl.ModMinimap -import com.lunarclient.apollo.mods.impl.ModSnaplook -import com.lunarclient.apollo.mods.impl.ModTeamView -import com.lunarclient.apollo.mods.impl.ModWaypoints -import com.lunarclient.apollo.module.modsetting.ModSettingModule -import com.lunarclient.apollo.module.richpresence.RichPresenceModule -import com.lunarclient.apollo.module.richpresence.ServerRichPresence -import com.lunarclient.apollo.module.serverrule.ServerRuleModule -import com.lunarclient.apollo.module.staffmod.StaffMod -import com.lunarclient.apollo.module.staffmod.StaffModModule -import com.lunarclient.apollo.player.ApolloPlayer - -class LunarClientListener( - private val plugin: SpeedHG -) : ApolloListener { - - private val modSettingModule = Apollo.getModuleManager().getModule( ModSettingModule::class.java ) - private val staffModModule = Apollo.getModuleManager().getModule( StaffModModule::class.java ) - - private val richPresenceModule = Apollo.getModuleManager().getModule( RichPresenceModule::class.java ) - - private val serverRuleModule = Apollo.getModuleManager().getModule( ServerRuleModule::class.java ) - - init { - this.handle( ApolloRegisterPlayerEvent::class.java, this::onApolloRegister ) - } - - @Listen - fun onApolloRegister( - event: ApolloRegisterPlayerEvent - ) { - val player = event.player - - setModSettings( player ) - setRichPresence( player ) - setServerRules( player ) - } - - private fun setServerRules( - player: ApolloPlayer - ) { - serverRuleModule.options.set( player, ServerRuleModule.COMPETITIVE_GAME, plugin.pluginConfig.data.game.competitiveGame ) - serverRuleModule.options.set( player, ServerRuleModule.COMPETITIVE_COMMANDS, plugin.pluginConfig.data.game.competitiveCommands ) - serverRuleModule.options.set( player, ServerRuleModule.ANTI_PORTAL_TRAPS, true ) - } - - private fun setRichPresence( - player: ApolloPlayer - ) { - val teamMaxSize = plugin.pluginConfig.data.game.teams["maximum_players"] as? Int ?: 2 - - val playerState = when( plugin.gameManager.getCurrentStateType() ) - { - GameStateTypes.WAITING -> plugin.pluginConfig.data.game.playerStates[ "waiting" ]?.scoreboard ?: "N/A" - GameStateTypes.PRE_START -> plugin.pluginConfig.data.game.playerStates[ "pre_start" ]?.scoreboard ?: "N/A" - GameStateTypes.IMMUNITY -> plugin.pluginConfig.data.game.playerStates[ "immunity" ]?.scoreboard ?: "N/A" - GameStateTypes.BATTLE -> plugin.pluginConfig.data.game.playerStates[ "battle" ]?.scoreboard ?: "N/A" - GameStateTypes.FEAST -> plugin.pluginConfig.data.game.playerStates[ "feast" ]?.scoreboard ?: "N/A" - GameStateTypes.DEATHMATCH -> plugin.pluginConfig.data.game.playerStates[ "deathmatch" ]?.scoreboard ?: "N/A" - GameStateTypes.END -> plugin.pluginConfig.data.game.playerStates[ "end" ]?.scoreboard ?: "N/A" - else -> throw IllegalStateException("Current game state is null!") - } - - val presence = ServerRichPresence.builder() - .gameName( plugin.pluginConfig.data.game.name ) - .gameState( plugin.gameManager.getCurrentStateType()!!.name ) - .gameVariantName( plugin.pluginConfig.data.game.variantName ) - .playerState(playerState.replace( "%time%", TimeUtils.scoreboardTimeFromState() )) - .teamCurrentSize( 0 ) // TODO: Add team manager - .teamMaxSize( teamMaxSize ) - .build() - - richPresenceModule.overrideServerRichPresence( player, presence ) - } - - private fun setModSettings( - player: ApolloPlayer - ) { - if (LuckPermsUtils.hasPermission( player.uniqueId, "mcscrims.staff" )) - staffModModule.enableStaffMods( player, listOf( StaffMod.XRAY )) - else - staffModModule.disableAllStaffMods( player ) - - if (LuckPermsUtils.hasPermission( player.uniqueId, "speedhg.bypass.modSettings" )) - return - - modSettingModule.options.set( player, ModMinimap.ENABLED, false ) - modSettingModule.options.set( player, ModFreelook.ENABLED, false ) - modSettingModule.options.set( player, ModSnaplook.ENABLED, false ) - modSettingModule.options.set( player, ModWaypoints.ENABLED, false ) - modSettingModule.options.set( player, ModTeamView.ENABLED, true ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/listener/SoupListener.kt b/src/main/kotlin/club/mcscrims/speedhg/listener/SoupListener.kt new file mode 100644 index 0000000..98f3f2e --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/listener/SoupListener.kt @@ -0,0 +1,48 @@ +package club.mcscrims.speedhg.listener + +import org.bukkit.Material +import org.bukkit.attribute.Attribute +import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerInteractEvent +import org.bukkit.inventory.EquipmentSlot +import org.bukkit.inventory.ItemStack +import kotlin.math.min + +class SoupListener : Listener { + + @EventHandler( + priority = EventPriority.HIGHEST, + ignoreCancelled = true + ) + fun onSoup( + event: PlayerInteractEvent + ) { + val player = event.player + val action = event.action + + if ( !action.isRightClick ) + return + + if ( !event.hasItem() || + event.material != Material.MUSHROOM_STEW ) + return + + if ( event.hand == EquipmentSlot.OFF_HAND ) + return + + if ( player.health < requireNotNull(player.getAttribute( Attribute.GENERIC_MAX_HEALTH )?.defaultValue )) + { + player.health = min( player.health + 7, requireNotNull(player.getAttribute( Attribute.GENERIC_MAX_HEALTH )?.defaultValue )) + player.inventory.setItemInMainHand(ItemStack( Material.BOWL )) + } + else if ( player.foodLevel < 20 ) + { + player.foodLevel += 6 + player.saturation += 7 + player.inventory.setItemInMainHand(ItemStack( Material.BOWL )) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/recraft/Recraft.kt b/src/main/kotlin/club/mcscrims/speedhg/recraft/Recraft.kt deleted file mode 100644 index ea01188..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/recraft/Recraft.kt +++ /dev/null @@ -1,93 +0,0 @@ -package club.mcscrims.speedhg.recraft - -import org.bukkit.Material -import org.bukkit.entity.Player -import org.bukkit.inventory.ItemStack - - -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 ) - return - - 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() - - 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 ) - - 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 byMaterial( - material: Material - ): RecraftMaterial? - { - return recraftMaterials.stream().filter { it.containsKey( material ) }.findFirst().orElse( null ) - } - - fun getRecraftPoints(): Float - { - var points = 0f - - for ( recraftMaterial in recraftMaterials ) - points += recraftMaterial.getPoints() - - return points - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftInspector.kt b/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftInspector.kt deleted file mode 100644 index cc39d04..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftInspector.kt +++ /dev/null @@ -1,52 +0,0 @@ -package club.mcscrims.speedhg.recraft - -import club.mcscrims.speedhg.SpeedHG -import club.mcscrims.speedhg.game.GameStateTypes -import org.bukkit.Bukkit -import org.bukkit.scheduler.BukkitRunnable - -class RecraftInspector( - private val plugin: SpeedHG -) { - - private val beforeState = plugin.pluginConfig.data.game.recraftNerf[ "before_state" ] as String - private val recraftNerfEnabled = plugin.pluginConfig.data.game.recraftNerf[ "enabled" ] as Boolean - private val maxRecraftAmount = plugin.pluginConfig.data.game.recraftNerf[ "max_amount" ] as Double - - fun startRunnable() - { - if ( !recraftNerfEnabled ) - return - - object : BukkitRunnable() { - - val gameStateType = GameStateTypes.valueOf( beforeState.uppercase() ) - - override fun run() - { - if (!plugin.gameManager.isBefore( gameStateType )) - { - this.cancel() - return - } - - Bukkit.getOnlinePlayers().stream() - .filter { !it.isDead } - .forEach { player -> - val recraft = Recraft() - recraft.calcRecraft( *player.inventory.contents ) - - if ( recraft.getRecraftPoints() > maxRecraftAmount ) - { - plugin.chatManager.sendMessage( player, "recraftNerf.too_much" ) - - while ( recraft.getRecraftPoints() > maxRecraftAmount ) - recraft.decrease( player, 1 ) - } - } - } - - }.runTaskTimer( plugin, 10L, 20L ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftMaterial.kt b/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftMaterial.kt deleted file mode 100644 index 938f36c..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftMaterial.kt +++ /dev/null @@ -1,43 +0,0 @@ -package club.mcscrims.speedhg.recraft - -import org.bukkit.Material -import java.util.concurrent.ConcurrentHashMap - -class RecraftMaterial( - val maxSoupAmount: Int, - val materials: Array -): ConcurrentHashMap() { - - 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 } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftUtils.kt b/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftUtils.kt deleted file mode 100644 index 2400353..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftUtils.kt +++ /dev/null @@ -1,27 +0,0 @@ -package club.mcscrims.speedhg.recraft - -import org.bukkit.Bukkit -import org.bukkit.Material -import org.bukkit.NamespacedKey -import org.bukkit.inventory.ItemStack -import org.bukkit.inventory.ShapelessRecipe - -object RecraftUtils { - - 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 ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/scoreboard/ScoreboardManager.kt b/src/main/kotlin/club/mcscrims/speedhg/scoreboard/ScoreboardManager.kt new file mode 100644 index 0000000..80e067d --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/scoreboard/ScoreboardManager.kt @@ -0,0 +1,113 @@ +package club.mcscrims.speedhg.scoreboard + +import club.mcscrims.speedhg.SpeedHG +import club.mcscrims.speedhg.game.GameState +import club.mcscrims.speedhg.util.trans +import club.mcscrims.speedhg.util.transList +import fr.mrmicky.fastboard.adventure.FastBoard +import net.kyori.adventure.text.Component +import org.bukkit.Bukkit +import org.bukkit.Statistic +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.player.PlayerQuitEvent +import java.util.UUID +import java.util.concurrent.ConcurrentHashMap + +class ScoreboardManager( + private val plugin: SpeedHG +): Listener { + + private val boards = ConcurrentHashMap() + + init { + Bukkit.getScheduler().runTaskTimer( plugin, { -> + updateAllBoards() + }, 0L, 10L ) + + plugin.server.pluginManager.registerEvents( this, plugin ) + } + + @EventHandler + fun onJoin( + event: PlayerJoinEvent + ) { + val board = FastBoard( event.player ) + board.updateTitle(event.player.trans( "scoreboard.title" )) + boards[ event.player.uniqueId ] = board + } + + @EventHandler + fun onQuit( + event: PlayerQuitEvent + ) { + boards.remove( event.player.uniqueId )?.delete() + } + + private fun updateAllBoards() + { + boards.forEach { (key, value) -> + val player = Bukkit.getPlayer( key ) + + if ( player != null ) + updateBoard( player, value ) + } + } + + private fun updateBoard( + player: Player, + board: FastBoard + ) { + val gm = plugin.gameManager + val state = gm.currentState + + board.updateTitle(player.trans( "scoreboard.title" )) + + val online = Bukkit.getOnlinePlayers().size.toString() + val max = Bukkit.getMaxPlayers().toString() + val kitName = "None" // TODO + + val lines: List + + if ( state == GameState.LOBBY || state == GameState.STARTING ) + { + val timeString = if ( state == GameState.STARTING ) formatTime( gm.timer ) else "Waiting..." + + lines = player.transList( "scoreboard.lobby", mapOf( + "online" to online, + "max" to max, + "kit" to kitName, + "time" to timeString + )) + } + else + { + val timeString = formatTime( gm.timer ) + val alive = gm.alivePlayers.size.toString() + val kills = player.getStatistic( Statistic.PLAYER_KILLS ).toString() + val border = String.format( "%.0f", player.world.worldBorder.size ) + + lines = player.transList( "scoreboard.ingame", mapOf( + "timer" to timeString, + "alive" to alive, + "kills" to kills, + "border" to border, + "kit" to kitName + )) + } + + board.updateLines( lines ) + } + + private fun formatTime( + seconds: Int + ): String + { + val m = seconds / 60 + val s = seconds % 60 + return String.format( "%02d:%02d", m, s ) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/util/AbilityUtils.kt b/src/main/kotlin/club/mcscrims/speedhg/util/AbilityUtils.kt new file mode 100644 index 0000000..07ee07e --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/util/AbilityUtils.kt @@ -0,0 +1,101 @@ +package club.mcscrims.speedhg.util + +import club.mcscrims.speedhg.SpeedHG +import org.bukkit.Color +import org.bukkit.GameMode +import org.bukkit.Location +import org.bukkit.Particle +import org.bukkit.entity.Player +import org.bukkit.scheduler.BukkitRunnable +import org.bukkit.util.Vector +import java.util.function.Consumer + +object AbilityUtils { + + private val plugin = SpeedHG.instance + + fun createBeam( + startLocation: Location, + direction: Vector, + particle: Particle, + range: Double, + step: Double, + onHit: (Player) -> Unit + ) { + val normalizedDirection = direction.normalize() + + object : BukkitRunnable() + { + var traveledDistance = 0.0 + var currentLocation = startLocation.clone() + + override fun run() + { + if ( traveledDistance >= range) + { + this.cancel() + return + } + + currentLocation.world.spawnParticle( particle, currentLocation, 5, 0.0, 0.0, 0.0, 0.0 ) + + val nearestPlayer = currentLocation.world.getNearbyEntities( currentLocation, 0.5, 0.5, 0.5 ) + .filterIsInstance().minByOrNull { it.location.distance( currentLocation ) } + + if ( nearestPlayer != null ) + { + onHit( nearestPlayer ) + this.cancel() + return + } + + currentLocation.add(normalizedDirection.multiply( step )) + traveledDistance += step + } + + }.runTaskTimer( plugin, 0L, 1L ) + } + + fun drawParticleLine( + startLocation: Location, + endLocation: Location, + particle: Particle, + steps: Int, + dustColor: Color + ) { + if ( steps <= 0 ) throw IllegalArgumentException( "Steps must be greater than 0." ) + val world = startLocation.world ?: throw IllegalStateException( "World cannot be null." ) + if ( startLocation.world != endLocation.world ) throw IllegalStateException( "Locations must be in the same world." ) + + val diffX = ( endLocation.x - startLocation.x ) / steps + val diffY = ( endLocation.y - startLocation.y ) / steps + val diffZ = ( endLocation.z - startLocation.z ) / steps + + for ( i in 0..steps ) + { + val x = startLocation.x + diffX * i + val y = startLocation.y + diffY * i + val z = startLocation.z + diffZ * i + + world.spawnParticle( particle, x, y, z, 1, Particle.DustOptions( dustColor, 1f )) + } + } + + fun runForNearbyPlayers( + player: Player, + radius: Double, + filter: (Player) -> Boolean, + runnable: Consumer + ): MutableCollection + { + if ( radius <= 0.0 ) throw IllegalArgumentException( "Radius must be greater than 0.0." ) + + val world = player.world + val nearbyPlayers = world.getNearbyPlayers( player.location, radius ).filter( filter ) + .filter { it != player && it.gameMode == GameMode.SURVIVAL }.toMutableList() + + nearbyPlayers.forEach( runnable ) + return nearbyPlayers + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/util/DirectionUtil.kt b/src/main/kotlin/club/mcscrims/speedhg/util/DirectionUtil.kt deleted file mode 100644 index 1d94d84..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/util/DirectionUtil.kt +++ /dev/null @@ -1,78 +0,0 @@ -package club.mcscrims.speedhg.util - -import club.mcscrims.speedhg.SpeedHG -import net.kyori.adventure.text.Component -import org.bukkit.entity.Player -import org.bukkit.util.Vector -import kotlin.math.atan2 - -object DirectionUtil { - - private val plugin = SpeedHG.instance - private val directions = arrayOf("north", "northEast", "east", "southEast", "south", "southWest", "west", "northWest") - - fun getDirectionToPlayer( - player: Player, - nearestPlayer: Player - ): Component - { - val yaw = getDirection( player, nearestPlayer ) - var normalizedYaw = yaw % 360 - - if ( normalizedYaw < 0 ) - normalizedYaw += 360 - - val index = (( normalizedYaw + 22.5 ) / 45 ).toInt() % 8 - return plugin.chatFormatter.format( "compass.directions.${directions[ index ]}" ) - } - - private fun getDirection( - fromPlayer: Player, - toPlayer: Player - ): Double - { - val fromLocation = fromPlayer.location - val toLocation = toPlayer.location - - val directionVector = Vector( toLocation.x - fromLocation.x, 0.0, toLocation.z - fromLocation.z ).normalize() - - val angle = atan2( directionVector.x, directionVector.z ) - val yaw = Math.toDegrees( angle ) - - var adjustedYaw = yaw + 180 - - if ( adjustedYaw < 0) - adjustedYaw += 360 - - return adjustedYaw - } - - fun getTargetPlayerInLineOfSight( - player: Player, - maxDistance: Double, - width: Double - ): Player? - { - val direction = player.eyeLocation.direction.normalize() - val origin = player.eyeLocation.toVector() - val world = player.world - - val nearbyPlayers = world.getNearbyEntitiesByType( Player::class.java, player.location, maxDistance, maxDistance, maxDistance ) - - return nearbyPlayers - .filter { it.uniqueId != player.uniqueId } - .filter { nearby -> - val nearbyLocation = nearby.location.add( 0.0, 0.5, 0.0 ).toVector() - - val toEntitiy = nearbyLocation.subtract( origin ) - - val distanceAlongLine = toEntitiy.dot( direction ) - if ( distanceAlongLine !in 0.0..maxDistance ) return@filter false - - val perpendicularDistance = toEntitiy.subtract(direction.multiply( distanceAlongLine )).length() - perpendicularDistance <= width - } - .minByOrNull { it.location.distanceSquared( player.location ) } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/util/Extensions.kt b/src/main/kotlin/club/mcscrims/speedhg/util/Extensions.kt new file mode 100644 index 0000000..4d7bc6c --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/util/Extensions.kt @@ -0,0 +1,55 @@ +package club.mcscrims.speedhg.util + +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.entity.Player + +private val langManager get() = SpeedHG.instance.languageManager + +private val legacySerializer = LegacyComponentSerializer.builder() + .character('§') + .hexColors() + .useUnusualXRepeatedCharacterHexFormat() + .build() + +fun Player.sendMsg( + key: String, + vararg placeholders: Pair +) { + val component = langManager.getComponent( this, key, placeholders.toMap() ) + this.sendMessage( component ) +} + +fun Player.trans( + key: String, + vararg placeholders: Pair +): Component +{ + return langManager.getComponent( this, key, placeholders.toMap() ) +} + +fun Player.transList( + key: String, + placeholders: Map +): List +{ + val rawList = langManager.getRawMessageList( this, key ) + + return rawList.map { line -> + var replaced = line + placeholders.forEach { (k, v) -> + replaced = replaced.replace( "<$k>", v ) + } + MiniMessage.miniMessage().deserialize( replaced ) + } +} + +fun Component.toLegacyString(): String +{ + return legacySerializer.serialize( this ) +} + +val Player.getDisplayName: String + get() = legacySerializer.serialize( this.displayName() ) \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/util/LuckPermsUtils.kt b/src/main/kotlin/club/mcscrims/speedhg/util/LuckPermsUtils.kt deleted file mode 100644 index 592eb69..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/util/LuckPermsUtils.kt +++ /dev/null @@ -1,61 +0,0 @@ -package club.mcscrims.speedhg.util - -import club.mcscrims.speedhg.SpeedHG -import net.luckperms.api.cacheddata.CachedDataManager -import net.luckperms.api.model.group.Group -import net.luckperms.api.model.user.User -import org.bukkit.entity.Player -import java.util.UUID - -object LuckPermsUtils { - - private val plugin = SpeedHG.instance - private val luckPerms = plugin.luckPerms - - fun getUser( - player: Player - ): User? - { - return luckPerms.userManager.getUser( player.uniqueId ) - } - - fun editUser( - player: Player, - action: (User) -> Unit - ) { - luckPerms.userManager.loadUser( player.uniqueId ).thenAcceptAsync( action ) - } - - fun getGroup( - groupName: String - ): Group? - { - return luckPerms.groupManager.getGroup( groupName ) - } - - fun getCachedData( - player: Player - ): CachedDataManager? - { - return getUser( player )?.cachedData - } - - fun hasPermission( - uniqueId: UUID, - permission: String - ): Boolean - { - val cachedData = luckPerms.userManager.loadUser( uniqueId ).get().cachedData - return cachedData.permissionData.checkPermission( permission ).asBoolean() - } - - fun hasPermission( - player: Player, - permission: String - ): Boolean - { - return getCachedData( player )?.permissionData?.checkPermission( permission )?.asBoolean() - ?: player.hasPermission( permission ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/util/RandomCollection.kt b/src/main/kotlin/club/mcscrims/speedhg/util/RandomCollection.kt deleted file mode 100644 index 37ecea2..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/util/RandomCollection.kt +++ /dev/null @@ -1,73 +0,0 @@ -package club.mcscrims.speedhg.util - -import java.util.* - -class RandomCollection { - - private val map = TreeMap>() - private val names = hashMapOf, String>() - - private val random = Random() - private var total = 0.0 - - fun add( - weight: Double, - result: E - ) { - if ( weight <= 0 ) return - total += weight - map[ total ] = listOf( result ) - } - - fun add( - name: String, - weight: Double, - result: E - ) { - if ( weight <= 0 ) return - total += weight - map[ total ] = listOf( result ) - names[listOf( result )] = name - } - - fun add( - weight: Double, - result: List - ) { - if ( weight <= 0 ) return - total += weight - map[ total ] = result - } - - fun add( - name: String, - weight: Double, - result: List - ) { - if ( weight <= 0 ) return - total += weight - map[ total ] = result - names[ result ] = name - } - - fun getName( - key: E - ): String - { - return names.getOrDefault(listOf( key ), "" ) - } - - fun getName( - key: List - ): String - { - return names.getOrDefault( key, "" ) - } - - fun getRandom(): List - { - val value = random.nextDouble() * total - return map.higherEntry( value ).value - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/util/TimeUtils.kt b/src/main/kotlin/club/mcscrims/speedhg/util/TimeUtils.kt deleted file mode 100644 index 0d7c9d1..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/util/TimeUtils.kt +++ /dev/null @@ -1,29 +0,0 @@ -package club.mcscrims.speedhg.util - -import club.mcscrims.speedhg.SpeedHG - -object TimeUtils { - - private val plugin = SpeedHG.instance - - fun scoreboardTimeFromState(): String - { - val currentTime = plugin.gameManager.getCurrentState()?.getRemainingSeconds() - ?: throw IllegalArgumentException("Remaining seconds for state is null!") - return scoreboardTime( currentTime ) - } - - fun scoreboardTime( - totalSeconds: Int - ): String - { - val hours = totalSeconds / 3600 - val minutes = (totalSeconds % 3600) / 60 - val seconds = totalSeconds % 60 - - if ( totalSeconds > 3600 ) - return String.format( "%02d:%02d:%02d", hours, minutes, seconds ) - return String.format( "%02d:%02d", minutes, seconds ) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/world/WorldManager.kt b/src/main/kotlin/club/mcscrims/speedhg/world/WorldManager.kt deleted file mode 100644 index e77d378..0000000 --- a/src/main/kotlin/club/mcscrims/speedhg/world/WorldManager.kt +++ /dev/null @@ -1,159 +0,0 @@ -package club.mcscrims.speedhg.world - -import club.mcscrims.speedhg.SpeedHG -import org.bukkit.Bukkit -import org.bukkit.GameRule -import org.bukkit.Location -import org.bukkit.World -import org.popcraft.chunky.api.ChunkyAPI -import java.io.File - -class WorldManager( - private val plugin: SpeedHG -) { - - private var worldName = "world" - private lateinit var world: World - - fun highestLocationWithRadius( - center: Location, - radius: Int - ): Location - { - getWorld() - val minX = center.blockX - radius - val minZ = center.blockZ - radius - val maxX = center.blockX + radius - val maxZ = center.blockZ + radius - - var highestY = center.blockY - var highestX = minX - var highestZ = minZ - - for ( x in minX..maxX ) - for ( z in minZ..maxZ ) - { - val y = world.getHighestBlockYAt( x, z ) - - if ( y > highestY ) - { - highestY = y - highestX = x - highestZ = z - } - } - - val highest = Location( world, highestX.toDouble(), highestY.toDouble(), highestZ.toDouble() ) - return highest - } - - /* - * DELETION >> - */ - - fun deleteWorld() - { - getWorld() - Bukkit.unloadWorld( worldName, false ) - val folder = File( worldName ) - deleteFolder( folder ) - } - - private fun deleteFolder( - folder: File - ) { - val files = folder.listFiles() - - if ( files != null ) - for ( f in files ) - { - if ( f.isDirectory ) - deleteFolder( f ) - else - f.delete() - } - - folder.delete() - } - - /* - * WORLD >> - */ - - lateinit var spawnLocation: Location - var borderDecrease: Double = 100.0 - - fun setupWorld() - { - val world = getWorld() ?: return - plugin.logger.info("Setting up world...") - - // BORDER >> - - plugin.logger.info("Setting up world... [STAGE [1]: WORLDBORDER]") - - spawnLocation = Location( world, 0.0, world.getHighestBlockYAt( 0, 0).toDouble(), 0.0 ) - world.worldBorder.center = spawnLocation - - world.worldBorder.size = plugin.pluginConfig.data.world.border["size"]!! - world.worldBorder.warningDistance = plugin.pluginConfig.data.world.border["warning_distance"]!!.toInt() - world.worldBorder.damageAmount = plugin.pluginConfig.data.world.border["damage"]!! - - borderDecrease = plugin.pluginConfig.data.world.border["decrease"]!! - - // GAMERULES >> - - plugin.logger.info("Setting up world... [Stage [2]: GAMERULES]") - - world.setGameRule( GameRule.ANNOUNCE_ADVANCEMENTS, false ) - world.setGameRule( GameRule.DO_INSOMNIA, false ) - world.setGameRule( GameRule.DISABLE_RAIDS, true ) - world.setGameRule( GameRule.DO_PATROL_SPAWNING, false ) - world.setGameRule( GameRule.DO_TRADER_SPAWNING, false ) - - // CHUNKY >> - -// plugin.logger.info("Setting up world... [Stage [3]: CHUNKY]") -// val chunky = Bukkit.getServicesManager().load( ChunkyAPI::class.java ) -// -// if ( chunky == null || chunky.version() != 0 ) -// { -// plugin.isReady = true -// return -// } -// -// val radius = world.worldBorder.size / 2 -// -// chunky.startTask( worldName, "square", 0.0, 0.0, radius, radius, "concentric" ) -// chunky.onGenerationComplete { plugin.isReady = true } -// -// plugin.server.dispatchCommand( Bukkit.getConsoleSender(), "chunky silent" ) - - // FINISH >> - - plugin.logger.info("World has been set up!") - } - - private fun setWorld( - worldName: String - ): World? - { - this.worldName = worldName - val world = Bukkit.getWorld( worldName ) - - if ( world != null ) - this.world = world - - return world - } - - fun getWorld(): World? - { - return if ( !::world.isInitialized ) - try { - setWorld( plugin.pluginConfig.data.world.name ) - } catch ( _: Exception ) { null } - else this.world - } - -} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index d90d427..38722db 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,94 +1,21 @@ # -# |-----| |-----| |-----| |-----| |---\ | | |-----| -# | | | | | | \ | | | -# |-----| |-----| |---| |---| | | |-----| | |--| -# | | | | | / | | | | -# |-----| | |-----| |-----| |---/ | | |-----| -# v1.0.0 by TDSTOS +# SpeedHG v1.0.0 by McScrims-Network +# https://github.com/McScrims-Network # -# Organization: McScrims-Network (https://github.com/McScrims-Network/) -# Owner: TDSTOS (https://github.com/TDSTOS) -# Contributors: stavgun (https://github.com/stavgun) -# - -debug: false -language: 'en_US' - -restart_time: 3 - -network: - enabled: false - serverId: 'server-1' - type: REDIS - redis: - host: 'localhost' - port: 6379 - user: 'default' - password: '' - database: 0 - timeout: 5000 - poolSize: 10 - -cooldown: 30 -needed_hits: 15 - -world: - name: 'Default' - border: - size: 1000.0 - warning_distance: 5.0 - damage: 5.0 - decrease: 100.0 game: - name: 'SpeedHG' - variantName: 'Solo - Single Kit' + min-players: 2 + lobby-time: 60 + invincibility-time: 60 + border-start: 300.0 + border-end: 20.0 + border-shrink-time: 600 # 10 Minuten - minimumPlayers: 8 - - competitiveGame: false - competitiveCommands: - - 'server' - - 'servers' - - 'hub' - - 'lobby' - - 'l' - - playerStates: - waiting: - scoreboard: 'Waiting - %time%' - duration: "FIXED:-1" - preStart: - scoreboard: 'Waiting - %time%' - duration: "FIXED:300" - immunity: - scoreboard: 'Playing - %time%' - duration: "FIXED:90" - battle: - scoreboard: 'Playing - %time%' - duration: "INCREASING" - feast: - scoreboard: 'Playing - %time%' - duration: "FIXED:300" - deathmatch: - scoreboard: 'Playing - %time%' - duration: "INCREASING" - end: - scoreboard: 'Ending - %time%' - duration: "FIXED:60" - - recraftNerf: - enabled: false - maxAmount: 64 - beforeState: 'FEAST' - - teams: - enabled: false - maximumPlayers: 2 - - blockedKits: [] - - blockedPerks: [] - - perks: - maximumAmount: 2 \ No newline at end of file +anti-runner: + enabled: true + check-radius: 20.0 + warn-time: 15 + punish-time: 25 + # Einstellungen für die Cave-Erkennung + 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 \ No newline at end of file diff --git a/src/main/resources/database.json b/src/main/resources/database.json deleted file mode 100644 index d1a02e0..0000000 --- a/src/main/resources/database.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "connectionString": "", - "host": "localhost", - "port": 27017, - "database": "minecraft", - "username": "root", - "password": "pass", - "authDatabase": "admin", - "ssl": false, - "replicaSet": "", - "connectionTimeout": 30, - "poolSize": 10 -} \ No newline at end of file diff --git a/src/main/resources/kits.yml b/src/main/resources/kits.yml deleted file mode 100644 index 94bc72c..0000000 --- a/src/main/resources/kits.yml +++ /dev/null @@ -1,60 +0,0 @@ -anchor: - offensive extra damage: 1.0 - -armorer: - kills until new armor: 2.0 - -blackpanther: - enderpearl hit damage: 3.0 - extra damage on top: 0.5 - default hit radius: 3.0 - explosion multiplier: 3.0 - -gladiator: - cage radius: 23.0 - cage height: 10.0 - wither effect after x seconds: 180.0 - -goblin: - bunker radius: 10.0 - bunker time until disappear: 15.0 - knockback and pullin radius: 7.0 - kit steal time: 60.0 - soup steal chance: 20.0 - -icemage: - chance for slowness: 2.0 - -poseidon: - default hit radius: 3.0 - lightning hit damage: 4.0 - -rattlesnake: - maximum jump distance: 10.0 - speed duration: 10.0 - poison duration: 8.0 - maximum negative effect duration: 16.0 - default jump radius: 10.0 - -tesla: - disable push at height: 50.0 - default thunder radius: 5.0 - push strength: 1.0 - fire tick duration: 5.0 - time until thunder disables: 7.0 - thunder damage: 1.5 - -voodoo: - default curse radius: 3.0 - maximum effect duration: 15.0 - clicked players minimum health: 10.0 - voodoo hold duration: 5.0 - chance for wither effect: 5.0 - -perks: - knockback: - knockback strength: 1.5 - pullin: - pullin strength: 0.5 - radiusincrease: - new radius: 5.0 diff --git a/src/main/resources/languages/en_US.yml b/src/main/resources/languages/en_US.yml new file mode 100644 index 0000000..91a2825 --- /dev/null +++ b/src/main/resources/languages/en_US.yml @@ -0,0 +1,34 @@ +# +# SpeedHG v1.0.0 by McScrims-Network +# https://github.com/McScrims-Network +# + +default: + prefix: 'McScrims | ' + +game: + join: ' has joined the game.' + quit: ' has quit the game.' + +player: + welcome: 'Welcome to SpeedHG!' + +scoreboard: + title: 'SpeedHG' + lobby: + - " " + - "Spieler: /" + - "Kit: " + - "" + - "Waiting for start..." + - "" + - "play.mcscrims.club" + ingame: + - " " + - "Time: " + - "Players: " + - "Kills: " + - "" + - "Border: " + - "" + - "play.mcscrims.club" \ No newline at end of file diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml deleted file mode 100644 index 1c4bc25..0000000 --- a/src/main/resources/messages.yml +++ /dev/null @@ -1,23 +0,0 @@ -# -# |-----| |-----| |-----| |-----| |---\ | | |-----| -# | | | | | | \ | | | -# |-----| |-----| |---| |---| | | |-----| | |--| -# | | | | | / | | | | -# |-----| | |-----| |-----| |---/ | | |-----| -# v1.0.0 by TDSTOS -# -# Organization: McScrims-Network (https://github.com/McScrims-Network/) -# Owner: TDSTOS (https://github.com/TDSTOS) -# Contributors: stavgun (https://github.com/stavgun) -# - -default: - prefix: 'McScrims ' - no_permission: '%prefix% You don''t have permission to do that!' - player_not_found: '%prefix% This player could not be found!' - command_cooldown: '%prefix% Please wait {time} seconds before using this command again!' - -commands: - unknown: |- - %prefix% Unknown subcommand: {unknown} - %prefix% Use /{command} for an overview. \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 74c0e9d..e9a623f 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -6,9 +6,4 @@ api-version: '1.21' depend: - "WorldEdit" - "Apollo-Bukkit" - - "McScrims-CoreSystem" - -commands: - kits: - description: Open the kit selection menu - usage: /kits + - "McScrims-CoreSystem" \ No newline at end of file