Remove legacy modules, add language & scoreboard

Large refactor removing many legacy subsystems (abilities, kit system, database repos, recraft, world manager, extensive config classes, lunar/luckperms integrations and various listeners/commands). Introduces a lightweight LanguageManager, AntiRunningManager, ScoreboardManager, ConnectListener and a SoupListener; simplifies the main SpeedHG plugin to initialize these components and register the connection listener. Build changes: update Gradle wrapper to 8.10, remove paperweight and several external dependencies, add fr.mrmicky:fastboard and simplify shadowJar/build task configuration. Adds default language resource (languages/en_US.yml) and updates plugin/config resources. Purpose: simplify and decouple the plugin, reduce dependency surface and prepare for a leaner, modular rewrite.
This commit is contained in:
TDSTOS
2026-03-25 00:55:20 +01:00
parent b4db8dbfeb
commit e411879b20
57 changed files with 1172 additions and 5165 deletions

View File

@@ -2,7 +2,6 @@ plugins {
id("java") id("java")
id("maven-publish") id("maven-publish")
id("com.github.johnrengelman.shadow") version "8.1.1" 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("jvm") version libs.versions.kotlin
kotlin("kapt") version libs.versions.kotlin kotlin("kapt") version libs.versions.kotlin
} }
@@ -18,36 +17,11 @@ repositories {
maven("https://libraries.minecraft.net/") maven("https://libraries.minecraft.net/")
maven("https://repo.codemc.io/repository/maven-public/") maven("https://repo.codemc.io/repository/maven-public/")
maven("https://repo.lunarclient.dev") 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 { dependencies {
implementation("org.mongodb:mongodb-driver-kotlin-coroutine:4.11.1") implementation("fr.mrmicky:fastboard:2.1.3")
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")
compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT") compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT")
paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT")
} }
tasks { tasks {
@@ -65,10 +39,6 @@ tasks {
archiveVersion.set(project.version.toString()) archiveVersion.set(project.version.toString())
} }
assemble {
dependsOn( reobfJar )
}
build { build {
dependsOn( shadowJar ) dependsOn( shadowJar )
} }

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@@ -1,291 +1,61 @@
package club.mcscrims.speedhg package club.mcscrims.speedhg
import club.mcscrims.core.config.ConfigData import club.mcscrims.speedhg.config.LanguageManager
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.game.GameManager import club.mcscrims.speedhg.game.GameManager
import club.mcscrims.speedhg.command.KitsCommand import club.mcscrims.speedhg.game.modules.AntiRunningManager
import club.mcscrims.speedhg.database.PlayerRepository import club.mcscrims.speedhg.listener.ConnectListener
import club.mcscrims.speedhg.kit.KitInventoryListener import club.mcscrims.speedhg.scoreboard.ScoreboardManager
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 org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
class SpeedHG : JavaPlugin() { class SpeedHG : JavaPlugin() {
companion object { companion object {
internal lateinit var instance: SpeedHG lateinit var instance: SpeedHG
private set
fun Component.content(): String
{
return LegacyComponentSerializer.legacySection().serialize( this )
}
} }
private lateinit var configLoader: ConfigLoader lateinit var languageManager: LanguageManager
internal lateinit var pluginConfig: ConfigData<PluginConfig> private set
internal lateinit var messageConfig: ConfigData<MessageConfig>
internal lateinit var kitConfig: ConfigData<KitConfig>
internal lateinit var databaseConfig: ConfigData<DatabaseConfig>
internal lateinit var chatManager: ChatManager<MessageConfig> lateinit var gameManager: GameManager
internal lateinit var chatFormatter: ChatFormatter<MessageConfig> private set
private lateinit var mongoManager: MongoManager lateinit var antiRunningManager: AntiRunningManager
internal lateinit var statsRepository: StatsRepository private set
internal lateinit var playerRepository: PlayerRepository
internal lateinit var networkManager: SpigotNetworkManager lateinit var scoreboardManager: ScoreboardManager
internal lateinit var schedulerManager: SchedulerManager private set
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()
}
override fun onEnable() override fun onEnable()
{ {
loadConfigurations() instance = this
setupDatabase()
networkManager = SpigotNetworkManager.getInstance()!!
worldManager.setupWorld() saveDefaultConfig()
languageManager = LanguageManager( this )
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 )
gameManager = GameManager( this ) gameManager = GameManager( this )
gameManager.initialize() antiRunningManager = AntiRunningManager( this )
val cooldownManager = CooldownManager() scoreboardManager = ScoreboardManager( this )
val hitCounterManager = HitCounterManager()
abilityContext = AbilityContext( cooldownManager, hitCounterManager )
kitManager = KitManager( this )
kitManager.initialize()
kitInventoryManager = KitInventoryManager( this, kitManager )
setupLuckPerms()
registerListener() registerListener()
registerCommands()
RecraftUtils.registerRecipes() logger.info("SpeedHG wurde geladen!")
RecraftInspector( this ).startRunnable()
} }
override fun onDisable() override fun onDisable()
{ {
kitManager.clearAll() super.onDisable()
closeDatabase()
networkManager.shutdown()
} }
private fun registerListener() private fun registerListener()
{ {
server.pluginManager.registerEvents(GameStateListener( this, gameManager ), this ) val pm = Bukkit.getPluginManager()
server.pluginManager.registerEvents(KitListener( this, kitManager ), this )
server.pluginManager.registerEvents(KitInventoryListener( this, kitManager, kitInventoryManager ), this ) pm.registerEvents( ConnectListener(), this )
server.pluginManager.registerEvents(AbilityHitListener( this, abilityContext ), this )
LunarClientListener( this )
} }
private fun registerCommands() }
{
getCommand("kits")?.setExecutor(KitsCommand( this, kitInventoryManager ))
}
fun getAlivePlayers(): List<Player>
{
val alivePlayers = mutableListOf<Player>()
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<PluginConfig>(
fileName = "config.yml",
format = ConfigFormat.YAML,
autoReload = true
)
messageConfig = configLoader.loadConfig<MessageConfig>(
fileName = "messages.yml",
format = ConfigFormat.YAML,
autoReload = false
)
kitConfig = configLoader.loadConfig<KitConfig>(
fileName = "kits.yml",
format = ConfigFormat.YAML,
autoReload = false
)
databaseConfig = configLoader.loadConfig<DatabaseConfig>(
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}" )
}
}
}

