diff --git a/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt b/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt index 6d3833b..8e99497 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/game/GameManager.kt @@ -110,6 +110,13 @@ class GameManager( !( currentState as BattleState ).afterFeast ) } + fun isBefore( + stateType: GameStateTypes + ): Boolean + { + return getCurrentStateType()?.points!! < stateType.points + } + fun shutdown() { currentState?.onExit( null ) @@ -118,6 +125,14 @@ class GameManager( } -enum class GameStateTypes { - WAITING, PRE_START, IMMUNITY, BATTLE, FEAST, DEATHMATCH, END +enum class GameStateTypes( + val points: Int +) { + WAITING( 0 ), + PRE_START( 1 ), + IMMUNITY( 2 ), + BATTLE( 3 ), + FEAST( 4 ), + DEATHMATCH( 5 ), + END( 6 ) } diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/KitManager.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/KitManager.kt index 6c1af6b..da98a35 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/KitManager.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/kit/KitManager.kt @@ -4,6 +4,7 @@ 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 net.kyori.adventure.text.Component import org.bukkit.Material import org.bukkit.entity.Player @@ -29,6 +30,14 @@ class KitManager( description = emptyList(), icon = Material.ANVIL ) + + registerKit( + kitClass = ArmorerKit::class.java, + id = "armorer", + displayName = plugin.chatFormatter.format( "kits.armorer.displayName" ), + description = emptyList(), + icon = Material.IRON_CHESTPLATE + ) } fun registerKit( diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/AnchorKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/AnchorKit.kt index 1a597f5..30e4c7c 100644 --- a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/AnchorKit.kt +++ b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/AnchorKit.kt @@ -19,6 +19,8 @@ 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, @@ -33,12 +35,10 @@ class AnchorKit( private lateinit var anvilItem: ItemStack - private var anvilPlaced: Boolean = false - private val extraDamage: Double = plugin.kitConfig.data.anchor[ "offensive extra damage" ]!! private val radius = if ( playStyle == PlayStyle.DEFENSIVE ) 7.5 else 5.0 - private lateinit var anvilLoc: Location + private val anvilList = ConcurrentHashMap() override fun onSelect( player: Player ) {} @@ -62,7 +62,7 @@ class AnchorKit( if ( !gameManager.isRunning() ) return - if ( !anvilPlaced ) + if (!anvilList.contains( attacker.uniqueId )) return if ( playStyle != PlayStyle.OFFENSIVE ) @@ -79,7 +79,7 @@ class AnchorKit( if ( !gameManager.isRunning() ) return - if ( !anvilPlaced ) + if (!anvilList.contains( attacker.uniqueId )) return victim.velocity.setX( 0.0 ) @@ -115,7 +115,7 @@ class AnchorKit( return } - if ( anvilPlaced ) + if (anvilList.contains( player.uniqueId )) { plugin.chatManager.sendMessage( player, "kits.anchor.messages.alreadyActivated" ) return @@ -129,18 +129,18 @@ class AnchorKit( return } - anvilLoc = eyeLocation.toBlockLocation() + 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 )) - anvilPlaced = true + anvilList[ player.uniqueId ]= anvilLoc plugin.schedulerManager.runLater( 20 * 30L ) { - if ( anvilPlaced ) + if (anvilList.contains( player.uniqueId )) { - anvilPlaced = false + anvilList.remove( player.uniqueId ) anvilLoc.block.type = Material.AIR anvilLoc.block.removeMetadata( KitMetaData.IS_ANVIL.getKey(), plugin ) } @@ -154,8 +154,8 @@ class AnchorKit( if ( !gameManager.isRunning() ) return - if ( !anvilPlaced ) - return + val anvilLoc = anvilList[ player.uniqueId ] + ?: return if (player.location.distance( anvilLoc ) <= radius ) return diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/ArmorerKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/ArmorerKit.kt new file mode 100644 index 0000000..91a2231 --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/ArmorerKit.kt @@ -0,0 +1,114 @@ +package club.mcscrims.speedhg.kit.impl + +import club.mcscrims.speedhg.SpeedHG +import club.mcscrims.speedhg.ability.AbilityContext +import club.mcscrims.speedhg.game.GameManager +import club.mcscrims.speedhg.kit.AbstractKit +import club.mcscrims.speedhg.kit.PlayStyle +import net.kyori.adventure.text.Component +import org.bukkit.Material +import org.bukkit.Sound +import org.bukkit.Statistic +import org.bukkit.enchantments.Enchantment +import org.bukkit.entity.Player +import org.bukkit.event.entity.EntityDamageByEntityEvent +import org.bukkit.event.player.PlayerInteractEvent +import org.bukkit.event.player.PlayerMoveEvent +import org.bukkit.inventory.ItemStack +import kotlin.math.roundToInt + +class ArmorerKit( + id: String, + displayName: Component, + description: List, + icon: Material, + playStyle: PlayStyle, + plugin: SpeedHG, + abilityContext: AbilityContext, + gameManager: GameManager +) : AbstractKit( id, displayName, description, icon, playStyle, plugin, abilityContext, gameManager ) { + + private val killsUntilNew: Double = plugin.kitConfig.data.armorer[ "kills until new armor" ]!! + + override fun onSelect( player: Player ) {} + + override fun onStart( player: Player ) {} + + override fun onHit( + attacker: Player, + victim: Player, + event: EntityDamageByEntityEvent + ) { + if ( !gameManager.isRunning() ) + return + + if ( victim.health > 0.0 ) + return + + val kills = attacker.getStatistic( Statistic.PLAYER_KILLS ) + + if (( kills.toDouble() / killsUntilNew ) % 2 != 0.0 ) + return + + upgradeArmor( attacker, kills ) + attacker.playSound( attacker, Sound.BLOCK_NOTE_BLOCK_PLING, 1f, 1f ) + } + + override fun onDamaged( attacker: Player, victim: Player, event: EntityDamageByEntityEvent ) {} + + override fun onInteract( player: Player, event: PlayerInteractEvent ) {} + + override fun onMove( player: Player, event: PlayerMoveEvent ) {} + + private fun upgradeArmor( + player: Player, + killCount: Int + ) { + val kills = killsUntilNew.roundToInt() + + val armorType = when( killCount / kills ) + { + 1, 2 -> ArmorType.LEATHER + 3, 4 -> ArmorType.CHAINMAIL + 5, 6 -> ArmorType.GOLD + 7, 8 -> ArmorType.IRON + else -> return + } + + val enchanted = ( killCount / kills ) % 2 == 0 + + val armor = createArmor( armorType, enchanted ) + player.inventory.armorContents = arrayOf( null, armor[0], null, armor[1] ) + + if ( !enchanted ) + { + plugin.chatManager.sendMessage( player, "kits.armorer.messages.upgrade.normal", "{armorType}" to armorType.name ) + return + } + + plugin.chatManager.sendMessage( player, "kits.armorer.upgrade.enchanted" ) + } + + private fun createArmor( + type: ArmorType, + enchanted: Boolean + ) = listOf( + ItemStack( type.materialChestplate ).apply { + if ( enchanted ) addEnchantment( Enchantment.PROTECTION, 1 ) + }, + ItemStack( type.materialBoots ).apply { + if ( enchanted ) addEnchantment( Enchantment.PROTECTION, 1 ) + } + ) + + enum class ArmorType( + val materialChestplate: Material, + val materialBoots: Material + ) { + LEATHER( Material.LEATHER_CHESTPLATE, Material.LEATHER_BOOTS ), + CHAINMAIL( Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_BOOTS ), + GOLD( Material.GOLDEN_CHESTPLATE, Material.GOLDEN_BOOTS ), + IRON( Material.IRON_CHESTPLATE, Material.IRON_BOOTS ) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlackPantherKit.kt b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlackPantherKit.kt new file mode 100644 index 0000000..802b4d5 --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/kit/impl/BlackPantherKit.kt @@ -0,0 +1,165 @@ +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.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.metadata.FixedMetadataValue + +class BlackPantherKit( + id: String, + displayName: Component, + description: List, + icon: Material, + playStyle: PlayStyle, + plugin: SpeedHG, + abilityContext: AbilityContext, + gameManager: GameManager +) : AbstractKit( id, displayName, description, icon, playStyle, plugin, abilityContext, gameManager ) { + + private lateinit var blackDye: ItemStack + private lateinit var blazePowder: ItemStack + + private val extraDamageAddition = plugin.kitConfig.data.blackPanther[ "extra damage on top" ]!! + private val defaultRadius = plugin.kitConfig.data.blackPanther[ "default hit radius" ]!! + private val explosionMultiplier = plugin.kitConfig.data.blackPanther[ "explosion multiplier" ]!! + + override fun onSelect( player: Player ) {} + + override fun onStart( + player: Player + ) { + when( playStyle ) + { + PlayStyle.DEFENSIVE -> + { + blackDye = ItemBuilder( plugin, Material.BLACK_DYE ) + .name(plugin.chatFormatter.format( "kits.anchor.items.blackDye.${playStyle.name.lowercase()}" ).content()) + .unbreakable( true ) + .hideAttributes() + .build() + } + + PlayStyle.OFFENSIVE -> + { + blackDye = ItemBuilder( plugin, Material.BLACK_DYE ) + .name(plugin.chatFormatter.format( "kits.anchor.items.blackDye.${playStyle.name.lowercase()}" ).content()) + .unbreakable( true ) + .hideAttributes() + .build() + + blazePowder = ItemBuilder( plugin, Material.BLAZE_POWDER ) + .name(plugin.chatFormatter.format( "kits.anchor.items.blazePowder" ).content()) + .unbreakable( true ) + .hideAttributes() + .build() + } + + else -> {} + } + } + + 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 ) + { + + return + } + + + } + + 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 extraDamage( + player: Player + ) { + player.setMetadata( KitMetaData.BP_EXTRA_DAMAGE.getKey(), FixedMetadataValue( plugin, true )) + plugin.chatManager.sendMessage( player, "kits.blackPanther.messages.extraDamage.activated" ) + + plugin.schedulerManager.runLater( 12 * 30L ) { + + player.removeMetadata( KitMetaData.BP_EXTRA_DAMAGE.getKey(), plugin ) + plugin.chatManager.sendMessage( player, "kits.blackPanther.messages.extraDamage.deactivated" ) + + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/recraft/Recraft.kt b/src/main/kotlin/club/mcscrims/speedhg/recraft/Recraft.kt new file mode 100644 index 0000000..ea01188 --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/recraft/Recraft.kt @@ -0,0 +1,93 @@ +package club.mcscrims.speedhg.recraft + +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack + + +class Recraft { + + private val recraftMaterials = listOf( + RecraftMaterial( 1, arrayOf( Material.RED_MUSHROOM, Material.BROWN_MUSHROOM )), + RecraftMaterial( 1, arrayOf( Material.COCOA_BEANS )), + RecraftMaterial( 1, arrayOf( Material.CACTUS )) + ) + + fun calcRecraft( + vararg items: ItemStack? + ) { + recraftMaterials.forEach( RecraftMaterial::reset ) + + for ( item in items ) + { + if ( item == null ) + return + + for ( recraftMaterial in recraftMaterials ) + { + val type = item.type + + if (recraftMaterial.containsKey( type )) + recraftMaterial[ type ] = recraftMaterial.getOrDefault( type , 0 ) + item.amount + } + } + } + + fun decrease( + player: Player, + amount: Int + ) { + val lowestMaterials = mutableListOf() + + for ( recraftMaterial in recraftMaterials ) + if ( recraftMaterial.getLowestMaterial() != null ) + lowestMaterials.add( recraftMaterial.getLowestMaterial()!! ) + + var highestMaterial: Material? = null + var i = 0f + + for ( lowestMaterial in lowestMaterials ) + { + val recraftMaterial = byMaterial( lowestMaterial ) + + if (recraftMaterial!![ lowestMaterial ]!! * recraftMaterial.getMaterialValue() > i ) + { + i = recraftMaterial[ lowestMaterial ]!! * recraftMaterial.getMaterialValue() + highestMaterial = lowestMaterial + } + } + + val recraftMaterial = byMaterial( highestMaterial!! ) + recraftMaterial?.decrease( highestMaterial, amount ) + + for ( item in player.inventory.contents ) + { + if ( item == null ) + continue + + if ( item.type == highestMaterial ) + { + item.amount -= amount + break + } + } + } + + fun byMaterial( + material: Material + ): RecraftMaterial? + { + return recraftMaterials.stream().filter { it.containsKey( material ) }.findFirst().orElse( null ) + } + + fun getRecraftPoints(): Float + { + var points = 0f + + for ( recraftMaterial in recraftMaterials ) + points += recraftMaterial.getPoints() + + return points + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftInspector.kt b/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftInspector.kt new file mode 100644 index 0000000..c64fac3 --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftInspector.kt @@ -0,0 +1,52 @@ +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 Int + + fun startRunnable() + { + if ( !recraftNerfEnabled ) + return + + object : BukkitRunnable() { + + val gameStateType = GameStateTypes.valueOf( beforeState.uppercase() ) + + override fun run() + { + if (!plugin.gameManager.isBefore( gameStateType )) + { + this.cancel() + return + } + + Bukkit.getOnlinePlayers().stream() + .filter { !it.isDead } + .forEach { player -> + val recraft = Recraft() + recraft.calcRecraft( *player.inventory.contents ) + + if ( recraft.getRecraftPoints() > maxRecraftAmount ) + { + plugin.chatManager.sendMessage( player, "recraftNerf.too_much" ) + + while ( recraft.getRecraftPoints() > maxRecraftAmount ) + recraft.decrease( player, 1 ) + } + } + } + + }.runTaskTimer( plugin, 10L, 20L ) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftMaterial.kt b/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftMaterial.kt new file mode 100644 index 0000000..938f36c --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftMaterial.kt @@ -0,0 +1,43 @@ +package club.mcscrims.speedhg.recraft + +import org.bukkit.Material +import java.util.concurrent.ConcurrentHashMap + +class RecraftMaterial( + val maxSoupAmount: Int, + val materials: Array +): ConcurrentHashMap() { + + fun getPoints(): Float + { + return getOrDefault( getLowestMaterial(), 0 ).toFloat() + } + + fun decrease( + material: Material, + amount: Int, + ) { + put( material, get( material )!! - amount ) + } + + fun getLowestMaterial(): Material? + { + if ( size > 1 ) + { + if (values.stream().anyMatch { int -> int == 0 }) + return null + + val materialIntegerEntry = entries.stream().min(Comparator.comparingInt { it.value }) + return materialIntegerEntry.map { it.key }.orElse( null ) + } + else return keys.stream().findFirst().orElse( null ) + } + + fun getMaterialValue() = ( maxSoupAmount / size ).toFloat() + + fun reset() + { + replaceAll { _, _ -> 0 } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftUtils.kt b/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftUtils.kt new file mode 100644 index 0000000..2400353 --- /dev/null +++ b/src/main/kotlin/club/mcscrims/speedhg/recraft/RecraftUtils.kt @@ -0,0 +1,27 @@ +package club.mcscrims.speedhg.recraft + +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.ShapelessRecipe + +object RecraftUtils { + + fun registerRecipes() + { + val soup = ItemStack( Material.MUSHROOM_STEW ) + + val cocoRecipe = ShapelessRecipe(NamespacedKey.minecraft( "cocoa_soup" ), soup ) + cocoRecipe.addIngredient( Material.COCOA_BEANS ) + cocoRecipe.addIngredient( Material.BOWL ) + + val cactiRecipe = ShapelessRecipe(NamespacedKey.minecraft( "cacti_soup" ), soup ) + cactiRecipe.addIngredient( Material.CACTUS ) + cactiRecipe.addIngredient( Material.BOWL ) + + Bukkit.addRecipe( cocoRecipe ) + Bukkit.addRecipe( cactiRecipe ) + } + +} \ No newline at end of file