Update kit management & anchor kit

This commit is contained in:
TDSTOS
2025-12-09 01:01:00 +01:00
parent a3b521940c
commit 728b7534ac
6 changed files with 395 additions and 121 deletions

View File

@@ -3,60 +3,82 @@ 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
abstract class AbstractKit(
val id: String,
val displayName: String,
val displayName: Component,
val description: List<String>,
val icon: Material,
val playStyle: PlayStyle,
protected val plugin: SpeedHG,
protected val abilityContext: AbilityContext
protected val abilityContext: AbilityContext,
protected val gameManager: GameManager
) {
lateinit var config: Map<String, Double>
open fun onSelect(player: Player) {
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 )
}
open fun onStart(player: Player) {
protected fun hasCooldown(
player: Player,
key: String
): Boolean
{
return abilityContext.getRemainingCooldown( player, key ) > 0
}
open fun onHit(attacker: Player, victim: Player, event: EntityDamageByEntityEvent) {
protected fun startCooldown(
player: Player,
key: String,
seconds: Int
) {
abilityContext.cooldownManager.startCooldown( player, key, seconds )
}
open fun onInteract(player: Player, event: PlayerInteractEvent) {
protected fun getRemainingCooldown(
player: Player,
key: String
): Double
{
return abilityContext.getRemainingCooldown( player, key )
}
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 incrementHits(player: Player, key: String): Int {
return abilityContext.incrementHit(player, key)
}
protected fun getHits(player: Player, key: String): Int {
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 resetHits(
player: Player,
key: String
) {
abilityContext.hitCounterManager.resetHits( player, key )
}
protected fun abilityResult(
@@ -65,6 +87,28 @@ abstract class AbstractKit(
requiredHits: Int? = null,
cooldownSeconds: Int? = null
): AbilityResult {
return abilityContext.canUseAbility(player, abilityKey, requiredHits, cooldownSeconds)
return abilityContext.canUseAbility( player, abilityKey, requiredHits, cooldownSeconds )
}
}
enum class PlayStyle {
OFFENSIVE, DEFENSIVE, NULL
}
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,29 +1,75 @@
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 kitManager: KitManager) : Listener {
class KitListener(
private val plugin: SpeedHG,
private val kitManager: KitManager
) : Listener {
@EventHandler
fun onEntityDamage(event: EntityDamageByEntityEvent) {
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( attacker ) != null )
kitManager.triggerHit( attacker, victim, event )
if (kitManager.getSelectedKit( victim ) != null )
kitManager.triggerDamaged( victim, attacker, event )
}
@EventHandler
fun onPlayerInteract(event: PlayerInteractEvent) {
fun onPlayerInteract(
event: PlayerInteractEvent
) {
val player = event.player
if (kitManager.getSelectedKit(player) != null) {
kitManager.triggerInteract(player, event)
}
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,41 +1,73 @@
package club.mcscrims.speedhg.kit
import club.mcscrims.speedhg.SpeedHG
import club.mcscrims.speedhg.kit.impl.AnchorKit
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) {
class KitManager(
private val plugin: SpeedHG
) {
private val kits = ConcurrentHashMap<String, AbstractKit>()
private val selectedKits = ConcurrentHashMap<UUID, AbstractKit>()
fun registerKit(kit: AbstractKit) {
kit.config = plugin.kitConfig.data.getConfigForKit(kit.id)
fun initialize()
{
registerKit(
kitClass = AnchorKit::class.java as Class<AbstractKit>,
id = "anchor",
displayName = plugin.chatFormatter.format( "kits.anchor.displayName" ),
description = emptyList(),
icon = Material.ANVIL
)
}
fun registerKit(
kitClass: Class<AbstractKit>,
id: String,
displayName: Component,
description: List<String>,
icon: Material
) {
val kit = kitClass.getDeclaredConstructor().newInstance( id, displayName, description, icon, PlayStyle.NULL, 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? {
fun getKit(
id: String
): AbstractKit?
{
return kits[id.lowercase()]
}
fun getAllKits(): Collection<AbstractKit> {
fun getAllKits(): Collection<AbstractKit>
{
return kits.values
}
fun selectKit(player: Player, kitId: String): Boolean {
val kit = getKit(kitId) ?: return false
fun selectKit(
player: Player,
kitId: String
): Boolean
{
val kit = getKit( kitId ) ?: return false
val previousKit = selectedKits[player.uniqueId]
previousKit?.cleanup(player)
previousKit?.cleanup( player )
selectedKits[player.uniqueId] = kit
try {
kit.onSelect(player)
kit.onSelect( player )
} catch (e: Exception) {
plugin.logger.severe("Error during onSelect for kit ${kit.id} and player ${player.name}: ${e.message}")
e.printStackTrace()
@@ -44,56 +76,99 @@ class KitManager(private val plugin: SpeedHG) {
return true
}
fun getSelectedKit(player: Player): AbstractKit? {
fun getSelectedKit(
player: Player
): AbstractKit?
{
return selectedKits[player.uniqueId]
}
fun startKitForPlayer(player: Player) {
fun startKitForPlayer(
player: Player
) {
val kit = selectedKits[player.uniqueId] ?: return
try {
kit.onStart(player)
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) {
fun triggerHit(
attacker: Player,
victim: Player,
event: EntityDamageByEntityEvent
) {
val kit = selectedKits[attacker.uniqueId] ?: return
try {
kit.onHit(attacker, victim, event)
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 triggerInteract(player: Player, event: PlayerInteractEvent) {
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)
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 clearPlayerSelection(player: Player) {
val kit = selectedKits.remove(player.uniqueId)
kit?.cleanup(player)
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 clearAll() {
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)
}
if ( selectedKits[player.uniqueId] == kit ) kit.cleanup( player )
}
}
selectedKits.clear()
}
}

View File

@@ -1,85 +1,167 @@
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.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
import org.bukkit.metadata.FixedMetadataValue
class AnchorKit(
id: String,
displayName: Component,
description: List<String>,
icon: Material,
playStyle: PlayStyle,
plugin: SpeedHG,
abilityContext: AbilityContext
) : AbstractKit(
id = "anchor",
displayName = "Anchor",
description = listOf(
"§7Heavy and strong.",
"§7Deal extra damage after 3 hits.",
"§7Gain resistance when standing still."
),
icon = Material.ANVIL,
plugin = plugin,
abilityContext = abilityContext
) {
abilityContext: AbilityContext,
gameManager: GameManager
) : AbstractKit( id, displayName, description, icon, playStyle, plugin, abilityContext, gameManager ) {
private val abilityKey = "anchor_ability"
private val hitCounterKey = "anchor_hits"
private lateinit var anvilItem: ItemStack
override fun onSelect(player: Player) {
player.sendMessage("§aYou selected §6Anchor§a!")
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
override fun onSelect( player: Player ) {}
override fun onStart(
player: Player
) {
anvilItem = ItemBuilder( plugin, Material.ANVIL )
.name(plugin.chatFormatter.format( "kits.anchor.items.anvil.${playStyle.name.lowercase()}" ).content())
.unbreakable( true )
.hideAttributes()
.build()
player.inventory.setItem( 0, anvilItem )
}
override fun onStart(player: Player) {
player.inventory.addItem(ItemStack(Material.STONE_SWORD))
player.inventory.helmet = ItemStack(Material.IRON_HELMET)
player.inventory.chestplate = ItemStack(Material.IRON_CHESTPLATE)
player.inventory.leggings = ItemStack(Material.IRON_LEGGINGS)
player.inventory.boots = ItemStack(Material.IRON_BOOTS)
override fun onHit(
attacker: Player,
victim: Player,
event: EntityDamageByEntityEvent
) {
if ( !gameManager.isRunning() )
return
if ( !anvilPlaced )
return
if ( playStyle != PlayStyle.OFFENSIVE )
return
event.damage += extraDamage
}
override fun onHit(attacker: Player, victim: Player, event: EntityDamageByEntityEvent) {
val currentHits = incrementHits(attacker, hitCounterKey)
override fun onDamaged(
attacker: Player,
victim: Player,
event: EntityDamageByEntityEvent
) {
if ( !gameManager.isRunning() )
return
val result = abilityResult(
player = attacker,
abilityKey = abilityKey,
requiredHits = 3,
cooldownSeconds = 10
)
if ( !anvilPlaced )
return
if (result.success) {
val extraDamage = config["offensive extra damage"] ?: 1.0
event.damage += extraDamage
victim.velocity.setX( 0.0 )
victim.velocity.setZ( 0.0 )
victim.world.playSound( victim.location, Sound.BLOCK_ANVIL_HIT, 3f, 3f )
}
attacker.sendMessage("§6⚓ Anchor Ability! §eDealt ${extraDamage} extra damage!")
victim.sendMessage("§c${attacker.name} used Anchor ability!")
} else if (result.missingHits > 0) {
attacker.sendMessage("§e⚓ Hits: $currentHits/3")
} else if (result.remainingCooldown > 0) {
attacker.sendMessage("§c⚓ Cooldown: ${String.format("%.1f", result.remainingCooldown)}s")
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
}
}
override fun onInteract(player: Player, event: PlayerInteractEvent) {
if (event.action.isRightClick && event.item?.type == Material.ANVIL) {
if (!hasCooldown(player, "anchor_resistance")) {
player.addPotionEffect(PotionEffect(PotionEffectType.RESISTANCE, 100, 0))
player.sendMessage("§6⚓ Resistance activated!")
startCooldown(player, "anchor_resistance", 15)
} else {
val remaining = getRemainingCooldown(player, "anchor_resistance")
player.sendMessage("§c⚓ Resistance on cooldown: ${String.format("%.1f", remaining)}s")
if ( anvilPlaced )
{
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
}
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
plugin.schedulerManager.runLater( 20 * 30L ) {
if ( anvilPlaced )
{
anvilPlaced = false
anvilLoc.block.type = Material.AIR
anvilLoc.block.removeMetadata( KitMetaData.IS_ANVIL.getKey(), plugin )
}
}
}
override fun cleanup(player: Player) {
super.cleanup(player)
player.sendMessage("§7Anchor kit cleaned up.")
override fun onMove(
player: Player,
event: PlayerMoveEvent
) {
if ( !gameManager.isRunning() )
return
if ( !anvilPlaced )
return
if (player.location.distance( anvilLoc ) <= radius )
return
player.teleport( player.location )
player.playSound( player, Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f )
}
}
}