View File

@@ -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()
}
}

View File

@@ -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() )
}
}
}

View File

@@ -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 )
}
}

View File

@@ -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<UUID, ConcurrentHashMap<String, Long>>()
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()
}
}

View File

@@ -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<UUID, ConcurrentHashMap<String, Int>>()
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()
}
}

View File

@@ -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<out String>
): Boolean {
if ( sender !is Player )
{
plugin.chatManager.sendSenderMessage( sender, "default.only_players" )
return true
}
kitInventoryManager.openKitInventory( sender, 1 )
return true
}
}

View File

@@ -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<String, Double> = getAnchorConfigurations(),
@ConfigField(name = "armorer", description = "Armorer configurations")
val armorer: Map<String, Double> = getArmorerConfigurations(),
@ConfigField(name = "blackpanther", description = "BlackPanther configurations")
val blackPanther: Map<String, Double> = getBlackPantherConfigurations(),
@ConfigField(name = "blitzcrank", description = "Blitzcrank configurations")
val blitzcrank: Map<String, Double> = getBlitzcrankConfigurations(),
@ConfigField(name = "gladiator", description = "Gladiator configurations")
val gladiator: Map<String, Double> = getGladiatorConfigurations(),
@ConfigField(name = "goblin", description = "Goblin configurations")
val goblin: Map<String, Double> = getGoblinConfigurations(),
@ConfigField(name = "icemage", description = "IceMage configurations")
val iceMage: Map<String, Double> = getIceMageConfigurations(),
@ConfigField(name = "poseidon", description = "Poseidon configurations")
val poseidon: Map<String, Double> = getPoseidonConfigurations(),
@ConfigField(name = "rattlesnake", description = "Rattlesnake configurations")
val rattlesnake: Map<String, Double> = getRattlesnakeConfigurations(),
@ConfigField(name = "tesla", description = "Tesla configurations")
val tesla: Map<String, Double> = getTeslaConfigurations(),
@ConfigField(name = "voodoo", description = "Voodoo configurations")
val voodoo: Map<String, Double> = getVoodooConfigurations(),
@ConfigField(name = "perks", description = "Perk configuration")
val perks: Map<String, Pair<String, Double>> = getPerkConfiguration()
) {
fun getAllKitConfigurations(): Map<String, Double>
{
return anchor + armorer + blackPanther + gladiator + goblin + iceMage + poseidon +
rattlesnake + tesla + voodoo
}
fun getConfigForKit(id: String): Map<String, Double>
{
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 )
)

View File

@@ -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<String, Map<String, String>>()
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<String, String>
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 ) ?: "<red>Missing Key: $key</red>"
}
fun getRawMessageList(
player: Player,
key: String
): List<String>
{
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("<red>Missing List: $key</red>")
}
fun getComponent(
player: Player,
key: String,
placeholders: Map<String, String>
): Component
{
val raw = getRawMessage( player, key )
val tags = placeholders.map { (k, v) -> Placeholder.parsed( k, v ) }
return miniMessage.deserialize( raw, *tags.toTypedArray() )
}
}

View File

@@ -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<String, String> = getDefaultMessages(),
@ConfigField(name = "commands", description = "Command messages")
val commandMessages: Map<String, String> = getCommandMessages(),
@ConfigField(name = "death", description = "Death messages")
val deathMessages: Map<String, List<String>> = getDeathMessages(),
@ConfigField(name = "kits", description = "Kit messages")
val kitMessages: Map<String, KitConfig> = getKitMessages(),
@ConfigField(name = "kits-default", description = "Default kit messages")
val defaultKitMessages: Map<String, String> = getDefaultKitMessages()
) {
data class KitConfig(
val name: String = "",
val description: List<String>,
val items: List<KitItemConfig>,
val messages: Map<String, String>
)
data class KitItemConfig(
val itemName: String,
val playStyleNames: Map<String, String>
)
fun getAllMessages(): Map<String, String>
{
return defaultMessages + commandMessages + defaultKitMessages + kitMessages.flatMap { it.value.messages.entries }.associate { it.key to it.value }
}
fun getListMessages(): Map<String, List<String>>
{
return deathMessages
}
fun getKitItemNames(
kit: String,
itemKey: String
): Map<String, String>?
{
return kitMessages[ kit ]?.items?.find { it.itemName == itemKey }?.playStyleNames
}
}
private fun getDefaultMessages(): Map<String, String> = mapOf(
"default.prefix" to "<gradient:dark_green:green>McScrims</gradient> <dark_gray>┃</dark_gray><reset>",
"default.no_permission" to "%prefix% <red>You don't have permission to do that!</red>",
"default.player_not_found" to "%prefix% <red>This player could not be found!</red>",
"default.command_cooldown" to "%prefix% <red>Please wait {time} seconds before using this command again!</red>",
"default.reload" to "%prefix% <green>Successfully reloaded the plugin.</green>",
"default.only_players" to "%prefix% <red>Only players can execute this command.</red>"
)
private fun getCommandMessages(): Map<String, String> = mapOf(
"commands.unknown" to "%prefix% <red>Unknown subcommand: {unknown}</red> \n%prefix% <gray>Use /{command} for an overview.</gray>"
)
private fun getDeathMessages(): Map<String, List<String>> = mapOf(
"player" to listOf(
"<black>☠</black> <red>{player} was killed by {killer}!</red>"
),
"entity" to listOf(
"<black>☠</black> <red>{player} was killed by {entity}!</red>"
),
"world" to listOf(
"<black>☠</black> <red>{player} has died!</red>"
)
)
private fun getDefaultKitMessages(): Map<String, String> = mapOf(
"kits.missingHits" to ""
)
private fun getKitMessages(): Map<String, MessageConfig.KitConfig> = mapOf(
"anchor" to MessageConfig.KitConfig(
name = "<dark_gray>Anchor</dark_gray>",
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 "<gray>Anchor (right-click)</gray>",
"defensive" to "<gray>Anchor (right-click)</gray>"
)
)),
messages = mapOf(
"tooFarAway" to "%prefix% <red>You can only place your anchor in a {radius} block radius!</red>",
"alreadyActivated" to "%prefix% <red>You have already placed an anchor!</red>",
"broken" to "%prefix% "
)
),
"armorer" to MessageConfig.KitConfig(
name = "<gray>Armorer</gray>",
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% <green>Your armor has been upgraded to {armorType}.</green>",
"upgrade.enchanted" to "%prefix% <purple>Your armor has been enchanted.</purple>"
)
),
"blackpanther" to MessageConfig.KitConfig(
name = "<gradient:purple:blue>Black Panther</gradient>",
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 "<dark_gray>Push</dark_gray> <gray>(right-click)</gray>",
"defensive" to "<purple>Wakanda Forever!</purple> <gray>(right-click)</gray>"
)
),
MessageConfig.KitItemConfig(
itemName = "blazePowder",
playStyleNames = mapOf(
"offensive" to "<yellow>Extra Damage</yellow> <gray>(right-click)</gray>",
"defensive" to "<yellow>Extra Damage</yellow> <gray>(right-click)</gray>"
)
)
),
messages = mapOf(
"wakandaForever.hit" to "%prefix% <purple>You have hit {hit} players with WAKANDA FOREVER!</purple>",
"extraDamage.activated" to "%prefix% <green>Your extra damage is now activated.</green>",
"extraDamage.deactivated" to "%prefix% <red>Your extra damage is now deactivated.</red>"
)
),
"blitzcrank" to MessageConfig.KitConfig(
name = "<gradient:orange:yellow>Blitzcrank</gradient>",
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 "<blue>Slow</blue> <gray>(right-click)</gray>",
"offensive" to "<blue>Slow</blue> <gray>(right-click)</gray>"
)
),
MessageConfig.KitItemConfig(
itemName = "fishingrod",
playStyleNames = mapOf(
"defensive" to "<gold>Hook</gold> <gray>(right-click)</gray>",
"offensive" to "<gold>Hook</gold> <gray>(right-click)</gray>"
)
),
MessageConfig.KitItemConfig(
itemName = "pufferfish",
playStyleNames = mapOf(
"defensive" to "<yellow>Stun</yellow> <gray>(right-click)</gray>",
"offensive" to "<yellow>Stun</yellow> <gray>(right-click)</gray>"
)
)
),
messages = mapOf(
"ultimate.target" to "%prefix% <yellow>You have been slowed by {player}!</yellow>",
"ultimate.player" to "%prefix% <blue>You have slowed {nearby} players.</blue>",
"no_player_in_sight" to "%prefix% <red>There is no player in your radius and/or sight to hook!</red>",
"hook.player" to "%prefix% <gold>You have hooked {player}!</gold>",
"hook.target" to "%prefix% <gold>You have been hooked to a Blitzcrank!</gold>",
"stun.target" to "%prefix% <light_blue>You have been stunned!</light_blue>",
"stun.player" to "%prefix% <light_blue>You have stunned {nearby} nearby players.</light_blue>"
)
)
)

View File

@@ -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<String, Any> = getAnnouncementConfiguration(),
@ConfigField(name = "game", description = "Game configuration")
val game: GameConfig = GameConfig()
) {
data class WorldConfig(
val name: String = "Default",
val border: Map<String, Double> = 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<String> = emptyList(),
val playerStates: Map<String, StateConfig> = getPlayerStates(),
val recraftNerf: Map<String, Any> = getRecraftNerf(),
val teams: Map<String, Any> = getTeams(),
val blockedKits: List<String> = emptyList(),
val blockedPerks: List<String> = emptyList(),
val perks: Map<String, Any> = 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
)

View File

@@ -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<String>,
@BsonProperty
val server: String
)
/**
* MongoDB-Repository für Spieler-Daten
*/
class PlayerRepository(
connection: MongoConnection
): MongoRepository<KitPlayer>( 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<KitPlayer>
{
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<String>
) {
val filter = Filters.eq("uuid", uuid.toString())
val update = Updates.set("unlockedKits", unlockedKits)
connection.updateOne(collectionName, filter, update)
}
}

View File

@@ -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<PlayerStats>( 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<PlayerStats>
{
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<PlayerStats>
{
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<PlayerStats>
{
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 )
}
}

View File

@@ -1,138 +1,370 @@
package club.mcscrims.speedhg.game package club.mcscrims.speedhg.game
import club.mcscrims.speedhg.SpeedHG import club.mcscrims.speedhg.SpeedHG
import club.mcscrims.speedhg.game.impl.BattleState import club.mcscrims.speedhg.util.sendMsg
import club.mcscrims.speedhg.game.impl.DeathmatchState import club.mcscrims.speedhg.util.trans
import club.mcscrims.speedhg.game.impl.EndState import net.kyori.adventure.title.Title
import club.mcscrims.speedhg.game.impl.FeastState import org.bukkit.*
import club.mcscrims.speedhg.game.impl.ImmunityState import org.bukkit.attribute.Attribute
import club.mcscrims.speedhg.game.impl.PreStartState
import club.mcscrims.speedhg.game.impl.WaitingState
import org.bukkit.Location
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.util.BoundingBox import org.bukkit.event.EventHandler
import java.util.concurrent.ConcurrentHashMap 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( class GameManager(
private val plugin: SpeedHG private val plugin: SpeedHG
) { ): Listener {
private var currentState: GameState? = null var currentState: GameState = GameState.LOBBY
internal val gameStateTypes = ConcurrentHashMap<GameStateTypes, GameState>() private set
private val winners = ArrayList<Player>() var timer = 0
internal lateinit var feastLocation: Location val alivePlayers = mutableSetOf<UUID>()
internal lateinit var feastBox: BoundingBox
internal var feastHeight: Int = 1
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( when( currentState )
this, {
plugin, GameState.LOBBY ->
plugin.schedulerManager, {
plugin.pluginConfig.data.getDuration( "waiting" ).seconds if ( Bukkit.getOnlinePlayers().size >= minPlayers )
) {
setGameState( GameState.STARTING )
timer = lobbyTime
}
}
gameStateTypes[ GameStateTypes.WAITING ] = currentState!! GameState.STARTING ->
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 ) if ( Bukkit.getOnlinePlayers().size < minPlayers )
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 ) setGameState( GameState.LOBBY )
gameStateTypes[ GameStateTypes.DEATHMATCH ] = DeathmatchState( this, plugin, plugin.schedulerManager, plugin.pluginConfig.data.getDuration( "deathmatch" ).seconds ) Bukkit.getOnlinePlayers().forEach { player ->
gameStateTypes[ GameStateTypes.END ] = EndState( this, plugin, plugin.schedulerManager, plugin.pluginConfig.data.getDuration( "end" ).seconds ) player.sendMsg( "game.start-aborted" )
}
return
}
try { timer--
currentState?.onEnter( null )
} catch ( e: Exception ) { if (timer in listOf( 60, 30, 10, 5, 4, 3, 2, 1 ))
plugin.logger.severe("Error during onEnter for state ${currentState?.name}: ${e.message}") {
e.printStackTrace() 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( fun setGameState(
stateType: GameStateTypes newState: GameState
) { ) {
val previousState = currentState this.currentState = newState
val nextState = gameStateTypes[ stateType ]!! }
try { private fun startGame()
currentState?.onExit( nextState ) {
} catch ( e: Exception ) { setGameState( GameState.INVINCIBILITY )
plugin.logger.severe("Error during onExit for state ${currentState?.name}: ${e.message}") timer = invincibilityTime
e.printStackTrace()
}
if ( nextState is FeastState ) val world = Bukkit.getWorld( "world" ) ?: return
{ world.time = 0
feastLocation = nextState.feastLocation world.setStorm( false )
feastBox = nextState.feastBox
feastHeight = nextState.feastHeight
}
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 { val speedEffect = PotionEffect(
nextState.onEnter( previousState ) PotionEffectType.SPEED,
} catch ( e: Exception ) { timer,
plugin.logger.severe("Error during onEnter for state ${nextState.name}: ${e.message}") 0,
e.printStackTrace() false,
} false,
} true
)
fun addWinners( val hasteEffect = PotionEffect(
vararg winners: Player 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<Player> = winners.toList() private fun checkWin()
fun getCurrentState(): GameState? = currentState
fun getCurrentStateType(): GameStateTypes? = gameStateTypes.filter { it.value.name == currentState?.name }.keys.firstOrNull()
fun isRunning(): Boolean
{ {
return getCurrentStateType() == GameStateTypes.IMMUNITY || if ( currentState != GameState.INGAME && currentState != GameState.INVINCIBILITY ) return
getCurrentStateType() == GameStateTypes.BATTLE ||
getCurrentStateType() == GameStateTypes.FEAST || if ( alivePlayers.size <= 1 )
getCurrentStateType() == GameStateTypes.DEATHMATCH {
val winnerUUID = alivePlayers.firstOrNull()
val winnerName = if ( winnerUUID != null ) Bukkit.getPlayer( winnerUUID )?.name ?: "N/A" else "N/A"
endGame( winnerName )
}
} }
fun isBeforeFeast(): Boolean private fun endGame(
{ winnerName: String
return getCurrentStateType() == GameStateTypes.WAITING || ) {
getCurrentStateType() == GameStateTypes.PRE_START || setGameState( GameState.ENDING )
getCurrentStateType() == GameStateTypes.IMMUNITY || timer = 15
(getCurrentStateType() == GameStateTypes.BATTLE &&
!( currentState as BattleState ).afterFeast ) 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( // --- Helfer Methoden ---
stateType: GameStateTypes
): Boolean private fun teleportRandomly(
{ player: Player,
return getCurrentStateType()?.points!! < stateType.points 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 ) val players = Bukkit.getOnlinePlayers().filter { alivePlayers.contains( it.uniqueId ) }
currentState = null
}
} for ( p in players )
{
var nearest: Player? = null
var minDistance = Double.MAX_VALUE
enum class GameStateTypes( for ( target in players )
val points: Int {
) { if ( p == target ) continue
WAITING( 0 ),
PRE_START( 1 ), val dist = p.location.distanceSquared( target.location )
IMMUNITY( 2 ), if ( dist < minDistance )
BATTLE( 3 ), {
FEAST( 4 ), minDistance = dist
DEATHMATCH( 5 ), nearest = target
END( 6 ) }
} }
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
}
}

View File

@@ -1,109 +1,9 @@
package club.mcscrims.speedhg.game package club.mcscrims.speedhg.game
import club.mcscrims.core.config.DurationType enum class GameState {
import club.mcscrims.speedhg.SpeedHG LOBBY, // Warten auf Spieler
import club.mcscrims.spigot.scheduler.SchedulerManager STARTING, // Countdown läuft
import club.mcscrims.spigot.scheduler.TaskRegistration INVINCIBILITY, // Schutzzeit (Spieler verteilt, kein Schaden)
import org.bukkit.Bukkit INGAME, // Kampfphase (Schaden an, Border schrumpft)
import org.bukkit.entity.Player ENDING // Gewinner steht fest, Server startet neu
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<String, String>
) {
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
}

View File

@@ -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
}
}

View File

@@ -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 )
}
}

View File

@@ -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()
}
}

View File

@@ -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<Location>( 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<ItemStack>()
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<ItemStack>()
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<ItemStack>()
netheriteItems.add( 1.0, ItemStack( Material.NETHERITE_INGOT ))
netheriteItems.add( 1.0, ItemStack( Material.SMITHING_TABLE ))
val sizeableItems = RandomCollection<ItemStack>()
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<ItemStack>()
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<RandomCollection<ItemStack>>()
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() )
}
}

View File

@@ -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() )
}
}

View File

@@ -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
}
}

View File

@@ -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() {}
}

View File

@@ -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<UUID, Long>()
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 )
}
}
}

View File

@@ -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<Component>,
val icon: Material,
val playStyle: PlayStyle,
protected val plugin: SpeedHG,
protected val abilityContext: AbilityContext,
protected val gameManager: GameManager
) {
lateinit var config: Map<String, Double>
val items = mutableListOf<ItemStack>()
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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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<AbstractKit>,
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<Component>()
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<String>()
runBlocking {
val kitPlayer = plugin.playerRepository.findByUuid( player.uniqueId )
if ( kitPlayer != null ) unlockedKits.addAll( kitPlayer.unlockedKits )
}
return unlockedKits.contains( kit.id )
}
}

View File

@@ -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" )
}
}

View File

@@ -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<String, AbstractKit>()
private val selectedKits = ConcurrentHashMap<UUID, AbstractKit>()
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<out AbstractKit>,
id: String,
displayName: Component,
description: List<Component>,
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<AbstractKit>
{
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()
}
}

View File

@@ -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<Component>,
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<UUID, Location>()
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 )
}
}

View File

@@ -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<Component>,
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 )
}
}

View File

@@ -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<Component>,
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" )
}
}
}

View File

@@ -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<Component>,
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() )
}
}

View File

@@ -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", "<name>" 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", "<name>" to player.name )
}
}
}

View File

@@ -1,62 +1,47 @@
package club.mcscrims.speedhg.listener package club.mcscrims.speedhg.listener
import club.mcscrims.speedhg.SpeedHG 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.util.sendMsg
import club.mcscrims.speedhg.util.DirectionUtil
import club.mcscrims.spigot.chat.getDisplayName
import kotlinx.coroutines.runBlocking
import org.bukkit.Bukkit
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.Sound import org.bukkit.Sound
import org.bukkit.attribute.Attribute import org.bukkit.attribute.Attribute
import org.bukkit.entity.Entity
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.entity.Projectile
import org.bukkit.entity.Tameable
import org.bukkit.event.Event import org.bukkit.event.Event
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.block.Action
import org.bukkit.event.block.BlockBreakEvent import org.bukkit.event.block.BlockBreakEvent
import org.bukkit.event.block.BlockPlaceEvent import org.bukkit.event.block.BlockPlaceEvent
import org.bukkit.event.block.BlockSpreadEvent
import org.bukkit.event.block.LeavesDecayEvent import org.bukkit.event.block.LeavesDecayEvent
import org.bukkit.event.enchantment.EnchantItemEvent import org.bukkit.event.enchantment.EnchantItemEvent
import org.bukkit.event.entity.EntityDamageByEntityEvent import org.bukkit.event.entity.EntityDamageByEntityEvent
import org.bukkit.event.entity.EntityDamageEvent
import org.bukkit.event.entity.FoodLevelChangeEvent import org.bukkit.event.entity.FoodLevelChangeEvent
import org.bukkit.event.entity.PlayerDeathEvent
import org.bukkit.event.inventory.ClickType import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.CraftItemEvent import org.bukkit.event.inventory.CraftItemEvent
import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.inventory.InventoryOpenEvent import org.bukkit.event.inventory.InventoryOpenEvent
import org.bukkit.event.inventory.InventoryType 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.PlayerDropItemEvent
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.event.player.PlayerItemDamageEvent import org.bukkit.event.player.PlayerItemDamageEvent
import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerQuitEvent import org.bukkit.event.player.PlayerQuitEvent
import org.bukkit.inventory.EnchantingInventory import org.bukkit.inventory.EnchantingInventory
import org.bukkit.inventory.EquipmentSlot
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable import org.bukkit.inventory.meta.Damageable
import java.util.Random import java.util.Random
import kotlin.math.min
class GameStateListener( class GameStateListener : Listener {
private val plugin: SpeedHG,
private val gameManager: GameManager private val plugin = SpeedHG.instance
) : Listener { private val gameManager = plugin.gameManager
@EventHandler @EventHandler
fun onLeavesDecay( fun onLeavesDecay(
event: LeavesDecayEvent event: LeavesDecayEvent
) { ) {
if ( gameManager.isRunning() ) if ( gameManager.currentState == GameState.INVINCIBILITY ||
gameManager.currentState == GameState.INGAME )
return return
event.isCancelled = true event.isCancelled = true
@@ -68,40 +53,29 @@ class GameStateListener(
) { ) {
val player = event.player val player = event.player
if ( gameManager.getCurrentStateType() == GameStateTypes.FEAST ) if ( gameManager.currentState == GameState.INVINCIBILITY ||
{ gameManager.currentState == GameState.INGAME )
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() )
return return
event.isCancelled = true 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( private val beforeInvisMaterials = mapOf(
Material.OAK_LOG, Material.OAK_LOG to Sound.BLOCK_WOOD_BREAK,
Material.DARK_OAK_LOG, Material.DARK_OAK_LOG to Sound.BLOCK_WOOD_BREAK,
Material.BIRCH_LOG, Material.BIRCH_LOG to Sound.BLOCK_WOOD_BREAK,
Material.ACACIA_LOG, Material.ACACIA_LOG to Sound.BLOCK_WOOD_BREAK,
Material.JUNGLE_LOG, Material.JUNGLE_LOG to Sound.BLOCK_WOOD_BREAK,
Material.SPRUCE_LOG, Material.SPRUCE_LOG to Sound.BLOCK_WOOD_BREAK,
Material.STONE Material.STONE to Sound.BLOCK_STONE_BREAK
) )
private val alwaysMaterials = setOf( private val alwaysMaterials = mapOf(
Material.RED_MUSHROOM, Material.RED_MUSHROOM to Sound.BLOCK_GRASS_BREAK,
Material.BROWN_MUSHROOM, Material.BROWN_MUSHROOM to Sound.BLOCK_GRASS_BREAK,
Material.COCOA_BEANS, Material.COCOA_BEANS to Sound.BLOCK_WOOD_BREAK,
Material.CACTUS Material.CACTUS to Sound.BLOCK_WOOL_BREAK
) )
@EventHandler @EventHandler
@@ -110,35 +84,26 @@ class GameStateListener(
) { ) {
val player = event.player val player = event.player
if ( !gameManager.isRunning() ) if ( gameManager.currentState != GameState.INVINCIBILITY &&
gameManager.currentState != GameState.INGAME )
{ {
event.isCancelled = true 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 return
} }
val block = event.block 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() )) pickupBlock( event, player )
return
event.isCancelled = true
player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f )
return return
} }
if ( gameManager.getCurrentStateType() == GameStateTypes.IMMUNITY && if (alwaysMaterials.containsKey( block.type ))
beforeInvisMaterials.contains( block.type ))
{ {
pickup( event, player ) pickupBlock( event, player )
return
}
if (alwaysMaterials.contains( block.type ))
{
pickup( event, player )
return return
} }
@@ -148,46 +113,51 @@ class GameStateListener(
event.block.type = Material.AIR event.block.type = Material.AIR
event.block.tick() event.block.tick()
plugin.chatManager.sendMessage( player, "build.no_diamonds" ) player.sendMsg( "build.no_diamonds" )
player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) player.playSound( player.location, Sound.ENTITY_VILLAGER_NO, 1f, 1f )
return 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 event.isCancelled = true
plugin.chatManager.sendMessage( player, "build.no_iron_before_feast" ) player.sendMsg( "build.no_iron_before_feast" )
player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) 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, event: BlockBreakEvent,
player: Player player: Player
) { ) {
val block = event.block
event.isCancelled = true event.isCancelled = true
val sound = if (beforeInvisMaterials.containsKey( block.type ))
beforeInvisMaterials[ block.type ]!!
else alwaysMaterials[ block.type ]!!
if (!hasInventorySpace( player )) if (!hasInventorySpace( player ))
{ {
event.block.drops.forEach { player.world.dropItem( event.block.location, it ) } block.drops.forEach { player.world.dropItem( block.location, it ) }
event.block.type = Material.AIR player.playSound( player.location, sound, 1f, 1f )
block.type = Material.AIR
return return
} }
event.block.drops.forEach { player.inventory.addItem( it ) } block.drops.forEach { player.inventory.addItem( it ) }
event.block.type = Material.AIR player.playSound( player.location, sound, 1f, 1f )
block.type = Material.AIR
} }
private fun hasInventorySpace( private fun hasInventorySpace( player: Player ): Boolean
player: Player = player.inventory.any { it == null || it.type == Material.AIR }
): Boolean
{
return player.inventory.any { it == null || it.type == Material.AIR }
}
@EventHandler @EventHandler
fun onDropItem( fun onDropItem(
@@ -195,75 +165,15 @@ class GameStateListener(
) { ) {
val player = event.player val player = event.player
if ( !gameManager.isRunning() ) if ( gameManager.currentState != GameState.INVINCIBILITY &&
gameManager.currentState != GameState.INGAME )
{ {
event.isCancelled = true 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 return
} }
val kit = plugin.kitManager.getSelectedKit( player ) // TODO: add kit item check
?: 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( "<direction>" ).replacement( direction ).once() }
plugin.chatManager.getAudience( player ).sendActionBar( actionBar )
} }
private val swordNerf = 0.5 private val swordNerf = 0.5
@@ -277,11 +187,7 @@ class GameStateListener(
fun onDamageEntity( fun onDamageEntity(
event: EntityDamageByEntityEvent event: EntityDamageByEntityEvent
) { ) {
val damager = event.damager val damager = event.damager as? Player ?: return
if ( damager !is Player )
return
val itemName = damager.inventory.itemInMainHand.type.name val itemName = damager.inventory.itemInMainHand.type.name
if (itemName.endsWith( "_SWORD" )) if (itemName.endsWith( "_SWORD" ))
@@ -312,26 +218,16 @@ class GameStateListener(
} }
@EventHandler @EventHandler
fun onJoin( fun onJoin( event: PlayerJoinEvent ) { disableHitCooldown( event.player ) }
event: PlayerJoinEvent
) {
disableCooldown( event.player )
}
@EventHandler @EventHandler
fun onQuit( fun onQuit( event: PlayerQuitEvent ) { disableHitCooldown( event.player ) }
event: PlayerQuitEvent
) {
disableCooldown( event.player )
}
private fun disableCooldown( private fun disableHitCooldown(
player: Player player: Player
) { ) {
val attackSpeed = player.getAttribute( Attribute.GENERIC_ATTACK_SPEED ) 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 private val lapisLazuli = Material.LAPIS_LAZULI
@@ -354,10 +250,9 @@ class GameStateListener(
val item = event.currentItem val item = event.currentItem
?: return ?: return
// prevent taking it out
if ( item.type == lapisLazuli && event.rawSlot == 1 ) if ( item.type == lapisLazuli && event.rawSlot == 1 )
event.isCancelled = true 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 event.isCancelled = true
} }
@@ -385,52 +280,12 @@ class GameStateListener(
( inventory as EnchantingInventory ).secondary = ItemStack( lapisLazuli, 64 ) ( 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 @EventHandler
fun onFoodLevelChange( fun onFoodLevelChange(
event: FoodLevelChangeEvent event: FoodLevelChangeEvent
) { ) {
if ( gameManager.isRunning() ) if ( gameManager.currentState == GameState.INVINCIBILITY ||
gameManager.currentState == GameState.INGAME )
return return
event.foodLevel = 20 event.foodLevel = 20
@@ -441,244 +296,40 @@ class GameStateListener(
fun onCraftItem( fun onCraftItem(
event: CraftItemEvent event: CraftItemEvent
) { ) {
if ( !gameManager.isRunning() ) if ( gameManager.currentState == GameState.INVINCIBILITY ||
return gameManager.currentState == GameState.INGAME )
val player = event.whoClicked
if ( player !is Player )
return return
val player = event.whoClicked as? Player ?: return
val item = event.recipe.result 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 ( 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 event.result = Event.Result.DENY
plugin.chatManager.sendMessage( player, "craft.no_shield" ) player.sendMsg( "craft.no_shield" )
player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f ) player.playSound( player.location, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f )
return return
} }
if (!item.type.name.contains( "iron", true )) if (!item.type.name.contains( "iron", true ))
return return
if ( gameManager.isBeforeFeast() ) // TODO: add before feast check and deny item crafting
{
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
}
if ( item.type.maxDurability > 0 ) if ( item.type.maxDurability > 0 )
{ {
item.editMeta { meta -> ( meta as Damageable ).damage /= 2 } 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 ) } // TODO: add 0.1 to ironFarmed in database
}
@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" ))
} }
} }

View File

@@ -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 )
}
}

View File

@@ -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 ))
}
}
}

View File

@@ -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<Material>()
for ( recraftMaterial in recraftMaterials )
if ( recraftMaterial.getLowestMaterial() != null )
lowestMaterials.add( recraftMaterial.getLowestMaterial()!! )
var highestMaterial: Material? = null
var i = 0f
for ( lowestMaterial in lowestMaterials )
{
val recraftMaterial = byMaterial( lowestMaterial )
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
}
}

View File

@@ -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 )
}
}

View File

@@ -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<Material>
): ConcurrentHashMap<Material, Int>() {
fun getPoints(): Float
{
return getOrDefault( getLowestMaterial(), 0 ).toFloat()
}
fun decrease(
material: Material,
amount: Int,
) {
put( material, get( material )!! - amount )
}
fun getLowestMaterial(): Material?
{
if ( size > 1 )
{
if (values.stream().anyMatch { int -> int == 0 })
return null
val materialIntegerEntry = entries.stream().min(Comparator.comparingInt { it.value })
return materialIntegerEntry.map { it.key }.orElse( null )
}
else return keys.stream().findFirst().orElse( null )
}
fun getMaterialValue() = ( maxSoupAmount / size ).toFloat()
fun reset()
{
replaceAll { _, _ -> 0 }
}
}

View File

@@ -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 )
}
}

View File

@@ -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<UUID, FastBoard>()
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<Component>
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 )
}
}

View File

@@ -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<Player>().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<in Player>
): MutableCollection<Player>
{
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
}
}

View File

@@ -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 ) }
}
}

View File

@@ -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<String, String>
) {
val component = langManager.getComponent( this, key, placeholders.toMap() )
this.sendMessage( component )
}
fun Player.trans(
key: String,
vararg placeholders: Pair<String, String>
): Component
{
return langManager.getComponent( this, key, placeholders.toMap() )
}
fun Player.transList(
key: String,
placeholders: Map<String, String>
): List<Component>
{
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() )

View File

@@ -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 )
}
}

View File

@@ -1,73 +0,0 @@
package club.mcscrims.speedhg.util
import java.util.*
class RandomCollection<E> {
private val map = TreeMap<Double, List<E>>()
private val names = hashMapOf<List<E>, 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<E>
) {
if ( weight <= 0 ) return
total += weight
map[ total ] = result
}
fun add(
name: String,
weight: Double,
result: List<E>
) {
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<E>
): String
{
return names.getOrDefault( key, "" )
}
fun getRandom(): List<E>
{
val value = random.nextDouble() * total
return map.higherEntry( value ).value
}
}

View File

@@ -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 )
}
}

View File

@@ -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
}
}

View File

@@ -1,94 +1,21 @@
# #
# |-----| |-----| |-----| |-----| |---\ | | |-----| # SpeedHG v1.0.0 by McScrims-Network
# | | | | | | \ | | | # https://github.com/McScrims-Network
# |-----| |-----| |---| |---| | | |-----| | |--|
# | | | | | / | | | |
# |-----| | |-----| |-----| |---/ | | |-----|
# 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)
#
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: game:
name: 'SpeedHG' min-players: 2
variantName: 'Solo - Single Kit' lobby-time: 60
invincibility-time: 60
border-start: 300.0
border-end: 20.0
border-shrink-time: 600 # 10 Minuten
minimumPlayers: 8 anti-runner:
enabled: true
competitiveGame: false check-radius: 20.0
competitiveCommands: warn-time: 15
- 'server' punish-time: 25
- 'servers' # Einstellungen für die Cave-Erkennung
- 'hub' ignore-vertical-distance: 15.0 # Wenn Höhenunterschied > 15, Timer ignorieren
- 'lobby' ignore-cave-surface-mix: true # Ignorieren, wenn einer Sonne hat und der andere nicht
- '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

View File

@@ -1,13 +0,0 @@
{
"connectionString": "",
"host": "localhost",
"port": 27017,
"database": "minecraft",
"username": "root",
"password": "pass",
"authDatabase": "admin",
"ssl": false,
"replicaSet": "",
"connectionTimeout": 30,
"poolSize": 10
}

View File

@@ -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

View File

@@ -0,0 +1,34 @@
#
# SpeedHG v1.0.0 by McScrims-Network
# https://github.com/McScrims-Network
#
default:
prefix: '<gradient:dark_green:gold>McScrims</gradient> <dark_gray>|</dark_gray> <reset>'
game:
join: '<prefix><green><name></green> <gray>has joined the game.</gray>'
quit: '<prefix><red><name></red> <gray>has quit the game.</gray>'
player:
welcome: 'Welcome to <gradient:red:gold>SpeedHG</gradient>!'
scoreboard:
title: '<gradient:gold:yellow><bold>SpeedHG</bold></gradient>'
lobby:
- "<gray><st> "
- "Spieler: <green><online>/<max>"
- "Kit: <yellow><kit>"
- ""
- "<gray>Waiting for start..."
- ""
- "<yellow>play.mcscrims.club"
ingame:
- "<gray><st> "
- "Time: <green><timer>"
- "Players: <red><alive>"
- "Kills: <green><kills>"
- ""
- "Border: <red><border>"
- ""
- "<yellow>play.mcscrims.club"

View File

@@ -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: '<gradient:dark_green:green>McScrims</gradient> <dark_gray>┃</dark_gray><reset>'
no_permission: '%prefix% <red>You don''t have permission to do that!</red>'
player_not_found: '%prefix% <red>This player could not be found!</red>'
command_cooldown: '%prefix% <red>Please wait {time} seconds before using this command again!</red>'
commands:
unknown: |-
%prefix% <red>Unknown subcommand: {unknown}</red>
%prefix% <gray>Use /{command} for an overview.</gray>

View File

@@ -6,9 +6,4 @@ api-version: '1.21'
depend: depend:
- "WorldEdit" - "WorldEdit"
- "Apollo-Bukkit" - "Apollo-Bukkit"
- "McScrims-CoreSystem" - "McScrims-CoreSystem"
commands:
kits:
description: Open the kit selection menu
usage: /kits